Unity Term Book
核心与架构

Tag & Layer

在 Unity 中,Tag 给 GameObject 贴上脚本识别标签(CompareTag);Layer 把对象分组,用于 Physics 碰撞矩阵、Camera Culling 与 Raycast。Tag 偏逻辑识别,Layer 偏物理与渲染过滤——正确配置 Collision Matrix,能让交互过滤清晰高效。

想象一下...

Tag 就像胸牌——“Player”“Enemy”“Treasure”——警卫一眼认出并立刻反应(CompareTag)。Layer 则像建筑里的分区平面图——员工只能进获准的楼层(Camera Culling Mask),房间也决定谁能和谁发生物理交互(Physics Layer Collision Matrix)。

概念详解

Tag 是挂在 GameObject 上的字符串标签(每个 GO 一个)。优先用 gameObject.CompareTag(“Player”),而不是 gameObject.tag == “Player”,以避免字符串分配。内置 Tag: Untagged、Respawn、Finish、EditorOnly、MainCamera、Player、GameController

Layer 是整数 0–31(最多 32 层)。每个 Layer 对应 LayerMask(32 位位掩码)中的一位。 Layer 驱动:(1) Camera Culling Mask——只渲染勾选的 Layer; (2) Physics Layer Collision Matrix——哪些 Layer 会碰撞; (3) Raycasting LayerMask——射线能打到哪些 Layer。

LayerMaskLayerMask.GetMask(“Enemy”, “NPC”) 可组合多个层。用 ~ 取反 (排除):~LayerMask.GetMask(“Player”) = 除 Player 外的所有层。

良好实践:按职责命名 Layer(不要用对象名), 在 Edit → Project Settings → Tags and Layers 中创建。例如: Ground、Projectile、Interactable、UI、PostProcess

对比:Tag 与 Layer

🏷 Tag

用途

在脚本中识别对象类型(CompareTag)

数量

无上限(字符串);每个 GO 只有 1 个 tag

应用

OnTriggerEnter、FindWithTag、脚本比较

示例

PlayerEnemyTreasure

📚 Layer

用途

为 Physics 与 Camera 渲染分组对象

数量

最多 32 层(0–7 由 Unity 保留),位 0–31

应用

Culling Mask、Collision Matrix、Raycast LayerMask

示例

GroundProjectileUI

动手步骤

1

创建新的 Tag/Layer

Edit → Project Settings → Tags and Layers。在 Tags 或 Layers 列表中添加条目。

2

在 GameObject 上指定

在 Hierarchy 选中 GO → Inspector → 顶部的 Tag 或 Layer 下拉框。

3

配置 Collision Matrix

Edit → Project Settings → Physics → Layer Collision Matrix → 取消勾选不应碰撞的层对。

4

设置 Camera Culling Mask

选中 Camera → Inspector → Culling Mask → 勾选要渲染的 Layer(UI Camera 通常只渲 UI)。

5

在 Raycast 中使用 LayerMask

Physics.Raycast(origin, dir, distance, LayerMask.GetMask("Ground")) —— 只命中 Ground。

交互模拟器

点击 GameObject 选中。修改 Tag 和 Layer。查看 Collision Matrix,并尝试 Raycast。

Hierarchy

选择一个 GameObject

Selected: None|点击 GameObject 编辑 Tag/Layer

代码示例

基础

在 OnTriggerEnter 中用 Tag 识别对象。

using UnityEngine;

public class TreasureChest : MonoBehaviour
{
  private void OnTriggerEnter(Collider other)
  {
      // CompareTag does not allocate a string — better than ==
      if (other.CompareTag("Player"))
      {
          Debug.Log("Player collected the treasure chest!");
          gameObject.SetActive(false);
      }
  }
}

// Find every Enemy in the scene by Tag
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
foreach (var e in enemies) e.GetComponent<EnemyAI>().Alert();

代码示例

进阶

Raycast 中的 LayerMask,以及运行时切换 Layer。

using UnityEngine;

public class PlayerGroundCheck : MonoBehaviour
{
  [SerializeField] private float checkDistance = 0.1f;

  // Build LayerMask from names — cache it, do not rebuild every frame
  private readonly int _groundMask = LayerMask.GetMask("Ground", "Platform");

  public bool IsGrounded =>
      Physics.Raycast(transform.position, Vector3.down, checkDistance, _groundMask);

  void Update()
  {
      if (IsGrounded)
          GetComponent<Rigidbody>().drag = 5f;   // friction while grounded
      else
          GetComponent<Rigidbody>().drag = 0f;   // no drag in the air
  }

  // Change Layer at runtime (e.g. ghost through walls)
  public void SetGhostMode(bool ghost)
  {
      gameObject.layer = LayerMask.NameToLayer(ghost ? "Ghost" : "Player");
  }
}

📌 快速记忆

  • Tag = 脚本中识别;Layer = 控制 Physics 与 Camera
  • 优先用 CompareTag(),避免 tag == 产生 GC
  • LayerMask 是位掩码——用 GetMask(),别写魔法数字
  • 最多 32 个 Layer;Collision Matrix 里关掉不需要的碰撞对

⚠️ 常见错误

  • ❌ 使用 gameObject.tag == "Player"

    每次调用都分配字符串 → GC 压力

    ✅ gameObject.CompareTag("Player")

  • ❌ 硬编码层位:layerMask = 8

    Layer 顺序一变就坏

    ✅ LayerMask.GetMask("Ground")