c++ - खेल एक दूसरे से बात कर रहे ऑब्जेक्ट्स




design-patterns design (5)

"एक संदेश शैली प्रणाली" से सावधान रहें, यह शायद कार्यान्वयन पर निर्भर करता है, लेकिन आमतौर पर आप स्थैतिक प्रकार की जांच खो देंगे, और फिर कुछ त्रुटियों को डीबग करना मुश्किल हो सकता है। ध्यान दें कि ऑब्जेक्ट की विधियों को कॉल करना यह पहले से ही एक संदेश जैसी प्रणाली है।

शायद आप केवल अमूर्तता के कुछ स्तरों को याद कर रहे हैं, उदाहरण के लिए नेविगेशन के लिए एक खिलाड़ी मानचित्र के बारे में सभी को जानने के बजाय एक नेविगेटर का उपयोग कर सकता है। आप यह भी कहते हैं कि this has usually descended into setting lots of pointers , ये पॉइंटर्स क्या हैं? शायद, आप उन्हें गलत अमूर्तता के लिए दे रहे हैं? .. वस्तुओं को सीधे इंटरफेस और इंटरमीडिएट्स के बिना, दूसरों के बारे में पता है, कसकर युग्मित डिजाइन प्राप्त करने का एक सीधा तरीका है।

वस्तुओं से निपटने और उन्हें एक दूसरे से बात करने का एक अच्छा तरीका क्या है?

अब तक मेरे सभी गेम शौक / छात्र छोटे हैं इसलिए इस समस्या को आम तौर पर बदसूरत तरीके से हल किया गया था, जिससे कड़े एकीकरण और परिपत्र निर्भरताएं उत्पन्न हुईं। जो परियोजनाएं मैं कर रहा था उसके आकार के लिए ठीक था।

हालांकि मेरी परियोजनाएं आकार और जटिलता में बड़ी हो रही हैं और अब मैं कोड का पुनः उपयोग करना शुरू कर दूंगा, और अपना सिर एक आसान स्थान बनाना चाहता हूं।

मेरे पास मुख्य समस्या है आमतौर पर Player की लाइनों के साथ Map बारे में जानना आवश्यक है और इसलिए Enemy , यह आमतौर पर बहुत सारे पॉइंटर्स सेट करने और कई निर्भरताओं को रखने में उतरता है, और यह जल्दी से गड़बड़ हो जाता है।

मैंने एक संदेश शैली प्रणाली के साथ सोचा है। लेकिन मैं वास्तव में नहीं देख सकता कि इससे निर्भरता कैसे कम हो जाती है, क्योंकि मैं अभी भी पॉइंटर्स को हर जगह भेज रहा हूं।

पीएस: मुझे लगता है कि इससे पहले चर्चा की गई है, लेकिन मुझे नहीं पता कि इसे सिर्फ मेरी जरूरत है।


@ एमवीसी का केलॉग सुझाव सुझाव मान्य है, और कुछ गेमों में उपयोग किया जाता है, हालांकि यह वेब ऐप्स और ढांचे में अधिक आम है। यह इसके लिए अधिक हो सकता है और इसके लिए बहुत अधिक हो सकता है।

मैं आपके डिजाइन पर पुनर्विचार करूंगा, खिलाड़ी को दुश्मनों से बात करने की ज़रूरत क्यों है? क्या वे दोनों अभिनेता वर्ग से वंचित नहीं हो सकते थे? अभिनेताओं को मानचित्र से बात करने की ज़रूरत क्यों है?

जैसा कि मैंने पढ़ा है मैंने लिखा है कि यह एक एमवीसी ढांचे में फिट होना शुरू कर देता है ... मैंने हाल ही में बहुत अधिक रेल काम किया है। हालांकि, मैं शर्त लगाने के लिए तैयार हूं, उन्हें केवल चीजों को जानने की जरूरत है, वे एक और अभिनेता के साथ टकराव कर रहे हैं, और उनके पास एक स्थिति है, जो किसी भी तरह से मानचित्र के सापेक्ष होना चाहिए।

यहां Asteroids कार्यान्वयन है जिस पर मैंने काम किया था। आप खेल हो सकते हैं, और शायद जटिल है।


यह शायद न केवल खेल कक्षाओं पर लागू होता है बल्कि सामान्य ज्ञान में कक्षाओं के लिए भी लागू होता है। आपके सुझाए गए संदेश पंप के साथ एमवीसी (मॉडल-व्यू-कंट्रोलर) पैटर्न एक साथ आपको चाहिए।

"दुश्मन" और "प्लेयर" शायद एमवीसी के मॉडल हिस्से में फिट होगा, इससे कोई फर्क नहीं पड़ता है, लेकिन अंगूठे के नियम में सभी मॉडल और विचार नियंत्रक के माध्यम से बातचीत करते हैं। तो, आप संदर्भ '(पॉइंटर्स से बेहतर) (लगभग) इस' नियंत्रक 'वर्ग से अन्य सभी वर्ग उदाहरणों को रखना चाहते हैं, चलो इसे ControlDispatcher नाम दें। इसमें एक संदेश पंप जोड़ें (आप जिस प्लेटफ़ॉर्म के लिए कोडिंग कर रहे हैं उसके आधार पर भिन्न होता है), इसे पहले (तुरंत किसी अन्य वर्ग से पहले और अन्य ऑब्जेक्ट्स का हिस्सा) को तुरंत चालू करें (या अन्य ऑब्जेक्ट्स को कंट्रोल डिस्पैचर में संदर्भ के रूप में संग्रहीत किया गया है)।

बेशक, कंट्रोल डिस्पैचर क्लास को शायद अधिक विशिष्ट नियंत्रकों में आगे विभाजित करना होगा, प्रति कोड कोड को लगभग 700-800 लाइनों पर रखने के लिए (यह कम से कम मेरे लिए सीमा है) और इसमें अधिक थ्रेड पंपिंग भी हो सकते हैं और आपकी जरूरतों के आधार पर संदेश संसाधित करना।

चियर्स


यहां सी ++ 11 के लिए लिखी गई एक साफ घटना प्रणाली है जिसका आप उपयोग कर सकते हैं। यह प्रतिनिधियों के लिए टेम्पलेट्स और स्मार्ट पॉइंटर्स के साथ-साथ लैम्बडा का भी उपयोग करता है। यह बहुत लचीला है। नीचे आपको एक उदाहरण भी मिलेगा। अगर आपको इसके बारे में कोई सवाल है तो मुझे [email protected] पर ईमेल करें।

ये कक्षाएं आपको उनसे जुड़े मनमानी डेटा के साथ घटनाओं को भेजने का एक तरीका है और उन कार्यों को सीधे बाध्य करने का एक आसान तरीका है जो आपके प्रतिनिधि को कॉल करने से पहले सही रूपांतरण के लिए पहले से ही परिवर्तित तर्क प्रकार स्वीकार करते हैं।

असल में, प्रत्येक घटना आईवेन्टडाटा कक्षा से ली गई है (यदि आप चाहें तो इसे आईवेन्ट कह सकते हैं)। प्रत्येक "फ्रेम" जिसे आप ProcessEvents () कहते हैं, उस बिंदु पर इवेंट सिस्टम सभी प्रतिनिधियों के माध्यम से लूप करता है और उन प्रतिनिधियों को कॉल करता है जिन्हें अन्य ईवेंट द्वारा सब्सक्राइब किया गया है जो प्रत्येक ईवेंट प्रकार की सदस्यता ले चुके हैं। कोई भी यह चुन सकता है कि वे कौन सी घटनाओं की सदस्यता लेना चाहते हैं, क्योंकि प्रत्येक ईवेंट प्रकार में एक अद्वितीय आईडी होती है। आप इस तरह की घटनाओं की सदस्यता लेने के लिए लैम्बडा का भी उपयोग कर सकते हैं: AddListener (MyEvent :: ID (), [&] (shared_ptr ev) {अपनी बात करें} ..

वैसे भी, यहां सभी कार्यान्वयन के साथ कक्षा है:

#pragma once

#include <list>
#include <memory>
#include <map>
#include <vector>
#include <functional>

class IEventData {
public:
    typedef size_t id_t; 
    virtual id_t GetID() = 0; 
}; 

typedef std::shared_ptr<IEventData> IEventDataPtr; 
typedef std::function<void(IEventDataPtr&)> EventDelegate; 

class IEventManager {
public:
    virtual bool AddListener(IEventData::id_t id, EventDelegate proc) = 0;
    virtual bool RemoveListener(IEventData::id_t id, EventDelegate proc) = 0; 
    virtual void QueueEvent(IEventDataPtr ev) = 0; 
    virtual void ProcessEvents() = 0; 
}; 


#define DECLARE_EVENT(type) \
    static IEventData::id_t ID(){ \
        return reinterpret_cast<IEventData::id_t>(&ID); \
    } \
    IEventData::id_t GetID() override { \
        return ID(); \
    }\

class EventManager : public IEventManager {
public:
    typedef std::list<EventDelegate> EventDelegateList; 

    ~EventManager(){
    } 
    //! Adds a listener to the event. The listener should invalidate itself when it needs to be removed. 
    virtual bool AddListener(IEventData::id_t id, EventDelegate proc) override; 

    //! Removes the specified delegate from the list
    virtual bool RemoveListener(IEventData::id_t id, EventDelegate proc) override; 

    //! Queues an event to be processed during the next update
    virtual void QueueEvent(IEventDataPtr ev) override; 

    //! Processes all events
    virtual void ProcessEvents() override; 
private:
    std::list<std::shared_ptr<IEventData>> mEventQueue; 
    std::map<IEventData::id_t, EventDelegateList> mEventListeners; 

}; 

//! Helper class that automatically handles removal of individual event listeners registered using OnEvent() member function upon destruction of an object derived from this class. 
class EventListener {
public:
    //! Template function that also converts the event into the right data type before calling the event listener. 
    template<class T>
    bool OnEvent(std::function<void(std::shared_ptr<T>)> proc){
        return OnEvent(T::ID(), [&, proc](IEventDataPtr data){
            auto ev = std::dynamic_pointer_cast<T>(data); 
            if(ev) proc(ev); 
        }); 
    }
protected:
    typedef std::pair<IEventData::id_t, EventDelegate> _EvPair; 
    EventListener(std::weak_ptr<IEventManager> mgr):_els_mEventManager(mgr){

    }
    virtual ~EventListener(){
        if(_els_mEventManager.expired()) return; 
        auto em = _els_mEventManager.lock(); 
        for(auto i : _els_mLocalEvents){
            em->RemoveListener(i.first, i.second); 
        }
    }

    bool OnEvent(IEventData::id_t id, EventDelegate proc){
        if(_els_mEventManager.expired()) return false; 
        auto em = _els_mEventManager.lock(); 
        if(em->AddListener(id, proc)){
            _els_mLocalEvents.push_back(_EvPair(id, proc)); 
        }
    }
private:
    std::weak_ptr<IEventManager> _els_mEventManager; 
    std::vector<_EvPair>        _els_mLocalEvents; 
    //std::vector<_DynEvPair> mDynamicLocalEvents; 
}; 

और सीपीपी फ़ाइल:

#include "Events.hpp"

using namespace std; 

bool EventManager::AddListener(IEventData::id_t id, EventDelegate proc){
    auto i = mEventListeners.find(id); 
    if(i == mEventListeners.end()){
        mEventListeners[id] = list<EventDelegate>(); 
    }
    auto &list = mEventListeners[id]; 
    for(auto i = list.begin(); i != list.end(); i++){
        EventDelegate &func = *i; 
        if(func.target<EventDelegate>() == proc.target<EventDelegate>()) 
            return false; 
    }
    list.push_back(proc); 
}

bool EventManager::RemoveListener(IEventData::id_t id, EventDelegate proc){
    auto j = mEventListeners.find(id); 
    if(j == mEventListeners.end()) return false; 
    auto &list = j->second; 
    for(auto i = list.begin(); i != list.end(); ++i){
        EventDelegate &func = *i; 
        if(func.target<EventDelegate>() == proc.target<EventDelegate>()) {
            list.erase(i); 
            return true; 
        }
    }
    return false; 
}

void EventManager::QueueEvent(IEventDataPtr ev) {
    mEventQueue.push_back(ev); 
}

void EventManager::ProcessEvents(){
    size_t count = mEventQueue.size(); 
    for(auto it = mEventQueue.begin(); it != mEventQueue.end(); ++it){
        printf("Processing event..\n"); 
        if(!count) break; 
        auto &i = *it; 
        auto listeners = mEventListeners.find(i->GetID()); 
        if(listeners != mEventListeners.end()){
            // Call listeners
            for(auto l : listeners->second){
                l(i); 
            }
        }
        // remove event
        it = mEventQueue.erase(it); 
        count--; 
    }
}

मैं किसी भी कक्षा के लिए बेस क्लास के रूप में सुविधा के लिए इवेंट लिस्टनर क्लास का उपयोग करता हूं जो घटनाओं को सुनना चाहता है। यदि आप इस कक्षा से अपनी श्रवण कक्षा प्राप्त करते हैं और इसे अपने इवेंट मैनेजर के साथ आपूर्ति करते हैं, तो आप अपनी घटनाओं को पंजीकृत करने के लिए बहुत सुविधाजनक फ़ंक्शन OnEvent (..) का उपयोग कर सकते हैं। और आधार वर्ग स्वचालित रूप से आपके व्युत्पन्न वर्ग को सभी घटनाओं से नष्ट कर देगा जब इसे नष्ट कर दिया जाएगा। जब आपकी कक्षा नष्ट हो जाती है तो इवेंट मैनेजर से एक प्रतिनिधि को हटाने के लिए भूलना बहुत सुविधाजनक है, लगभग निश्चित रूप से आपके प्रोग्राम को क्रैश होने का कारण बन जाएगा।

कक्षा में स्थिर कार्य घोषित करके और उसके पते को एक int में कास्टिंग करके किसी ईवेंट के लिए एक अद्वितीय प्रकार आईडी प्राप्त करने का एक साफ तरीका। चूंकि प्रत्येक वर्ग में अलग-अलग पते पर यह विधि होगी, इसका उपयोग कक्षा की घटनाओं की अनूठी पहचान के लिए किया जा सकता है। यदि आप चाहें तो एक अद्वितीय आईडी प्राप्त करने के लिए आप टाइपनाम () को int में भी डाल सकते हैं। ऐसा करने के कई तरीके हैं।

तो यहां इसका उपयोग करने के तरीके पर एक उदाहरण दिया गया है:

#include <functional>
#include <memory>
#include <stdio.h>
#include <list>
#include <map>

#include "Events.hpp"
#include "Events.cpp"

using namespace std; 

class DisplayTextEvent : public IEventData {
public:
    DECLARE_EVENT(DisplayTextEvent); 

    DisplayTextEvent(const string &text){
        mStr = text; 
    }
    ~DisplayTextEvent(){
        printf("Deleted event data\n"); 
    }
    const string &GetText(){
        return mStr; 
    }
private:
    string mStr; 
}; 

class Emitter { 
public:
    Emitter(shared_ptr<IEventManager> em){
        mEmgr = em; 
    }
    void EmitEvent(){
        mEmgr->QueueEvent(shared_ptr<IEventData>(
            new DisplayTextEvent("Hello World!"))); 
    }
private:
    shared_ptr<IEventManager> mEmgr; 
}; 

class Receiver : public EventListener{
public:
    Receiver(shared_ptr<IEventManager> em) : EventListener(em){
        mEmgr = em; 

        OnEvent<DisplayTextEvent>([&](shared_ptr<DisplayTextEvent> data){
            printf("It's working: %s\n", data->GetText().c_str()); 
        }); 
    }
    ~Receiver(){
        mEmgr->RemoveListener(DisplayTextEvent::ID(), std::bind(&Receiver::OnExampleEvent, this, placeholders::_1)); 
    }
    void OnExampleEvent(IEventDataPtr &data){
        auto ev = dynamic_pointer_cast<DisplayTextEvent>(data); 
        if(!ev) return; 
        printf("Received event: %s\n", ev->GetText().c_str()); 
    }
private:
    shared_ptr<IEventManager> mEmgr; 
}; 

int main(){
    auto emgr = shared_ptr<IEventManager>(new EventManager()); 


    Emitter emit(emgr); 
    {
        Receiver receive(emgr); 

        emit.EmitEvent(); 
        emgr->ProcessEvents(); 
    }
    emit.EmitEvent(); 
    emgr->ProcessEvents(); 
    emgr = 0; 

    return 0; 
}

संपादित करें: नीचे मैं एक मूल घटना संदेश प्रणाली का वर्णन करता हूं जिसका मैंने उपयोग किया है। और यह मेरे लिए हुआ कि दोनों स्कूल परियोजनाएं खुले स्रोत और वेब पर हैं। आप http://sourceforge.net/projects/bpfat/ पर इस संदेश प्रणाली का दूसरा संस्करण (और काफी कुछ) पा सकते हैं .. आनंद लें, और सिस्टम के अधिक विस्तृत विवरण के लिए नीचे पढ़ें!

मैंने एक जेनेरिक मैसेजिंग सिस्टम लिखा है और इसे कुछ हद तक गेम में पेश किया है जो पीएसपी के साथ-साथ कुछ एंटरप्राइज़ लेवल एप्लिकेशन सॉफ़्टवेयर पर जारी किए गए हैं। संदेश प्रणाली का बिंदु केवल उस डेटा को पास करना है जो किसी संदेश या घटना को संसाधित करने के लिए आवश्यक है, जिस शब्दावली का आप उपयोग करना चाहते हैं, उसके आधार पर, ताकि ऑब्जेक्ट्स को एक दूसरे के बारे में पता न हो।

इसे पूरा करने के लिए उपयोग की जाने वाली वस्तुओं की सूची का एक त्वरित छत इस प्रकार की रेखाओं के साथ है:

struct TEventMessage
{
    int _iMessageID;
}

class IEventMessagingSystem
{
    Post(int iMessageId);
    Post(int iMessageId, float fData);
    Post(int iMessageId, int iData);
    // ...
    Post(TMessageEvent * pMessage);
    Post(int iMessageId, void * pData);
}

typedef float(*IEventMessagingSystem::Callback)(TEventMessage * pMessage);

class CEventMessagingSystem
{
    Init       ();
    DNit       ();
    Exec       (float fElapsedTime);

    Post       (TEventMessage * oMessage);

    Register   (int iMessageId, IEventMessagingSystem* pObject, FObjectCallback* fpMethod);
    Unregister (int iMessageId, IEventMessagingSystem* pObject, FObjectCallback * fpMethod);
}

#define MSG_Startup            (1)
#define MSG_Shutdown           (2)
#define MSG_PlaySound          (3)
#define MSG_HandlePlayerInput  (4)
#define MSG_NetworkMessage     (5)
#define MSG_PlayerDied         (6)
#define MSG_BeginCombat        (7)
#define MSG_EndCombat          (8)

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

अगला इंटरफ़ेस क्लास है जो मैसेजिंग सिस्टम को कॉलबैक करते समय कास्टिंग के लिए उपयोग करने के लिए एक सामान्य वस्तु देता है। इसके अतिरिक्त यह मैसेजिंग सिस्टम में विभिन्न डेटा प्रकारों को पोस्ट () के लिए 'उपयोग करने में आसान' इंटरफेस भी प्रदान करता है।

इसके बाद हमारे पास हमारे कॉलबैक टाइपपीफ हैं, बस इसे इंटरफ़ेस क्लास के प्रकार की ऑब्जेक्ट की अपेक्षा करें और एक TEVEMessage पॉइंटर के साथ पास हो जाएगा ... वैकल्पिक रूप से आप पैरामीटर कॉन्स बना सकते हैं लेकिन मैंने पहले जैसी चीजों के लिए ट्रिकल अप प्रोसेसिंग का उपयोग किया है स्टैक डीबगिंग और मैसेजिंग सिस्टम की तरह।

अंतिम और मूल पर CEventMessagingSystem ऑब्जेक्ट है। इस ऑब्जेक्ट में कॉलबैक ऑब्जेक्ट स्टैक्स (या लिंक की गई सूचियां या कतार या फिर आप डेटा स्टोर करना चाहते हैं) की एक सरणी है। कॉलबैक ऑब्जेक्ट्स, जो ऊपर दिखाए गए हैं, को ऑब्जेक्ट के पॉइंटर के साथ-साथ उस ऑब्जेक्ट पर कॉल करने की विधि को बनाए रखने की आवश्यकता नहीं है (और विशिष्ट रूप से परिभाषित किया गया है)। जब आप रजिस्टर करते हैं () आप संदेश आईडी की सरणी स्थिति के तहत ऑब्जेक्ट स्टैक पर एक प्रविष्टि जोड़ते हैं। जब आप पंजीकरण नहीं करते हैं () आप उस प्रविष्टि को हटा देते हैं।

यह मूल रूप से यह है। अब इसमें यह शर्त है कि आईवेन्ट मैसेजिंग सिस्टम और TEventMessage ऑब्जेक्ट के बारे में सबकुछ जानने की जरूरत है ... लेकिन इस ऑब्जेक्ट को अक्सर नहीं बदला जाना चाहिए और केवल उस जानकारी के कुछ हिस्सों को पास करना चाहिए जो घटना के आधार पर तर्क के लिए महत्वपूर्ण हैं। इस तरह किसी खिलाड़ी को मानचित्र या दुश्मन के बारे में जानने की आवश्यकता नहीं होती है ताकि वह घटनाओं को सीधे भेज सके। एक प्रबंधित ऑब्जेक्ट एपीआई को इसके बारे में कुछ भी जानने के बिना, एक बड़ी प्रणाली को भी कॉल कर सकता है।

उदाहरण के लिए: जब एक दुश्मन मर जाता है तो आप इसे ध्वनि प्रभाव खेलना चाहते हैं। मान लें कि आपके पास एक ध्वनि प्रबंधक है जो आईवेन्ट मैसेजिंग सिस्टम इंटरफ़ेस को विरासत में लेता है, आप मैसेजिंग सिस्टम के लिए कॉलबैक सेट अप करेंगे जो TEventMessagePlaySoundEffect या उस तरह के कुछ को स्वीकार करेगा। जब ध्वनि प्रभाव सक्षम होते हैं तो ध्वनि प्रबंधक तब इस कॉलबैक को पंजीकृत करेगा (या कॉलबैक को अनधिकृत करें जब आप आसान ऑन / ऑफ क्षमताओं के लिए सभी ध्वनि प्रभावों को म्यूट करना चाहते हैं)। इसके बाद, आपके पास दुश्मन ऑब्जेक्ट भी आईवेन्ट मैसेजिंग सिस्टम से प्राप्त होगा, एक TEventMessagePlaySoundEffect ऑब्जेक्ट को एक साथ रखेगा (इसके संदेश आईडी के लिए MSG_PlaySound की आवश्यकता होगी और उसके बाद ध्वनि प्रभाव की आईडी की आवश्यकता होगी, चाहे वह एक int आईडी हो या ध्वनि का नाम हो प्रभाव) और बस पोस्ट (और oEventMessagePlaySoundEffect) कॉल करें।

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

एक और परिवर्तन उन ऑब्जेक्ट्स के लिए है जिन्हें केवल पोस्ट () डेटा की आवश्यकता होती है, आप IEventMessagingSystem में विधियों का एक स्थिर सेट बना सकते हैं, इसलिए उन्हें उनसे उत्तराधिकारी नहीं होना चाहिए (इसका उपयोग एक्सेस और कॉलबैक क्षमताओं की आसानी के लिए किया जाता है, प्रत्यक्ष रूप से नहीं - पोस्ट () कॉल के लिए आवश्यक)।

एमवीसी का उल्लेख करने वाले सभी लोगों के लिए, यह एक बहुत अच्छा पैटर्न है, लेकिन आप इसे कई अलग-अलग शिष्टाचार और विभिन्न स्तरों पर लागू कर सकते हैं। वर्तमान परियोजना जो मैं व्यावसायिक रूप से काम कर रहा हूं वह एक एमवीसी सेटअप लगभग 3 गुना अधिक है, पूरे आवेदन का वैश्विक एमवीसी है और फिर प्रत्येक एमवी और सी के डिजाइन के अनुसार एक स्वयं निहित एमवीसी पैटर्न भी है। तो मैंने यहां क्या करने की कोशिश की है, यह समझाया गया है कि सी को कैसे बनाना है जो कि किसी भी प्रकार के एम को किसी दृश्य में आने की आवश्यकता के बिना संभालने के लिए पर्याप्त सामान्य है ...

उदाहरण के लिए, जब कोई वस्तु 'मर जाती है' एक ध्वनि प्रभाव खेलना चाहती है .. आप ध्वनि प्रणाली के लिए एक संरचना तैयार करेंगे जैसे TEventMessageSoundEffect जो TEventMessage से प्राप्त होता है और ध्वनि प्रभाव आईडी में जोड़ता है (चाहे वह प्रीलोडेड इंट हो या हो, एसएफएक्स फ़ाइल का नाम, हालांकि वे आपके सिस्टम में ट्रैक किए जाते हैं)। फिर सभी ऑब्जेक्ट को उचित डेथ शोर के साथ एक TEventMessageSoundEffect ऑब्जेक्ट को एक साथ रखना होगा और पोस्ट (& oEventMessageSoundEffect) को कॉल करना होगा; ऑब्जेक्ट .. ध्वनि मानना ​​म्यूट नहीं है (आप ध्वनि प्रबंधक को अनधिकृत करना चाहते हैं।

संपादित करें: नीचे दी गई टिप्पणी के संबंध में इसे थोड़ा सा स्पष्ट करने के लिए: किसी संदेश को भेजने या प्राप्त करने के लिए किसी ऑब्जेक्ट को केवल आईवेन्ट मैसेजिंग सिस्टम इंटरफ़ेस के बारे में जानना आवश्यक है, और यह एकमात्र ऑब्जेक्ट है जिसे EventMessaging सिस्टम को अन्य सभी ऑब्जेक्ट्स के बारे में जानने की आवश्यकता है। यह आपको अलगाव देता है। कोई भी वस्तु जो संदेश प्राप्त करना चाहता है बस इसके लिए रजिस्टर (एमएसजी, ऑब्जेक्ट, कॉलबैक) एस। फिर जब कोई ऑब्जेक्ट पोस्ट (एमएसजी, डेटा) कहता है तो यह इवेंट मैसेजिंग सिस्टम को इंटरफ़ेस के माध्यम से भेजता है, इसके बारे में जानता है, ईएमएस तब घटना के प्रत्येक पंजीकृत ऑब्जेक्ट को सूचित करेगा। आप एक MSG_PlayerDied कर सकते हैं कि अन्य सिस्टम हैंडल करते हैं, या प्लेयर MSG_PlaySound, MSG_Respawn आदि को कॉल कर सकता है ताकि उन संदेशों को उन संदेशों पर कार्य करने के लिए सुन सकें। गेम इंजन के भीतर विभिन्न प्रणालियों के लिए एक सारणीकृत एपीआई के रूप में पोस्ट (एमएसजी, डेटा) के बारे में सोचें।

ओह! एक और चीज जो मुझे इंगित करती थी। जिस प्रणाली का मैं ऊपर वर्णन करता हूं वह अन्य उत्तर में पर्यवेक्षक पैटर्न को फिट करता है। इसलिए यदि आप थोड़ा और अधिक समझने के लिए एक और सामान्य विवरण चाहते हैं, तो यह एक छोटा सा लेख है जो इसे एक अच्छा वर्णन देता है।

उम्मीद है कि यह मदद करता है और आनंद लें!





tightly-coupled-code