ฉันพยายามที่จะทำให้หัวของฉันอยู่รอบ ๆ ต้นไม้พฤติกรรมดังนั้นฉันจึงพยายามหารหัสทดสอบ สิ่งหนึ่งที่ฉันกำลังดิ้นรนคือจะเอาไว้ใช้โหนดที่กำลังทำงานอยู่ในขณะที่บางสิ่งที่มีลำดับความสำคัญสูงกว่ามาพร้อม
พิจารณาต้นไม้พฤติกรรมที่เรียบง่ายและเป็นตำนานต่อไปนี้สำหรับทหาร:
สมมติว่ามีเห็บจำนวนหนึ่งหายไปและไม่มีศัตรูอยู่ใกล้ทหารกำลังยืนอยู่บนพื้นหญ้าดังนั้นโหนดSit downจึงถูกเลือกเพื่อดำเนินการ:
ตอนนี้การกระทำของSit downต้องใช้เวลาในการดำเนินการเนื่องจากมีภาพเคลื่อนไหวให้เล่นดังนั้นมันจึงกลับมาRunning
เป็นสถานะ เห็บหรือสองไปโดยภาพเคลื่อนไหวยังคงทำงานอยู่ แต่ศัตรูใกล้? ทริกเกอร์เงื่อนไขโหน ตอนนี้เราต้องจองโหนดSit down โดยเร็วเพื่อให้เราสามารถรันโหนดAttackได้ ทหารจะไม่นั่งลงด้วยซ้ำ - เขาอาจกลับทิศทางการเคลื่อนไหวของเขาหากเขาเพิ่งเริ่มนั่ง สำหรับความสมจริงที่เพิ่มขึ้นหากเขาผ่านจุดเปลี่ยนแปลงในภาพเคลื่อนไหวเราอาจเลือกที่จะให้เขานั่งลงแล้วยืนอีกครั้งหรืออาจให้เขาสะดุดในการตอบสนองต่อภัยคุกคามแทน
ลองอย่างที่ฉันเป็นฉันไม่สามารถหาคำแนะนำเกี่ยวกับวิธีจัดการสถานการณ์เช่นนี้ได้ วรรณกรรมและวิดีโอทั้งหมดที่ฉันบริโภคไปในช่วงไม่กี่วันที่ผ่านมา (และมันก็เยอะมาก) ดูเหมือนจะเป็นประเด็นนี้ สิ่งที่ใกล้เคียงที่สุดที่ฉันสามารถหาได้คือแนวคิดของการรีเซ็ตโหนดที่ทำงานอยู่ แต่นั่นก็ไม่ได้ให้โหนดเหมือนนั่งลงโอกาสที่จะพูดว่า "เดี๋ยวก่อนฉันยังไม่เสร็จ!"
ฉันคิดว่าอาจจะกำหนดPreempt()
หรือInterrupt()
วิธีการในNode
ชั้นฐานของฉัน โหนดที่แตกต่างกันสามารถจัดการกับมันวิธีที่พวกเขาเห็นพอดี Success
แต่ในกรณีนี้เราจะพยายามที่จะได้รับกลับมาทหารบนเท้าของเขาโดยเร็วและจากนั้นกลับมา ฉันคิดว่าวิธีการนี้จะกำหนดให้ฐานของฉันNode
มีแนวคิดเกี่ยวกับเงื่อนไขแยกต่างหากจากการกระทำอื่น ๆ ด้วยวิธีนี้เอ็นจิ้นสามารถตรวจสอบเงื่อนไขเท่านั้นและหากผ่านให้ยึดเอาโหนดดำเนินการใด ๆ ในปัจจุบันก่อนเริ่มการทำงานของการดำเนินการ หากไม่สามารถสร้างความแตกต่างนี้ได้เอ็นจิ้นจะต้องดำเนินการโหนดอย่างไม่เลือกปฏิบัติและอาจทำให้เกิดการกระทำใหม่ก่อนที่จะทำการจองก่อน
สำหรับการอ้างอิงด้านล่างเป็นชั้นฐานปัจจุบันของฉัน อีกครั้งนี่คือการขัดขวางดังนั้นฉันพยายามที่จะทำให้สิ่งต่าง ๆ ง่ายที่สุดเท่าที่จะทำได้และเพิ่มความซับซ้อนเมื่อฉันต้องการมันและเมื่อฉันเข้าใจมันซึ่งเป็นสิ่งที่ฉันกำลังดิ้นรนอยู่ในตอนนี้
public enum ExecuteResult
{
// node needs more time to run on next tick
Running,
// node completed successfully
Succeeded,
// node failed to complete
Failed
}
public abstract class Node<TAgent>
{
public abstract ExecuteResult Execute(TimeSpan elapsed, TAgent agent, Blackboard blackboard);
}
public abstract class DecoratorNode<TAgent> : Node<TAgent>
{
private readonly Node<TAgent> child;
protected DecoratorNode(Node<TAgent> child)
{
this.child = child;
}
protected Node<TAgent> Child
{
get { return this.child; }
}
}
public abstract class CompositeNode<TAgent> : Node<TAgent>
{
private readonly Node<TAgent>[] children;
protected CompositeNode(IEnumerable<Node<TAgent>> children)
{
this.children = children.ToArray();
}
protected Node<TAgent>[] Children
{
get { return this.children; }
}
}
public abstract class ConditionNode<TAgent> : Node<TAgent>
{
private readonly bool invert;
protected ConditionNode()
: this(false)
{
}
protected ConditionNode(bool invert)
{
this.invert = invert;
}
public sealed override ExecuteResult Execute(TimeSpan elapsed, TAgent agent, Blackboard blackboard)
{
var result = this.CheckCondition(agent, blackboard);
if (this.invert)
{
result = !result;
}
return result ? ExecuteResult.Succeeded : ExecuteResult.Failed;
}
protected abstract bool CheckCondition(TAgent agent, Blackboard blackboard);
}
public abstract class ActionNode<TAgent> : Node<TAgent>
{
}
ใครบ้างมีความเข้าใจอย่างถ่องแท้ที่สามารถนำพาฉันไปในทิศทางที่ถูกต้อง? ความคิดของฉันอยู่ในแนวที่ถูกต้องหรือไร้เดียงสาอย่างที่ฉันกลัวหรือเปล่า?
Stop()
กลับบางส่วนก่อนออกจากโหนดที่ใช้งานอยู่)