机翻警告,加了点东西,官方废话我直接不翻译

官方文档
节点功能请看B站视频:NodeCanvas系例教程-行为树全节点功能讲解
源码解析也有个大概的:NodeCanvas行为树插件源码解析

入门

框架概念

GraphOwner 组件

节点对象参数

节点面板上Task节点的执行对象,默认为 Owner组件挂载的对象,这几个挂载的组件全都继承了GraphOwner脚本,自行右键查看。
NodeCanvas(中文文档) - 图1
image.png

Tasks(Actions & Conditions)

Tasks是实际的Actions 和Conditions;发生了什么和如果发生。在 NodeCanvas 中,Tasks被分配给节点,而不是包含功能的节点本身。这使得设计具有很大的模块化和灵活性,因为它将设计与实际实现分离,并且允许出于各种不同的原因使用相同的Tasks,因为功能被封装在其中。这是 NodeCanvas 的核心概念。
包中包含各种Tasks,当然,创建自己的Tasks也很容易,使用简单的 API 可以非常自定义。
使用 NodeCanvas,您有两个广泛的选项来实现您自己的功能,主要是指Actions 和Conditions:
您可以创建自己的Tasks。
您可以将功能保留在自己的 MonoBehaviours 中,并使用 脚本控制Tasks与其交互。

Tasks 目录

有空会补充每个Task的实际作用,其实不太想写太多了,看源码自己照着写比较实在。

Actions

Animation Legacy(动画家系统遗留)

PlayAnimationAdvanced.cs

Animator

MecanimPlayAnimation.cs
MecanimSetBool.cs
MecanimSetFloat.cs
MecanimSetIK.cs
MecanimSetInt.cs
MecanimSetLayerWeight.cs
MecanimSetLookAt.cs
MecanimSetTrigger.cs

Application

LoadScene.cs

Audio

PlayAudioAtPosition.cs

Blackboard Variables

ComposeVector.cs
DecomposeVector.cs
Dictionary Specific
EvaluateCurve.cs
GetOtherBlackboardVariable.cs
GetToString.cs
List Specific
LoadBlackboard.cs
NormalizeVector.cs
SampleCurve.cs
SaveBlackboard.cs
SetBoolean.cs
SetBooleanRandom.cs
SetEnum.cs
SetFloat.cs
SetFloatRandom.cs
SetInt.cs
SetIntRandom.cs
SetOtherBlackboardVariable.cs
SetVariable.cs
SetVector3.cs
TriggerBoolean.cs

Dictionary

AddElementToDictionary.cs
GetDictionaryElement.cs

Lists

AddElementToList.cs
ClearList.cs
GetCloserGameObjectInList.cs
GetIndexOfElement.cs
GetListCount.cs
InsertElementToList.cs
PickListElement.cs
PickRandomListElement.cs
RemoveElementFromList.cs
SetListElement.cs
ShuffleList.cs
SortGameObjectListByDistance.cs

Camera

CameraFader.cs
FadeIn.cs
FadeOut.cs

Dialogue

Say.cs
SayRandom.cs
StartDialogueTree.cs

GameObject

CreateGameObject.cs
CreatePrimitive.cs
DestroyGameObject.cs
FindAllWithName.cs
FindAllWithTag.cs
FindChildByName.cs
FindClosestWithTag.cs
FindObjectOfType.cs
FindObjectsOfType.cs
FindWithName.cs
FindWithTag.cs
GetAllChildGameObjects.cs
GetComponent.cs
GetDistance.cs
GetGameObjectPosition.cs
InstantiateGameObject.cs
LookAt.cs
RemoveComponent.cs
SetObjectActive.cs

Input

GetInputAxis.cs
GetMousePosition.cs
GetMouseScrollDelta.cs
WaitMousePick.cs
WaitMousePick2D.cs

Movement

Direct

CurveTransformTween.cs
InputMove.cs
MoveAway.cs
MoveTowards.cs
RotateAway.cs
RotateTowards.cs

Pathfinding

FindClosestEdge.cs
Flee.cs
MoveToGameObject.cs
MoveToPosition.cs
Patrol.cs
Wander.cs

Physics

GetLinecastInfo.cs
GetLinecastInfo2D.cs
GetLinecastInfo2DAll.cs
GetOverlapSphereObjects.cs

Script Control

GetField.cs
SendMessage.cs
SendMessageToType.cs
SetField.cs
ExecuteFunction_Multiplatform.cs
GetProperty_Multiplatform.cs
ImplementedAction_Multiplatform.cs
SetProperty_Multiplatform.cs
ExecuteFunction.cs
GetProperty.cs
ImplementedAction.cs
SetProperty.cs

Utility

DebugBeep.cs
DebugDrawLine.cs
DebugLogText.cs
DebugLogVariable.cs
ForceFinishGraph.cs
GraphOwnerControl.cs
RunForever.cs
SendEvent.cs
SendEventToObjects.cs
ShoutEvent.cs
SwitchBehaviour.cs
Wait.cs

Conditions

Animator

MecanimCheckBool.cs
MecanimCheckFloat.cs
MecanimCheckInt.cs
MecanimIsInTransition.cs
Blackboard Variables
CheckBoolean.cs
CheckBooleanTrigger.cs
CheckEnum.cs
CheckFloat.cs
CheckInt.cs
CheckString.cs
CheckUnityObject.cs
CheckVariable.cs
CheckVectorDistance.cs
StringContains.cs
Dictionaries
TryGetValue.cs

Lists

ListContainsElement.cs
ListIsEmpty.cs
GameObject
CanSeeTarget.cs
CheckDistanceToGameObject.cs
CheckLOS.cs
CheckLOS2D.cs
CheckSpeed.cs
HasComponent.cs
IsActive.cs
IsInFront.cs

Input

CheckButtonInput.cs
CheckKeyboardInput.cs
CheckMousePick.cs
CheckMousePick2D.cs

Movement

PathExists.cs

Script Control

CheckCSharpEvent.cs
CheckField.cs
CheckStaticCSharpEvent.cs
CheckUnityEvent.cs
CheckFunction_Multiplatform.cs
CheckProperty_Multiplatform.cs
CheckFunction.cs
CheckProperty.cs

System Events

CheckCollision.cs
CheckCollision2D.cs
CheckMouse.cs
CheckMouse2D.cs
CheckMouseClick.cs
CheckMouseClick2D.cs
CheckTrigger.cs
CheckTrigger2D.cs

UGUI

ButtonClicked.cs
InterceptEvent.cs

Utility

CheckEvent.cs
CheckEventValue.cs
DebugCondition.cs
Probability.cs
Timeout.cs

自定义Tasks

NodeCanvas 是围绕这样一个概念构建的,即图形的节点只对流负责,而不是负责什么,如果它发生。Tasks对此负责,因此有两种Tasks,即ActionTasks和ConditionTasks。Tasks被分配给需要它们的节点,因此Tasks可以在不同类型的系统之间重用。这个概念允许将图形设计与Actions或Conditions的实际功能实现分离,从而实现非破坏性工作流程。
NodeCanvas 包括一个Tasks向导实用程序窗口,使您可以更轻松地创建新Tasks。可以通过“Assets”中的右键菜单和“创建/ParadoxNotion/NodeCanvas/New Tasks”下访问Tasks向导。

Action Tasks

简而言之,要创建一个 Action Task,您必须从 ActionTask 基类派生并根据需要覆盖重写虚方法。当Action完成时,您必须调用 EndAction(bool) 以根据传递的 bool 参数成功或失败结束Action。
以下是您可以用来创建自己的Action Task的核心 API。

  1. //这是将对其执行操作的代理。人话,tasks的实际操作对象
  2. public Component agent {get;}
  3. //在需要时手动读/写变量的黑板。
  4. public IBlackboard blackboard {get;}
  5. //这是操作运行的时间(以秒为单位)。
  6. public float elapsedTime {get;}
  7. //仅在第一次执行操作时和其他任何操作之前调用。
  8. //如果一切正常,则返回 null。 如果有问题,返回一个信息字符串。
  9. virtual protected string OnInit()
  10. //执行操作时调用一次。
  11. virtual protected void OnExecute()
  12. //Action运行时每帧调用一次。
  13. virtual protected void OnUpdate()
  14. //当Action因任何原因停止时调用。
  15. //要么是因为你调用了 EndAction,要么是导致Action被中断。
  16. virtual protected void OnStop()
  17. //当Action暂停时调用,通常在行为图暂停时仍在运行时调用。
  18. virtual protected void OnPause()
  19. //Send an event to the behaviour graph. Use this along with the CheckEvent condition.
  20. protected void SendEvent(string)
  21. //Similar to above, but sends a value along with the event.
  22. protected void SendEvent<T>(string, T)
  23. //You can use coroutines from within action task as normal.
  24. protected Coroutine StartCoroutine(IEnumerator)
  25. //You must call this to end the action. You can call this from wherever you want,
  26. //although typicaly is done in either OnExecute or OnUpdate
  27. public void EndAction(bool)

举例

一个简单的action是这样的:

  1. using UnityEngine;
  2. using NodeCanvas.Framework;
  3. public class SimpleAction : ActionTask{
  4. protected override void OnExecute(){
  5. Debug.Log("My agent is " + agent.name);
  6. EndAction(true);
  7. }
  8. }

一个简单的延迟Action是这样的:

  1. using UnityEngine;
  2. using NodeCanvas.Framework;
  3. public class WaitAction : ActionTask {
  4. public float timeToWait;
  5. protected override void OnUpdate(){
  6. if (elapsedTime > timeToWait)
  7. EndAction(true);
  8. }
  9. }

Condition Tasks

简而言之,要创建Condition Tasks,您必须从ConditionTask基类继承,重写虚方法 OnCheck()并返回结果。
以下是用于创建自定义Condition Tasks的核心 API:

  1. //This is the agent for whom the action will take place.
  2. public Component agent {get;}
  3. //This is the blackboard that you can use to read/write variables manualy if needed.
  4. public IBlackboard blackboard {get;}
  5. //Same as action task.
  6. virtual protected string OnInit()
  7. //This is called when the condition is checked. Override and return true or false.
  8. virtual protected bool OnCheck()
  9. //This is a helper method to make the condition return true or false from outside the OnCheck method.
  10. protected void YieldReturn(bool)

举例
  1. using UnityEngine;
  2. using NodeCanvas.Framework;
  3. public class EmptyCondition : ConditionTask{
  4. protected override bool OnCheck(){
  5. return true;
  6. }
  7. }

使用Task Attributes

更新说明:[EventReceiver] 自 v3.0.0 起已弃用。要使用 Unity 事件,您现在必须使用“.router”属性。’.router’ 属性返回一个 EventRouter,您可以通过它订阅和取消订阅 EventRouter 拥有的(普通 c#)事件。对于 ActionTasks,最好在“OnExecute”/“OnStop”中完成。对于 ConditionTasks,最好在“OnEnable”/“OnDisable”中完成。
image.png
如果您想使用 Unity 的消息(如 OnTriggerEnter、OnTriggerExit 等),也可以在 Task 类上使用此属性。指定您要收听的消息,然后您可以像往常一样在任务中使用它们。以下是现在可以收听和使用的事件消息 protected override void OnEnable() {
router.onTriggerEnter += OnTriggerEnter;
router.onTriggerExit += OnTriggerExit;
}:
image.pngimage.pngimage.png

  1. protected override void OnEnable() {
  2. router.onTriggerEnter += OnTriggerEnter;
  3. router.onTriggerExit += OnTriggerExit;
  4. }
  5. protected override void OnDisable() {
  6. router.onTriggerEnter -= OnTriggerEnter;
  7. router.onTriggerExit -= OnTriggerExit;
  8. }
  9. public void OnTriggerEnter(ParadoxNotion.EventData<Collider> data) {
  10. if ( !specifiedTagOnly || data.value.gameObject.CompareTag(objectTag) ) {
  11. stay = true;
  12. if ( checkType == TriggerTypes.TriggerEnter || checkType == TriggerTypes.TriggerStay ) {
  13. saveGameObjectAs.value = data.value.gameObject;
  14. YieldReturn(true);
  15. }
  16. }
  17. }
  18. public void OnTriggerExit(ParadoxNotion.EventData<Collider> data) {
  19. if ( !specifiedTagOnly || data.value.gameObject.CompareTag(objectTag) ) {
  20. stay = false;
  21. if ( checkType == TriggerTypes.TriggerExit ) {
  22. saveGameObjectAs.value = data.value.gameObject;
  23. YieldReturn(true);
  24. }
  25. }
  26. }

[RequiredField]

在可空的公共字段上使用它,如果它确实为空或空字符串,则在字符串的情况下使其显示为红色。此外,如果在执行任务时此类字段为空,则初始化将失败。这使您不必一直进行空检查。
image.png

[TagField]

在公共字符串 r BBparameter 字段上使用它来显示 TagField 控件。
image.png
image.png

[LayerField]

在公共 int r BBParameter 字段上使用它来显示 LayerField 控件。
图和TagField 差不多 。

注意 1:所有公共继承字段也将显示以供检查。
注意 2:如果您真的需要一个非常特殊的编辑器,您可以覆盖虚拟受保护的 void OnTaskInspectorGUI() 并将其包装在 #if UNITY_EDITOR

[Category(string)]

在 Task 类上使用它来指定要显示的类别。请注意,您可以使用“/”来使用子类别。添加节点时的菜单就是按照类别分的,所以这里的设置能指定文件分配。
image.png

[Name(string)]

如果你的类名由于某种原因太奇怪或者你只是想用一个特定的名字来显示,在 Task 类上使用这个属性。
image.pngimage.png

[Description(string)]

使用此属性将允许您提供可以在任务检查器中读取的帮助描述。
要指定在节点上分配时显示的信息,您必须覆盖“虚拟字符串 信息”属性并返回您想要显示的字符串,这意味着任务最终将要执行或检查的内容。这对于在编辑器中查看行为时直接了解将发生的情况非常有用。如果该属性未被覆盖,则任务名称将改为显示。

状态机(FSM)

状态

FSM 中除了少数几个节点之外的每个节点都是一个状态。一个状态会在它进入、运行时和退出时立即执行一些操作。在 FSM 中,在任何给定帧中只能有一个状态处于活动状态。当一个状态处于活动状态时,它的所有传出转换都会按帧进行评估,一旦它们中的任何一个返回 True,就会发生该转换,结果,当前状态退出,而新状态进入。在一帧内不能发生超过一个过渡。

过渡

FSM 中的每个状态都可以有传出的转换。在 NodeCanvas 中,转换是基于条件的,这意味着每个转换评估都基于一个或多个可以分配给该转换的条件任务。或者,没有分配任何条件的转换称为“OnFinish”转换,顾名思义,它只会在父状态完成后立即发生。当您在一个状态中有多个传出转换时,您可以通过状态的 Inspector GUI 对它们进行评估的顺序进行优先级排序。这显然很重要,因为一次只能发生一次转换。
默认情况下,转换是按帧评估的,但还有一个选项可以仅在状态完成后评估转换。可以在状态的检查器 GUI 中找到选择一种或另一种行为的选项,分别命名为“连续检查”(默认)和“状态完成后检查”。

FSM节点参考

动作状态(Action State)

Action State 节点本身拥有一个 Action List。在 Enter 时,分配的操作将并行执行或依次执行。这可以在检查器中设置。一旦所有动作完成(如果有的话),状态就被认为完成了。在播放模式下,正在运行的动作也会在其前面标有播放图标。
OnEnter:列出的所有操作都将执行(OnExecute)。
OnExitt:列出的所有操作都将停止(OnStop)。
Finished:当列出的所有操作都已结束时(EndAction)。
您可以通过选中“重复状态操作”选项来选择是否希望列出的操作重复自身。这样做自然会使状态无法完成,宁愿永远运行下去。
您还可以像往常一样通过操作列表下方的相关 GUI 控件设置是否希望列出的操作按顺序运行或并行运行。

任意状态(Any State)

Any State 节点用于根据 Condition 从 Any State 转换到任何其他状态。它可以有任意数量的不断检查的转换。如果有任何转换条件为真,则 FSM 的当前状态(无论是什么)将退出并且任何状态转换的目标之一将进入。
您还可以标记“不要重新触发活动状态”,以防止任何状态转换到已经处于活动状态的状态。

On FMS Enter

On FSM Enter 节点包含一个操作列表,以及所需的可选先决条件列表。如果满足条件,则 FSM 启动后将立即执行此处的操作。

On FSM Exit

On FSM Exit 节点包含一个操作列表,以及所需的可选先决条件列表。如果满足条件,一旦 FSM 停止,此处的操作就会执行。

On FSM Update

On FSM Update 节点包含一个操作列表,以及所需的可选先决条件列表。只要满足条件,只要 FSM 正在运行,这里的操作就会与任何其他状态并行执行。

子行为数(Sub Behaviour Tree)

子行为树节点被分配了一个完整的行为树。在 Enter 时,行为树将根据检查器中选择的设置执行一次或永久执行。如果设置为“Run Once”,则此状态将在一个行为树周期完成后立即结束。
您可以选择指定两个要发送的事件,一个用于嵌套行为树的根节点状态返回 Success,另一个用于返回 Failure。一旦发生任何一种情况,就会发送事件。在此节点的转换上使用检查事件条件以利用该事件。
子行为树状态非常强大,因为您可以在行为的顶层拥有广泛的状态,并在每个状态更深入地使用行为树来定义状态的行为!
OnEnter:行为树将启动。
OnExit:行为树将停止。
Finished:行为树完成时。
当此 FSM 暂停时,如果此节点是当前 FSM 状态,子行为树也将暂停。

子状态机(Sub FSM)

Sub FSM 被分配另一个完整的 FSM。在 Enter 上,该 FSM 将执行。该状态将在子 FSM 完成时完成。当然,如果转换的条件为真,则转换将发生并且此状态将正常退出。因此,通过使用子 FSM 节点,您可以创建所谓的分层状态机。
OnEnter : FSM 将启动
OnExit : FSM 将停止
完成:除非子 FSM 以某种方式完成,否则永远不会。
当此 FSM 暂停时,如果此节点处于当前 FSM 状态,则子 FSM 也将暂停。

FSM回调

当 FSM 状态进入、更新或退出时,您可以接收与 FSMOwner 连接到相同游戏对象的 MonoBehaviours 的回调。可以使用的回调如下:
OnStateEnter(IState)
OnStateUpdate(IState)
OnStateExit(IState)
为此,您需要在 MonoBehaviour 中实现“IStateCallbackReceiver”接口。为方便起见,这是一个代码示例。

  1. using UnityEngine;
  2. public class StateCallbacksExample : MonoBehaviour, IStateCallbackReceiver {
  3. public void OnStateEnter(IState state){
  4. }
  5. public void OnStateUpdate(IState state){
  6. }
  7. public void OnStateExit(IState state){
  8. }
  9. }

IState 是一个接口,其中包含有关调用回调的当前状态的信息。

  1. public interface IState{
  2. //The name of the state
  3. string name {get;}
  4. //The tag of the state
  5. string tag {get;}
  6. //The elapsed time of the state
  7. float elapsedTime {get;}
  8. //The FSM this state belongs to
  9. FSM FSM {get;}
  10. //An array of the state's transition connections
  11. FSMConnection[] GetTransitions();
  12. //Evaluates the state's transitions and returns true if a transition has been performed
  13. bool CheckTransitions();
  14. }

这些回调并不意味着用于对整个状态进行建模,而是用于获得通知并执行您可能需要执行的一些额外操作。

自定义FSM节点

虽然通常使用 FSM 和 NodeCanvas 的推荐方法是创建动作和条件任务并在动作状态上使用这些任务,但也很有可能创建自己的 FSM 状态节点作为替代方案。
为此,您只需创建一个派生自 FSMState 的类并根据需要覆盖方法,然后在状态完成时调用 Finish()。一旦您创建了该类,它将显示为添加到 FSM 画布中。如果需要,您甚至可以指定自定义图标。这是一个简单的延迟状态:

  1. using UnityEngine;
  2. using NodeCanvas.Framework;
  3. using ParadoxNotion.Design;
  4. namespace NodeCanvas.StateMachines{
  5. [Category("My States")]
  6. [Icon("SomeIconName")] //Icon 必须在资源文件夹中
  7. public class SampleState : FSMState {
  8. public BBParameter<float> timeout;
  9. private float timer;
  10. //在第一次进入状态之前调用一次
  11. protected override void OnInit(){
  12. Debug.Log("Init");
  13. }
  14. //进入状态时。 不是当它重新开始时
  15. protected override void OnEnter(){
  16. Debug.Log("Enter");
  17. }
  18. //只要状态处于活动状态
  19. protected override void OnUpdate(){
  20. timer += Time.deltaTime;
  21. if (timer >= timeout.value)
  22. Finish();
  23. }
  24. //当状态处于活动状态并进入另一个状态时,它就会Exits。 当整个 FSM 停止时也是如此。
  25. protected override void OnExit(){
  26. Debug.Log("Exit");
  27. timer = 0;
  28. }
  29. //当状态处于活动状态且 FSM 暂停时
  30. protected override void OnPause(){
  31. Debug.Log("Pause");
  32. }
  33. }
  34. }

就是这样,你现在有一个自定义的延迟状态。请记住,双击一个节点会在您的 IDE 中打开它。
最后,这里有一些从 FSMState 类继承的重要属性和方法。
FSM FSM
此状态的父 FSM 图。
组件 graphAgent
FSM 的代理。
Blackboard graphBlackboard
FSM 的黑板。
float elapsedTime
此状态运行的时间(以秒为单位)。
void Finish()
调用以完成状态。
void SendEvent(string name)
调用以将事件发送到 FSM 图。

使用自定义类型

NodeCanvas 可用于您可能需要的任何类型。但是,为了在编辑器中描述这一点,保持它干净并且不会被数千种可能的类型弄乱,存在一个名为 Preferred Types Editor 的自定义编辑器窗口,您可以在其中限制在使用 NodeCanvas 时将在各种菜单中显示的类型. 具体而言,该列表将投入使用的情况包括:

  • 创建 Blackboard 变量。
  • 使用通用节点和任务 (T)。
  • 在代理未知时使用反射任务。
  • 为 AOT 平台(WebGL、xBox、iOS)生成必要的文件

可以通过“Tools/ParadoxNotion/NodeCanvas/Preferred Types Editor”统一顶部菜单来打开首选类型编辑器。您会发现,默认情况下列表中已有多种类型,但您可以随意修改列表,但您认为适合您的项目。
NodeCanvas(中文文档) - 图13