আমি কীভাবে আমার সি ++ সত্তা-উপাদান-সিস্টেমগুলির উপাদানগুলি সঠিকভাবে অ্যাক্সেস করতে পারি?


18

(আমি যা বর্ণনা করছি তা এই নকশার উপর ভিত্তি করে: একটি সত্তা সিস্টেমের কাঠামো কী ? , নীচে স্ক্রোল করুন এবং আপনি এটি খুঁজে পাবেন)

C ++ এ সত্তা-উপাদান সিস্টেম তৈরি করতে আমার কিছু সমস্যা হচ্ছে। আমি আমার উপাদান ক্লাস আছে:

class Component { /* ... */ };

অন্যান্য উপাদান তৈরি করার জন্য যা আসলে একটি ইন্টারফেস। সুতরাং, একটি কাস্টম উপাদান তৈরি করতে, আমি কেবল ইন্টারফেসটি প্রয়োগ করেছি এবং ইন-গেমের জন্য ব্যবহৃত ডেটা যুক্ত করব:

class SampleComponent : public Component { int foo, float bar ... };

এই উপাদানগুলি একটি সত্তা শ্রেণীর ভিতরে সংরক্ষণ করা হয়, যা সত্তার প্রতিটি উদাহরণকে একটি অনন্য আইডি দেয়:

class Entity {
     int ID;
     std::unordered_map<string, Component*> components;
     string getName();
     /* ... */
};

উপাদানগুলির নামটি হ্যাশ করে উপাদানগুলিকে সত্তায় যুক্ত করা হয় (এটি সম্ভবত এত বড় ধারণা নয়)। আমি যখন একটি কাস্টম উপাদান যুক্ত করি, তখন এটি উপাদান উপাদান (বেস শ্রেণি) হিসাবে সংরক্ষণ করা হয়।

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

/* System and Node implementations: (not the interfaces!) */

class SampleSystem : public System {
        std::list<SampleNode> nodes; //uses SampleNode, not Node
        void update();
        /* ... */
};

class SampleNode : public Node {
        /* Here I define which components SampleNode (and SampleSystem) "needs" */
        SampleComponent* sc;
        PhysicsComponent* pc;
        /* ... more components could go here */
};

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

উত্তর:


23

আপনি যদি Componentসংগ্রহগুলিতে সমস্তগুলি একসাথে সঞ্চয় করতে যাচ্ছেন তবে আপনার অবশ্যই সংগ্রহের মধ্যে সংরক্ষণ করা ধরণ হিসাবে একটি সাধারণ বেস শ্রেণিটি ব্যবহার করতে হবে এবং আপনি যখন Componentসংগ্রহের মধ্যে অ্যাক্সেস করার চেষ্টা করবেন তখন আপনাকে অবশ্যই সঠিক ধরণে ফেলে দিতে হবে । ভুল উত্সযুক্ত শ্রেণিতে কাস্ট করার চেষ্টা করার সমস্যাগুলি টেমপ্লেট এবং typeidফাংশনটির চতুর ব্যবহারের মাধ্যমে নির্মূল করা যেতে পারে :

একটি মানচিত্র যেমন ঘোষণা করা হয়েছে:

std::unordered_map<const std::type_info* , Component *> components;

একটি অ্যাডক্যাম্পোনেন্ট ফাংশন যেমন:

components[&typeid(*component)] = component;

এবং একটি getComp घटक:

template <typename T>
T* getComponent()
{
    if(components.count(&typeid(T)) != 0)
    {
        return static_cast<T*>(components[&typeid(T)]);
    }
    else 
    {
        return NullComponent;
    }
}

আপনি কোনও মিসকাস্ট পাবেন না। এর কারণটি typeidউপাদানটির রানটাইম টাইপের (সর্বাধিক উত্পন্ন উত্পন্ন প্রকারের) টাইপের তথ্যে একটি পয়েন্টার ফিরিয়ে দেবে। যেহেতু উপাদানটি সেই জাতীয় তথ্যের সাথে কী হিসাবে সংরক্ষণ করা হয়, তাই অনন্যতম প্রকারের কারণে কাস্ট সম্ভবত সমস্যা তৈরি করতে পারে না। আপনি টেমপ্লেট টাইপটিতে সংকলন টাইম টাইপ চেকিংও পান কারণ এটি উপাদান থেকে প্রাপ্ত একটি প্রকার হতে static_cast<T*>হবে বা এর সাথে মিল নেই unordered_map

যদিও আপনাকে বিভিন্ন ধরণের উপাদানগুলি সাধারণ সংগ্রহে সঞ্চয় করার দরকার নেই। যদি আপনি কোনও Entityসমন্বিত এসগুলির ধারণাটি ত্যাগ করেন Componentএবং এর পরিবর্তে প্রতিটি Componentস্টোর একটি Entity(বাস্তবে এটি সম্ভবত একটি পূর্ণসংখ্যার আইডি হবে), তবে আপনি প্রতিটি উত্পন্ন উপাদান টাইপটি তার পরিবর্তে ডেরাইভেড প্রকারের সংগ্রহের মধ্যে সংরক্ষণ করতে পারেন সাধারণ বেস টাইপ করুন এবং সেই আইডির মাধ্যমে Componentএকটি "সম্পর্কিত" সন্ধান করুন Entity

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


3
আমি প্রায় 6 বছর ধরে সি ++ ব্যবহার করছি, তবুও প্রতি সপ্তাহে আমি কিছু নতুন কৌশল শিখি।
নাইট 666

উত্তর করার জন্য ধন্যবাদ. আমি প্রথমে প্রথম পদ্ধতিটি ব্যবহার করার চেষ্টা করব এবং পরে যদি আমি পরে নিতে পারি তবে আমি অন্যটি ব্যবহার করার একটি উপায় সম্পর্কে চিন্তা করব। তবে, addComponent()পদ্ধতিটিও কি টেম্পলেট পদ্ধতি হওয়ার দরকার নেই? যদি আমি একটি সংজ্ঞায়িত addComponent(Component* c)করি তবে যে কোনও উপ-উপাদান আমি যুক্ত করব তা একটি Componentপয়েন্টারে সংরক্ষণ করা typeidহবে এবং সর্বদা Componentবেস শ্রেণীর সাথে উল্লেখ করা হবে ।
ফেডেরিকো

2
টাইপড আপনাকে পয়েন্টারটি বেস শ্রেণীর হলেও বস্তুর প্রকৃত প্রকারটি দেবে
শেই গম্বল

আমি চিউয়ের উত্তরটি সত্যিই পছন্দ করেছি, তাই আমি মিংডব্লিউ 32 এ এটি প্রয়োগের চেষ্টা করেছি। আমি ফেডারেশন রিকো দ্বারা উল্লিখিত ইস্যুতে দৌড়েছি যেখানে অ্যাডকোম্পোনেন্ট () সমস্ত উপাদানকে উপাদান হিসাবে সঞ্চয় করে কারণ টাইপড হ'ল উপাদানটির জন্য উপাদান হিসাবে ফিরে আসছে। এখানে কেউ উল্লেখ করেছেন যে টাইপডকে অবজেক্টের প্রকৃত প্রকারটি দেওয়া উচিত যদিও পয়েন্টারটি বেস বেস শ্রেণিতে হয়, তবে আমি মনে করি এটি সংকলক ইত্যাদির উপর ভিত্তি করে আলাদা হতে পারে ইত্যাদি অন্য কেউ কি এটি নিশ্চিত করতে পারবেন? আমি উইন্ডোজ g-তে জি ++ স্টডি = সি ++ ১১ মিংডব্লু using ব্যবহার করছিলাম I. আমি কেবলমাত্র টেমপ্লেট হিসাবে getComp घटक () সংশোধন করে শেষ করেছি, তারপরে সে থেকে টাইপটি সেভ
করেছিলাম

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

17

চেউয়ের কাছে এটি ঠিক আছে তবে আপনি যদি সি ++ 11 ব্যবহার করেন তবে আপনার কাছে কিছু নতুন ধরণের ব্যবহার করতে পারেন।

const std::type_info*আপনার মানচিত্রে কী হিসাবে ব্যবহার করার পরিবর্তে , আপনি std::type_index( cppreferences.com দেখুন ) ব্যবহার করতে পারেন , যা চারপাশে একটি মোড়ক std::type_info। আপনি এটি ব্যবহার করবেন কেন? std::type_indexআসলে সঙ্গে সম্পর্ক সঞ্চয় করে std::type_infoএকটি পয়েন্টার হিসাবে, কিন্তু যে সম্পর্কে চিন্তা করতে আপনার জন্য এক পয়েন্টার কম।

আপনি যদি সত্যিই সি ++ 11 ব্যবহার করেন তবে আমি Componentস্মার্ট পয়েন্টারগুলির মধ্যে রেফারেন্সগুলি সঞ্চয় করার পরামর্শ দেব । সুতরাং মানচিত্রটি এমন কিছু হতে পারে:

std::map<std::type_index, std::shared_ptr<Component> > components

একটি নতুন এন্ট্রি যুক্ত করা যেতে পারে তাই:

components[std::type_index(typeid(*component))] = component

যেখানে componentটাইপ হয় std::shared_ptr<Component>। প্রদত্ত প্রকারের একটি রেফারেন্স পুনরুদ্ধার করার Componentমতো দেখতে পাওয়া যেতে পারে:

template <typename T>
std::shared_ptr<T> getComponent()
{
    std::type_index index(typeid(T));
    if(components.count(std::type_index(typeid(T)) != 0)
    {
        return static_pointer_cast<T>(components[index]);
    }
    else
    {
        return NullComponent
    }
}

static_pointer_castপরিবর্তে এর ব্যবহারটিও নোট করুন static_cast


1
আমি আসলে আমার নিজস্ব প্রকল্পে এই ধরণের পদ্ধতির ব্যবহার করছি।
বিজোচ

এটি আসলে বেশ সুবিধাজনক, যেহেতু আমি রেফারেন্স হিসাবে সি ++ 11 মান ব্যবহার করে সি ++ শিখছি। একটি জিনিস যদিও আমি লক্ষ্য করেছি, তা হ'ল আমি ওয়েবের চারপাশে খুঁজে পাওয়া সমস্ত সত্তা-উপাদান সিস্টেমগুলি কিছু প্রকারের ব্যবহার করে cast। আমি ভাবতে শুরু করি যে এটি বাস্তবায়ন করা অসম্ভব, বা বর্ণহীন একটি অনুরূপ সিস্টেমের নকশা।
ফেডেরিকো

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

@vijoc তারা কখনও কখনও মেমরির সংহত সমস্যার কারণে তাদের পরিচয় করিয়ে দেওয়ার কারণে খারাপ বলে বিবেচিত হয়।
aktartar
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.