【用unity实现100个游戏之15】开发一个类保卫萝卜的Unity2D塔防游戏3(附项目源码)

news2024/11/27 20:32:18

文章目录

  • 先看本次实现的最终效果
  • 前言
  • 绘制炮塔UI
  • 炮塔转向敌人
  • 生成炮弹
  • 旋转我们的子弹
  • 对敌人造成伤害,回收子弹
  • 自动发射子弹
  • 添加攻击间隔
  • 显示伤害字体
  • 设计通用泛型单例
  • 创建更多炮塔
  • 升级增加伤害
  • 升级缩短攻击间隔
  • 添加货币
  • 杀死敌人获取金币
  • 源码
  • 完结

先看本次实现的最终效果

在这里插入图片描述

前言

本期紧接着上一篇,主要内容主要是实现不同炮塔、攻击行为逻辑和伤害飘字效果。

绘制炮塔UI

在这里插入图片描述

新增炮塔脚本,配置炮塔攻击范围

[RequireComponent(typeof(CircleCollider2D))]
public class Turret : MonoBehaviour
{
    [SerializeField] private float attackRange = 3f; // 视为攻击范围
    private bool _gameStarted; // 游戏是否已经开始

    private void Start()
    {
        _gameStarted = true; // 设置游戏为已开始状态
    }

    private void OnDrawGizmos()
    {
        if (!_gameStarted)
        {
            // 在Scene视图中绘制攻击范围的可视化表示
            GetComponent<CircleCollider2D>().radius = attackRange; // 设置CircleCollider2D的半径为攻击范围
            Gizmos.DrawWireSphere(transform.position, attackRange); // 在指定位置绘制表示攻击范围的线框球体
        }
    }
}

在这里插入图片描述

炮塔转向敌人

修改Turret

public class Turret : MonoBehaviour
{
    //...

    public Enemy CurrentEnemyTarget { get; set; }
    private List<Enemy> _enemies;

    private void Start()
    {
        _gameStarted = true; // 设置游戏为已开始状态
        _enemies = new List<Enemy>();
    }

    private void Update()
    {
        GetCurrentEnemyTarget();
        RotateTowardsTarget();
    }

    //获取当前敌人目标
    private void GetCurrentEnemyTarget()
    {
        if (_enemies.Count <= 0)
        {
            CurrentEnemyTarget = null;
            return;
        }
        CurrentEnemyTarget = _enemies[0];
    }

    //面向敌人
    private void RotateTowardsTarget()
    {
        if (CurrentEnemyTarget == null) return;
        Vector3 targetPos = CurrentEnemyTarget.transform.position - transform.position;
        float angle = Vector3.SignedAngle(transform.up, targetPos, transform.forward);
        transform.Rotate(0f, 0f, angle);
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Enemy"))
        {
            Enemy newEnemy = other.GetComponent<Enemy>();
            _enemies.Add(newEnemy);
        }
    }

    private void OnTriggerExit2D(Collider2D other)
    {
        if (other.CompareTag("Enemy"))
        {
            Enemy enemy = other.GetComponent<Enemy>();
            if (_enemies.Contains(enemy))
            {
                _enemies.Remove(enemy);
            }
        };
    }

	//...
}

记得修改敌人标签,添加刚体和碰撞体
在这里插入图片描述

效果,进入范围获取了目标,敌人超出范围,不再跟随
在这里插入图片描述

生成炮弹

新增Projectile,子弹控制脚本

public class Projectile : MonoBehaviour
{
    [SerializeField, Header("子弹移动速度")] 
    private float moveSpeed = 10f;
    
    private Enemy _enemyTarget; // 目标敌人

    private void Update()
    {
        if (_enemyTarget != null)
        {
            MoveProjectile();
        }
    }

    private void MoveProjectile()
    {
        // 将子弹向目标位置移动
        transform.position = Vector2.MoveTowards(transform.position,
            _enemyTarget.transform.position,
            moveSpeed * Time.deltaTime);
    }

    public void SetEnemy(Enemy enemy)
    {
        // 设置目标敌人
        _enemyTarget = enemy;
    }
}

制作子弹预制体,并挂载Projectile 脚本
在这里插入图片描述
新增TurretProjectile,控制子弹生成脚本

public class TurretProjectile : MonoBehaviour
{
    [SerializeField] private Transform projectileSpawnPosition; // 发射子弹的位置
    private ObjectPooler pooler; // 对象池管理器
    private Turret _turret;// 炮塔组件
    private Projectile _currentProjectileLoaded;// 当前装载的子弹

    private void Start()
    {
        pooler = GetComponent<ObjectPooler>(); // 获取对象池管理器组件
        _turret = GetComponent<Turret>();
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.G))
        {
            LoadProjectile(); // 测试按下G键时加载子弹
        }

         // 如果有敌人目标、已加载子弹且敌人目标的健康值大于0
        if (_turret.CurrentEnemyTarget != null && _currentProjectileLoaded != null && _turret.CurrentEnemyTarget._enemyHealth.CurrentHealth > 0f)
        {
            // 解除子弹的父节点关系
            _currentProjectileLoaded.transform.parent = null;
            // 设置子弹的目标敌人
            _currentProjectileLoaded.SetEnemy(_turret.CurrentEnemyTarget);
        }
    }

    private void LoadProjectile()
    {
        GameObject newInstance = pooler.GetInstanceFromPool(); // 从对象池获取一个新的子弹实例
        newInstance.transform.localPosition = projectileSpawnPosition.position; // 设置子弹的位置为发射位置
        newInstance.transform.SetParent(projectileSpawnPosition); // 设置子弹的父节点为发射位置
        _currentProjectileLoaded = newInstance.GetComponent<Projectile>();// 获取子弹组件
        newInstance.SetActive(true); // 激活子弹
    }
}

挂载脚本,并配置参数
在这里插入图片描述
效果
在这里插入图片描述

旋转我们的子弹

修改Projectile

private void Update()
{
    if (_enemyTarget != null)
    {
        MoveProjectile();
        RotateProjectile();
    }
}

//控制子弹自动旋转
private void RotateProjectile()
{
    Vector3 enemyPos = _enemyTarget.transform.position - transform.position; // 计算目标敌人相对于子弹的位置向量
    float angle = Vector3.SignedAngle(transform.up, enemyPos, transform.forward); // 计算旋转角度

    transform.Rotate(0f, 0f, angle); // 绕Z轴旋转子弹
}

效果
在这里插入图片描述

对敌人造成伤害,回收子弹

修改Projectile

[SerializeField, Header("子弹造成的伤害值")] 
private float damage = 2f;
[SerializeField, Header("造成伤害的距离")] 
private float minDistanceToDealDamage = 0.1f;

private void MoveProjectile()
{
    // 将子弹向目标位置移动
    //...
    
    // 计算子弹与目标的距离
    float distanceToTarget = (_enemyTarget.transform.position - transform.position).magnitude;
    
    // 如果子弹与目标的距离小于最小距离,造成伤害并回收子弹
    if (distanceToTarget < minDistanceToDealDamage)
    {
        _enemyTarget._enemyHealth.DealDamage(damage);
        ObjectPooler.ReturnToPool(gameObject);
    }
}

效果
在这里插入图片描述

自动发射子弹

修改Projectile

public TurretProjectile Turretowner { get; set; }

private void MoveProjectile()
{
    // ...
    
    // 如果子弹与目标的距离小于最小距离,造成伤害并回收子弹
    if (distanceToTarget < minDistanceToDealDamage)
    {
        Turretowner.ResetTurretProjectile(); //重置装载的子弹
        //...
    }
}

修改TurretProjectile

private void Update()
{
    if(IsTurretEmpty() && _turret.CurrentEnemyTarget != null){
        LoadProjectile();
    }
	//...
}
//加载子弹
private void LoadProjectile()
{
    //...
    _currentProjectileLoaded.Turretowner = this;
}
    
//判断是否没有装载子弹
private bool IsTurretEmpty()
{
    return _currentProjectileLoaded == null;
}

//重置装载的子弹
public void ResetTurretProjectile()
{
    _currentProjectileLoaded = null;
}

在这里插入图片描述

添加攻击间隔

修改TurretProjectile

[SerializeField, Header("攻击间隔")] 
private float delayBtwAttacks = 2f;//攻击间隔

private float nextAttackTime;//下一次攻击时间

private void Update()
{
    if (Time.time > nextAttackTime)
    {
        // 如果有敌人目标、已加载子弹且敌人目标的健康值大于0
        if (_turret.CurrentEnemyTarget != null && _turret.CurrentEnemyTarget._enemyHealth.CurrentHealth > 0f)
        {
        	LoadProjectile();
        	
            // 解除子弹的父节点关系
            _currentProjectileLoaded.transform.parent = null;
            // 设置子弹的目标敌人
            _currentProjectileLoaded.SetEnemy(_turret.CurrentEnemyTarget);
        }
        nextAttackTime = Time.time + delayBtwAttacks;
    }

	//加载子弹
    private void LoadProjectile()
    {
        //。。。
        _currentProjectileLoaded.ResetProjectile();//重置炮弹
    }
}

修改Projectile

//重置炮弹
public void ResetProjectile()
{
    _enemyTarget = null;
    transform.localRotation = Quaternion.identity;
}

效果,每两秒发动一次攻击
在这里插入图片描述

显示伤害字体

绘制字体UI
在这里插入图片描述

配置动画,这个动画就大家自由发挥吧,怎么放大缩小或者上飘
在这里插入图片描述

新增脚本DamageText

public class DamageText : MonoBehaviour
{
    public TextMeshPro DmgText => GetComponent<TextMeshPro>();
    
    // 回收飘字
    public void ReturnTextToPool(){
        transform.SetParent(null);
        ObjectPooler.ReturnToPool(gameObject);
    }
}

挂载脚本到字体,动画完成时添加事件
在这里插入图片描述

新增DamageTextManager

public class DamageTextManager : MonoBehaviour
{
    public ObjectPooler Pooler { get; set; }

    public DamageText damageText;//飘字特效预制体
    public static DamageTextManager Instance;

    void Awake()
    {
        Instance = this;
    }


    private void Start()
    {
        Pooler = GetComponent<ObjectPooler>();
    }
}

挂载脚本,配置参数
在这里插入图片描述

修改Projectile,添加敌人受伤委托

public static Action<Enemy, float> OnEnemyHit;//敌人受伤委托

private void MoveProjectile()
{
    // 。。。
    
    // 如果子弹与目标的距离小于最小距离,造成伤害并回收子弹
    if (distanceToTarget < minDistanceToDealDamage)
    {
        OnEnemyHit?.Invoke(_enemyTarget, damage);
        
		//。。。
    }
}

新增EnemyFX

public class EnemyFX : MonoBehaviour
{
    [SerializeField] private Transform textDamageSpawnPosition; // 伤害数字显示位置

    private Enemy _enemy; // 敌人对象

    private void Awake()
    {
        _enemy = GetComponent<Enemy>(); // 获取敌人组件
    }

    // 敌人受到攻击时的处理方法
    private void EnemyHit(Enemy enemy, float damage)
    {
        if (_enemy == enemy) // 判断是否为当前敌人
        {
            GameObject newInstance = DamageTextManager.Instance.Pooler.GetInstanceFromPool(); // 从对象池中获取伤害数字的实例
            TextMeshPro damageText = newInstance.GetComponent<DamageText>().DmgText; // 获取伤害数字的Text组件
            damageText.text = damage.ToString(); // 设置显示的伤害数值
            newInstance.transform.SetParent(textDamageSpawnPosition); // 设置父物体为伤害数字显示位置
            newInstance.transform.position = textDamageSpawnPosition.position; // 设置显示位置
            newInstance.SetActive(true); // 激活显示
        }
    }

    private void OnEnable()
    {
        Projectile.OnEnemyHit += EnemyHit; // 注册敌人被攻击事件的监听
    }

    private void OnDisable()
    {
        Projectile.OnEnemyHit -= EnemyHit; // 取消注册敌人被攻击事件的监听
    }
}

挂载脚本
在这里插入图片描述
效果
在这里插入图片描述

设计通用泛型单例

新增泛型单例

using UnityEngine;

//简单的泛型单例
public class Singleton<T> : MonoBehaviour where T : Component
{
    private static T instance;

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = FindObjectOfType<T>();
                if (instance == null)
                {
                    GameObject newInstance = new GameObject();
                    instance = newInstance.AddComponent<T>();
                }
            }
            return instance;
        }
    }

    public void Awake()
    {
        instance = this as T;
    }
}

修改DamageTextManager ,简化代码

public class DamageTextManager : Singleton<DamageTextManager>
{
    public ObjectPooler Pooler { get; set; }

    public DamageText damageText;//飘字特效预制体

    private void Start()
    {
        Pooler = GetComponent<ObjectPooler>();
    }
}

创建更多炮塔

在这里插入图片描述
效果
在这里插入图片描述

升级增加伤害

修改TurretProjectile

[SerializeField, Header("造成的伤害值")]
protected float damage = 2f;
public float Damge { get; set; }

private void Start()
{
    //。。。
    Damge = damage;
}

//加载子弹
protected virtual void LoadProjectile()
{
    //。。。
    
    _currentProjectileLoaded.damage = Damge;
}

新增TurretUpgrade

public class TurretUpgrade : MonoBehaviour
{
    [SerializeField]
    private int upgradeInitialCost; // 升级初始费用
    [SerializeField]
    private int upgradeCostIncremental; // 升级费用递增值
    [SerializeField]
    private float damageIncremental; // 伤害递增值

    private TurretProjectile _turretProjectile;

    private void Start()
    {
        _turretProjectile = GetComponent<TurretProjectile>(); // 获取 TurretProjectile 组件
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.D))
        {
            UpgradeTurret(); // 测试升级炮塔
        }
    }

    private void UpgradeTurret()
    {
        _turretProjectile.Damge += damageIncremental; // 增加炮塔的伤害值
    }
}

配置参数测试
在这里插入图片描述

效果,按一次D,伤害增加2
在这里插入图片描述

升级缩短攻击间隔

修改TurretProjectile

public float DelayPerShot { get; set; }

DelayPerShot = delayBtwAttacks;

protected virtual void Update()
{
    if (Time.time > _nextAttackTime)
    {
        // 。。。
        
        _nextAttackTime = Time.time + DelayPerShot;
    }
}

修改TurretUpgrade

[SerializeField, Header("攻击间隔递减值")]
private float delayReduce;

private void UpgradeTurret()
{
    _turretProjectile.Damge += damageIncremental; // 增加炮塔的伤害值
    _turretProjectile.DelayPerShot -= delayReduce;// 减少炮塔的攻击间隔
}

测试配置
在这里插入图片描述
效果
在这里插入图片描述

添加货币

添加

public class CurrencySystem : Singleton<CurrencySystem>
{
    [SerializeField]
    private int coinTest; // 测试用的初始金币数量
    private string CURRENCY_SAVE_KEY = "MYGAME_CURRENCY";

    public int TotalCoins { get; set; } // 总金币数量

    private void Start()
    {
    	PlayerPrefs.DeleteKey(CURRENCY_SAVE_KEY);//开始清空金币数据
        LoadCoins(); // 加载金币数量
    }

    private void LoadCoins()
    {
        TotalCoins = PlayerPrefs.GetInt(CURRENCY_SAVE_KEY, coinTest); // 从 PlayerPrefs 中读取金币数量,默认为 coinTest 的值
    }

    public void AddCoins(int amount)
    {
        TotalCoins += amount; // 增加金币数量
        PlayerPrefs.SetInt(CURRENCY_SAVE_KEY, TotalCoins); // 将金币数量保存到 PlayerPrefs
        PlayerPrefs.Save(); // 保存 PlayerPrefs 的修改
    }

    public void RemoveCoins(int amount)
    {
        if (TotalCoins >= amount)
        {
            TotalCoins -= amount; // 减少金币数量
            PlayerPrefs.SetInt(CURRENCY_SAVE_KEY, TotalCoins); // 将金币数量保存到 PlayerPrefs
            PlayerPrefs.Save(); // 保存 PlayerPrefs 的修改
        }
    }
}

修改TurretUpgrade

public int UpgradeCost { get; set; }

private void Start()
{
    //。。。
    UpgradeCost = upgradeInitialCost;
}

private void UpgradeTurret()
{
    if (CurrencySystem.Instance.TotalCoins >= UpgradeCost)
    {
        _turretProjectile.Damge += damageIncremental; // 增加炮塔的伤害值
        _turretProjectile.DelayPerShot -= delayReduce;// 减少炮塔的攻击间隔
        UpdateUpgrade();
    }
}

private void UpdateUpgrade()
{
    CurrencySystem.Instance.RemoveCoins(UpgradeCost);//扣除金额
    UpgradeCost += upgradeCostIncremental;//加下次升级金额
}

配置,升级消耗10,每次升级消耗+20
在这里插入图片描述

效果,升级扣除了金额
在这里插入图片描述

杀死敌人获取金币

修改CurrencySystem,每杀一个敌人加一金币

private void AddCoins(Enemy enemy)
{
    AddCoins(1);
}

private void OnEnable()
{
    EnemyHealth.OnEnemyKilled += AddCoins;
}
private void OnDisable()
{
    EnemyHealth.OnEnemyKilled -= AddCoins;
}

效果,杀死敌人金额增加
在这里插入图片描述

源码

见本项目最后一节

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1218816.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有趣的按钮分享

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 广告打完&#xff0c;我们进入正题&#xff0c;先看效果&#xff1a; 废话不多&#xff0c;上源码&#xff1a; <button class&quo…

牛只识别 牛脸识别 个体识别 身份识别

融合YOLOv5s与通道剪枝算法的奶牛轻量化个体识别方法 Light-weight recognition network for dairy cows based on the fusion of YOLOv5s and channel pruning algorithm 论文链接 知网链接 点击进入正文 该文章讨论了奶牛花斑、光照条件、不同剪枝方法、不同剪枝率对准确率的…

OPPO发布AndesGPT大模型;Emu Video和Emu Edit的新突破

&#x1f989; AI新闻 &#x1f680; OPPO发布全新ColorOS 14及自主训练的AndesGPT大模型 摘要&#xff1a;OPPO在2023 OPPO开发者大会上发布了全新的ColorOS 14&#xff0c;并正式推出了自主训练的安第斯大模型&#xff08;AndesGPT&#xff09;。AndesGPT拥有对话增强、个人…

Linux学习教程(第三章 Linux文件和目录管理)2

第三章 Linux文件和目录管理(初识Linux命令) 十一、Linux 删除空目录(rmdir命令) Linux rmdir命令:删除空目录 和 mkdir 命令(创建空目录)恰好相反,rmdir(remove empty directories 的缩写)命令用于删除空目录,此命令的基本格式为: [root@localhost ~]# rmdir […

应用在城市井盖积水检测中的深水液位传感芯片

城市井盖积水检测系统以实现城市下水道水雨情信息“全要素、全量程、全覆盖”自动测报为目标&#xff0c;具备下水道水位、雨量、流速、流量、雨量、气象参数、现场图像、视频等水文信息采集、传输、处理及预警等功能&#xff0c;有效提升了雨水情信息的时效性和准确度&#xf…

【Java开发的主要应用领域】

【点我-这里送书】 本人详解 作者&#xff1a;王文峰&#xff0c;参加过 CSDN 2020年度博客之星&#xff0c;《Java王大师王天师》 公众号&#xff1a;JAVA开发王大师&#xff0c;专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生&#xff0c;期待你的…

类BERT模型蒸馏实战

机器学习模型已经变得越来越大&#xff0c;以至于训练模型可能会给那些没有空闲集群的人带来痛苦。 此外&#xff0c;即使使用训练好的模型&#xff0c;当你的硬件与模型对其运行的期望不符时&#xff0c;推理的时间和内存成本也会飙升。 因此&#xff0c;为了缓解这个问题&…

idea 环境搭建及运行java后端源码

1、 idea 历史版本下载及安装 建议下载和我一样的版本&#xff0c;2020.3 https://www.jetbrains.com/idea/download/other.html&#xff0c;idea分为专业版本&#xff08;Ultimate&#xff09;和社区版本&#xff08;Community&#xff09;&#xff0c;前期可以下载专业版本…

新品|CASAIM-IS(2ND)自动化智能检测系统正式上市,打造更高效、更智能、更安全新体验!

全新第二代中科广电CASAIM-IS自动化智能检测系统正式上市&#xff0c;集合CASAIM最新的“智能控制、智能成像、智能检测”三智技术&#xff0c;为中小型精密复杂工件测量及检测提供一站式高效全自动化智能检测解决方案

设计模式(5)-使用设计模式实现简易版springIoc

自定义简易版springIoc 1 spring使用回顾 自定义spring框架前&#xff0c;先回顾一下spring框架的使用&#xff0c;从而分析spring的核心&#xff0c;并对核心功能进行模拟。 数据访问层。定义UserDao接口及其子实现类 public interface UserDao {public void add(); }public…

使用VC++设计程序,进行全局固定阈值分割、自适应阈值分割

图像分割 文章目录 图像分割实验内容一、全局固定阈值分割全局固定阈值分割的原理全局固定阈值分割的实验代码全局固定阈值分割的实验现象 二、自适应阈值分割自适应阈值分割的实验原理自适应阈值分割的实验代码自适应阈值分割的实验现象 实验内容 实验目的&#xff1a; &…

移交计划书、移交确认单

项目移交过程文件&#xff1a; 1、移交计划书 2、移交确认单 1、移交计划 2、移交确认单

Day48 力扣动态规划 : 647. 回文子串 |516.最长回文子序列 |动态规划总结篇

Day48 力扣动态规划 : 647. 回文子串 &#xff5c;516.最长回文子序列 &#xff5c;动态规划总结篇 647. 回文子串第一印象看完题解的思路dp递推公式初始化递归顺序 实现中的困难感悟代码 516.最长回文子序列第一印象我的尝试遇到的问题 看完题解的思路dp递推公式初始化 实现中…

基于springboot实现大学生体质测试管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现大学生体质测试管理系统演示 摘要 大学生体质测试管理系统提供给用户一个简单方便体质测试管理信息&#xff0c;通过留言区互动更方便。本系统采用了B/S体系的结构&#xff0c;使用了java技术以及MYSQL作为后台数据库进行开发。系统主要分为系统管理员、教师…

C/C++高频面经-秋招篇

自己在秋招找工作过程中遇到的一些C/C面试题&#xff0c;大中小厂都有&#xff0c;分享出来&#xff0c;希望能帮到有缘人。 C语言 snprintf()的使用 函数原型为int snprintf(char *str, size_t size, const char *format, …) 两点注意&#xff1a; (1) 如果格式化后的字符…

《Linux从练气到飞升》No.30 深入理解 POSIX 信号量与生产消费模型

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

vs2017 编译Qt 5.11.2 源码

SDK 10.0.22000.194 有 2种编译方式 &#xff0c;第二种 看下面 推荐使用方式二&#xff0c;简单方便&#xff0c;唯一不好是慢 方式一: 1、问题描述&#xff1a; 使用VS编译程序时&#xff0c;运行库选择多线程&#xff08;/MT&#xff09;&#xff0c;表示采用多线程静态…

安卓用户当心: CERT-IN 发布高危漏洞警告

已发现的漏洞一旦被利用&#xff0c;将构成严重风险&#xff0c;可能导致未经授权访问敏感信息。 印度计算机应急响应小组&#xff08;CERT-IN&#xff09;在最近发布的一份公告中&#xff0c;就影响印度安卓用户的新安卓漏洞发出了重要警告。 该警告对使用安卓 11、12、12L、…

modbus转profinet网关连接PLC与变频器控制摆辊应用在涂布机案例

通过兴达易控modbus转profinet网关的应用&#xff0c;PLC能够直接与变频器进行通讯&#xff0c;并实现对摆辊的精确控制。兴达易控modbus转profinet网关&#xff08;XD-MDPN100&#xff09;作为一个高性能的转换设备&#xff0c;能够稳定可靠地完成modbus和profinet之间的数据转…