উপাদান ভিত্তিক সত্তা সিস্টেম ব্যবহারিকভাবে ব্যবহার করা


59

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


প্রতিটি গেম সত্তা (খেলা বস্তু) গঠিত হয় বৈশিষ্ট্যাবলী এবং (, কিন্তু 'বহিরাগত কোড' দ্বারা = ডেটা, যা আচরণে দ্বারা ব্যবহার করা যেতে পারে) আচরণে (= যুক্তি, যা ধারণ করে OnUpdate()এবং OnMessage())। সুতরাং, উদাহরণস্বরূপ, একটি ব্রেকআউট ক্লোন, প্রত্যেক ইট গঠিত করা হবে (উদাহরণস্বরূপ!): PositionAttribute , ColorAttribute , HealthAttribute , RenderableBehaviour , HitBehaviour । শেষটি এর মতো দেখতে পেল (এটি কেবল সি # তে লেখা একটি অ-কর্ম উদাহরণ):

void OnMessage(Message m)
{
    if (m is CollisionMessage) // CollisionMessage is inherited from Message
    {
        Entity otherEntity = m.CollidedWith; // Entity CollisionMessage.CollidedWith
        if (otherEntity.Type = EntityType.Ball) // Collided with ball
        {
            int brickHealth = GetAttribute<int>(Attribute.Health); // owner's attribute
            brickHealth -= otherEntity.GetAttribute<int>(Attribute.DamageImpact);
            SetAttribute<int>(Attribute.Health, brickHealth); // owner's attribute

            // If health is <= 0, "destroy" the brick
            if (brickHealth <= 0)
                SetAttribute<bool>(Attribute.Alive, false);
        }
    }
    else if (m is AttributeChangedMessage) // Some attribute has been changed 'externally'
    {
        if (m.Attribute == Attribute.Health)
        {
            // If health is <= 0, "destroy" the brick
            if (brickHealth <= 0)
                SetAttribute<bool>(Attribute.Alive, false);
        }
    }
}

আপনি যদি এই সিস্টেমে আগ্রহী হন তবে আপনি এখানে (.ppt) আরও পড়তে পারেন ।


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

তো, আমি কী জিজ্ঞাসা করতে চাই? আচরণগুলি (উপাদানগুলি) কীভাবে ডিজাইন করবেন। গেমডেভ এসই-তে আমি এখানে পড়েছি যে সর্বাধিক সাধারণ ভুলটি অনেকগুলি উপাদান তৈরি করা এবং সহজভাবে "সবকিছুকে একটি উপাদান তৈরি করা" component আমি পড়েছি যে এটি একটি উপাদানের রেন্ডারিংয়ের না প্রস্তাব দেওয়া হচ্ছে, কিন্তু এটা (তাই পরিবর্তে বাইরে এটা করতে RenderableBehaviour , এটা হয়তো হওয়া উচিত RenderableAttribute , এবং যদি কোনো সত্তার করেছে RenderableAttribute সত্যতে সেট, তারপর Renderer(ক্লাস এর সাথে সম্পর্কিত নয় উপাদানগুলি, তবে ইঞ্জিনে নিজেই) এটি স্ক্রিনে আঁকতে হবে?)।

তবে, আচরণ / উপাদানগুলি সম্পর্কে কীভাবে? ধরুন যে, আমি একটি স্তর পেয়েছেন, এবং স্তরের, একটি যাক Entity button, Entity doorsএবং Entity player। যখন প্লেয়ার বোতামটির সাথে সংঘর্ষ হয় (এটি একটি ফ্লোর বোতাম, যা চাপ দ্বারা টগলড হয়), এটি টিপুন। বোতাম টিপলে, এটি দরজা খুলবে। আচ্ছা, এখন কীভাবে করব?

আমি ভালো কিছু সঙ্গে আসা পর্যন্ত করেছি: খেলোয়াড় পেয়েছে CollisionBehaviour , যা পরীক্ষা যদি খেলোয়াড় কিছু ধাক্কা দিতে যাচ্ছিল। যদি সে একটি বোতামের সাথে সংঘর্ষ CollisionMessageহয় তবে এটি buttonসত্তাকে একটি প্রেরণ করে । বার্তায় সমস্ত প্রয়োজনীয় তথ্য থাকবে: বোতামটির সাথে সংঘর্ষে কে। বোতামটি টগলযোগ্য বেহাইভ্যুর পেয়েছে , যা গ্রহণ করবে CollisionMessage। এটি পরীক্ষা করবে এটি কার সাথে সংঘর্ষ করেছিল এবং যদি সেই সত্তার ওজন বাটনটি টগল করার পক্ষে যথেষ্ট পরিমাণে হয় তবে বোতামটি টগল হয়ে যায়। এখন, এটি বোতামটির টগলডঅ্যাট্রিবিউট সেট করে । ঠিক আছে, তবে এখন কি?

বোতামটি অন্য সমস্ত অবজেক্টকে তাগল করা হয়েছে তা জানাতে কি অন্য বার্তা পাঠাতে হবে? আমি মনে করি যে আমি যদি এটির মতো করে করি তবে আমার হাজার হাজার বার্তা ছিল এবং এটি খুব অগোছালো হয়ে উঠবে। সুতরাং সম্ভবত এটি আরও ভাল: দরজাগুলি নিয়মিত যাচাই করে যেগুলির সাথে লিঙ্কযুক্ত বোতামটি টিপে আছে কিনা এবং ততক্ষণে এটির খোলারঅ্যাট্রিবিউট পরিবর্তন করে । তবে তার অর্থ এই যে দরজার OnUpdate()পদ্ধতিটি নিয়মিত কিছু করে চলেছে (এটি কি আসলেই সমস্যা?)

এবং দ্বিতীয় সমস্যা: যদি আমার আরও ধরণের বোতাম থাকে। একটিকে চাপ দিয়ে চাপানো হয়, দ্বিতীয়টি গুলি করে তাগল করা হয়, তৃতীয়টি টগল করা হয় যদি এটির উপরে জল isেলে দেওয়া হয়, ইত্যাদি This

Behaviour -> ToggleableBehaviour -> ToggleOnPressureBehaviour
                                 -> ToggleOnShotBehaviour
                                 -> ToggleOnWaterBehaviour

বাস্তব গেমগুলি কী এভাবে কাজ করে বা আমি কি নির্বোধ? সম্ভবত আমার কাছে কেবল একটি টোগলযোগ্য বেহাইভোর থাকতে পারে এবং এটি বাটনটাইপঅ্যাট্রিবিউট অনুসারে আচরণ করবে । সুতরাং এটি যদি একটি হয় ButtonType.Pressure, এটি এটি করে, যদি এটি হয় তবে এটি ButtonType.Shotঅন্য কিছু করে ...

তাহলে আমি কী চাই? আমি আপনাকে জিজ্ঞাসা করতে চাই যে আমি এটি সঠিকভাবে করছি কিনা, অথবা আমি কেবল বোকা এবং আমি উপাদানগুলির বিন্দুটি বুঝতে পারি না। গেমগুলিতে উপাদানগুলি কীভাবে কার্যকরভাবে কাজ করে তার কোনও ভাল উদাহরণ আমি খুঁজে পাইনি, আমি কীভাবে উপাদানটি তৈরি করব তা বর্ণনা করার জন্য কিছু টিউটোরিয়াল পেয়েছি, তবে কীভাবে এটি ব্যবহার করবেন তা নয়।

উত্তর:


46

উপাদানগুলি দুর্দান্ত, তবে এমন একটি সমাধান খুঁজে পেতে কিছুটা সময় নিতে পারে যা আপনার ভাল লাগে। চিন্তা করবেন না, আপনি সেখানে পাবেন। :)

সংগঠিত উপাদান

আপনি সঠিক পথে আছেন, আমি বলব। আমি দরজাটি দিয়ে শুরু করে এবং সুইচগুলি দিয়ে শেষ করে, বিপরীতে সমাধানটি বর্ণনা করার চেষ্টা করব। আমার বাস্তবায়ন ঘটনাগুলির প্রচুর ব্যবহার করে; নীচে আমি বর্ণনা করছি আপনি কীভাবে ইভেন্টগুলি আরও দক্ষতার সাথে ব্যবহার করতে পারেন যাতে তারা কোনও সমস্যায় পরিণত না হয়।

যদি তাদের মধ্যে সত্তাগুলির সংযোগ স্থাপনের জন্য আপনার কাছে কোনও ব্যবস্থা থাকে, আমার কাছে সুইচটি সরাসরি চাপ দেওয়া দরজাটি অবহিত করতে চাইত, তবে দরজা কী করবে তা সিদ্ধান্ত নিতে পারে।

আপনি সত্তা সংযোগ করতে না পারলে, আপনার সমাধান আমি যা করবো তার খুব কাছে। আমার জেনেরিক অনুষ্ঠানের জন্য দরজাটি শুনতে হবে ( SwitchActivatedEventসম্ভবত)। যখন স্যুইচগুলি সক্রিয় হয়, তারা এই ইভেন্টটি পোস্ট করে।

আপনি সুইচ একাধিক প্রকার থাকা তবে আমি চাই PressureToggle, WaterToggleএবং একটি ShotToggleআচরণে খুব, কিন্তু আমি নিশ্চিত বেস নই ToggleableBehaviourকোন ভাল, তাই আমি দূরে যে (সঙ্গে করতে চাই, যদি না, অবশ্যই, আপনি একটি ভাল আছে এটি রাখার কারণ)।

Behaviour -> ToggleOnPressureBehaviour
          -> ToggleOnShotBehaviour
          -> ToggleOnWaterBehaviour

দক্ষ ইভেন্ট হ্যান্ডলিং

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

আপনার কাছে এমন EventDispatcherএকটি subscribeপদ্ধতির সাথে থাকতে পারে যা দেখতে কিছুটা (সিউডোকোড) দেখাচ্ছে:

EventDispatcher.subscribe(event_type, function)

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

এই পদ্ধতিটি সিস্টেমটি উল্লেখযোগ্যভাবে আরও দক্ষ:

আমি স্ট্যাকওভারফ্লোতে কিছু সময় আগে এর একটি সাধারণ বাস্তবায়ন পোস্ট করেছি। এটি পাইথনে লেখা রয়েছে তবে এটি এখনও আপনাকে সহায়তা করতে পারে:
https://stackoverflow.com/a/7294148/627005

এই বাস্তবায়নটি বেশ জেনেরিক: এটি উপাদান থেকে কেবলমাত্র ফাংশন নয়, যে কোনও ধরণের ফাংশন নিয়ে কাজ করে। পরিবর্তে আপনার যদি এটির প্রয়োজন না হয় তবে আপনার পদ্ধতিতে আপনার functionএকটি behaviorপ্যারামিটার থাকতে পারে subscribe- আচরণের উদাহরণ যা বিজ্ঞাপিত করা দরকার।

বৈশিষ্ট্য এবং আচরণ

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

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

  • বৈশিষ্ট্যগুলি অন্য কোনও উপাদান ব্যবহার করে না (অন্য গুণাবলী বা আচরণ নয়), সেগুলি স্বয়ংসম্পূর্ণ।

  • আচরণগুলি অন্য ব্যবহারগুলি ব্যবহার করে না বা জানে না। তারা কেবল কয়েকটি গুণাবলী সম্পর্কে (যা তাদের কঠোরভাবে প্রয়োজন) সম্পর্কে জানে ।

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


@ হিশের মন্তব্য

সেই সমস্যাটি কি সাধারণ উপাদানগুলির সাথেও ঘটবে না?

যাইহোক, আমাকে ইভেন্টের প্রকারগুলি পরীক্ষা করতে হবে না কারণ প্রতিটি ফাংশন সর্বদা সঠিক ইভেন্টের সঠিকভাবে প্রাপ্ত তা নিশ্চিত ।

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

এবং সবশেষে, আমি আমার গেম লজিক কোডের জন্য পাইথন ব্যবহার করি (যদিও ইঞ্জিনটি সি ++ তে রয়েছে), তাই কাস্টিংয়ের প্রয়োজন নেই। পাইথন তার হাঁস-টাইপিংয়ের কাজটি করে এবং সবকিছু ঠিকঠাক করে। তবে আমি হাঁসের টাইপিংয়ের সাথে কোন ভাষা ব্যবহার না করলেও আমি এটি করতাম (সরল উদাহরণ):

class SomeBehavior
{
  public:
    SomeBehavior(std::map<std::string, Attribute*> attribs, EventDispatcher* events)
        // For the purposes of this example, I'll assume that the attributes I
        // receive are the right ones. 
        : health_(static_cast<HealthAttribute*>(attribs["health"])),
          armor_(static_cast<ArmorAttribute*>(attribs["armor"]))
    {
        // Boost's polymorphic_downcast would probably be more secure than
        // a static_cast here, but nonetheless...
        // Also, I'd probably use some smart pointers instead of plain
        // old C pointers for the attributes.

        // This is how I'd subscribe a function to a certain type of event.
        // The dispatcher returns a `Subscription` object; the subscription 
        // is alive for as long this object is alive.
        subscription_ = events->subscribe(event::type<DamageEvent>(),
            std::bind(&SomeBehavior::onDamageEvent, this, _1));
    }

    void onDamageEvent(std::shared_ptr<Event> e)
    {
        DamageEvent* damage = boost::polymorphic_downcast<DamageEvent*>(e.get());
        // Simplistic and incorrect formula: health = health - damage + armor
        health_->value(health_->value() - damage->amount() + armor_->protection());
    }

    void update(boost::chrono::duration timePassed)
    {
        // Behaviors also have an `update` function, just like
        // traditional components.
    }

  private:
    HealthAttribute* health_;
    ArmorAttribute* armor_;
    EventDispatcher::Subscription subscription_;
};

আচরণগুলির থেকে পৃথক, গুণাবলীর কোনও updateকার্য থাকে না - তাদের প্রয়োজন হয় না, তাদের উদ্দেশ্য ডেটা রাখা, জটিল গেম যুক্তি সম্পাদন করা নয়।

আপনার বৈশিষ্ট্যগুলি এখনও কিছু সাধারণ যুক্তি সম্পাদন করতে পারে। এই উদাহরণে, একটি HealthAttributeসম্ভবত নিশ্চিত যে 0 <= value <= max_healthএটি সর্বদা সত্য। এটি HealthCriticalEventএকই সত্তার অন্যান্য উপাদানগুলিতে একটি প্রেরণ করতে পারে যখন এটি নীচে নেমে আসে, বলুন, 25 শতাংশ, তবে এটি এর চেয়ে জটিল কোনও যুক্তি সম্পাদন করতে পারে না।


একটি বৈশিষ্ট্য শ্রেণীর উদাহরণ:

class HealthAttribute : public EntityAttribute
{
  public:
    HealthAttribute(Entity* entity, double max, double critical)
        : max_(max), critical_(critical), current_(max)
    { }

    double value() const {
        return current_;
    }    

    void value(double val)
    {
        // Ensure that 0 <= current <= max 
        if (0 <= val && val <= max_)
            current_ = val;

        // Notify other components belonging to this entity that
        // health is too low.
        if (current_ <= critical_) {
            auto ev = std::shared_ptr<Event>(new HealthCriticalEvent())
            entity_->events().post(ev)
        }
    }

  private:
    double current_, max_, critical_;
};

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

2
@ টমসনটম হ্যাঁ, এটিই। শ্রোতাদের যে কোনও ডেটা জানা দরকার তা ইভেন্টগুলি ধারণ করা খুব ভাল সমাধান।
পল মানতা

3
এটি একটি দুর্দান্ত উত্তর! (যেমনটি আপনার পিডিএফ) - আপনি যখন চান্স পাবেন তখন আপনি কীভাবে এই সিস্টেমটির সাথে রেন্ডারিং পরিচালনা করবেন তা সম্পর্কে কিছুটা ব্যাখ্যা করতে পারবেন ? এই বৈশিষ্ট্য / আচরণের মডেলটি আমার কাছে সম্পূর্ণ নতুন, তবে খুব আগ্রহী।
মাইকেল 16

1
@ টমসনটম রেন্ডারিং সম্পর্কে, মাইকেলকে আমি যে উত্তর দিয়েছি তা দেখুন। সংঘর্ষের জন্য, আমি ব্যক্তিগতভাবে একটি শর্টকাট নিয়েছি। আমি বক্স 2 ডি নামে একটি লাইব্রেরি ব্যবহার করেছি যা ব্যবহার করা বেশ সহজ এবং সংঘর্ষগুলি আমার থেকে অনেক ভাল পরিচালনা করে। তবে আমি সরাসরি আমার গেমের লজিক কোডে লাইব্রেরিটি ব্যবহার করি না। প্রত্যেকের Entityএকটি আছে EntityBody, যা সমস্ত কুৎসিত বিট বিমূর্ত করে। আচরণগুলি এরপরে অবস্থানটি পড়তে EntityBodyপারে, তার উপর বাহিনী প্রয়োগ করতে পারে, দেহের জয়েন্টগুলি এবং মোটরগুলি ব্যবহার করতে পারে ইত্যাদি Box
পল মানতা

1
@ স্টিলিনাক্স্লিচ তাই আপনি আর্টেমিসের বিকাশকারী! : ডি আমি বোর্ডগুলিতে Component/ Systemস্কিমটি কয়েকবার উল্লেখ করেছি । আমাদের বাস্তবায়নের মধ্যে বেশ কয়েকটি মিল রয়েছে।
পল মানতা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.