การจัดการสถานะของเกม (เกม, เมนู, หน้าจอ Titles, ฯลฯ )


11

โดยพื้นฐานแล้วในทุก ๆ เกมที่ฉันทำมาจนถึงตอนนี้ฉันมักจะมีตัวแปรเช่น "current_state" ซึ่งสามารถเป็น "เกม", "ชื่อหน้าจอ", "หน้าจอเกม" เป็นต้น

และจากฟังก์ชั่นอัพเดทของฉันฉันมีขนาดใหญ่มาก:

if current_state == "game" 
  game stuf
  ...
else if current_state == "titlescreen"
  ...

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


คุณใช้ภาษากรอบงานและอื่นใด
Petr Abdulin

ปกติ Lua + รัก ฉันเพิ่งค้นพบว่ากรอบงานที่แตกต่างกันมีวิธีการจัดการที่แตกต่างกัน SFML ดูเหมือนว่าจะมีระดับหน้าจอที่ดีมาก
David Gomes

1
คุณเคยดูเครื่องจักรของรัฐหรือไม่?
Darcara

1
คุณสามารถค้นหานักเล่นเกมในแถบค้นหาที่ด้านบนขวา ควรให้ผลลัพธ์บ้าง
TravisG

ต้อง Darcara ที่สอง - ดูเหมือนว่าสิ่งที่เครื่องรัฐใช้สำหรับ
balajeerc

คำตอบ:


14

เมื่อคุณพูดถึงหน้าจอฉันรู้สึกว่ามันเป็นการดีที่จะแยกตรรกะทั้งหมดนั้นออกเป็นหน้าจอที่แตกต่างกัน สิ่งที่ฉันทำตามปกติ:

กำหนดอินเตอร์เฟสที่เรียกว่าหน้าจอและให้ใช้หลายหน้าจอ เช่น LoadingScreen, MainMenuScreen, GameScreen, GameOverScreen, HighScoreScreen เป็นต้นในเกมของคุณคุณใส่ตัวแปรที่เก็บหน้าจอปัจจุบัน แต่ละวงคุณเรียก screen.update () และแสดงหน้าจอปัจจุบัน วิธีนี้จะช่วยให้คุณประหยัดได้มาก "ถ้าสถานะนี้ทำเช่นนั้น" เนื่องจากสถานะของคุณถูกกำหนดโดยหน้าจอปัจจุบัน

นี่จะแยกตรรกะของคุณออกเป็นอย่างมาก

รหัสตัวอย่าง:

### Screen interface ###
public interface Screen {

    public void show();

    public void update(float delta);

    public void render(float delta);

    public void hide ();
}

### An implementation of screen ###
public class MainMenuScreen implements Screen {

    private Game game;

    public MainMenuScreen(Game game) {
        this.game = game;
    }

    public void show() {
        // init stuff
    }

    public void update(float delta) {
        // react to clicks, update animations etc.
        if (buttonwasclicked) {
            game.setScreen(new GameScreen(game)); // change the screen
        }
    }

    public void render(float delta) {
        // draw everything
    }

    public void hide() {
        // release all resources, as the screen is being hidden
    }
}

### Game, drawing the appropriate screen ###
public class Game {

    public Screen screen;

    public void update() {
        screen.update(getDeltaTime);
        screen.render();
    }

    public void setScreen(Screen screen) {
        this.screen.hide();

        this.screen = screen;
        this.screen.show();
    }
}

หรือขึ้นอยู่กับการตั้งค่าเกมของคุณคุณอาจจะวนซ้ำไม่สิ้นสุดเหมือนเกมของคุณ

while(true) {
    calculatetimesincelastframe()
    screen.update(time);
    screen.render(time);
}

5

หากคุณกำลังใช้ชนชั้นกลางมีห้องสมุดรัฐเครื่องที่ดีที่จะไปพร้อมกับมันเรียกว่าstatefull ใช้งานง่ายและนำเสนอแนวคิดเดียวกันกับที่ Matsemann เสนอ


2

หากcurrent_stateตัวแปรของคุณเป็นสตริงนี่เป็นเรื่องง่ายใน Lua:

game_states = {}
function game_states.game()
    -- game stuff
end
function game_states.titlescreen()
    -- title screen stuff
end

-- then, inside the Update function:
game_states[current_state]()

1

สิ่งที่ฉันทำคร่าวๆดังนี้

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

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


คำตอบนี้เป็นคำตอบที่ดีไปสู่การที่แตกต่างกันคำถาม
Matsemann

งั้นเหรอ เขาถามวิธีตั้งค่าสถานะเกมต่าง ๆ ของฉันและวิธีแก้ปัญหาของฉันไม่ได้ใช้เครื่องสถานะเหมือนตอนนี้ แต่แทนที่จะแบ่งบิตออกเป็นระบบต่าง ๆ ซึ่งไม่ใช่เครื่องสถานะ แต่แทนที่จะเป็น DAG
Alex Ames

1

นี่คือวิธีที่ฉันจัดระเบียบสถานะของฉันใน Lua + Love2d มันหลีกเลี่ยงคำสั่ง if / then ที่มีความยาว

ก่อนอื่นฉันสร้างคลาสพื้นฐานที่มีเมธอด update (dt) และ render () คุณสามารถให้วิธีการจัดการเหตุการณ์เช่น onKeyDown (คีย์) ฉันเรียกคลาสนี้ว่าคลาส แต่วัตถุใด ๆ ที่ใช้เมธอดจะทำงาน จากนั้นฉันสร้างอินสแตนซ์ของคลาสนั้นสำหรับแต่ละสถานะของเกมโดยใช้วิธีการที่จำเป็น ฉันจะสร้างตารางคีย์ / ค่าที่มีชื่อรัฐและอินสแตนซ์ของรัฐ จากนั้นติดตาม currentState ที่ขอบเขตโกลบอลเพื่อให้รัฐสามารถเปลี่ยนแปลงได้เมื่อตรงตามเงื่อนไขที่กำหนด

states = {}
states["title"] = title   -- Where title implements Stage class.
states["game"] = game     -- You could create the instance of 'game' lazily too.
currentState = "title"

function love.update(dt)
    if states[currentState] ~= nil then
       states[currentState]:update(dt) 
    end
end

-1

ถึงแม้ว่ามันจะไม่สวย แต่ก็โอเคที่จะจัดการกับวิธีนี้ IMO คุณสามารถทำให้มันสะอาดยิ่งขึ้นโดยใช้ฟังก์ชั่นสำหรับแต่ละรัฐเช่น:

if current_state == "game" 
  game()
else if current_state == "titlescreen"
  titlescreen()

หรือสิ่งอื่นที่รบกวนคุณในแนวทางนี้ (ฉันหมายถึงยกเว้นว่าวิธีการอัปเดตนั้นยาวมาก)

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