Version: Unity 6.0 (6000.0)
语言 : 中文
游戏对象基础
变换组件

GameObject

Switch to Scripting

Unity 的__ GameObject__ 类表示任何可以存在于场景 (Scene) 中的事物。游戏对象是 Unity 中场景的构建块,可充当用于确定游戏对象外观及其作用的功能组件的容器。

GameObject 类提供了一组方法,使您可以在代码中使用游戏对象。这些方法包括:在游戏对象之间查找、建立连接和发送消息,添加或删除附加到游戏对象的组件,以及设置与它们在场景中的状态相关的值。

有关 GameObject 类的每个成员的详尽参考,请参阅游戏对象脚本参考

有关在 Unity 编辑器中的场景和层级视图中使用游戏对象的信息,请参阅游戏对象简介

场景状态属性

所有游戏对象都在检视面板顶部共用一组与场景中游戏对象状态相关的控件,这些控件可以通过游戏对象的脚本 API 进行控制。

检视面板 (Inspector) 视图中的典型游戏对象。它在这个例子中是一个方向光。场景状态属性以红色显示。
检视面板 (Inspector) 视图中的典型游戏对象。它在这个例子中是一个方向光。场景状态属性以红色显示。

活动状态

游戏对象的活动状态
游戏对象的活动状态

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

游戏对象的活动状态由游戏对象名称左侧的复选框表示。可以使用 GameObject.SetActive 进行控制。

此外,还可以使用 GameObject.activeSelf 读取游戏对象的当前活动状态。使用 GameObject.activeInHierarchy 读取游戏对象在场景中是否实际处于活动状态。GameObject.activeInHierarchy 不可或缺,因为游戏对象是否实际处于活动状态取决于其自身的活动状态及其所有父项的活动状态。如果其所有父项都不处于活动状态,则即使它自身设置为活动状态也不会生效。

静态状态

游戏对象的静态状态
游戏对象的静态状态

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

Tags and Layers

游戏对象的标签 (Tag) 和层 (Layer) 字段
游戏对象的标签 (Tag) 和层 (Layer) 字段

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

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

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

添加和删除组件

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

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

访问组件

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

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

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

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

    // Change the mass of the object's Rigidbody.
    rb.mass = 10f;
}

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

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

    // Add a force to the Rigidbody.
    rb.AddForce(Vector3.up * 10f);
}

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

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

访问其他游戏对象上的组件

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

在检视面板中使用变量链接到游戏对象

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

public class Chef : MonoBehaviour
{
    public GameObject stove;

    // Other variables and functions...
}

此变量在检视面板中会显示为游戏对象 (GameObject) 字段

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

将预制件从项目 (Project) 窗口拖动到检视面板窗口的 GameObject 字段中
将预制件从项目 (Project) 窗口拖动到检视面板窗口的 GameObject 字段中

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

public class Chef : MonoBehaviour {

    public GameObject stove;

    void Start() {
        // Start the chef 2 units in front of the stove.
        transform.position = stove.transform.position + Vector3.forward * 2f;
    }
}

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

public Transform playerTransform;

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

查找子游戏对象

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

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");

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

发送和广播消息

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

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

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

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

按名称或标签来查找游戏对象

只要有某种信息可以识别游戏对象,就可以在场景层级视图中的任何位置找到该游戏对象。可使用 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");
}

创建和销毁游戏对象

可以在项目运行时创建和销毁游戏对象。在 Unity 中,可以使用 Instantiate 方法来创建游戏对象,它可以生成现有对象的新副本。

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

Destroy 方法将在帧更新完成后或选择在短时间延迟后销毁对象:

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

请注意,Destroy 函数可以销毁个别组件而不影响游戏对象本身。一个常见的错误就是编写 this 并设想它会销毁脚本所附加到的游戏对象:

 Destroy(this);

this 表示脚本,而不是游戏对象。它实际上只会销毁调用它的脚本组件。它会保留完整的游戏对象,但会删除脚本组件。

原始对象

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

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

Unity 游戏对象菜单中提供的原始形状
Unity 游戏对象菜单中提供的原始形状

其他资源

游戏对象基础
变换组件