สถานะเกม 'สแต็ค'?


52

ฉันกำลังคิดเกี่ยวกับวิธีใช้สถานะเกมในเกมของฉัน สิ่งสำคัญที่ฉันต้องการคือ:

  • รัฐชั้นนำกึ่งโปร่งใสสามารถดูผ่านเมนูหยุดชั่วคราวเพื่อเกมที่อยู่เบื้องหลัง

  • บางสิ่งบางอย่าง OO- ฉันพบว่าการใช้และเข้าใจทฤษฎีนี้ง่ายขึ้นรวมถึงการรักษาองค์กรและการเพิ่มมากขึ้น



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

states.push_back(new MainMenuState());
states.push_back(new OptionsMenuState());
states.pop_front();

เพื่อเป็นตัวแทนของผู้เล่นที่โหลดจากนั้นไปที่ตัวเลือกแล้วเมนูหลัก
นี่เป็นความคิดที่ดีหรือ ... ฉันควรมองอย่างอื่นไหม

ขอบคุณ


คุณต้องการที่จะเห็น MainMenuState ด้านหลัง OptionsMenuState หรือไม่? หรือเพียงแค่หน้าจอเกมด้านหลัง OptionsMenuState?
Skizz

แผนคือว่ารัฐจะมีความทึบ / isTransparent ค่า / ธง ฉันจะตรวจสอบและดูว่ารัฐชั้นนำมีจริงนี้และถ้าเป็นเช่นนั้นมันมีคุณค่า จากนั้นแสดงด้วยความทึบที่มากกว่ารัฐอื่น ในกรณีนี้ฉันจะไม่
The Duck Communist

ฉันรู้ว่ามันช้าในวันนี้ แต่สำหรับผู้อ่านในอนาคต: อย่าใช้newตามวิธีที่แสดงในตัวอย่างโค้ดมันแค่ขอหน่วยความจำรั่วหรือข้อผิดพลาดร้ายแรงอื่น ๆ
Pharap

คำตอบ:


44

ฉันทำงานกับเอ็นจิ้นเดียวกับ coderanger ฉันมีมุมมองที่แตกต่าง :)

ก่อนอื่นเราไม่มี FSM สแต็ก - เรามีสแต็กของรัฐ สแต็กของสถานะสร้าง FSM เดี่ยว ฉันไม่ทราบว่าสแต็คของ FSM จะเป็นอย่างไร อาจซับซ้อนเกินไปที่จะทำสิ่งใดในทางปฏิบัติด้วย

ปัญหาที่ใหญ่ที่สุดของฉันกับ Global State Machine ของเราคือมันเป็นสแต็กของรัฐไม่ใช่เป็นชุดของรัฐ ซึ่งหมายความว่าเช่น ... / MainMenu / การโหลดแตกต่างจาก ... / กำลังโหลด / MainMenu ขึ้นอยู่กับว่าคุณมีเมนูหลักก่อนหรือหลังหน้าจอการโหลด (เกมแบบอะซิงโครนัสและการโหลดส่วนใหญ่ขับเคลื่อนด้วยเซิร์ฟเวอร์ )

จากตัวอย่างสองประการที่ทำให้สิ่งนี้น่าเกลียด

  • มันนำไปสู่เช่นสถานะ LoadingGameplay ดังนั้นคุณจึงมี Base / Loading และ Base / Gameplay / LoadingGameplay สำหรับการโหลดภายในสถานะ Gameplay ซึ่งต้องทำซ้ำรหัสมากในสถานะการโหลดปกติ (แต่ไม่ใช่ทั้งหมดและเพิ่มบางอย่างเพิ่มเติม )
  • เรามีฟังก์ชั่นหลายอย่างเช่น "ถ้าในผู้สร้างตัวละครไปที่การเล่นเกมถ้าในการเล่นไปที่ตัวละครเลือกถ้าในตัวเลือกเลือกกลับไปเข้าสู่ระบบ" เพราะเราต้องการที่จะแสดงหน้าต่างอินเทอร์เฟซเดียวกันในรัฐต่าง ๆ ปุ่มยังคงใช้งานได้

แม้จะมีชื่อมันก็ไม่ได้ "โลก" มากนัก ระบบเกมภายในส่วนใหญ่ไม่ได้ใช้เพื่อติดตามสถานะภายในของพวกเขาเพราะพวกเขาไม่ต้องการรัฐของพวกเขาเล่นกับระบบอื่น ๆ อื่น ๆ เช่นระบบ UI สามารถใช้ แต่คัดลอกสถานะลงในระบบสถานะท้องถิ่นของตนเองเท่านั้น (โดยเฉพาะอย่างยิ่งฉันจะระมัดระวังต่อระบบสำหรับสถานะ UI สถานะ UI ไม่ใช่สแต็กมันเป็น DAG จริง ๆ และพยายามบังคับโครงสร้างอื่น ๆ บนนั้นจะทำให้ UIs ที่น่าผิดหวังใช้งานเท่านั้น)

สิ่งที่ดีสำหรับการแยกงานสำหรับการรวมรหัสจากโปรแกรมเมอร์โครงสร้างพื้นฐานที่ไม่ทราบว่าโครงสร้างการไหลของเกมเป็นจริงได้อย่างไรดังนั้นคุณสามารถบอกคนที่เขียน patcher ว่า "ใส่รหัสของคุณใน Client_Patch_Update" และคนที่เขียนกราฟิก กำลังโหลด "ใส่รหัสของคุณใน Client_MapTransfer_OnEnter" และเราสามารถสลับลอจิกโฟลว์บาง ๆ ได้โดยไม่มีปัญหา

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

GSM ยังเป็นตัวชี้ฟังก์ชั่นและพฤติกรรมที่ไม่ใช่ในท้องถิ่นมีแนวโน้มที่จะทำการดีบักสิ่งต่าง ๆ ยากขึ้นแม้ว่าการดีบั๊กการเปลี่ยนสถานะขนาดใหญ่เหล่านั้นก็ไม่สนุกมากก่อนที่เราจะมี ชุดของรัฐแทนที่จะเป็นกองซ้อนของรัฐไม่ได้ช่วยสิ่งนี้จริงๆ แต่คุณควรระวัง ฟังก์ชั่นเสมือนจริงมากกว่าตัวชี้ฟังก์ชั่นอาจบรรเทาที่ค่อนข้าง


คำตอบที่ดีขอบคุณ! ฉันคิดว่าฉันสามารถรับประโยชน์มากมายจากโพสต์และประสบการณ์ในอดีตของคุณ : D + 1 / Tick
พรรคคอมมิวนิสต์ Duck

สิ่งที่ดีเกี่ยวกับลำดับชั้นคือคุณสามารถสร้างสถานะยูทิลิตี้ที่เพิ่งผลักไปด้านบนและไม่ต้องกังวลเกี่ยวกับสิ่งที่กำลังทำงานอยู่
coderanger

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

จุดที่ UIs นั้นเป็น DAG จริง ๆ ก็ดี แต่ฉันไม่เห็นด้วยที่จะสามารถแสดงในสแต็กได้อย่างแน่นอน กราฟอะคิลิกกำกับที่เชื่อมต่อใด ๆ (และฉันไม่สามารถนึกถึงกรณีที่มันไม่ได้เป็น DAG ที่เชื่อมต่อ) สามารถแสดงเป็นต้นไม้ได้และสแต็กก็เป็นต้นไม้
Ed Ropple

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

11

ต่อไปนี้เป็นตัวอย่างการใช้สแต็ก gamestate ที่ฉันพบว่ามีประโยชน์มาก: http://creators.xna.com/en-US/samples/gamestatemanagement

มันเขียนด้วยภาษา C # และเพื่อคอมไพล์คุณจำเป็นต้องมีกรอบ XNA แต่คุณสามารถตรวจสอบรหัสเอกสารและวิดีโอเพื่อให้ได้แนวคิด

มันสามารถรองรับการเปลี่ยนสถานะรัฐโปร่งใส (เช่นกล่องข้อความโมดอล) และสถานะการโหลด (ที่จัดการการยกเลิกการโหลดของสถานะที่มีอยู่และการโหลดของรัฐต่อไป)

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


5

สิ่งนี้คล้ายกับสิ่งที่เราใช้กองซ้อนของ FSM โดยทั่วไปเพียงแค่ให้แต่ละฟังก์ชั่นการเข้าออกและติ๊กและเรียกพวกเขาในการสั่งซื้อ ทำงานได้ดีมากสำหรับการจัดการสิ่งต่าง ๆ เช่นการโหลดเกินไป


3

หนึ่งใน "Game Programming Gems" เล่มหนึ่งมีการใช้งานเครื่องของรัฐในจุดประสงค์สำหรับเกมในสหรัฐอเมริกา http://emergent.net/Global/Documents/textbook/Chapter1_GameAppFramework.pdfมีตัวอย่างของวิธีการใช้สำหรับเกมเล็ก ๆ และไม่ควรใช้เฉพาะกับ Gamebryo


ส่วนแรกของ "การเขียนโปรแกรมเกมสวมบทบาทกับ DirectX" ยังใช้ระบบรัฐ (และระบบกระบวนการ - ความแตกต่างที่น่าสนใจมาก)
Ricket

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

3

เพียงเพื่อเพิ่มมาตรฐานเพียงเล็กน้อยที่จะอภิปรายในระยะ CS คลาสสิกสำหรับชนิดของโครงสร้างข้อมูลนี้เป็นหุ่นยนต์ที่ขยายลง


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

1
"ฉันไม่แน่ใจว่าการใช้งานสแต็คของรัฐในโลกแห่งความเป็นจริงนั้นใกล้เคียงกับหุ่นยนต์แบบกดลง" ความแตกต่างคืออะไร? ทั้งสองมีชุดของรัฐที่ จำกัด ประวัติของรัฐและการดำเนินงานดั้งเดิมที่จะผลักดันและรัฐป๊อป ไม่มีการดำเนินการอื่น ๆ ที่คุณพูดถึงนั้นแตกต่างไปจากเดิมอย่างสิ้นเชิง "ป๊อปสองสถานะ" ปรากฏขึ้นสองครั้ง "swap" เป็นป๊อปและพุช การส่งผ่านข้อมูลไม่ได้อยู่ในแนวความคิดหลัก แต่ทุก ๆ เกมที่ใช้ "FSM" จะมีข้อมูลเพิ่มเติมโดยไม่รู้สึกว่าชื่อนั้นไม่มีผลบังคับใช้อีกต่อไป
เอื้อเฟื้อเผื่อแผ่

ในหุ่นยนต์แบบเลื่อนลงสถานะเดียวที่สามารถส่งผลต่อการเปลี่ยนแปลงของคุณคือสถานะที่อยู่ด้านบน ไม่อนุญาตให้สลับสองสถานะที่อยู่ตรงกลาง แม้แต่การมองรัฐที่อยู่ตรงกลางก็ไม่ได้รับอนุญาต ฉันรู้สึกเหมือนการขยายความหมายของคำว่า "FSM" นั้นสมเหตุสมผลและมีประโยชน์ (และเรายังคงมีคำว่า "DFA" และ "NFA" สำหรับความหมายที่ จำกัด มากที่สุด) แต่ "pushdown automaton" เป็นคำทางวิทยาศาสตร์คอมพิวเตอร์อย่างเคร่งครัดและ มีเพียงความสับสนรอถ้าเราใช้กับระบบสแต็กตามทุกเดียว

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

1
จุดดีคงที่!
munificent

1

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

ฉันเพิ่งจะมีรัฐระบุรัฐที่จะปฏิบัติตามเมื่อมันออก

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


บางทีฉันอาจจะเข้าใจผิดคำถาม?
Skizz

ไม่คุณไม่ได้ ฉันคิดว่าผู้ลงคะแนนเสียงทำ
เป็ดคอมมิวนิสต์

การใช้สแต็กไม่ได้ขัดขวางการใช้การเปลี่ยนสถานะอย่างชัดเจน
dash-tom-bang

1

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

class StateMachine
{
public:
    StateMachine(Engine *);
    void Push(State *);
    State *Pop();
    void Update();
    Engine *GetEngine();

private:
    std::stack<State *> _states;
    Engine *_engine;
};

สถานะถูกผลักด้วยสถานะปัจจุบันและเครื่องเป็นพารามิเตอร์

void StateMachine::Push(State *state)
{
    State *from = 0;
    if (!_states.empty()) from = _states.top();
    _states.push(state);
    state->Enter(this, from);
}

รัฐจะโผล่ในแบบเดียวกัน ไม่ว่าคุณจะโทรEnter()ไปที่ด้านล่างStateนี้เป็นคำถามการใช้งาน

State *StateMachine::Pop()
{
    _ASSERT(!_states.empty());
    State *state = _states.top();
    State *to = 0;
    _states.pop();
    if (!_states.empty()) to = _states.top();
    state->Exit(this, to);
    return state;
}

เมื่อเข้าสู่การอัปเดตหรือออกจากระบบข้อมูลStateจะได้รับทุกอย่างที่ต้องการ

void SomeGameState::Enter(StateMachine *sm, State *from)
{
    Engine *eng = sm->GetEngine();
    eng->GetKeyboard()->KeyDown.Bind(this, &SomeGameState::KeyDown);
    LoadLevelState *state = new LoadLevelState();
    state->SetLevel(eng->GetSaveGame()->GetLevelName());
    state->Load.Bind(this, &SomeGameState::OnLevelLoaded);
    sm->Push(state);
}

void SomeGameState::Update(StateMachine *sm)
{
    Engine *eng = sm->GetEngine();
    float time = eng->GetFrameTime();
    if (shouldExit)
        sm->Pop();
}

void SomeGameState::Exit(StateMachine *sm, State *from)
{
    Engine *eng = sm->GetEngine();
    eng->GetKeyboard()->KeyDown.UnsubscribeAll(this);
}

0

ฉันใช้ระบบที่คล้ายคลึงกันมากในหลาย ๆ เกมและพบว่ามีข้อยกเว้นสองสามข้อมันทำหน้าที่เป็นโมเดล UI ที่ยอดเยี่ยม

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

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

มีการเพิ่มฟังก์ชันตัวช่วยสองสามตัวเพื่อลดความซับซ้อนของงานทั่วไปเช่นสลับ (ป๊อป & พุชสำหรับการไหลเชิงเส้น) และรีเซ็ต (สำหรับการเลื่อนกลับไปที่เมนูหลักหรือสิ้นสุดโฟลว์)


ในฐานะที่เป็นโมเดล UI สิ่งนี้สมเหตุสมผลดี ฉันลังเลที่จะเรียกพวกเขาว่ารัฐเพราะในหัวของฉันฉันจะเชื่อมโยงกับ internals ของเกมหลักในขณะที่ "เมนูหลัก", "เมนูตัวเลือก", "หน้าจอเกม" และ "หน้าจอหยุด" อยู่ในระดับที่สูงขึ้น และมักจะไม่มีการโต้ตอบกับสถานะภายในของเกมหลักและเพียงแค่ส่งคำสั่งไปยังโปรแกรมหลักของแบบฟอร์ม "หยุดชั่วคราว", "เลิกหยุดชั่วคราว", "โหลดระดับ 1", "เริ่มระดับ", "เริ่มระดับใหม่" "บันทึก" และ "กู้คืน", "ตั้งค่าระดับเสียง 57" ฯลฯ เห็นได้ชัดว่าสิ่งนี้อาจแตกต่างกันไปตามเกม
Kevin Cathcart

0

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

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

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


0

ฉันใช้ระบบนี้แน่นอนในหลาย ๆ ระบบ ตัวอย่างเมนูส่วนหน้าและในเกม (หรือที่รู้จักว่า "หยุด") มีสถานะกองซ้อนของตนเอง UI ในเกมใช้บางอย่างเช่นนี้แม้ว่าจะมีแง่มุม "ทั่วโลก" (เช่นเฮลธ์บาร์และแผนที่ / เรดาร์) ที่การสลับสถานะอาจแต้มสี แต่อัปเดตด้วยวิธีทั่วไปทั่วทั้งรัฐ

เมนูในเกมอาจ "ดีกว่า" ที่แสดงโดย DAG แต่ด้วยเครื่องจักรสถานะโดยนัย (ตัวเลือกเมนูแต่ละรายการที่ไปยังหน้าจออื่นรู้วิธีไปที่นั่นและการกดปุ่มย้อนกลับจะโผล่สถานะด้านบนเสมอ) ผลคือ เหมือนเดิมทุกประการ.

บางส่วนของระบบอื่น ๆ เหล่านี้ยังมี "แทนที่รัฐบน" การทำงาน แต่ที่ถูกนำมาใช้มักจะเป็นตามด้วยStatePop()StatePush(x);

การจัดการการ์ดหน่วยความจำมีความคล้ายคลึงกันเนื่องจากจริง ๆ แล้วฉันได้ทำการ "การดำเนินการ" เข้าไปในคิวการดำเนินการ เมื่อคุณเริ่มใช้โครงสร้างแบบนี้ ("มีสิ่งหนึ่งเกิดขึ้นตอนนี้และเมื่อเสร็จแล้วก็ปรากฏตัวเอง") มันจะเริ่มติดทุกส่วนของรหัส แม้แต่ AI ก็เริ่มใช้สิ่งนี้ AI คือ "clueless" จากนั้นเปลี่ยนเป็น "ระวัง" เมื่อผู้เล่นทำเสียง แต่ไม่เห็นและในที่สุดก็ยกระดับเป็น "ใช้งาน" เมื่อพวกเขาเห็นผู้เล่น (และไม่เหมือนเกมที่มีเวลาน้อยกว่าคุณไม่สามารถซ่อนได้ ในกล่องกระดาษแข็งและทำให้ศัตรูลืมคุณ!

GameState.h:

enum GameState
{
   k_frontend,
   k_gameplay,
   k_inGameMenu,
   k_moviePlayback,
   k_numStates
};

void GameStatePush(GameState);
void GameStatePop();
void GameStateUpdate();

GameState.cpp:

// k_maxNumStates could be bigger, but we don't need more than
// one of each state on the stack.
static const int k_maxNumStates = k_numStates;
static GameState s_states[k_maxNumStates] = { k_frontEnd };
static int s_numStates = 1;

static void (*s_startupFunctions)()[] =
   { FrontEndStart, GameplayStart, InGameMenuStart, MovieStart };
static void (*s_shutdownFunctions)()[] =
   { FrontEndStop, GameplayStop, InGameMenuStop, MovieStop };
static void (*s_updateFunctions)()[] =
   { FrontEndUpdate, GameplayUpdate, InGameMenuUpdate, MovieUpdate };

static void GameStateStart(GameState);
static void GameStateStop(GameState);

void GameStatePush(GameState gs)
{
   Assert(s_numStates < k_maxNumStates);
   GameStateStop(s_states[s_numStates - 1])
   s_states[s_numStates] = gs;
   s_numStates++;
   GameStateStart(gs);
}

void GameStatePop()
{
   Assert(s_numStates > 1);  // can't pop last state
   s_numStates--;
   GameStateStop(s_states[s_numStates]);
   GameStateStart(s_states[s_numStates - 1]);
}

void GameStateUpdate()
{
   GameState current = s_states[s_numStates - 1];
   s_updateFunctions[current]();
}

void GameStateStart(GameState gs)
{
   s_startupFunctions[gs]();
}

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