ইঞ্জিন অংশগুলির মধ্যে মিথস্ক্রিয়া কীভাবে বাস্তবায়ন করবেন?


10

গেম ইঞ্জিনের অংশগুলির মধ্যে তথ্য বিনিময় কীভাবে কার্যকর করা উচিত সে সম্পর্কে আমি একটি প্রশ্ন জিজ্ঞাসা করতে চাই।

ইঞ্জিন চারটি ভাগে বিভক্ত: যুক্তি, তথ্য, ইউআই, গ্রাফিক্স। শুরুতে আমি পতাকাগুলির মাধ্যমে এই বিনিময়টি করেছি। উদাহরণস্বরূপ যদি ডেটাতে নতুন অবজেক্টটি যুক্ত করা হয় তবে isNewকোনও বস্তুর শ্রেণিতে পতাকাটি সেট করা হবে true। এবং এরপরে ইঞ্জিনের গ্রাফিক্সের অংশটি এই পতাকাটি পরীক্ষা করবে এবং গেমের জগতে এই বস্তুকে যুক্ত করবে।

যদিও, এই পদ্ধতির সাথে আমি প্রতিটি ধরণের প্রতিটি পতাকার প্রক্রিয়া করার জন্য অনেক কোড লিখতে হয়েছিল।

আমি কিছু ইভেন্ট সিস্টেম ব্যবহার করার কথা ভেবেছিলাম, তবে এটি সঠিক সমাধান হবে কিনা তা জানার মতো পর্যাপ্ত অভিজ্ঞতা আমার নেই।

ইভেন্ট সিস্টেমটিই কি একমাত্র যথাযথ পন্থা, নাকি আমার অন্য কিছু ব্যবহার করা উচিত?

আমি যদি ওগ্রিকে গ্রাফিক্স ইঞ্জিন হিসাবে ব্যবহার করি তবে তা যদি বিবেচিত হয়।


এটি খুব অস্পষ্ট প্রশ্ন। আপনার সিস্টেমগুলি কীভাবে ইন্টারেক্ট করে তা আপনার সিস্টেমগুলি কীভাবে ডিজাইন করা হয়েছে এবং কী ধরনের এনক্যাপসুলেশন শেষ করছেন তা মিলিয়ে চলেছে। তবে একটি জিনিস দাঁড়িয়ে আছে: "এবং তারপরে ইঞ্জিনের গ্রাফিক্সের অংশটি এই পতাকাটি পরীক্ষা করবে এবং গেমের জগতে এই অবজেক্টটি যুক্ত করবে।" ইঞ্জিনের গ্রাফিক্স কেন বিশ্বের জিনিস যুক্ত করছে ? দেখে মনে হচ্ছে পৃথিবীর গ্রাফিক্স মডিউলটি কী রেন্ডার করতে হবে তা বলা উচিত।
টেট্রাড

ইঞ্জিনে "গ্রাফিক্স" অংশটি ওগ্রাকে নিয়ন্ত্রণ করে (উদাহরণস্বরূপ, দৃশ্যে এটি যুক্ত করতে বলে) tells তবে এটি করার জন্য এটি যে নতুন বিষয়টির জন্য "ডেটা" অনুসন্ধান করে (এবং তার পরে ওগ্রে দৃশ্যে এটি যুক্ত করতে বলে) তবে অভিজ্ঞতার অভাবে এই পন্থাটি সঠিক বা ভুল কিনা তা আমি জানি না।
ব্যবহারকারী

উত্তর:


20

আমার প্রিয় গেম ইঞ্জিন স্ট্রাকচারটি হ'ল ইন্টারফেস এবং অবজেক্ট <-> প্রায় সমস্ত অংশের মধ্যে যোগাযোগের জন্য মেসেজিং ব্যবহার করে উপাদান উপাদান।

আপনার প্রধান ইঞ্জিন অংশ যেমন আপনার দৃশ্যের পরিচালক, রিসোর্স লোডার, অডিও, রেন্ডারার, পদার্থবিজ্ঞান ইত্যাদি জন্য একাধিক ইন্টারফেস রয়েছে

আমার কাছে থ্রিডি দৃশ্য / জগতের সমস্ত বস্তুর দায়িত্বে থাকা দৃশ্যের পরিচালক রয়েছে have

অবজেক্ট একটি খুব পারমাণবিক শ্রেণি, কেবলমাত্র কয়েকটি জিনিস যা আপনার দৃশ্যের প্রায় প্রতিটি ক্ষেত্রেই সাধারণ, আমার ইঞ্জিনে অবজেক্ট বর্গটি কেবলমাত্র অবস্থান, আবর্তন, উপাদানগুলির একটি তালিকা এবং একটি অনন্য আইডি ধারণ করে। প্রতিটি বস্তুর আইডি একটি স্ট্যাটিক ইনট দ্বারা উত্পন্ন হয়, যাতে কোনও দুটি বস্তুর প্রত্যেকেরই একই আইডি না থাকে, এটি আপনাকে আইডির মাধ্যমে কোনও আইটেমটিতে বার্তা প্রেরণ করতে দেয়, পরিবর্তে অবজেক্টের পয়েন্টার না রেখে।

অবজেক্টের উপাদানগুলির তালিকা হ'ল বস্তুগুলি প্রধান বৈশিষ্ট্য। উদাহরণস্বরূপ, 3 ডি ওয়ার্ল্ডে আপনি দেখতে পাচ্ছেন এমন কোনও কিছুর জন্য আপনি নিজের বস্তুকে একটি রেন্ডার উপাদান দেবেন যা রেন্ডার জাল সম্পর্কে তথ্য ধারণ করে। আপনি যদি পদার্থবিদ্যার জন্য কোনও বস্তু চান তবে আপনি এটি একটি পদার্থবিজ্ঞান উপাদান দিতে পারেন। আপনি যদি ক্যামেরা হিসাবে অভিনয় করার জন্য কিছু চান তবে এটি একটি ক্যামেরার উপাদান দিন। উপাদানগুলির তালিকাটি চালিয়ে যেতে পারে।

ইন্টারফেস, বস্তু এবং উপাদানগুলির মধ্যে যোগাযোগের মূল বিষয়। আমার ইঞ্জিনে আমার একটি জেনেরিক বার্তা শ্রেণি রয়েছে যা কেবলমাত্র একটি অনন্য আইডি এবং একটি বার্তার ধরণের আইডি ধারণ করে। অনন্য আইডি হ'ল আপনি যে বার্তায় বার্তাটি যেতে চান তার আইডি এবং বার্তাটি প্রাপ্ত বার্তাটি বার্তা প্রাপ্ত আইডিটি ব্যবহার করে তাই এটি কী ধরণের বার্তা তা জানে।

বিষয়গুলি প্রয়োজন হলে বার্তাটি পরিচালনা করতে পারে এবং তারা তাদের প্রতিটি উপাদানগুলিতে বার্তাটি প্রেরণ করতে পারে এবং উপাদানগুলি বার বার বার্তাটি দিয়ে গুরুত্বপূর্ণ কাজগুলি করে। উদাহরণস্বরূপ, আপনি যদি অবজেক্টের সেটপেজ বার্তা প্রেরণ ও অবজেক্টের অবস্থান পরিবর্তন করতে চান তবে বার্তাটি পাওয়ার পরে অবজেক্টটি তার অবস্থানের পরিবর্তনশীল আপডেট করতে পারে, তবে রেন্ডার উপাদানটি রেন্ডার জালটির অবস্থান আপডেট করতে বার্তা প্রয়োজন হতে পারে, এবং পদার্থ বিজ্ঞানের উপাদানটির পদার্থ বিজ্ঞানের শরীরের অবস্থান আপডেট করার জন্য বার্তার প্রয়োজন হতে পারে।

এখানে দৃশ্যের পরিচালক, অবজেক্ট এবং উপাদান এবং বার্তা প্রবাহের একটি খুব সাধারণ বিন্যাস, যা আমি সি ++ তে প্রায় এক ঘন্টার মধ্যে বেত্রাঘাত করি। রান করার সময় এটি কোনও বস্তুর অবস্থান নির্ধারণ করে এবং বার্তাটি রেন্ডার উপাদানটির মধ্য দিয়ে যায়, তারপরে বস্তু থেকে অবস্থানটি পুনরুদ্ধার করে। উপভোগ করুন!

এছাড়াও, আমি যে কারও জন্য সি ++ এর চেয়ে সাবলীল হতে পারে তার জন্য নীচের কোডটির একটি সি # সংস্করণ এবং স্কেলা সংস্করণ লিখেছি ।

#include <iostream>
#include <stdio.h>

#include <list>
#include <map>

using namespace std;

struct Vector3
{
public:
    Vector3() : x(0.0f), y(0.0f), z(0.0f)
    {}

    float x, y, z;
};

enum eMessageType
{
    SetPosition,
    GetPosition,    
};

class BaseMessage
{
protected: // Abstract class, constructor is protected
    BaseMessage(int destinationObjectID, eMessageType messageTypeID) 
        : m_destObjectID(destinationObjectID)
        , m_messageTypeID(messageTypeID)
    {}

public: // Normally this isn't public, just doing it to keep code small
    int m_destObjectID;
    eMessageType m_messageTypeID;
};

class PositionMessage : public BaseMessage
{
protected: // Abstract class, constructor is protected
    PositionMessage(int destinationObjectID, eMessageType messageTypeID, 
                    float X = 0.0f, float Y = 0.0f, float Z = 0.0f)
        : BaseMessage(destinationObjectID, messageTypeID)
        , x(X)
        , y(Y)
        , z(Z)
    {

    }

public:
    float x, y, z;
};

class MsgSetPosition : public PositionMessage
{
public:
    MsgSetPosition(int destinationObjectID, float X, float Y, float Z)
        : PositionMessage(destinationObjectID, SetPosition, X, Y, Z)
    {}
};

class MsgGetPosition : public PositionMessage
{
public:
    MsgGetPosition(int destinationObjectID)
        : PositionMessage(destinationObjectID, GetPosition)
    {}
};

class BaseComponent
{
public:
    virtual bool SendMessage(BaseMessage* msg) { return false; }
};

class RenderComponent : public BaseComponent
{
public:
    /*override*/ bool SendMessage(BaseMessage* msg)
    {
        // Object has a switch for any messages it cares about
        switch(msg->m_messageTypeID)
        {
        case SetPosition:
            {                   
                // Update render mesh position/translation

                cout << "RenderComponent handling SetPosition\n";
            }
            break;
        default:
            return BaseComponent::SendMessage(msg);
        }

        return true;
    }
};

class Object
{
public:
    Object(int uniqueID)
        : m_UniqueID(uniqueID)
    {
    }

    int GetObjectID() const { return m_UniqueID; }

    void AddComponent(BaseComponent* comp)
    {
        m_Components.push_back(comp);
    }

    bool SendMessage(BaseMessage* msg)
    {
        bool messageHandled = false;

        // Object has a switch for any messages it cares about
        switch(msg->m_messageTypeID)
        {
        case SetPosition:
            {               
                MsgSetPosition* msgSetPos = static_cast<MsgSetPosition*>(msg);
                m_Position.x = msgSetPos->x;
                m_Position.y = msgSetPos->y;
                m_Position.z = msgSetPos->z;

                messageHandled = true;
                cout << "Object handled SetPosition\n";
            }
            break;
        case GetPosition:
            {
                MsgGetPosition* msgSetPos = static_cast<MsgGetPosition*>(msg);
                msgSetPos->x = m_Position.x;
                msgSetPos->y = m_Position.y;
                msgSetPos->z = m_Position.z;

                messageHandled = true;
                cout << "Object handling GetPosition\n";
            }
            break;
        default:
            return PassMessageToComponents(msg);
        }

        // If the object didn't handle the message but the component
        // did, we return true to signify it was handled by something.
        messageHandled |= PassMessageToComponents(msg);

        return messageHandled;
    }

private: // Methods
    bool PassMessageToComponents(BaseMessage* msg)
    {
        bool messageHandled = false;

        auto compIt = m_Components.begin();
        for ( compIt; compIt != m_Components.end(); ++compIt )
        {
            messageHandled |= (*compIt)->SendMessage(msg);
        }

        return messageHandled;
    }

private: // Members
    int m_UniqueID;
    std::list<BaseComponent*> m_Components;
    Vector3 m_Position;
};

class SceneManager
{
public: 
    // Returns true if the object or any components handled the message
    bool SendMessage(BaseMessage* msg)
    {
        // We look for the object in the scene by its ID
        std::map<int, Object*>::iterator objIt = m_Objects.find(msg->m_destObjectID);       
        if ( objIt != m_Objects.end() )
        {           
            // Object was found, so send it the message
            return objIt->second->SendMessage(msg);
        }

        // Object with the specified ID wasn't found
        return false;
    }

    Object* CreateObject()
    {
        Object* newObj = new Object(nextObjectID++);
        m_Objects[newObj->GetObjectID()] = newObj;

        return newObj;
    }

private:
    std::map<int, Object*> m_Objects;
    static int nextObjectID;
};

// Initialize our static unique objectID generator
int SceneManager::nextObjectID = 0;

int main()
{
    // Create a scene manager
    SceneManager sceneMgr;

    // Have scene manager create an object for us, which
    // automatically puts the object into the scene as well
    Object* myObj = sceneMgr.CreateObject();

    // Create a render component
    RenderComponent* renderComp = new RenderComponent();

    // Attach render component to the object we made
    myObj->AddComponent(renderComp);

    // Set 'myObj' position to (1, 2, 3)
    MsgSetPosition msgSetPos(myObj->GetObjectID(), 1.0f, 2.0f, 3.0f);
    sceneMgr.SendMessage(&msgSetPos);
    cout << "Position set to (1, 2, 3) on object with ID: " << myObj->GetObjectID() << '\n';

    cout << "Retreiving position from object with ID: " << myObj->GetObjectID() << '\n';

    // Get 'myObj' position to verify it was set properly
    MsgGetPosition msgGetPos(myObj->GetObjectID());
    sceneMgr.SendMessage(&msgGetPos);
    cout << "X: " << msgGetPos.x << '\n';
    cout << "Y: " << msgGetPos.y << '\n';
    cout << "Z: " << msgGetPos.z << '\n';
}

1
এই কোডটি দেখতে খুব সুন্দর লাগছে। আমাকে ityক্যের স্মরণ করিয়ে দেয়।
টিলি

আমি জানি এটি একটি পুরানো উত্তর, তবে আমার কয়েকটি প্রশ্ন রয়েছে। কোনও 'আসল' গেমের কোডিং দুঃস্বপ্ন তৈরি করে শত শত বার্তা প্রেরণা থাকে না? এছাড়াও, যদি আপনার প্রয়োজন হয় (উদাহরণস্বরূপ) মূল চরিত্রটি সঠিকভাবে আঁকার জন্য মুখোমুখি হয় তবে আপনি কী করবেন। আপনার কি নতুন গেটসপ্রাইটমেসেজ তৈরি করার দরকার নেই এবং প্রতিবারের প্রতিটি রেন্ডারে পাঠানো উচিত? এটি কি খুব ব্যয়বহুল হয়ে ওঠে না? ভাবছি! ধন্যবাদ।
you786

আমার শেষ প্রজেক্টে আমরা মেসেজগুলি লিখতে এক্সএমএল ব্যবহার করেছি এবং পাইথন স্ক্রিপ্টটি আমাদের সময় নির্মানের সময় সমস্ত কোড তৈরি করেছিল। বিভিন্ন বার্তা বিভাগের জন্য আপনি একাধিক এক্সএমএল বিভক্ত করতে পারেন। আপনি বার্তা প্রেরণের জন্য ম্যাক্রোগুলি তৈরি করতে পারেন, ফাংশন কলের মতো প্রায় নিবিষ্ট করে তুলতে, যদি কোনও বার্তা না দিয়ে কোনও চরিত্রের মুখোমুখি হওয়ার প্রয়োজন হয় তবে আপনাকে এখনও উপাদানটির পয়েন্টারটি পেতে হবে এবং তারপরে ফোনটি ফাংশনটি জানতে হবে এটি (যদি আপনি মেসেজিং ব্যবহার না করে থাকেন)। রেন্ডারকম্পোনেন্ট রেন্ডারারের সাথে নিবন্ধন করতে পারে যাতে আপনার এটি প্রতিটি ফ্রেমে কোয়েরি করতে হবে না।
নিক ফস্টার

2

আমি মনে করি এটি দৃশ্য পরিচালক এবং ইন্টারফেসগুলি ব্যবহার করার সর্বোত্তম উপায়। বার্তাগুলি কার্যকর হয়েছে তবে আমি এটিকে মাধ্যমিক হিসাবে ব্যবহার করব। আন্তঃ থ্রেড যোগাযোগের জন্য বার্তাটি ভাল। আপনি যেখানেই পারেন বিমূর্ততা (ইন্টারফেস) ব্যবহার করুন।

আমি ওগ্রে সম্পর্কে তেমন কিছু জানি না, তাই আমি সাধারণত কথা বলি।

মূলত, আপনার কাছে প্রধান গেম লুপ রয়েছে। এটি ইনপুট সিগন্যাল পায়, এআই গণনা করে (সাধারণ গতি থেকে জটিল এআই এবং গেম যুক্তিতে), লোড সংস্থানগুলি [, ইত্যাদি] এবং বর্তমান অবস্থাকে রেন্ডার করে। এটি মৌলিক উদাহরণ, সুতরাং আপনি ইঞ্জিনগুলি এই অংশগুলিতে আলাদা করতে পারবেন (ইনপুট ম্যানেজার, আইআইএমএনএজার, রিসোর্স ম্যানেজার, রেন্ডারম্যানেজার)। এবং আপনার সিনম্যানেজার থাকা উচিত যা খেলায় উপস্থিত সমস্ত অবজেক্টকে ধরে রাখে।

এই অংশ এবং তাদের উপ-অংশগুলির প্রতিটিটির ইন্টারফেস রয়েছে। সুতরাং এই অংশগুলি তাদের এবং কেবল তাদের কাজ করার জন্য সংগঠিত করার চেষ্টা করুন। তাদের উপ-অংশগুলি ব্যবহার করা উচিত যা তাদের পিতামাতার অংশের উদ্দেশ্যে অভ্যন্তরীণভাবে যোগাযোগ করে। এইভাবে আপনি পুরো পুনর্লিখন ছাড়াই আনারোলিংয়ের সুযোগ ছাড়াই এমব্রয়লে উঠবেন না।

আপনি যদি সি ++ ব্যবহার করছেন তবে RAII প্যাটার্নটি বিবেচনা করুন ps


2
আরআইআই কোনও প্যাটার্ন নয়, এটি একটি জীবনযাত্রার উপায়।
শটগান নিনজা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.