Version: 2020.1
重要的类
重要的类 - MonoBehaviour

重要的类 - GameObject

Unity 的 GameObject 类用于表示任何可以存在于场景中的事物。

此页面涉及使用 Unity GameObject 类进行的脚本编写。若要了解如何在 Unity 编辑器的场景和层级视图中使用 GameObject,请参阅用户手册的 GameObject 部分。有关 GameObject 类的每个成员的详尽参考,请参阅 GameObject 脚本参考

GameObject 是 Unity 中场景的构建块,可充当用于确定 GameObject 外观以及 GameObject 作用的功能组件的容器。

在脚本编写中,GameObject 类提供了允许在代码中使用的方法的集合,包括查找、建立连接和在 GameObject 之间发送消息,以及添加或移除附加到 GameObject 的组件和设置与其在场景中的状态相关的值。

场景状态属性

可以通过脚本修改一些与场景中的 GameObject 状态相关的属性。在编辑器中选择了 GameObject 时,这些属性通常与 Inspector 顶部附近显示的控件对应。

它们不与任何特定组件相关,会显示在 GameObject 的 Inspector 顶部(组件列表上方)。

在 Inspector 中查看的典型 GameObject。在此例中是方向光。场景状态属性具有红色轮廓。
在 Inspector 中查看的典型 GameObject。在此例中是方向光。场景状态属性具有红色轮廓。

所有 GameObject 都在 Inspector 顶部共享一组与场景中 GameObject 状态相关的控件,这些控件可以通过 GameObject 的脚本 API 进行控制。

如果要快速列出 GameObject 类的所有可用 API,请参阅 GameObject 脚本参考

活动状态

GameObject 的活动状态
GameObject 的活动状态

GameObject 在默认情况下处于活动状态,但可以停用,这会关闭附加到 GameObject 的所有组件。这通常意味着它将变得不可见,不会接收任何正常回调或事件,例如 UpdateFixedUpdate

GameObject 的活动状态由 GameObject 名称左侧的复选框表示。可以使用 GameObject.SetActive 控制此状态。

还可以使用 GameObject.activeSelf 读取当前活动状态,使用 GameObject.activeInHierarchy 读取 GameObject 是否在场景中实际处于活动状态。这两者中的后者是必要的,因为 GameObject 是否实际处于活动状态取决于其自身的活动状态,以及其所有父项的活动状态。如果其所有父项都不处于活动状态,则尽管它自己设置为活动状态,它也不会处于活动状态。

静态状态

GameObject 的静态状态
GameObject 的静态状态

Unity 的一些系统(如全局光照、遮挡、批处理、导航和反射探针)依赖于 GameObject 的静态状态。可以使用 GameObjectUtility.SetStaticEditorFlags 控制哪些 Unity 系统将 GameObject 视为静态。有关详细信息,请阅读此处的静态 GameObject

标签和层

GameObject 的静态状态
GameObject 的静态状态

标签 (Tag)提供一种在场景中标记和识别 GameObject 类型的方式,而层 (Layer)提供一种类似但不同的方式在某些内置操作(例如渲染物理碰撞)中包括或排除 GameObject 组。

有关如何在编辑器中使用标签和层的更多信息,请参阅关于标签的主要用户手册页面。

可以使用 GameObject.tagGameObject.layer 属性通过脚本修改标签和层值。还可以使用 CompareTag 方法高效地检查 GameObject 的标签,该方法包括验证标签是否存在,不会导致任何内存分配。

添加和移除组件

可以在运行时添加或移除组件,这对于以程序化方式创建 GameObject 或修改 GameObject 行为方式可能非常有用。请注意,还可以通过脚本启用或禁用脚本组件和某些类型的内置组件,而不销毁它们。

在运行时添加组件的最佳方法是使用 AddComponent<Type>(在显示的尖括号中指定组件类型)。若要移除组件,必须对组件本身使用 Object.Destroy 方法。

访问组件

最简单的情况是 GameObject 上的脚本需要访问附加到同一个 GameObject 的另一个组件(请记住,附加到 GameObject 的其他脚本本身也是组件)。为此,第一步是获取对要使用的组件实例的引用。这通过 GetComponent 方法来完成。通常要将组件对象分配给变量,而此操作使用以下代码实现。在此示例中,脚本获取对同一个 GameObject 上的 Rigidbody 组件的引用:

void Start ()
{
    Rigidbody rb = GetComponent<Rigidbody>();
}

获得对组件实例的引用后,可以像在 Inspector 中一样设置其属性的值:

void Start ()
{
    Rigidbody rb = GetComponent<Rigidbody>();

    // 更改对象的刚体质量。
    rb.mass = 10f;
}

还可以对组件引用调用方法,例如:

void Start ()
{
    Rigidbody rb = GetComponent<Rigidbody>();

    // 向刚体添加作用力。
    rb.AddForce(Vector3.up * 10f);
}

注意:可以将多个自定义脚本附加到同一个 GameObject。如果需要从一个脚本访问另一个脚本,可以像往常一样使用 GetComponent,只需使用脚本类的名称(或文件名)来指定所需的组件类型。

如果尝试检索尚未实际添加到 GameObject 的组件类型,则 GetComponent 将返回 null;如果尝试更改 null 对象上的任何值,将在运行时出现 null 引用错误。

访问其他 GameObject 上的组件

尽管它们有时会独立运行,但脚本通常会跟踪其他 GameObject 或者(更常见的情况)其他 GameObject 上的组件。例如,在烹饪游戏中,厨师可能需要知道炉子的位置。Unity 提供了许多不同方法来检索其他对象,每种方法都适合特定情况。

在 Inspector 中使用变量链接到 GameObject

查找相关游戏对象的最直接方法是向脚本添加公共的游戏对象变量:

public class Chef : MonoBehaviour
{
    public GameObject stove;

    // 其他变量和函数...
}

此变量在 Inspector 中会显示为 GameObject 字段

现在可以将对象从场景或 Hierarchy 面板拖到此变量上,对其进行分配。

将预制件从 Project 窗口拖动到 Inspector 窗口的 GameObject 字段中
将预制件从 Project 窗口拖动到 Inspector 窗口的 GameObject 字段中

GetComponent 函数和组件访问变量与其他任何变量一样可用于此对象,因此可以使用如下代码:

public class Chef : MonoBehaviour {

    public GameObject stove;

    void Start() {
        // 在 stove 前方 2 个单位处启动 chef。
        transform.position = stove.transform.position + Vector3.forward * 2f;
    }
}

此外,如果在脚本中声明组件类型的公共变量,则可以拖动已附加该组件的任何游戏对象。这样可以直接访问组件而不是游戏对象本身。

public Transform playerTransform; 

在处理具有永久连接的单个对象时,将对象与变量链接在一起是最有用的方法。可以使用数组变量来链接同一类型的多个对象,但仍然必须在 Unity Editor 中(而不是在运行时)进行连接。在运行时定位对象通常很方便,因此 Unity 提供了两种基本方法来执行此操作,如下所述。

查找子 GameObject

有时,游戏场景会使用许多相同类型的 GameObject,例如可收集对象、路径点和障碍物。这些游戏对象可能需要由监督或响应它们的特定脚本来跟踪(例如,寻路脚本可能需要使用所有路径点)。可以使用变量来链接这些游戏对象,但是如果必须将每个新路标拖动到脚本中的变量,会使设计过程变得繁琐。同样,如果删除一个路标,则必须删除对丢失游戏对象的变量引用,这很麻烦。此类情况下,可使一组游戏对象成为一个父游戏对象的所有子对象,这种管理多个游戏对象的方式通常会更好。可以使用父游戏对象的变换组件来检索子游戏对象(因为所有游戏对象都具有隐式变换):

using UnityEngine;

public class WaypointManager : MonoBehaviour {
    public Transform[] waypoints;

    void Start()
    {
        waypoints = new Transform[transform.childCount];
        int i = 0;

        foreach (Transform t in transform)
        {
            waypoints[i++] = t;
        }
    }
}

还可以使用 Transform.Find 方法按名称查找特定子对象: transform.Find("Frying Pan");

当 GameObject 具有可以在游戏运行过程中添加和移除的子 GameObject 时,这种功能可能很有用。可以在游戏运行过程中拾取和放下的工具或器皿就是这方面的一个很好例子。

发送和广播消息

在编辑项目时,可以在 Inspector 中设置 GameObject 之间的引用。但是,有时无法提前设置这些内容(例如,在游戏中查找与角色最近的项目,或引用在场景加载后实例化的 GameObject)。在这些情况下,可以在运行时查找引用并在 GameObject 之间发送消息。

通过 BroadcastMessage 可以发送对命名方法的调用,而无需具体说明应实现该方法的位置。可以使用它对特定 GameObject 或其任何子项上的每个 MonoBehaviour 调用命名方法。可以选择强制要求必须至少有一个接收方(否则会生成错误)。

SendMessage 更具体一些,只对 GameObject 本身(而不对其子项)发送对命名方法的调用。

SendMessageUpwards 是类似方法,但是对 GameObject 及其所有_父项_ 发送对命名方法的调用。

按名称或标签查找 GameObject

只要有某种信息可以识别游戏对象,就可以在场景层级视图中的任何位置找到该游戏对象。可使用 GameObject.Find 函数按名称检索各个对象:

GameObject player;

void Start()
{
    player = GameObject.Find("MainHeroCharacter");
}

还可以使用 GameObject.FindWithTagGameObject.FindGameObjectsWithTag 方法按标签查找对象或者对象集合。

例如,在一个烹饪游戏中有一个厨师角色,厨房中有多个炉子(每个都标记为 “Stove”):

GameObject chef;
GameObject[] stoves;

void Start()
{
    chef = GameObject.FindWithTag("Chef");
    stoves = GameObject.FindGameObjectsWithTag("Stove");
}

创建和销毁 GameObject

可以在项目运行期间创建和销毁 GameObject。在 Unity 中,可以使用 Instantiate 方法创建 GameObject。该方法可以生成现有对象的新副本。

有关如何实例化 GameObject 的完整说明和示例,请参阅在运行时实例化预制件

还有一个 Destroy 方法,该方法将在帧更新完成后或(可选)短时间延迟后销毁对象:

void OnCollisionEnter(Collision otherObj) {
    if (otherObj.gameObject.tag == "Garbage can") {
        Destroy(gameObject, 0.5f);
    }
}

请注意,Destroy 函数可以在不影响游戏对象本身的情况下销毁个别组件。一个常见错误编写以下代码,假设它会销毁脚本所附加到的 GameObject

 Destroy(this);

…然而,由于 “this” 表示脚本而不是 GameObject,因此它实际上只会销毁调用它的脚本组件,而留下移除了脚本组件的 GameObject。

原始对象

GameObject 类为 Unity GameObject 菜单中的可用选项提供了基于脚本的替代方案,可用于创建原始对象

若要创建 Unity 内置原始对象的实例,请使用 GameObject.CreatePrimitive,它会实例化所指定的类型的原始对象。可用的原始类型有 SphereCapsuleCylinderCubePlaneQuad

Unity GameObject 菜单中提供的原始形状
Unity GameObject 菜单中提供的原始形状
重要的类
重要的类 - MonoBehaviour