How to Develop a Game
Tag
PublishDate: 2025-06-01 | CreateDate: 2025-06-01 | LastModify: 2025-06-01 | Creator:ljf12825

在Unity中,Tag是用来标记和分类GameObject的一种轻量级方法,主要用于在代码中查找和判断物体的类型或身份

Tag的核心作用

功能示例
分类物体Player、Enemy、Item、UI 等
逻辑判断判断一个物体是不是玩家
查找特定对象GameObject.FindWithTag()
触发器/碰撞器逻辑判断if (other.CompareTag("Enemy"))

Tag的使用方法

1.设置Tag

1.选中一个 GameObject 2.Inspector 面板 → 上方的 “Tag” 下拉菜单 3.如果没有想要的标签 → 点击 Add Tag… → 添加一个新的字符串 4.回到物体,设置为刚才新建的 Tag

注意: Tag是字符串类型,但Unity会为你管理列表,不用硬编码

2.使用Tag查找对象

GameObject player = GameObject.FindWithTag("Player");

或者查找多个对象:

GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");

3.在触发器或碰撞中判断Tag

void OnTriggerEnter(Collider other) => if (other.CompareTag("Enemy")) Debug.Log("撞到敌人了!");

推荐使用CompareTag(),而不是other.tag == "Enemy",性能更好,也可避免拼写错误引发异常

示例

标记玩家

if (other.CompareTag("Player"))
{
    PlayerHealth hp = other.GetComponent<PlayerHealth>();
    hp.TakeDamage(10);
}

标记子弹、敌人、道具等

if (collision.CompareTag("Projectile")) Destroy(collision.gameObject);

用于全局查找对象(比如UI控件)

GameObject healthBar = GameObject.FindWithTag("HealthBar");

建议

Tag vs Layer

主要功能

应用场景

功能TagLayer
逻辑分类逻辑上的分类(如PlayerEnemyNPC等)不适合做逻辑分类
物理交互无直接影响控制物体与物理系统的交互(如碰撞、触发)
渲染控制无直接影响控制哪些物体由摄像机渲染,或者被物理引擎处理
代码查找可以通过代码查找特定标签的物体(FindWithTag()不支持通过代码直接查找物体
数量限制默认有7个内置标签,可自由添加自定义标签内置5个层,支持最多32个层(Layer)

性能优化

GameObject.FindWithTag()GameObject.Find()性能差异

1.GameObject.Find()

`GameObject.Find()用于根据物体的名字查找游戏对象。它的工作原理时遍历当前场景中的所有游戏对象,并检查它们的名字是否与给定的字符串匹配。由于它们是基于字符串的比较来查找物体,查找过程中需要遍历所有场景中的物体,并逐一比较名字,性能相对较低,尤其是在场景中有大量物体时

性能特点:

适用场景:
适用于场景中物体不多,或者物体名字是唯一且不需要频繁查找的场景

2.GameObject.FindWithTag()

GameObject.FindWithTag()是根据物体的Tag查找物体。Unity内部对标签进行了优化处理,标签通常是通过整数索引来管理,而不是字符串比较,因此比Find()更高效

性能特点:

适用场景:
适用于当你需要按类型查找物体时,比如查找所有敌人、玩家或道具等。尤其当场景中有大量物体时,FindWithTag()能显著提高查找效率

高级Tag管理方法

在大型项目中,游戏对象的数量通常非常庞大,简单的Tag管理可能会导致代码混乱、性能瓶颈等问题。因此,合理的Tag管理变得尤为重要

避免硬编码Tag字符串

虽然Unity允许在Inspector中设置Tag,但使用字符串类型的Tag容易导致代码中出现硬编码,导致在后期修改时很不方便。

硬编码(Hardcoding)是指在程序代码中直接使用固定值,而不是通过变量、常量、配置文件等灵活方式配置。这种做法会导致编码的可维护性差,一旦需要修改这些值,开发者就需要修改代码本身,甚至重新编译程序

示例:

if (gameObject.CompareTag("Enemy")) // Enemry就是硬编码
{
    // 做一些敌人的处理
}

为了解决这个问题,可以使用常量类来管理所有的Tag

示例:

public static class Tags
{
    public const string Player = "Player";
    public const string Enemy = "Enemy";
    public const string Item = "Item";
}

使用常量类可以避免拼写错误,并使代码更具可维护性。例如:

GameObject player = GameObject.FindWithTag(Tags.Player);

统一的Tag命名规则

在多人开发的项目中,多个开发者可能会使用不同的命名方式来为物体指定Tag,这容易造成命名冲突或不一致的情况。为了避免这种情况,可以提前指定一个统一的Tag命名规则。
例如,可以按照功能、类型等进行分类:

通过规范化命名,能够提高项目的可读性和协作效率

Tag与其他Unity功能的结合

Tag与事件系统结合

在游戏开发中,很多逻辑需要根据物体的类型来触发不同的事件。使用Tag可以帮助我们快速识别不同类型的物体,并在适当的时候触发事件

示例:使用Tag触发事件

void OnCollisionEnter(Collision collision)
{
    if (collision.gameObject.CompareTag(Tags.Player))
    {
        // 触发玩家碰撞事件
        EventManager.TriggerEvent("PlayerHit");
    }
    else if (collision.gameObject.CompareTag(Tags.Enemy))
    {
        // 触发敌人碰撞事件
        EventManager.TriggerEvent("EnemyHit");
    }
}

这种方法不仅能提高代码的可读性,还能使事件管理更加灵活

Tag与Layer结合使用

有时,Unity中需要将物体分类以控制它们与物理引擎的交互,或者控制它们的渲染。通过将Tag和Layer结合使用,可以达到更精细的控制

示例:使用Layer控制物理交互

void OnCollisionEnter(Collision collision)
{
    // 如果碰撞对象是敌人,并且它属于特定的Layer
    if (collision.gameObject.CompareTag(Tags.Enemy) && collision.gameObject.layer == LayerMask.NameToLayer("EnemyLayer"))
    {
        // 执行敌人的死亡逻辑
        Destroy(collision.gameObject);
    }
}

通过这种方式,可以使用Layer来精确控制物体的碰撞检测和物理交互,而使用Tag来区分物体的身份和类型

Tag的性能优化

避免频繁调用Find()和FindWithTag()

在大型项目中,频繁调用GameObject.Find()GameObject.FindWithTag()会对性能造成不小的影响。尤其是在Update()中反复调用这些方法时,可能会导致游戏的帧率大幅下降

解决方案:缓存查找结果 如果某个物体会被频繁访问,可以考虑将其引用存储在一个变量中,从而避免每次都进行查找

private GameObejct palyer;

void Start() => palyer = GameObject.FindWithTag(Tags.Player); // 缓存查找结果

void Update()
{
    if (player != null)
    {
        // 在这里使用缓存的player引用
    }
}

使用对象池(Object Pooling)优化查找性能

对于需要频繁查找的物体,使用对象池是要给不错的选择。对象池能够避免频繁地实例化和销毁对象,减少性能开销,同时提高代码的可复用性

示例:对象池模式

public class EnemyPool : MonoBehaviour
{
    public GameObject enemyPrefab;
    private List<GameObject> enemyPool = new List<GameObject>();

    public GameObject GetEnemy()
    {
        foreach (var enemy in enemyPool)
        {
            if (!enemy.activeInHierarchy)
            {
                enemy.SetActive(true);
                return enemy;
            }
        }

        var newEnemy = Instantiate(enemyPrefab);
        enemyPool.Add(newEnemy);
        return newEnemy;
    }
}

对象池能够使得FindWithTag()等查找操作不再频繁发生,从而提升性能