跳到主要内容

通过脚本使用 Cross-Scene Binding(Legacy)

在直接进入如何通过 C# 使用 Cross-Scene Binding 之前,我们先来熟悉一下如何在 Command 中使用 CommandGUI 绘制一个 Object Field。

我们将创建一个 Command,用来打印 Object Field 中指定的 GameObject 的名称。

打开 Assets 菜单,选择 Create -> Sequine -> C# Command Script,创建一个新的 Command 脚本。将文件命名为 LogGameObjectCommand(可选)。

Log GameObject

绘制 Object Field

要绘制一个 Object Field,首先需要声明一个 public 的 GameObject 字段来存储这个值。

自定义 Command 类型 章节中,你可能已经了解到,在绘制字段时通常需要通过 ref 关键字传入字段。但对于像 Object Field 这样通过引用进行赋值的字段,我们就不能使用 ref。取而代之的是,需要传入当前的值,以及当数值发生变化时会被调用的 setter 回调。

...
[System.Serializable]
public class LogGameObjectCommand : Command
{
public override float nodeWidth => 285;

public GameObject gameObjectToLog;

public override void Execute(CommandExecutionFlow _flow)
{
if (gameObjectToLog != null)
{
Debug.Log("The name is: " + gameObjectToLog.name);
}
Exit();
}

#if UNITY_EDITOR
public override void Editor_OnDrawContents(Vector2 _absPosition)
{
CommandGUI.DrawObjectField<GameObject>(
"GameObject to Log",
"The GameObject in which we print the name",
gameObjectToLog,
newGameObjectToLog => {
gameObjectToLog = newGameObjectToLog;
},
true);
}
#endif
}

CommandGUI.DrawObjectField 需要一个类型参数,用来指定该字段存储的是哪一种 Unity Object。下面是它的参数说明:

  • label:显示在 Command Node 内部的标签文本。
  • tooltip:当鼠标悬停在字段上时显示的提示信息。
  • currentObject:该字段当前的对象引用。
  • onObjectChanged:当用户修改字段时被调用的回调。这个回调需要一个参数,也就是用户通过该字段选择的新对象。
  • allowSceneObjects:是否允许选择场景中的对象。

注意,我们把 allowSceneObjects 设置为 true。这是因为我们会先在不使用 Cross-Scene Binding 的情况下尝试获取 GameObject。

因此,目前 务必Sequine Flow Component 中创建这个 Command,而不是在 Sequine Flow Asset 或 Sequine Flow Clip 中。

Sequine Flow Component

到这里,你应该已经可以把层级视图中的 GameObject 直接设置到 GameObject to Log 字段中了。尝试指定一个 GameObject,然后运行看看效果。

Log Scene GameObject

Cross-Scene 组件

现在假设我们使用的是 Sequine Flow AssetSequine Flow Clip,那么就无法直接访问场景中的对象,因为它们并不属于当前场景。

此时,我们需要通过 binder asset 来获取组件对象。

创建 Cross-Scene Binder Asset

如果你已经阅读过 快速开始 章节,那么应该知道我们有一种特定的资源类型叫做 Cross Scene Binder Asset,尽管实际上 binder asset 可以是 任意类型的资源

要创建一个 Cross Scene Binder Asset,打开 Assets 菜单,选择 Create -> Sequine -> Cross Scene Binder Asset。文件名最好能够代表你将要绑定的对象。

Create Binder Asset

绑定组件

在层级视图中,选中你希望作为目标的 GameObject。在 Inspector 中点击 Add Component -> Sequine -> Cross Scene Binder,为它添加 Cross Scene Binder 组件。

然后,在 Binder Asset 字段中指定之前创建好的 binder asset。

Cross Scene Binder 不能直接绑定 GameObject,因为它绑定的是 Component。不过这通常不是问题,因为 Component 本身也可以访问它所属的 GameObject。

接下来选择一个 Component to Bind。在这个例子中,我们可以任选其中一个组件。

Setup Cross Scene Binder

获取 Cross-Scene 组件

现在回到我们的 LogGameObjectCommand.cs 脚本。

我们可以通过 CrossSceneBindingUtility.GetObject 方法来获取已经绑定的组件,并在参数中传入 binder asset。还可以添加一个类型参数,用来指定要把对象转换成哪一种类型。

Component bindedComponent = CrossSceneBindingUtility.GetObject<Component>(targetBinderAsset);

下面是完整代码的示例。

LogGameObjectCommand.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Calcatz.Sequine;
using Calcatz.CookieCutter;

[System.Serializable]
[RegisterCommand("Custom/LogGameObjectCommand", typeof(SequineFlowCommandData))]
public class LogGameObjectCommand : Command
{
public override float nodeWidth => 285;

public UnityEngine.Object targetBinderAsset;

public override void Execute(CommandExecutionFlow _flow)
{
if (targetBinderAsset != null)
{
Component bindedComponent = CrossSceneBindingUtility.GetObject<Component>(targetBinderAsset);
if (bindedComponent != null)
{
GameObject gameObjectToLog = bindedComponent.gameObject;
Debug.Log("The name is: " + gameObjectToLog.name);
}
}
Exit();
}

#if UNITY_EDITOR
public override void Editor_OnDrawContents(Vector2 _absPosition)
{
CommandGUI.DrawObjectField<UnityEngine.Object>(
"Target Binder",
"The GameObject in which we print the name",
targetBinderAsset,
newGameObjectToLog => {
targetBinderAsset = newGameObjectToLog;
},
false);
}
#endif
}

注意,这一次我们把 CommandGUI.DrawObjectField 方法中的 allowSceneObjects 参数改成了 false。这是因为我们希望有意阻止用户把场景对象直接赋值给 Target Binder。

同时,由于我们把 CommandGUI.DrawObjectField 的类型参数改成了 UnityEngine.Object,因此它也允许我们把 任何类型的资源 作为 binder asset 来指定。

Binder Field

最后,使用 UnityEngine.Object 作为类型在打开 Select Object 对话框时可能会显得比较杂乱,因为它会列出项目中的几乎所有资源。在这种情况下,你可以把类型从 UnityEngine.Object 替换为 CrossSceneBinderAsset。这样就会限制只能把 Cross Scene Binder Asset 指定为 Target Binder。

...
public class LogGameObjectCommand : Command
{
...
public CrossSceneBinderAsset targetBinderAsset;
...
public override void Editor_OnDrawContents(Vector2 _absPosition)
{
CommandGUI.DrawObjectField<CrossSceneBinderAsset>(
"Target Binder",
"The GameObject in which we print the name",
targetBinderAsset,
newGameObjectToLog => {
targetBinderAsset = newGameObjectToLog;
},
true);
}
...
}

Cross Scene Binder Field

运行测试

我们只需要把之前创建好的 binder asset 指定到 Target Binder 字段中,就可以完成这个 Command 的设置。

现在来运行看看。

Play Cross Scene Binding

可以看到,我们已经成功获取到了跨场景绑定的对象,并打印出了它的名称。

提示
  • 你也可以使用 Singleton 的方式来访问场景对象。在某些情况下这种方式更加实用,因为它 不需要 进行 Cross-Scene Binding 的额外配置。
  • 除了使用 DrawObjectField,还可以使用 DrawBinderField 来绘制一个专门用于 Binder Asset 的 Object Field。这样我们就可以直接从场景中拖拽 GameObject,它会自动从 Cross Scene Binder 组件中获取对应的 Binder Asset。