ยึดเอาต้นไม้พฤติกรรม


25

ฉันพยายามที่จะทำให้หัวของฉันอยู่รอบ ๆ ต้นไม้พฤติกรรมดังนั้นฉันจึงพยายามหารหัสทดสอบ สิ่งหนึ่งที่ฉันกำลังดิ้นรนคือจะเอาไว้ใช้โหนดที่กำลังทำงานอยู่ในขณะที่บางสิ่งที่มีลำดับความสำคัญสูงกว่ามาพร้อม

พิจารณาต้นไม้พฤติกรรมที่เรียบง่ายและเป็นตำนานต่อไปนี้สำหรับทหาร:

ป้อนคำอธิบายรูปภาพที่นี่

สมมติว่ามีเห็บจำนวนหนึ่งหายไปและไม่มีศัตรูอยู่ใกล้ทหารกำลังยืนอยู่บนพื้นหญ้าดังนั้นโหนด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>
{
}

ใครบ้างมีความเข้าใจอย่างถ่องแท้ที่สามารถนำพาฉันไปในทิศทางที่ถูกต้อง? ความคิดของฉันอยู่ในแนวที่ถูกต้องหรือไร้เดียงสาอย่างที่ฉันกลัวหรือเปล่า?


คุณต้องดูที่เอกสารนี้: chrishecker.com/My_liner_notes_for_spore/ …ที่นี่เขาอธิบายว่าต้นไม้เดินอย่างไรไม่เหมือนเครื่องรัฐ แต่จากรูทที่แต่ละเห็บซึ่งเป็นเคล็ดลับที่แท้จริงในการทำปฏิกิริยา BT ไม่ควรต้องการข้อยกเว้นหรือเหตุการณ์ พวกมันรวมระบบต่างๆเข้าด้วยกันและตอบสนองต่อทุกสถานการณ์ด้วยการไหลลงมาจากรากเสมอ ซึ่งเป็นวิธีการ preemptivity ทำงานถ้าเงื่อนไขภายนอกของการตรวจสอบลำดับความสำคัญสูงกว่าก็จะไหลที่นั่น (โทรStop()กลับบางส่วนก่อนออกจากโหนดที่ใช้งานอยู่)
v.oddou

นี้aigamedev.com/open/article/popular-behavior-tree-designยังเป็นรายละเอียดอย่างมาก
v.oddou

คำตอบ:


6

ฉันพบว่าตัวเองกำลังถามคำถามเดียวกันกับคุณและฉันก็มีการสนทนาสั้น ๆ ที่ยอดเยี่ยมในส่วนความคิดเห็นของหน้าบล็อกนี้ที่ฉันได้รับพร้อมวิธีแก้ไขปัญหาอื่น

สิ่งแรกคือการใช้โหนดพร้อมกัน Concurrent node เป็นโหนดผสมแบบพิเศษ ประกอบด้วยลำดับของการตรวจสอบเงื่อนไขเบื้องต้นตามด้วยโหนดการดำเนินการเดียว มันอัพเดตโหนดลูกทั้งหมดแม้ว่าโหนดการดำเนินการจะอยู่ในสถานะ 'กำลังทำงาน' (ต่างจากโหนดลำดับที่ต้องเริ่มการอัปเดตจากโหนดชายด์ที่รันอยู่ในปัจจุบัน)

แนวคิดหลักคือการสร้างสถานะการส่งคืนสองสถานะเพิ่มเติมสำหรับโหนดการดำเนินการ: "การยกเลิก" และ "ยกเลิก"

ความล้มเหลวของการตรวจสอบเงื่อนไขเบื้องต้นในโหนดที่เกิดขึ้นพร้อมกันเป็นกลไกที่ทริกเกอร์การยกเลิกการทำงานของโหนดการดำเนินการ หากโหนดการดำเนินการไม่จำเป็นต้องมีตรรกะการยกเลิกในระยะยาวแล้วมันจะกลับ 'ยกเลิก' ทันที มิฉะนั้นจะเปลี่ยนเป็นสถานะ 'ยกเลิก' ซึ่งคุณสามารถใส่ตรรกะที่จำเป็นทั้งหมดเพื่อหยุดการกระทำที่ถูกต้อง


สวัสดีและยินดีต้อนรับสู่ GDSE คงจะดีมากถ้าคุณสามารถเปิดคำตอบนั้นจากบล็อกนั้นถึงที่นี่และในตอนท้ายลิงก์ไปยังบล็อกนั้น ลิงค์มีแนวโน้มที่จะตายมีคำตอบอย่างเต็มที่ทำให้มันขัดขืนมากขึ้น คำถามมี 8 คะแนนโหวตตอนนี้คำตอบที่ดีจะน่ากลัว
Katu

ฉันไม่คิดว่าสิ่งใดที่ทำให้ต้นไม้พฤติกรรมกลับไปสู่กลไกสถานะ จำกัด เป็นทางออกที่ดี วิธีการของคุณดูเหมือนว่าฉันจะต้องมองเห็นเงื่อนไขการออกทั้งหมดของแต่ละรัฐ เมื่อเป็นจริงแล้วข้อเสียของ FSM! BT มีข้อได้เปรียบในการเริ่มต้นที่รูทนี่เป็นการสร้าง FSM ที่เชื่อมต่ออย่างเต็มที่โดยปริยายหลีกเลี่ยงพวกเราที่จะเขียนเงื่อนไขทางออกอย่างชัดเจน
v.oddou

5

ฉันคิดว่าทหารของคุณอาจถูกย่อยสลายในจิตใจและร่างกาย (และสิ่งอื่นใด) จากนั้นร่างกายอาจย่อยสลายเป็นขาและมือ จากนั้นทุกส่วนต้องการแผนผังพฤติกรรมของตัวเองและส่วนต่อประสานสาธารณะสำหรับคำขอจากส่วนที่สูงหรือต่ำกว่า

ดังนั้นแทนที่จะจัดการไมโครแอคชั่นทุกการกระทำคุณเพียงแค่ส่งข้อความแบบทันทีทันใดเช่น "ร่างกายนั่งลงสักครู่" หรือ "ร่างกายวิ่งไปที่นั่น" และร่างกายจะจัดการภาพเคลื่อนไหวการเปลี่ยนสถานะความล่าช้าและสิ่งอื่น ๆ สำหรับ คุณ.

อีกทางหนึ่งร่างกายอาจจัดการพฤติกรรมเช่นนี้ได้ด้วยตัวเอง ถ้ามันไม่มีออเดอร์มันอาจถามว่า "เรานั่งที่นี่ได้ไหม" น่าสนใจยิ่งขึ้นเนื่องจากการห่อหุ้มคุณสามารถสร้างแบบจำลองคุณสมบัติเช่นความเหนื่อยล้าหรือการทำให้งัน

คุณอาจแลกเปลี่ยนชิ้นส่วน - ทำช้างด้วยสติปัญญาของซอมบี้เพิ่มปีกให้กับมนุษย์ (เขาไม่เคยสังเกตเห็น) หรืออะไรก็ตาม

หากปราศจากการสลายตัวเช่นนี้ฉันพนันได้เลยว่าคุณมีความเสี่ยงที่จะพบกับการระเบิดแบบ combinatorial ไม่ช้าก็เร็ว

นอกจากนี้: http://www.valvesoftware.com/publications/2009/ai_systems_of_l4d_mike_booth.pdf


ขอบคุณ เมื่ออ่านคำตอบของคุณ 3 ครั้งฉันคิดว่าฉันเข้าใจ ฉันจะอ่าน PDF ในสุดสัปดาห์นี้
ฉัน -

1
เมื่อคิดเกี่ยวกับเรื่องนี้ในชั่วโมงที่ผ่านมาฉันไม่แน่ใจว่าฉันเข้าใจความแตกต่างระหว่างการมี BTs แยกต่างหากสำหรับจิตใจ & ร่างกายกับ BT เดียวที่ถูกย่อยเป็นต้นไม้ย่อย (อ้างอิงผ่านมัณฑนากรพิเศษพร้อมสคริปต์เวลาสร้าง ผูกทุกอย่างเข้าด้วยกันเป็นหนึ่ง BT ใหญ่) ดูเหมือนว่าสำหรับฉันแล้วสิ่งนี้จะให้ประโยชน์ที่เป็นนามธรรมคล้ายกันและอาจทำให้เข้าใจได้ง่ายขึ้นว่าเอนทิตีที่กำหนดนั้นทำงานอย่างไรเพราะคุณไม่ต้องมองหา BT ที่แยกกันหลายตัว อย่างไรก็ตามฉันอาจจะไร้เดียงสา
ฉัน -

@ user13414 ความแตกต่างคือคุณจะต้องใช้สคริปต์พิเศษในการสร้างต้นไม้เมื่อใช้การเข้าถึงทางอ้อม (เช่นเมื่อโหนดร่างกายต้องถามต้นไม้ที่วัตถุแสดงถึงขา) อาจเพียงพอและไม่ต้องใช้สมองเพิ่มเติม รหัสน้อยลงข้อผิดพลาดน้อยลง นอกจากนี้คุณจะสูญเสียความสามารถในการสลับทรีย่อย (อย่างง่าย) ที่รันไทม์ แม้ว่าคุณไม่ต้องการความยืดหยุ่นเช่นนี้คุณจะไม่เสียอะไรเลย (รวมถึงความเร็วในการประมวลผล)
เงาในสายฝน

3

เมื่อคืนนอนอยู่บนเตียงฉันมีบางสิ่งที่ศักดิ์สิทธิ์ว่าฉันจะทำอย่างไรกับเรื่องนี้โดยไม่ต้องแนะนำความซับซ้อนที่ฉันมีต่อคำถามของฉัน มันเกี่ยวข้องกับการใช้คอมโพสิตแบบ "ขนาน" IMHO) "ขนาน" นี่คือสิ่งที่ฉันคิด:

ป้อนคำอธิบายรูปภาพที่นี่

หวังว่าจะยังอ่านได้ค่อนข้างดี จุดสำคัญคือ:

  • ลำดับSit down / Delay / Stand upเป็นลำดับภายในลำดับคู่ขนาน ( A ) ในทุกเห็บลำดับขนานจะตรวจสอบสภาพใกล้ศัตรู (ฤvertedษี) ถ้าข้าศึกเป็นที่อยู่ใกล้กับสภาพล้มเหลวและดังนั้นก็ไม่ลำดับขนานทั้งหมด (ทันทีแม้ว่าลำดับเด็กอยู่ตรงกลางผ่านนั่งลง , ความล่าช้าหรือยืนขึ้น )
  • เมื่อความล้มเหลวตัวเลือกBด้านบนลำดับขนานจะกระโดดลงไปในตัวเลือกCเพื่อจัดการกับการหยุดชะงัก ที่สำคัญตัวเลือกCจะไม่ทำงานหากลำดับคู่ขนานAเสร็จสมบูรณ์แล้ว
  • ตัวเลือกCจากนั้นพยายามยืนขึ้นตามปกติ แต่ยังสามารถกระตุ้นให้เกิดภาพเคลื่อนไหวที่สะดุดถ้าทหารอยู่ในตำแหน่งที่อึดอัดเกินไปที่จะยืนขึ้น

ฉันคิดว่ามันจะใช้งานได้ (กำลังจะลองด้วยความรวดเร็วในไม่ช้า) แม้ว่าจะมีความยุ่งเหยิงมากกว่าที่ฉันคิดไว้เล็กน้อย สิ่งที่ดีคือในที่สุดฉันจะสามารถห่อหุ้มต้นไม้ย่อยเป็นชิ้นส่วนของตรรกะที่ใช้ซ้ำได้และอ้างอิงจากหลายจุด นั่นจะช่วยลดความกังวลส่วนใหญ่ของฉันที่นั่นดังนั้นฉันคิดว่านี่เป็นวิธีแก้ปัญหาที่ทำงานได้

แน่นอนฉันยังคงอยากฟังถ้าใครมีความคิดเกี่ยวกับเรื่องนี้

UPDATE : แม้ว่าวิธีการนี้ใช้งานได้ทางเทคนิค แต่ฉันก็ตัดสินใจว่าจะให้ sux นั่นเป็นเพราะต้นไม้ย่อยที่ไม่เกี่ยวข้องจำเป็นต้อง "รู้" เกี่ยวกับเงื่อนไขที่กำหนดไว้ในส่วนอื่น ๆ ของต้นไม้เพื่อที่พวกเขาจะสามารถกระตุ้นการตายของตัวเอง ในขณะที่การแบ่งปันการอ้างอิงต้นไม้ย่อยจะช่วยบรรเทาความเจ็บปวดนี้ได้ แต่ก็ยังคงตรงกันข้ามกับสิ่งที่เราคาดหวังเมื่อมองดูต้นไม้พฤติกรรม อันที่จริงฉันทำผิดพลาดครั้งที่สองในเข็มที่ง่ายมาก

ดังนั้นฉันจะลงไปที่เส้นทางอื่น: การสนับสนุนที่ชัดเจนสำหรับการจองภายในโมเดลวัตถุและคอมโพสิตพิเศษที่ช่วยให้ชุดการกระทำที่แตกต่างกันสามารถดำเนินการได้เมื่อมีการจองล่วงหน้า ฉันจะโพสต์คำตอบแยกต่างหากเมื่อฉันทำงานบางอย่าง


1
หากคุณต้องการนำทรีย่อยกลับมาใช้อีกครั้งตรรกะสำหรับเวลาที่จะขัดจังหวะ ("ศัตรูใกล้" ที่นี่) น่าจะไม่ได้เป็นส่วนหนึ่งของทรีย่อย แต่ระบบอาจขอให้ทรีย่อย (เช่น B ที่นี่) ขัดจังหวะตัวเองเนื่องจากการกระตุ้นที่มีลำดับความสำคัญสูงกว่าและจากนั้นจะข้ามไปที่โหนดการขัดจังหวะที่ทำเครื่องหมายพิเศษ (C ที่นี่) ที่จะจัดการรับอักขระกลับสู่สถานะมาตรฐานบางแห่ง เช่นยืน บิตเช่นต้นไม้พฤติกรรมเทียบเท่ากับการจัดการข้อยกเว้น
นาธานรีด

1
คุณยังสามารถรวมตัวจัดการการขัดจังหวะหลาย ๆ อันได้ทั้งนี้ขึ้นอยู่กับสิ่งกระตุ้นที่ขัดจังหวะ ตัวอย่างเช่นหาก NPC กำลังนั่งอยู่และเริ่มก่อไฟคุณอาจไม่ต้องการให้เขายืนขึ้น (และแสดงเป้าหมายที่ใหญ่กว่า) แต่ให้อยู่ในระดับต่ำและช่วงชิงเพื่อปกปิด
นาธานรีด

@ นาธาน: ตลกที่คุณพูดถึงเกี่ยวกับ "การจัดการข้อยกเว้น" วิธีที่เป็นไปได้ครั้งแรกที่ฉันคิดขึ้นเมื่อคืนนี้คือความคิดของคอมโพสิตที่ได้รับการยกเว้นซึ่งจะมีลูกสองคน: หนึ่งสำหรับการดำเนินการตามปกติและอีกหนึ่งสำหรับการดำเนินการที่ถูกจองล่วงหน้า หากเด็กปกติผ่านหรือล้มเหลวผลลัพธ์นั้นจะแพร่กระจายขึ้น เด็กที่ถูกจองจะวิ่งได้ก็ต่อเมื่อมีการจองเกิดขึ้น โหนดทั้งหมดจะมีPreempt()วิธีการซึ่งจะไหลผ่านต้นไม้ อย่างไรก็ตามสิ่งเดียวที่จะ "จัดการ" อย่างนี้จริง ๆ ก็คือคอมโพสิตที่ยึดเอาไว้ซึ่งจะเปลี่ยนเป็นโหนดลูกที่ถูกจองไว้ทันที
ฉัน -

จากนั้นฉันก็นึกถึงวิธีการแบบขนานที่ฉันเขียนไว้ด้านบนและมันก็ดูดีกว่าเพราะมันไม่ต้องการ cruft พิเศษตลอดทั้ง API ถึงจุดที่คุณห่อหุ้มต้นไม้ย่อยฉันคิดว่ามีความซับซ้อนเกิดขึ้นซึ่งจะเป็นจุดทดแทนที่เป็นไปได้ นั่นอาจเป็นจุดที่คุณมีหลายเงื่อนไขที่ต้องตรวจสอบร่วมกันบ่อยๆ ในกรณีดังกล่าวรูทของการทดแทนจะเป็นคอมโพสิตตามลำดับโดยมีเงื่อนไขหลายอย่างเป็นลูก
ฉัน -

ฉันคิดว่า Subtrees รู้เงื่อนไขที่พวกเขาจำเป็นต้อง "กดปุ่ม" ก่อนที่จะดำเนินการมีความเหมาะสมอย่างสมบูรณ์แบบเพราะมันทำให้พวกเขามีอยู่ในตัวเองและชัดเจนมากเมื่อเทียบกับนัย หากเป็นข้อกังวลที่ใหญ่กว่านั้นอย่ารักษาเงื่อนไขไว้ในทรีย่อย แต่ที่ "ไซต์การโทร" ของมัน
Seivan

2

นี่คือทางออกที่ฉันได้ตัดสินในตอนนี้ ...

  • Nodeคลาสพื้นฐานของฉันมีInterruptวิธีการซึ่งโดยค่าเริ่มต้นจะไม่ทำอะไรเลย
  • เงื่อนไขคือโครงสร้าง "ชั้นหนึ่ง" ซึ่งพวกเขาจะต้องส่งคืนbool(ดังนั้นหมายความว่าพวกเขานั้นรวดเร็วในการดำเนินการและไม่ต้องการการอัพเดทมากกว่าหนึ่งครั้ง)
  • Node แสดงชุดของเงื่อนไขแยกต่างหากกับชุดของโหนดลูก
  • Node.Executeรันเงื่อนไขทั้งหมดก่อนและล้มเหลวทันทีหากเงื่อนไขใด ๆ ล้มเหลว หากเงื่อนไขสำเร็จ (หรือไม่มี) ก็จะเรียกExecuteCoreเพื่อให้คลาสย่อยสามารถทำงานได้จริง มีพารามิเตอร์ที่อนุญาตให้ข้ามเงื่อนไขได้ด้วยเหตุผลที่คุณจะเห็นด้านล่าง
  • Nodeยังอนุญาตให้มีเงื่อนไขในการดำเนินการแยกด้วยCheckConditionsวิธีการ แน่นอนNode.Executeจริง ๆ แล้วแค่เรียกCheckConditionsเมื่อมันจำเป็นต้องตรวจสอบเงื่อนไข
  • Selectorคอมโพสิตของฉันตอนนี้เรียกร้องCheckConditionsให้เด็กแต่ละคนที่จะพิจารณาดำเนินการ หากเงื่อนไขล้มเหลวมันจะเคลื่อนที่ไปตามลูกคนถัดไป หากพวกเขาผ่านมันจะตรวจสอบว่ามีเด็กที่กำลังดำเนินการอยู่แล้ว ถ้าเป็นเช่นนั้นจะเรียกInterruptและล้มเหลว นั่นคือทั้งหมดที่ทำได้ในจุดนี้โดยหวังว่าโหนดที่รันอยู่ในปัจจุบันจะตอบสนองต่อการร้องขอขัดจังหวะซึ่งสามารถทำได้โดย ...
  • ฉันได้เพิ่มInterruptibleโหนดซึ่งเป็นมัณฑนากรพิเศษเพราะมันมีการไหลของตรรกะเป็นปกติเป็นลูกที่ตกแต่งแล้วโหนดที่แยกต่างหากสำหรับการขัดจังหวะ มันรันลูกปกติของมันที่จะเสร็จสมบูรณ์หรือล้มเหลวตราบใดที่มันไม่ถูกขัดจังหวะ ถ้ามันถูกขัดจังหวะมันจะสลับไปยังการดำเนินการหยุดชะงักการจัดการโหนดลูกทันทีซึ่งอาจซับซ้อนเป็นต้นไม้ย่อยตามที่ต้องการ

ผลลัพธ์ที่ได้คืออะไรทำนองนี้ที่นำมาจากเข็มของฉัน:

ป้อนคำอธิบายรูปภาพที่นี่

ด้านบนเป็นต้นไม้พฤติกรรมสำหรับผึ้งซึ่งรวบรวมน้ำหวานและส่งคืนไปยังรังของมัน เมื่อไม่มีน้ำหวานและไม่ได้อยู่ใกล้ดอกไม้ที่มีอยู่มันก็เดินไป:

ป้อนคำอธิบายรูปภาพที่นี่

ถ้าโหนดนี้ไม่ถูกขัดจังหวะมันจะไม่มีทางล้มเหลวดังนั้นผึ้งจะเดินไปเรื่อย ๆ อย่างไรก็ตามเนื่องจากโหนดพาเรนต์เป็นตัวเลือกและมีชายน์ที่มีลำดับความสำคัญสูงกว่าสิทธิ์ของพวกเขาสำหรับการดำเนินการจึงถูกตรวจสอบอย่างต่อเนื่อง หากเงื่อนไขของพวกเขาผ่านไปตัวเลือกจะเพิ่มการขัดจังหวะและแผนผังย่อยด้านบนจะสลับไปที่พา ธ "ขัดจังหวะ" ทันทีซึ่งจะทำให้เกิดความล้มเหลวโดยเร็ว แน่นอนมันสามารถกระทำการอื่นได้ก่อน แต่เข็มของฉันไม่มีอะไรจะทำนอกจากการประกันตัว

แม้ว่าจะผูกคำถามนี้กลับไปที่คำถามของฉันคุณอาจจินตนาการว่าเส้นทาง "ขัดจังหวะ" อาจพยายามย้อนกลับแอนิเมชั่นนั่งลงและหากไม่ทำเช่นนั้นจะทำให้ทหารสะดุด ทั้งหมดนี้จะทำให้การเปลี่ยนผ่านสู่สถานะลำดับความสำคัญสูงขึ้นและนั่นคือเป้าหมายที่แท้จริง

ฉันคิดว่าฉันมีความสุขกับวิธีการนี้ - โดยเฉพาะอย่างยิ่งชิ้นส่วนหลักที่ฉันร่างไว้ด้านบน - แต่ตามจริงแล้วมันเป็นคำถามเพิ่มเติมเกี่ยวกับการเพิ่มจำนวนของการใช้งานตามเงื่อนไขและการกระทำที่เฉพาะเจาะจง ฉันไม่แน่ใจด้วยซ้ำไปว่าฉันสามารถตอบคำถามเหล่านี้ได้ดังนั้นฉันจะคิดต่อไป


1

ฉันแก้ไขปัญหาเดียวกันด้วยการประดิษฐ์มัณฑนากร "เมื่อ" มันมีเงื่อนไขและพฤติกรรมของเด็กสองคน ("จากนั้น" และ "เป็นอย่างอื่น") เมื่อมีการใช้งาน "เมื่อ" มันจะตรวจสอบสภาพและขึ้นอยู่กับผลลัพธ์ของมันทำงานแล้ว / เด็ก หากการเปลี่ยนแปลงผลลัพธ์ตามเงื่อนไขลูกที่รันอยู่ถูกรีเซ็ตและลูกที่สอดคล้องกับสาขาอื่นจะเริ่ม หากเด็กเสร็จสิ้นการดำเนินการทั้งหมด "เมื่อ" เสร็จสิ้นการดำเนินการ

จุดสำคัญคือไม่เหมือนกับ BT เริ่มต้นในคำถามนี้ที่มีการตรวจสอบสภาพเฉพาะเมื่อเริ่มต้นตามลำดับ "เมื่อ" ของฉันยังคงตรวจสอบเงื่อนไขขณะที่ทำงานอยู่ ดังนั้นส่วนบนของแผนผังพฤติกรรมจะถูกแทนที่ด้วย:

When[EnemyNear]
  Then
    AttackSequence
  Otherwise
    When[StandingOnGrass]
      Then
        IdleSequence
      Otherwise
        Hum a tune

สำหรับการใช้งานขั้นสูง "เมื่อ" ผู้ใช้ต้องการแนะนำการกระทำ "รอ" ที่ไม่ได้ทำอะไรเลยตามเวลาที่กำหนดหรือไม่มีกำหนด (จนกว่าจะถูกรีเซ็ตโดยพฤติกรรมของผู้ปกครอง) นอกจากนี้หากคุณต้องการสาขา "เมื่อ" เพียงสาขาเดียวสาขาอื่นสามารถมีการกระทำ "สำเร็จ" หรือ "ล้มเหลว" การที่ respiveley นั้นจะประสบความสำเร็จและล้มเหลวได้ทันที


ฉันคิดว่าวิธีการนี้ใกล้เคียงกับสิ่งที่นักประดิษฐ์ดั้งเดิมของ BT มีอยู่ในใจ มันใช้การไหลแบบไดนามิกมากขึ้นซึ่งเป็นสาเหตุที่รัฐ "ทำงาน" ใน BT เป็นรัฐที่อันตรายมากซึ่งควรจะใช้น้อยมาก เราควรออกแบบ BTs อยู่ในใจเสมอว่าความเป็นไปได้ที่จะกลับมาที่รูตได้ตลอดเวลา
v.oddou

0

ในขณะที่ฉันมาสาย แต่หวังว่าสิ่งนี้จะช่วยได้ ส่วนใหญ่เป็นเพราะฉันต้องการตรวจสอบให้แน่ใจว่าฉันไม่ได้พลาดบางสิ่งบางอย่างกับตัวเองเพราะฉันพยายามคิดออกเช่นกัน ฉันได้ขอยืมความคิดนี้เป็นส่วนใหญ่Unrealแล้ว แต่ถ้าไม่ทำให้เป็นDecoratorคุณสมบัติบนฐานNodeหรือผูกติดอยู่กับBlackboardมันเป็นเรื่องทั่วไปมากขึ้น

สิ่งนี้จะแนะนำชนิดโหนดใหม่ที่เรียกว่าGuardซึ่งเป็นเหมือนการรวมกันของDecoratorและCompositeและมีcondition() -> Resultลายเซ็นข้างupdate() -> Result

มีสามโหมดเพื่อระบุว่าควรยกเลิกเมื่อใด Guardส่งคืนSuccessหรือFailedการยกเลิกจริงขึ้นอยู่กับผู้โทร ดังนั้นสำหรับการSelectorโทรGuard:

  1. ยกเลิก.self -> ยกเลิกเฉพาะGuard(และลูกที่กำลังวิ่งอยู่เท่านั้น) หากมันกำลังทำงานและมีเงื่อนไขFailed
  2. ยกเลิก.lower-> ยกเลิกเฉพาะโหนดที่มีลำดับความสำคัญต่ำกว่าหากพวกเขากำลังทำงานและเงื่อนไขนั้นเป็นSuccessหรือRunning
  3. ยกเลิก.both -> ทั้งสอง.selfและ.lowerขึ้นอยู่กับเงื่อนไขและโหนดที่กำลังทำงาน คุณต้องการยกเลิกตัวเองหากกำลังทำงานอยู่และจะกำหนดfalseหรือยกเลิกโหนดที่กำลังทำงานหากพวกเขาพิจารณาว่ามีลำดับความสำคัญต่ำกว่าตามCompositeกฎ ( Selectorในกรณีของเรา) Successถ้าเงื่อนไขเป็น กล่าวอีกนัยหนึ่งมันเป็นทั้งแนวคิดรวมกัน

ชอบ Decoratorและแตกต่างCompositeจะใช้เวลาเพียงเด็กคนเดียว

แม้ว่าจะGuardใช้เวลาเพียงเด็กคนเดียวที่คุณสามารถทำรังเป็นจำนวนมากSequences, Selectorsหรือชนิดอื่น ๆNodesตามที่คุณต้องการรวมทั้งอื่น ๆหรือGuardsDecorators

Selector1 Guard.both[Sequence[EnemyNear?]] Sequence1 MoveToEnemy Attack Selector2 Sequence2 StandingOnGrass? Idle HumATune

ในสถานการณ์ข้างต้นทุกครั้ง Selector1มีการอัพเดตมันจะทำการตรวจสอบสภาพของยามที่เกี่ยวข้องกับลูก ๆ ของมันเสมอ ในกรณีข้างต้นSequence1ได้รับการปกป้องและจำเป็นต้องตรวจสอบก่อนSelector1ดำเนินการต่อกับrunningงาน

เมื่อใดก็ตามที่Selector2หรือSequence1กำลังทำงานทันทีที่EnemyNear?กลับมาsuccessในระหว่างการGuards condition()ตรวจสอบแล้วSelector1จะออกขัดจังหวะ / ยกเลิกไปที่running nodeแล้วดำเนินการต่อตามปกติ

กล่าวอีกนัยหนึ่งเราสามารถตอบสนองต่อสาขา "ว่าง" หรือ "โจมตี" โดยขึ้นอยู่กับเงื่อนไขสองสามประการที่ทำให้พฤติกรรมมีปฏิกิริยาโต้ตอบมากกว่าที่เราตัดสิน Parallel

นอกจากนี้ยังช่วยให้คุณสามารถปกป้องซิงเกิ้ลNodeที่มีลำดับความสำคัญสูงกว่าNodesในการทำงานในแบบเดียวกันComposite

Selector1 Guard.both[Sequence[EnemyNear?]] Sequence1 MoveToEnemy Attack Selector2 Guard.both[StandingOnGrass?] Idle HumATune

ถ้าHumATuneเป็นระยะยาวNode, มักจะตรวจสอบว่าคนแรกถ้ามันไม่ได้สำหรับSelector2 Guardดังนั้นหาก npc ถูกเทเลพอร์ทไปยังแพทช์หญ้าในครั้งต่อไปSelector2มันจะตรวจสอบGuardและยกเลิกHumATuneเพื่อที่จะทำงานIdle

หากมันหายตัวไปจากหญ้ามันจะยกเลิกการทำงานโหนด ( Idle) และย้ายไปHumATune

ตามที่คุณเห็นที่นี่การตัดสินใจขึ้นอยู่กับผู้โทรGuardไม่ใช่Guardตัวเอง กฎของผู้ที่ถือว่าlower priorityยังคงอยู่กับผู้โทร ในตัวอย่างทั้งสองก็เป็นที่กำหนดสิ่งที่ถือว่าเป็นSelectorlower priority

ถ้าคุณมีCompositeที่เรียกว่าแล้วคุณจะได้รับการกำหนดกฎระเบียบภายในการดำเนินการเฉพาะเจาะจงว่าRandom SelectorComposite

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.