CommandGraph
驱动 NPC 与事件行为的基于节点的可视化脚本系统——对话、分支、发放物品/金钱、传送、对战、任务
等等。每个可交互的角色都拥有一个 ActorCommandGraph;图在编辑器中编写,运行时作为协程执行。
图是一种资源。这条路径上没有任何东西需要编译,因此在无法打开源码的已编译版本(Starter、Indie 和 Pro)中,它的工作方式完全相同。
Namespace: OpenMon.MCE.Actors.CommandGraph
通常你在可视化编辑器中构建命令图(见
CommandGraph 指南),而非从 C# 构建。下面的运行时表层是
你用自定义节点扩展系统或以编程方式运行图时会接触的部分。节点类型本身(ShowDialog、
GivePlayerItem、TeleportPlayer 等)是已编写的资产:其字段在检视器中序列化,并非面向代码的
API。
ActorCommandGraph
单个角色的图的容器。
[Serializable]
public class ActorCommandGraph
{
public bool IsRunning { get; }
[SerializeReference] public CommandNode RootNode;
[SerializeReference] public List<CommandNode> Nodes;
public bool IsGraphEmpty { get; }
public IEnumerator Run(CommandParameterData parameterData,
Action<bool> onLoopRunningUpdatedCallback = null);
public CommandNode CreateNode(Type type, Vector2 position = default);
public void DeleteNode(CommandNode node);
public void ConnectNodes(CommandNode parent, CommandNode child, int parentIndex);
public void RemoveConnection(CommandNode parent, CommandNode child, int parentIndex);
public List<CommandNode> GetChildren(CommandNode node);
}
IsRunning
此图当前是否正在执行。
类型: bool(只读)
RootNode
执行开始处的入口 CommandNode(一个 RootNode)。按引用序列化,使多态节点类型能够往返。
类型: CommandNode
Nodes
图中的所有节点,包括从 RootNode 无法到达的节点(保留下来以便编辑器能持久化孤立/未连接的
节点)。
类型: List<CommandNode>
IsGraphEmpty
当没有根节点或根节点没有子节点时为 true。
类型: bool(只读)
Run(CommandParameterData parameterData, Action<bool> onLoopRunningUpdatedCallback = null)
执行图。设置 IsRunning,然后运行 RootNode.RunCommandAndContinue,将一个
CommandParameterData 上下文贯穿到每个节点。
返回: IEnumerator——从协程中 yield 它。
CreateNode(Type type, Vector2 position = default) / DeleteNode / ConnectNodes / RemoveConnection
面向编辑器的图变更:实例化某个 CommandNode 子类的节点、删除它、按输出端口索引连接/断开
父→子连接。
CommandNode
每种节点类型都派生自的抽象基类。
[Serializable]
public abstract class CommandNode : MonsterDatabaseData<CommandNode>
{
public bool IsRunning { get; }
public string name;
public string GUID;
public Vector2 Position;
public Action OnPortsUpdated;
public virtual IEnumerator RunCommandAndContinue(CommandParameterData parameterData);
protected abstract IEnumerator Run(CommandParameterData parameterData,
Action<CommandParameterData> callback);
public abstract List<string> GetInputPorts();
public abstract List<string> GetOutputPorts();
public abstract void AddChild(CommandNode child, int index);
public abstract void RemoveChild(CommandNode child, int index);
public abstract List<CommandNode> GetChildren();
public abstract List<CommandNode> GetIndexedChildren();
}
RunCommandAndContinue(CommandParameterData parameterData)
运行此节点(经由抽象 Run),然后继续到其子节点。当图以循环方式运行时处理循环门控。
Run(CommandParameterData parameterData, Action<CommandParameterData> callback)
protected abstract——节点的实际工作。在自定义节点中重写它。完成后用(可能已修改的)参数
数据调用 callback。
GetInputPorts() / GetOutputPorts()
节点向编辑器图暴露的命名输入/输出端口。线性节点每侧有一个未命名端口;分支节点每个分支暴露一个 输出。
返回: List<string>。
SingleExecutionPathCommandNode
大多数节点派生自 SingleExecutionPathCommandNode——一个恰好只有一个输出端口(NextCommand)
的 CommandNode。分支节点(If…、Switch…、While…)则暴露多个输出端口。
public abstract class SingleExecutionPathCommandNode : CommandNode
{
public CommandNode NextCommand;
// GetOutputPorts() => 一个未命名端口;AddChild 设置 NextCommand。
}
CommandParameterData
运行期间贯穿每个节点的执行上下文——自定义节点读写的表层。
public class CommandParameterData
{
public ActorCommandGraph Graph;
public GameObject Owner;
public Actor Actor;
public MCECharacterController.Direction PlayerDirection;
public PlayerCharacter PlayerCharacter;
public GlobalGameData GlobalGameData;
public MCESettings MCESettings;
public MonsterDatabaseInstance MonsterDatabase;
public IBattleLauncher BattleLauncher;
public ILocalizer Localizer;
public IConfigurationManager ConfigurationManager;
public IInputManager InputManager;
public PlayerTeleporter Teleporter;
public SavegameManager SavegameManager;
public MapSceneLauncher MapSceneLauncher;
public TimeManager TimeManager;
public QuestManager QuestManager;
public TradeManager TradeManager;
public bool UsingMove;
public MonsterInstance MoveUser;
public Move Move;
public bool IsRunningOnLoop;
public ICommandParameter[] ExtraParams;
public Action<CommandCallbackParams> Callback;
}
内置节点类别
节点按 Actors/CommandGraph/ 下的文件夹分组。代表性类型:
| 类别 | 示例节点 |
|---|---|
| Dialogs | ShowDialog, ShowChoiceDialog, ShowSignDialog, NextDialog |
| Items | GivePlayerItem, GivePlayerMoney, IfPlayerHasItem, ChooseItemDialog |
| Monsters | GiveMonsters, HealPlayerRoster, ChooseMonsterDialog, ChangeMonsterNickname |
| Battles | SingleWildFight, SingleTrainerFight, DoubleWildFight, StartTowerRun |
| Badges | GiveBadge, IfPlayerHasBadge |
| Dex | GiveDex, RegisterMonsterAsSeen, IfDexIsComplete |
| Quests | StartQuest, CompleteQuest, ProgressQuestObjective, IfCompletedQuest |
| Variables | SetBoolVariable, SetIntVariable, IfBool, IfCondition, WhileBool |
| Utils | TeleportPlayer, WaitTime, AutoSaveIfEnabled, SwitchTimeOfDay |
| Animations | Move, Look, LookAtPlayer, WalkToPlayer, FadeScreenToBlack |
| Shops / Trade | BuyInShop, SellInShop, TradeSelectedMonster |
编写自定义节点
using System;
using System.Collections;
using System.Collections.Generic;
using OpenMon.MCE.Actors.CommandGraph;
namespace MyGame.Events
{
[Serializable]
public class LogStepsTaken : SingleExecutionPathCommandNode
{
protected override IEnumerator Run(CommandParameterData parameterData,
Action<CommandParameterData> callback)
{
// 从图贯穿的共享上下文中读取。
uint steps = parameterData.GlobalGameData.StepsTaken;
UnityEngine.Debug.Log($"Player has taken {steps} steps.");
callback(parameterData);
yield break;
}
}
}