修饰节点

生命周期
1.先执行父节点的 CanExecute
如果返回成功,那么直接会在本帧,执行子节点。
子节点执行到Running,又会在本帧再次执行 CanExecute
。
2.然后会执行OverrideStatus
此方法,会传参一个,子节点当前执行状态。
如果子节点未执行,会一直提供一个Inactive。
Log证明:

Test:
现在有一个这样的需求:
- 在一秒钟之内,持续调用该节点。(需要一个计时器,超时,不允许执行,且返回执行成功)
- 如果子节点不在执行,那么不断的执行本节点逻辑;如果子节点在执行,不执行本节点逻辑。
- 如果该节点满足某个条件(testBool == true),那么就执行一次子节点,子节点执行结束后,还原该条件(testBool = false)
思路:
由上面的测试可以知道
- CanExecute的执行顺序是最先的,子节点的执行顺序第二,OverrideStatus的执行顺序最后。
- 当CanExecute返回false的时候,子节点不会执行。OverrideStatus会照常执行。
- 当CanExecute返回true的时候,子节点执行一次后,会立刻再次调用CanExecute。
- 子节点开始执行,会调用OnChildStarted()
- 子节点执行完一次,会调用OnChildExecuted(TaskStatus childStatus)
1.由于CanExecute是第一个执行的。因此本节点逻辑应写在此间。
但是当子节点执行后,会再次调用此方法。因此,当子节点未开始执行时,才能执行本节点逻辑。
本节点逻辑,有几率修改testBool条件。本处,会根据按键向列表中加项。
执行完本节点逻辑后,会判断列表中是否有项,有项,则可执行子节点。
2.子节点执行完毕后,需要知道子节点当前是否都已经完成。如果子节点全部执行结束,那么还原条件(testBool = false)
public override bool CanExecute()
{
// 子节点未执行,可执行本节点逻辑
if(!childExecuting)
{
DLogic();
}
return DCanExec();
}
public override void OnChildStarted()
{
childExecuting = true;
}
public override void OnEnd()
{
childExecuting = false;
}
public override void OnChildExecuted(TaskStatus childStatus)
{
// 子节点执行成功,还原条件
if(childStatus == TaskStatus.Success)
{
DReset();
}
base.OnChildExecuted(childStatus);
}
// 本节点逻辑,当子节点未执行时,每帧都先执行此逻辑,然后判定子节点是否可以执行。
private void DLogic()
{
if (Input.GetKeyDown(KeyCode.P))
{
dmgList.Add(1);
dmgList.Add(2);
}
}
// 子节点执行条件重置
private void DReset()
{
dmgList.Clear();
}
// 判定子节点是否可以执行
private bool DCanExec()
{
// 列表中有项,就可以执行子节点
return dmgList.Count > 0;
}
3.需要满足一个条件,子节点未执行时,本节点一直在running。
子节点执行时,本节点还是需要running。当超时后,才返回success。
当子节点执行结束,本节点需要重置数据,以便子节点可以再次执行。(上面已经做了,OnChildExecuted)
完整代码:
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
using System.Collections.Generic;
using UnityEngine;
public class BTMeleeCasting : Decorator
{
public SharedFloat waitTime = 1;
private float waitDuration;
private float startTime;
private bool childExecuting = false;
public List<int> dmgList = new List<int>();
public override int MaxChildren()
{
return 1;
}
public override void OnStart()
{
startTime = Time.time;
waitDuration = waitTime.Value;
}
public override bool CanExecute()
{
// 子节点未执行,可执行本节点逻辑
if(!childExecuting)
{
DLogic();
}
return DCanExec();
}
public override void OnChildStarted()
{
childExecuting = true;
}
public override void OnEnd()
{
childExecuting = false;
}
public override void OnChildExecuted(TaskStatus childStatus)
{
// 子节点执行成功,还原条件
if(childStatus == TaskStatus.Success)
{
DReset();
}
base.OnChildExecuted(childStatus);
}
// 本节点逻辑,当子节点未执行时,每帧都先执行此逻辑,然后判定子节点是否可以执行。
private void DLogic()
{
if (Input.GetKeyDown(KeyCode.P))
{
dmgList.Add(1);
dmgList.Add(2);
}
}
// 子节点执行条件重置
private void DReset()
{
dmgList.Clear();
}
// 判定子节点是否可以执行
private bool DCanExec()
{
// 超时,不可执行子节点
if (startTime + waitDuration < Time.time)
{
return false;
}
// 列表中有项,就可以执行子节点
return dmgList.Count > 0;
}
public override TaskStatus OverrideStatus(TaskStatus status)
{
// 超时,返回成功
if (startTime + waitDuration < Time.time)
{
return TaskStatus.Success;
}
// 否则就一直跑,或者子节点失败,返回失败。
else
{
if(status == TaskStatus.Failure)
{
return TaskStatus.Failure;
}
return TaskStatus.Running;
}
}
}
测试方式及预测:
按压P键位,会执行一次子节点,再按压P,再次执行子节点。
当超时,全部返回。
网友评论