2009-03-18 13 views
7

मैं एक गेम लिख रहा हूं, और मैं अपने अलग-अलग राज्यों को मॉडल करना चाहता हूं (गेम निर्माता समानता फ्रेम, मुझे लगता है) एक स्वच्छ, ऑब्जेक्ट उन्मुख तरीके से। पहले, मैंने इसे निम्न तरीके से किया है:आप आवेदन राज्यों को कैसे मॉडल करते हैं?

class Game 
{ 
    enum AppStates 
    { 
    APP_STARTING, 
    APP_TITLE, 
    APP_NEWGAME, 
    APP_NEWLEVEL, 
    APP_PLAYING, 
    APP_PAUSED, 
    APP_ENDED 
    }; 

    typedef AppState(Game::*StateFn)(); 
    typedef std::vector<StateFn> StateFnArray; 

    void Run() 
    { 
    // StateFn's to be registered here 

    AppState lastState(APP_STARTING); 
    while(lastState != APP_ENDED) 
    { 
     lastState = GetCycle_(lastState); 
    } 
    // cleanup 
    } 

protected: 
    // define StateFn's here 

    AppState GetCycle_(AppState a) 
    { 
    // pick StateFn based on passed variable, call it and return its result. 
    } 

    StateFnArray states_; 
}; 

यह एक छोटी परियोजना के लिए शायद ही प्रबंधनीय था। राज्यों का उपयोग करने वाले सभी चर गेम गेम में डंप किए गए थे, हालांकि मैं ऑब्जेक्ट-उन्मुखता को अधिकतम, केवल उजागर करने वाले चरों को एक से अधिक राज्यों द्वारा साझा करना चाहता हूं। मैं राज्य में ऐसा करने के बजाए इसे स्विच करते समय एक नया राज्य शुरू करने में सक्षम होना चाहता हूं जो कि अभी खत्म हो रहा है (क्योंकि इसमें कई परिणाम हो सकते हैं - APP_PLAYING APP_PAUSED, APP_GAMEOVER, APP_NEWLEVEL, आदि पर स्विच कर सकता है)।

मैं कुछ इस तरह के बारे में सोचा (! चेतावनी फजी STUFF!):

struct AppState 
{ 
    enum { LAST_STATE = -1; } 
    typedef int StateID; 
    typedef std::vector<AppState*> StateArray; 

    static bool Add(AppState *state, StateID desiredID); 
    // return false if desiredID is an id already assigned to 

    static void Execute(StateID state) 
    { 
    while(id != LAST_STATE) 
    { 
     // bounds check etc. 
     states_[id]->Execute(); 
    } 
    } 

    AppState() {}; 
    virtual ~AppState() {}; 

    virtual StateID Execute() =0; // return the ID for the next state to be executed 

protected: 
    static StageArray stages_; 
}; 

समस्या है कि यहाँ वर्ग और उदाहरण स्तरों गड़बड़ हो रहे हैं (बनाम आभासी स्थिर) है। राज्यों को ऐपस्टेट से उत्तराधिकारी होने की आवश्यकता है, लेकिन - मैं कैसे कल्पना करूंगा - उनमें से अधिकतर सभी स्थैतिक सदस्यों के साथ कक्षाएं होंगी, या कम से कम मुझे एक वर्ग से एक से अधिक उदाहरण की आवश्यकता नहीं होगी (टाइटलस्टेट, लेवलइन्ट्रोस्टेट, प्लेइंगस्टेट , GameOverState, EndSequenceState, EditorState ... - रुकने वाले राज्यों की देखभाल करने के बजाय, अब यह एक राज्य नहीं होगा, जहां यह समझ में आता है)।

यह सुंदर और कुशलतापूर्वक कैसे किया जा सकता है?

http://gamedevgeek.com/tutorials/managing-game-states-in-c/

असल में, आप खेल राज्यों के ढेर को बनाए रखने, और सिर्फ शीर्ष राज्य चलाएँ:

उत्तर

10

निम्न आलेख खेल राज्यों का प्रबंधन करने के लिए एक अच्छा और आसान तरीका देता है। आप सही हैं कि कई राज्यों में केवल एक उदाहरण होगा, लेकिन यह वास्तव में एक समस्या नहीं है। असल में, हालांकि, जिन राज्यों के बारे में आप बात कर रहे हैं उनमें कई उदाहरण हो सकते हैं। उदा .:

push TitleState 
push MenuState 
push LevelIntroState 
change_to PlayingState 
change_to GameOverState 
pop (back to MenuState) 

... और आप इतने पर LevelIntroState का एक नया उदाहरण साथ फिर से प्रारंभ कर सकते हैं।

2

यहाँ मेरी समाधान है:

  • हर राज्य में एक छोटा सा खेल की तरह है, इसलिए मैं एक ढेर पर खेल का एक सेट का प्रबंधन।
  • ईवेंट तब तक बुलबुला करते हैं जब तक कोई उन्हें रोक नहीं देता है (इसलिए "गेम" आगे उन्हें और नहीं देखते हैं)। यह मुझे मेनू में रहते हुए प्लस/माइनस के माध्यम से मानचित्र ज़ूम करने की अनुमति देता है। ओटीओएच, एएससी पहले खुले मेनू को निगलने के बाद जल्दी ही बुलबुले बंद कर देता है।
  • स्टैक पर प्रत्येक "गेम" में एक ही तरीके हैं: handleUserEvent(), keyDown(), keyUp(), mousePressed(), mouseReleased(), mouseMotion(), अद्यतन() (प्रतिपादन से पहले आंतरिक गणना), ड्रा() (प्रतिपादन), तैयार()

, समर्पण मैं प्राथमिकताओं के परतों का उपयोग कर रहा हूँ के लिए (एक बनावट में कुछ ऐसा है जो सिर्फ ड्रा() में लक्ष्य की सतह पर मुहर लगी है कैशिंग द्वारा प्रतिपादन अनुकूलन)। तो प्रत्येक गेम एक पारदर्शी कैनवास पर प्रस्तुत करेगा और परत रेंडरर उन्हें सही क्रम में प्रस्तुत करेगा। इस तरह, प्रत्येक गेम अपनी सभी परतों को परेशान किए बिना अपडेट कर सकता है जो हर कोई कर रहा है।

1

मैं गेमस्टेट्स की एक सूची के साथ गेम स्टेट मैनेजर का उपयोग करता हूं, जहां सूची में प्रत्येक आइटम "गेमस्टेट ऑब्जेक्ट" है जो IGameState लागू करता है और इसमें दो विधियां हैं।() और।HandleInput()

यह GameStateManager

GameStateManager.gi().setState("main menu") 

को फोन करके एक सिंगलटन के रूप में कार्यान्वित किया जाता है तो किसी भी राज्य में किसी भी दूसरे राज्य के लिए कूद कर सकते हैं और मुख्य लूप इस

while(isRunning) 
{ 
    GameStateManager.gi().getCurrentState().handleKeyboard(keysobject); 
    GameStateManager.gi().getCurrentState().handleMouse(mouseobject); 

    GameStateManager.gi().getCurrentState().render(screenobject); 

} 

इस तरह के तरह दिखता है राज्य बनाएं, बस एक नई कक्षा बनाएं जो IGameState लागू करती है, और इसे GameStateManager में जोड़ें।

:

3

मैं factory pattern किसी प्रकार की मेरी जल्द ही होने वाली है खेल में एक state pattern के साथ संयुक्त उपयोग कर रहा हूँ (नोट यह आपका मुख्य खेल के भीतर मिनी-गेम बनाने के लिए एक बहुत आसान तरीका है)।

कोड थोड़ा गन्दा हो सकता है लेकिन मैं इसे साफ़ करने की कोशिश करूंगा।

यह वह वर्ग है जिसे आप मेनू, खेल या जो भी हो, से सभी राज्यों से प्राप्त करेंगे।

class GameState { 
public: 
    virtual ~GameState() { } 

    virtual void Logic() = 0; 
    virtual void Render() = 0; 
}; 

यह कक्षा विभिन्न राज्यों को संभालने के लिए आपका इंटरफ़ेस होगा। आप गतिशील रूप से गतिशील रूप से जोड़ और आईडी कर सकते हैं।

class State { 
public: 
    State(); 
    virtual ~State(); 

    void Init(); 
    void Shutdown(); 
    void SetNext(std::string next_state); 
    void Exit(); 

    bool Logic(); 
    void Render(); 
protected: 
    bool Change(); 

    std::string state_id; 
    std::string next_state; 

    GameState *current_state; 
    std::vector<std::string> state_ids; 

    StateFactory *state_factory; 

    bool is_init; 
}; 

मैं विभिन्न गेमस्टेट डेरिवेटिव के निर्माण को संभालने के लिए एक मजेदार का उपयोग कर रहा हूं।

class BasicStateFunctor { 
public: 
    virtual GameState *operator()() = 0; 
}; 

template<class T> 
class StateFunctor : public BasicStateFunctor { 
public: 
    StateFunctor() { } 
    GameState *operator()() { 
     return new T; 
    } 
    typedef T type; 
}; 

आखिरकार एक कारखाना जो विभिन्न राज्यों को स्टोर और प्रबंधित करेगा।

class StateFactory { 
public: 
    StateFactory(); 
    virtual ~StateFactory(); 

    bool CheckState(std::string id); 
    GameState *GetState(std::string id); 
    template<class T> void AddState(std::string id); 
private: 
    typedef std::map<std::string, BasicStateFunctor*>::iterator StateIt; 
    std::map<std::string, BasicStateFunctor*> state_map; 
}; 

अपनी परिभाषा फ़ाइल में: यहाँ मैं सामान का एक बहुत बाहर छोड़ किया था, लेकिन उम्मीद है कि आप विचार मिलेगा।

bool StateFactory::CheckState(std::string id) 
{ 
    StateIt it = state_map.find(id); 
    if(it != state_map.end()) 
     return true; 
    else 
     return false; 
} 

GameState *StateFactory::GetState(std::string id) 
{ 
    StateIt it = state_map.find(id); 
    if(it != state_map.end()) 
    { 
     return (*(*it).second)(); 
    } else { 
     //handle error here 
} 

template<class T> void StateFactory::AddState(std::string id) 
{ 
    StateFunctor<T> *f = new StateFunctor<T>(); 
    state_map.insert(state_map.end(), std::make_pair(id, f)); 
} 

void State::Init() 
{ 
    state_factory = new StateFactory(); 
    state_factory->AddState<Game>("game"); 
    current_state = state_factory->GetState("game"); 
    is_init = true; 
} 

void State::SetNext(std::string new_state) 
{ 
    //if the user doesn't want to exit 
    if(next_state != "exit") { 
     next_state = new_state; 
    } 
} 

bool State::Change() 
{ 
    //if the state needs to be changed 
    if(next_state != "" && next_state != "exit") 
    { 

     //if we're not about to exit(destructor will call delete on current_state), 
     //call destructor if it's a valid new state 
     if(next_state != "exit" && state_factory->CheckState(next_state)) 
     { 
      delete current_state; 

      current_state = state_factory->GetState(next_state); 

     } 
     else if(next_state == "exit") 
     { 
       return true; 
     } 

     state_id = next_state; 

     //set NULL so state doesn't have to be changed 
     next_state = ""; 
    } 
    return false; 
} 

bool State::Logic() 
{ 
    current_state->Logic(); 
    return Change(); 
} 

और यहाँ कैसे आप इसका इस्तेमाल है: प्रारंभ और विभिन्न राज्यों को जोड़ने मैं Init में यह कर रहा हूँ,()।

State.Init(); 

//remember, here's the Init() code: 
state_factory = new StateFactory(); 
state_factory->AddState<Game>("game"); 
current_state = state_factory->GetState("game"); 
is_init = true; 
फ्रेम समारोह

State.Logic(); //Here I'm returning true when I want to quit 

के लिए और प्रतिपादन समारोह

State.Render(); 

यह सही नहीं हो सकता है के लिए

लेकिन यह मेरे लिए ठीक काम करता है। डिज़ाइन को आगे बढ़ाने के लिए आप राज्य के लिए सिंगलटन जोड़ना चाहते हैं और शायद स्टेटफैक्टरी को राज्य के अंदर एक छिपी कक्षा के रूप में बना सकते हैं।

संबंधित मुद्दे