รูปแบบสำหรับการดำเนินการของเกม


11

มีรูปแบบที่ยอมรับโดยทั่วไปสำหรับการดำเนินการต่าง ๆ ภายในเกมหรือไม่ วิธีที่ผู้เล่นสามารถทำการกระทำและ AI อาจทำการกระทำเช่นการย้ายการโจมตีการทำลายตนเองเป็นต้น

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

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

ดังนั้น ... มีวิธีที่ยอมรับมากขึ้นในการทำเช่นนี้หรือเสียงไม่เป็นไร?


มันฟังดูดีคำถามคือคุณหมายถึงคิว? เกมส่วนใหญ่มีการตอบสนองที่รวดเร็วมาก? "AI สามารถเข้าคิวการกระทำที่แตกต่าง"
AturSams

จุดดี. ไม่มีคิว เพียงแค่ต้องรู้ว่าไม่ว่างและหากไม่ดำเนินการกระทำ
Arkiliknam

คำตอบ:


18

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

ฉันมีคลาสฐานนามธรรมสำหรับการกระทำซึ่งไม่มีอะไรมากไปกว่าเสื้อคลุมรอบUpdateวิธีที่เรียกว่าแต่ละเฟรมและFinishedแฟล็กเพื่อระบุเมื่อการกระทำเสร็จสิ้นลง

abstract class Action
{
    abstract void Update(float elapsed);
    bool Finished;
}

ฉันยังใช้รูปแบบการออกแบบคอมโพสิตเพื่อสร้างประเภทของการกระทำที่สามารถโฮสต์และดำเนินการการกระทำอื่น ๆ ได้ นี่ก็เป็นคลาสนามธรรม เดือดลงไปที่:

abstract class CompositeAction : Action
{
    void Add(Action action) { Actions.Add(action); }
    List<Action> Actions;
}

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

class Parallel : CompositeAction
{
    override void Update(float elapsed) 
    {
        Actions.ForEach(a=> a.Update(elapsed));
        Actions.RemoveAll(a => a.Finished);
        Finished = Actions.Count == 0;
    }
}

และหนึ่งที่ควบคุมการกระทำตามลำดับ

class Sequence : CompositeAction
{
    override void Update(float elapsed) 
    {
        if (Actions.Count > 0) 
        {
            Actions[0].Update(elapsed);
            if (Actions[0].Finished)
                Actions.RemoveAt(0);
        }
        Finished = Actions.Count == 0;
    }
 }

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

// Create a parallel action to work as an action manager
Parallel actionManager = new Parallel();

// Send character1 to destination
Sequence actionGroup1 = new Sequence();
actionGroup1.Add(new MoveAction(character1, destination));
actionGroup1.Add(new TalkAction(character1, "Arrived at destination!"));
actionManager.Add(actionGroup1);

// Make character2 use a potion on himself
Sequence actionGroup2 = new Sequence();
actionGroup2.Add(new RemoveItemAction(character2, ItemType.Potion));
actionGroup2.Add(new SetHealthAction(character2, character2.MaxHealth));
actionGroup2.Add(new TalkAction(character2, "I feel better now!"));
actionManager.Add(actionGroup2);

// Every frame update the action manager
actionManager.Update(elapsed);

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

  • ดูเพิ่มเติม: คำตอบที่เกี่ยวข้องกันที่นี่

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

1
โดยปกติแล้วการกระทำของฉันเพียง แต่เปลี่ยนสถานะของหน่วยงานและการเปลี่ยนแปลงใด ๆ ที่จะส่งออกที่แสดงผลเกิดขึ้นเป็นผลมาจากการเปลี่ยนแปลงของรัฐที่ไม่ได้โดยวิธีการของการกระทำของตัวเอง ตัวอย่างเช่นเมื่อใช้โหมดแสดงภาพทันทีไม่จำเป็นต้องมีขั้นตอนเพิ่มเติมเนื่องจากDrawวิธีการสร้างอยู่ด้านบนของสถานะของเอนทิตีและการเปลี่ยนแปลงเป็นไปโดยอัตโนมัติ ในโหมดแสดงภาพคงเช่น Flash คุณสามารถใช้รูปแบบที่สังเกตได้เพื่อทำการเปลี่ยนแปลงเอนทิตีของคุณเผยแพร่ไปยังวัตถุแสดงผลหรือทำการเชื่อมต่อด้วยตนเองภายในเอนทิตี้ของตัวเอง
David Gouveia

1
ในสถานการณ์แรกสมมติว่าCharacterคลาสของคุณมีPositionคุณสมบัติและDrawวิธีการที่อ่านค่าปัจจุบันของPositionและวาดภาพที่ถูกต้องที่นั่น ในสถานการณ์นี้คุณจะต้องอัปเดตค่าPositionว่าผลลัพธ์จะปรากฏบนหน้าจอโดยอัตโนมัติ
David Gouveia

1
สถานการณ์ที่สองคือเมื่อคุณCharacterมีPositionทรัพย์สิน แต่มอบหมายให้เรนเดอร์ไปยังSpriteวัตถุบางประเภทซึ่งจะถูกแสดงผลโดยกราฟฉากหรือบางสิ่งบางอย่างโดยอัตโนมัติ ในสถานการณ์นี้คุณต้องแน่ใจว่าทั้งตำแหน่งของตัวละครและตำแหน่งสไปรต์นั้นซิงค์กันอยู่เสมอซึ่งเกี่ยวข้องกับการทำงานอีกเล็กน้อย อย่างไรก็ตามในทั้งสองกรณีฉันไม่เห็นว่าทำไมผู้จัดการการดำเนินการควรมีส่วนเกี่ยวข้องกับเรื่องนี้ :)
David Gouveia

1
ทั้งสองวิธีมีข้อดีและข้อเสีย .. ฉันไปกับวิธีที่สองสำหรับเกม 2D ของฉันและฉันรู้สึกเสียใจตลอดเวลาเพราะมันซับซ้อนมากขึ้นในการซิงค์ทุกอย่าง แต่ก็มีข้อดีเช่นกันเมื่อพยายามตรวจสอบเอนทิตีที่ถูกคลิกหรือสิ่งที่ควรหรือไม่ถูกดึงเพราะทุกสิ่งที่จะแสดงผลนั้นมีอยู่ในโครงสร้างข้อมูลเดียวกันแทนที่จะกระจายอยู่ระหว่างเอนทิตี N ชนิด
David Gouveia
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.