You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中玩家与Upgrade对象重复触发碰撞及代码优化咨询

技术问题解答

问题描述

玩家与Upgrade立方体对象触发碰撞时,OnTriggerEnter会触发两次,导致属性翻倍(例如本该获得2点Damage实际获得4点)。同时咨询:当前实现方式是否合理、switch替代if语句的优势、代码优化方法。

现有代码

碰撞检测代码

public void OnTriggerEnter(Collider other)
{
    if (other.CompareTag("Upgrade") && this != null)
    {
        Debug.Log("Collide:" + other.name);

        Upgrades upgradesScript = other.GetComponent<Upgrades>();
        
        if(upgradesScript != null)
        {
            Damage += upgradesScript.Damage;
            Health += upgradesScript.Health;
            Xspeed += upgradesScript.Xspeed;
        }
    }
}

Upgrade脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Upgrades : MonoBehaviour
{
    public float Damage; //id 0
    public float Xspeed; //id 1
    public float Health; //id 2
    public float itemID;
    public float stage = 1;

    void Start()
    {
        if(itemID == 0)
        {
            RandomDamage();
        }
        else if(itemID == 1)
        {
            RandomXspeed();
        }
        else
        {
            RandomHealth();
        }
    }

    void Update()
    {
        
    }

    public void RandomDamage()
    {
        Damage = Random.Range(1 , 3 );
    }

    public void RandomXspeed()
    {
       Xspeed = Random.Range(1 * stage, 3 * stage);
    }

    public void RandomHealth()
    {
        Health = Random.Range(5 , 10 );
    }
}

Inspector截图

Upgrade对象Inspector配置


一、碰撞触发两次的解决方法

原因分析

  1. 玩家或Upgrade对象上存在多个带Is Trigger勾选的Collider组件,导致碰撞事件被多次触发
  2. 碰撞逻辑同时挂载在玩家和Upgrade对象上,两边都执行了属性累加操作
  3. Upgrade对象未及时销毁/禁用,玩家持续碰撞时重复触发事件

解决方案

  1. 清理多余Collider:检查玩家和Upgrade对象,确保只有一个用于触发检测的Collider勾选Is Trigger
  2. 添加触发标记:在Upgrades脚本中增加标记,避免重复处理
    public bool isCollected = false;
    
    然后在碰撞逻辑中判断:
    if(upgradesScript != null && !upgradesScript.isCollected)
    {
        upgradesScript.isCollected = true;
        // 属性累加逻辑
        Destroy(other.gameObject); // 触发后立即销毁Upgrade对象
    }
    
  3. 单一触发源:确保只有玩家对象挂载碰撞检测脚本,Upgrade对象不处理碰撞逻辑

二、当前实现方式合理性分析

  • 优点:逻辑直观,新手易理解,基本实现了随机属性升级的核心需求
  • 缺点:
    • itemID用float类型不合理,应为int(整数ID无需浮点精度)
    • 三个属性同时存在于脚本中,即使某个Upgrade只对应一种属性,另外两个属性值为0,存在冗余
    • Start方法中的if-else分支扩展性差,新增属性类型时需修改原有逻辑
    • 无生命周期管理,容易出现重复触发问题

三、switch替代if语句的优势

  1. 可读性更强:多分支场景下,switch结构更清晰,一眼可识别所有分支条件
  2. 性能更优:针对整数/枚举类型的条件,编译器会将switch优化为跳转表,比多次if-else判断效率更高
  3. 扩展性更好:新增分支时直接添加case即可,无需修改原有分支逻辑,符合开闭原则
  4. 逻辑更严谨:可通过default分支统一处理未匹配的情况,避免if-else漏写else导致的逻辑漏洞

四、代码优化方法

1. 碰撞检测脚本优化

public void OnTriggerEnter(Collider other)
{
    // 提前过滤无效触发
    if (!other.CompareTag("Upgrade") || this == null) return;

    Debug.Log("Collide:" + other.name);
    var upgradesScript = other.GetComponent<Upgrades>();
    
    if(upgradesScript != null && !upgradesScript.isCollected)
    {
        upgradesScript.isCollected = true;
        // 仅累加对应属性,避免无意义的0值操作
        switch(upgradesScript.itemID)
        {
            case 0: Damage += upgradesScript.Damage; break;
            case 1: Xspeed += upgradesScript.Xspeed; break;
            case 2: Health += upgradesScript.Health; break;
        }
        Destroy(other.gameObject);
    }
}

2. Upgrade脚本优化

using UnityEngine;

public class Upgrades : MonoBehaviour
{
    // 改用int存储ID,类型更匹配
    public int itemID;
    public int stage = 1;
    public bool isCollected = false;

    // 单个属性存储升级值,避免冗余
    public float upgradeValue;

    void Start()
    {
        // switch替代if-else
        switch(itemID)
        {
            case 0:
                upgradeValue = Random.Range(1f, 3f);
                break;
            case 1:
                upgradeValue = Random.Range(1f * stage, 3f * stage);
                break;
            case 2:
                upgradeValue = Random.Range(5f, 10f);
                break;
            default:
                Debug.LogWarning($"未知ItemID:{itemID}");
                break;
        }
    }
}

3. 进阶优化:使用枚举替代魔法数字

定义枚举类型,消除0/1/2这类无意义的魔法数字,提升代码可读性:

public enum UpgradeType
{
    Damage,
    Xspeed,
    Health
}

修改Upgrade脚本:

public UpgradeType upgradeType;

void Start()
{
    switch(upgradeType)
    {
        case UpgradeType.Damage:
            upgradeValue = Random.Range(1f, 3f);
            break;
        case UpgradeType.Xspeed:
            upgradeValue = Random.Range(1f * stage, 3f * stage);
            break;
        case UpgradeType.Health:
            upgradeValue = Random.Range(5f, 10f);
            break;
    }
}

内容的提问来源于stack exchange,提问作者Qatawneh Razvan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 13:32:41