Spaces:
Runtime error
Runtime error
using System; | |
namespace Quantum | |
{ | |
public unsafe abstract partial class BTNode | |
{ | |
[public String Label; | ]|
[public Int32 Id; | ]|
[internal BTNode Parent; | ]|
[internal Int32 ParentIndex; | ]|
public abstract BTNodeType NodeType { get; } | |
/// <summary> | |
/// Called once, for every Node, when the BT is being initialized | |
/// </summary> | |
public virtual void Init(Frame frame, AIBlackboardComponent* blackboard, BTAgent* agent) | |
{ | |
var statusList = frame.ResolveList(agent->NodesStatus); | |
statusList.Add(0); | |
} | |
// -- STATUS -- | |
public BTStatus GetStatus(Frame frame, BTAgent* agent) | |
{ | |
var nodesAndStatus = frame.ResolveList(agent->NodesStatus); | |
return (BTStatus)nodesAndStatus[Id]; | |
} | |
public void SetStatus(Frame frame, BTStatus status, BTAgent* agent) | |
{ | |
var nodesAndStatus = frame.ResolveList(agent->NodesStatus); | |
nodesAndStatus[Id] = (Byte)status; | |
} | |
/// <summary> | |
/// Called whenever the BT execution includes this node as part of the current context | |
/// </summary> | |
/// <param name="btParams"></param> | |
public virtual void OnEnter(BTParams btParams) { } | |
public virtual void OnEnterRunning(BTParams btParams) { } | |
/// <summary> | |
/// Called when traversing the tree upwards and the node is already finished with its job. | |
/// Used by Composites and Leafs to remove their Services from the list of active services | |
/// as it is not anymore part of the current subtree. | |
/// Dynamic Composites also remove themselves | |
/// </summary> | |
/// <param name="btParams"></param> | |
public virtual void OnExit(BTParams btParams) { } | |
public virtual void OnAbort(BTParams btParams) | |
{ | |
} | |
/// <summary> | |
/// Called when getting out of a sub-branch and this node is being discarded | |
/// </summary> | |
/// <param name="btParams"></param> | |
public unsafe virtual void OnReset(BTParams btParams) | |
{ | |
SetStatus(btParams.Frame, BTStatus.Inactive, btParams.Agent); | |
} | |
public void EvaluateAbortNode(BTParams btParams) | |
{ | |
if (btParams.Agent->AbortNodeId == Id) | |
{ | |
btParams.Agent->AbortNodeId = 0; | |
} | |
} | |
public BTStatus RunUpdate(BTParams btParams, bool continuingAbort = false) | |
{ | |
var oldStatus = GetStatus(btParams.Frame, btParams.Agent); | |
if (oldStatus == BTStatus.Success || oldStatus == BTStatus.Failure) | |
{ | |
return oldStatus; | |
} | |
if (oldStatus == BTStatus.Abort) | |
{ | |
if (btParams.Agent->IsAborting == true) | |
{ | |
EvaluateAbortNode(btParams); | |
} | |
return oldStatus; | |
} | |
// If this node was inactive, this means that we're entering on it for the first time, so we call OnEnter | |
// An exception from this rule is when we chose this node to continue an abort process. In that case, | |
// we already executed OnEnter before, so we don't repeat it | |
if (oldStatus == BTStatus.Inactive && continuingAbort == false) | |
{ | |
OnEnter(btParams); | |
} | |
var newStatus = BTStatus.Failure; | |
try | |
{ | |
newStatus = OnUpdate(btParams); | |
if (btParams.Agent->IsAborting) | |
{ | |
newStatus = BTStatus.Abort; | |
} | |
// Used for debugging purposes | |
if (newStatus == BTStatus.Success) | |
{ | |
BTManager.OnNodeSuccess?.Invoke(btParams.Entity, Guid.Value); | |
BTManager.OnNodeExit?.Invoke(btParams.Entity, Guid.Value); | |
} | |
if (newStatus == BTStatus.Failure) | |
{ | |
BTManager.OnNodeFailure?.Invoke(btParams.Entity, Guid.Value); | |
BTManager.OnNodeExit?.Invoke(btParams.Entity, Guid.Value); | |
} | |
} | |
catch (Exception e) | |
{ | |
Log.Error("Exception in Behaviour Tree node '{0}' ({1}) - setting node status to Failure", Label, Guid); | |
Log.Exception(e); | |
} | |
SetStatus(btParams.Frame, newStatus, btParams.Agent); | |
if ((newStatus == BTStatus.Running || newStatus == BTStatus.Success) && | |
(oldStatus == BTStatus.Failure || oldStatus == BTStatus.Inactive)) | |
{ | |
OnEnterRunning(btParams); | |
} | |
if (newStatus == BTStatus.Running && NodeType == BTNodeType.Leaf) | |
{ | |
// If we are a leaf, we can store the current node | |
// We know that there has only one leaf node running at any time, no parallel branches possible | |
// The Run() method also return a tuple <BTStatus, BTNode(CurrentNode)> | |
btParams.Agent->Current = this; | |
} | |
return newStatus; | |
} | |
/// <summary> | |
/// Used by Decorators to evaluate if a condition succeeds or not. | |
/// Upon success, allow the flow to continue. | |
/// Upon failure, blocks the execution so another path is taken | |
/// </summary> | |
/// <param name="btParams"></param> | |
/// <returns></returns> | |
public virtual Boolean DryRun(BTParams btParams) | |
{ | |
return false; | |
} | |
public virtual Boolean OnDynamicRun(BTParams btParams) | |
{ | |
return true; | |
} | |
/// <summary> | |
/// Called every tick while this Node is part of the current sub-tree. | |
/// Returning "Success/Failure" will make the tree continue its execution. | |
/// Returning "Running" will store this Node as the Current Node and re-execute it on the next frame | |
/// unless something else interrputs | |
/// </summary> | |
/// <param name="btParams"></param> | |
/// <returns></returns> | |
protected abstract BTStatus OnUpdate(BTParams btParams); | |
} | |
} |