كيف ولماذا كتبنا ECS

في مقال سابق ، وصفت التقنيات والأساليب التي نستخدمها عند تطوير مطلق نار جديد سريع الخطى. لأن كانت مراجعة وحتى مقالة سطحية - اليوم سأبحث بشكل أعمق وأشرح بالتفصيل لماذا قررنا كتابة إطار عمل ECS الخاص بنا ولم نستخدم الأطر الموجودة. سيكون هناك أمثلة كود ومكافأة صغيرة في النهاية.


ما هو ECS كمثال


لقد وصفت بإيجاز ما هو Entity Component System ، وهناك مقالات عن Habré حول ECS (ومع ذلك ، في الأساس ، ترجمات المقالات - انظر إلى مراجعتي لأكثرها إثارة في نهاية المقالة ، كمكافأة). واليوم سأخبرك كيف نستخدم ECS - باستخدام مثال الشفرة الخاص بنا.

يصف الرسم البياني أعلاه جوهر المشغل ومكوناته وبياناته والأنظمة التي تعمل مع المشغل ومكوناته. الكائن الرئيسي في الرسم التخطيطي هو اللاعب:

  • يمكن أن تتحرك في الفضاء - مكونات التحويل والحركة ، MoveSystem ؛
  • لديه بعض الصحة وقد يموت - مكون الصحة ، الضرر ، نظام الضرر ؛
  • بعد ظهور الموت في نقطة إعادة الفداء - مكون التحويل للمنصب ، نظام RespawnSystem ؛
  • قد يكون غير محصن - مكون لا يقهر .

نحن نصف هذا برمز أولاً ، دعنا نحصل على واجهات للمكونات والأنظمة. يمكن أن تحتوي المكونات على طرق مساعدة شائعة ، يحتوي النظام على طريقة تنفيذ واحدة فقط ، والتي تتلقى حالة العالم عند الإدخال للمعالجة:

public interface IComponent { // < > } public interface ISystem { void Execute(GameState gs); } 

بالنسبة للمكونات ، نقوم بإنشاء فئات كعب الروتين التي يستخدمها منشئ الشفرة لدينا لتحويلها إلى رمز مكون مستخدم بالفعل. دعنا نحصل على بعض الفراغات للصحة والأضرار والتي لا تقهر (بالنسبة لبقية المكونات ستكون متشابهة).

 [Component] public class Health { [Max(1000)] //  -  1000 public int Hp; // -   public Health(int hp) {} } [Component] public class Damage { [DontSend] //      ,      public uint Amount; // -  public Entity Victim; //    public Entity Source; //    public Damage(uint amount, Entity victim, Entity source) {} } [Component] public class Invincible //   ,  ,    { } 

تحدد المكونات حالة العالم ، وبالتالي فهي تحتوي على بيانات فقط ، بدون طرق. في الوقت نفسه ، لا توجد بيانات في Invincible ، يتم استخدامها في المنطق كعلامة على عدم الحصانة - إذا كان جوهر اللاعب يحتوي على هذا المكون ، فإن اللاعب أصبح الآن غير محصن.

يستخدم المولد سمة المكون للعثور على الفئات الفارغة للمكونات. هناك حاجة إلى السمتين Max و DontSend كتلميحات عند إجراء تسلسل وتقليل حجم حالة العالم المنقولة عبر الشبكة أو المحفوظة على القرص. في هذه الحالة ، لن يقوم الخادم بتسلسل حقل المبلغ وإرساله عبر الشبكة (نظرًا لأن العملاء لا يستخدمون هذه المعلمة ، فإنه مطلوب فقط على الخادم). ويمكن تعبئة حقل Hp جيدًا في عدة أجزاء ، نظرًا لأقصى قيمة للصحة.

لدينا أيضًا فئة الكيان الجاهزة ، حيث نضيف معلومات حول جميع المكونات الممكنة لأي كيان ، وسوف ينشئ المولد بالفعل فئة حقيقية منه:

 public class Entity { public Health Health; public Damage Damage; public Invincible Invincible; // ... < > } 

بعد ذلك ، سينشئ المولد الخاص بنا رمز فئات المكونات Health و Damage و Invincible التي سيتم استخدامها بالفعل في منطق اللعبة:

 public sealed class Health : IComponent { public int Hp; public void Reset() { Hp = default(int); } // ... <  > } public sealed class Damage : IComponent { public int Amount; public Entity Victim; public Entity Source; public void Reset() { Amount = default(int); Victim = default(Entity); Source = default(Entity); } // ... <  > } public sealed class Invincible : IComponent { } 

كما ترى ، بقيت البيانات في الفصول الدراسية وتمت إضافة الطرق ، على سبيل المثال ، إعادة تعيين . هناك حاجة لتحسين وإعادة استخدام المكونات في تجمعات. لا تحتوي الطرق المساعدة الأخرى على منطق الأعمال - لن أعطيهم الإيجاز.

سيتم أيضًا إنشاء فئة لحالة العالم ، والتي تحتوي على قائمة بجميع المكونات والكيانات:

 public sealed class GameState { //  public Table<Movement> Movements; public Table<Health> Healths; public Table<Damage> Damages; public Table<Transform> Transforms; public Table<Invincible> Invincibles; //   public Entity CreateEntity() { /* <> */ } public void Copy(GameState gs2) { /* <> */ } public Entity this[uint id] { /* <> */ } // ... <   > } 

وأخيرًا ، الرمز الذي تم إنشاؤه لـ Entity :

 public sealed class Entity { public uint Id; //   public GameState GameState; //     //     : public Health Health { get { return GameState.Healths[Id]; } } public Damage Damage { get { return GameState.Damages[Id]; } } public Invincible Invincible { get { return GameState.Invincibles[Id]; } } // …     public Damage AddDamage() { return GameState.Damages.Insert(Id); } public Damage AddDamage(int total, Entity victim, Entity source) { var c = GameState.Damages.Insert(Id); c.Amount = total; c.Victim = victim; c.Source = source; return c; } public void DelDamage() { GameState.Damages.Delete(Id); } // … <     > } 

فئة الكيان هي في الأساس مجرد معرف مكون. يتم استخدام الإشارة إلى كائنات عالم GameState فقط في الطرق المساعدة لتسهيل كتابة كود منطق الأعمال. بمعرفة معرف أحد المكونات ، يمكننا استخدامه لتسلسل العلاقات بين الكيانات ، وتنفيذ الروابط في المكونات إلى كيانات أخرى. على سبيل المثال ، يحتوي عنصر الضرر على إشارة إلى الكيان الضحية لتحديد من تضرر.

هذا ينهي الرمز الذي تم إنشاؤه. بشكل عام ، نحتاج إلى مولد حتى لا نكتب طرقًا مساعدة في كل مرة. نحن نصف المكونات فقط على أنها بيانات ، ثم يقوم المولد بكل العمل. أمثلة على الطرق المساعدة:

  • إنشاء / حذف الكيانات ؛
  • إضافة / إزالة / نسخ مكون ، والوصول إليه إذا كان موجودًا ؛
  • قارن بين دولتين من العالم ؛
  • وضع تسلسل لحالة العالم ؛
  • ضغط دلتا
  • رمز صفحة ويب أو نافذة الوحدة لعرض حالة العالم والكيانات والمكونات (انظر التفاصيل أدناه) ؛
  • وغيرهم

دعنا ننتقل إلى رمز النظام. إنها تحدد منطق الأعمال. على سبيل المثال ، لنكتب رمز نظام يحسب الضرر للاعب:

 public sealed class DamageSystem : ISystem { void ISystem.Execute(GameState gs) { foreach (var damage in gs.Damages) { var invincible = damage.Victim.Invincible; if (invincible != null) continue; var health = damage.Victim.Health; if (health == null) continue; health.Hp -= damage.Amount; } } } 

يمر النظام بجميع مكونات الضرر في العالم ويتطلع لمعرفة ما إذا كان هناك مكون لا يقهر على لاعب محتمل ( ضحية ). إذا كان كذلك ، فإن اللاعب معرض للخطر ، ولا يتم تكبد الضرر. بعد ذلك ، نحصل على مكون صحة الضحية ونخفض صحة اللاعب بحجم الضرر.

خذ بعين الاعتبار الميزات الرئيسية للأنظمة:

  1. عادة ما يكون النظام عبارة عن فئة عديمة الحالة ، ولا يحتوي على أي بيانات داخلية ، ولا يحاول حفظه في مكان ما ، باستثناء البيانات حول العالم المرسلة من الخارج.
  2. عادة ما تمر الأنظمة بجميع المكونات من نوع معين وتعمل معها. وعادة ما يتم استدعاؤها حسب نوع المكون ( DamageDamageSystem ) أو من خلال الإجراء الذي يقومون به ( RespawnSystem ).
  3. يقوم النظام بتنفيذ الحد الأدنى من الوظائف. على سبيل المثال ، إذا ذهبنا إلى أبعد من ذلك ، بعد تنفيذ نظام DamageSystem ، فإن RemoveDamageSystem آخر سيزيل جميع مكونات الضرر . في العلامة التالية ، يمكن لتطبيق ApplyDamageSystem آخر استنادًا إلى تصوير اللاعب تعليق عنصر الضرر مع حدوث ضرر جديد. ثم سيتحقق PlayerDeathSystem من صحة اللاعب ( Health.Hp ) ، وإذا كان أقل من أو يساوي 0 ، فسوف يدمر جميع مكونات اللاعب باستثناء Transform ويضيف مكون العلم الميت .

الإجمالي نحصل على الفئات التالية والعلاقات بينها:


بعض الحقائق عن ECS


لدى ECS إيجابياتها وسلبياتها كنهج للتنمية وطريقة لتمثيل عالم اللعبة ، لذلك يقرر الجميع لنفسه ما إذا كان سيستخدمها أم لا. لنبدأ بالإيجابيات:

  • تكوين مقابل الميراث متعددة. في حالة التوريث المتعدد ، يمكن وراثة مجموعة من الوظائف غير الضرورية. في حالة ECS ، تظهر / تختفي الوظيفة عند إضافة / إزالة أحد المكونات.
  • فصل المنطق والبيانات. القدرة على تغيير المنطق (تغيير الأنظمة ، إزالة / إضافة مكونات) دون كسر البيانات. على سبيل المثال يمكنك تعطيل مجموعة الأنظمة المسؤولة عن وظيفة معينة في أي وقت ، وكل شيء آخر سيستمر في العمل وهذا لن يؤثر على البيانات.
  • تم تبسيط دورة اللعبة. يظهر تحديث واحد ، وتنقسم الدورة بأكملها إلى أنظمة. تتم معالجة البيانات عن طريق "التدفق" في النظام ، بغض النظر عن المحرك (لا توجد ملايين مكالمات التحديث ، كما هو الحال في الوحدة).
  • لا يعرف الكيان الفئات التي تؤثر عليه (ويجب ألا يعرف).
  • الاستخدام الفعال للذاكرة . يعتمد ذلك على تنفيذ ECS. يمكنك إعادة استخدام كائنات ومكونات الكيانات المنشأة باستخدام التجمعات ؛ يمكنك استخدام أنواع القيم للبيانات وتخزينها في الذاكرة جنبًا إلى جنب ( مكان البيانات ).
  • من الأسهل الاختبار عند فصل البيانات عن المنطق. خاصة عندما تفكر في أن المنطق هو نظام صغير يحتوي على عدة أسطر من التعليمات البرمجية.
  • عرض وتحرير حالة العالم في الوقت الحقيقي . لأن حالة العالم هي مجرد بيانات ، لقد كتبنا أداة تعرض على صفحة الويب حالة العالم بأكملها في مباراة على الخادم (بالإضافة إلى مشهد المطابقة ثلاثي الأبعاد). يمكن عرض وتعديل وحذف أي مكون من أي كيان. يمكن القيام بنفس الشيء في محرر الوحدة للعميل.



والآن السلبيات:

  • تحتاج إلى تعلم التفكير والتصميم وكتابة التعليمات البرمجية بشكل مختلف . فكر فيما يتعلق بالكيانات والمكونات والأنظمة. يتم تنفيذ العديد من أنماط التصميم في ECS بطريقة مختلفة تمامًا (انظر مثال على تنفيذ نمط الدولة في إحدى مقالات المراجعة في النهاية).
  • المزيد من التعليمات البرمجية . قابل للنقاش. من ناحية ، نظرًا لأننا قسمنا المنطق إلى أنظمة صغيرة ، بدلاً من وصف جميع الوظائف في فئة واحدة ، هناك المزيد من الفئات ، ولكن ليس هناك المزيد من التعليمات البرمجية.
  • يؤثر ترتيب أنظمة الاتصال على تشغيل اللعبة بأكملها . عادة ، تعتمد الأنظمة على بعضها البعض ، ويتم تعيين ترتيب تنفيذها من خلال القائمة ويتم تنفيذها في هذا الترتيب. على سبيل المثال ، يعتبر نظام DamageSystem الأول الضرر ، ثم يزيل RemoveDamageSystem مكون الضرر . إذا قمت بتغيير الطلب عن طريق الخطأ ، فسيعمل كل شيء بشكل مختلف. بشكل عام ، ينطبق هذا أيضًا على حالة OOP المعتادة ، إذا قمت بتغيير ترتيب استدعاءات الطريقة ، ولكن في ECS ، من الأسهل ارتكاب خطأ. على سبيل المثال ، إذا تم تشغيل جزء من المنطق على العميل للتنبؤ ، فيجب أن يكون الترتيب هو نفسه الموجود على الخادم.
  • نحن بحاجة إلى ربط بيانات وأحداث المنطق بطريقة العرض بطريقة أو بأخرى . في حالة الوحدة ، لدينا MVP:

    - نموذج - GameState من ECS.
    - عرض - معنا هذه فئات UnoBehavior Unity القياسية حصريًا ( Renderer ، Text ، إلخ) والفئات الجاهزة ؛
    - يستخدم المقدم GameState لتحديد أحداث ظهور / اختفاء الكيانات والمكونات ، وما إلى ذلك ، وإنشاء كائنات الوحدة من المباني الجاهزة وتغييرها وفقًا للتغيرات في حالة العالم.

هل تعلم أن:

  • لا تتعلق ECS فقط بمكان البيانات . بالنسبة لي ، هذا هو أكثر من نموذج برمجة ، نمط ، طريقة أخرى لتصميم عالم اللعبة - أطلق عليه كل ما تريد. موقع البيانات هو مجرد تحسين.
  • الوحدة ليس لها ECS! غالبًا ما تسأل المرشحين في مقابلة جماعية - ماذا تعرف عن ECS؟ إذا لم تكن قد سمعت ، أخبرهم ، فأجابوا: "آه ، هكذا هو الحال في الوحدة ، فأنا أعلم!". ولكن لا ، ليس مثل محرك الوحدة. هناك ، يتم دمج البيانات والمنطق في مكون MonoBehaviour ، و GameObject (إذا تمت المقارنة مع كيان في ECS) يحتوي على بيانات إضافية - اسم ومكان في التسلسل الهرمي ، وما إلى ذلك. يعمل مطورو Unity حاليًا على تنفيذ طبيعي لـ ECS في المحرك ، ويبدو حتى الآن أنه سيكون جيدًا. لقد استأجروا متخصصين في هذا المجال - آمل أن يكون الأمر باردًا.

معايير اختيارنا لإطار عمل ECS


عندما قررنا إنشاء لعبة على ECS ، بدأنا في البحث عن حل جاهز وكتبنا متطلباته بناءً على تجربة أحد المطورين. وقد رسموا كيف تلبي الحلول الحالية متطلباتنا. كان ذلك قبل عام ، في الوقت الحالي ، كان يمكن أن يتغير شيء ما. كحلول ، نظرنا في:

  • Entitas
  • أرتميس C #
  • Ash.net
  • ECS هو حلنا الخاص في الوقت الذي تصورناه فيه. على سبيل المثال افتراضاتنا ورغباتنا ، ما يمكننا القيام به بأنفسنا.

قمنا بتجميع جدول للمقارنة ، حيث قمت أيضًا بتضمين حلنا الحالي (الذي تم تحديده باسم ECS (الآن) ):


اللون الأحمر - الحل لا يدعم متطلباتنا ، برتقالي - يدعم جزئيًا ، أخضر - يدعم بالكامل.

بالنسبة لنا ، كان تشبيه العمليات للوصول إلى المكونات والبحث عن الكيانات في ECS هو العمليات في قاعدة بيانات SQL. لذلك ، استخدمنا مفاهيم مثل جدول (جدول) ، صلة (عملية ربط) ، مؤشرات (مؤشرات) ، إلخ.

سنصف متطلباتنا وإلى أي مدى تتوافق مكتبات وأطر عمل الجهات الخارجية معها:

  • مجموعات بيانات منفصلة (التاريخ ، الحالي ، المرئي ، الثابت) - القدرة على الحصول على حالات العالم وتخزينها بشكل منفصل (على سبيل المثال ، الحالة الحالية للمعالجة ، للعرض ، تاريخ الدولة ، إلخ). دعمت جميع القرارات التي تم النظر فيها هذا الشرط .
  • رقم تعريف الكيان كعدد صحيح - دعم لتمثيل كيان برقم المعرف الخاص به. من الضروري الإرسال عبر الشبكة والقدرة على ربط الكيانات في تاريخ الدول. لا شيء من الحلول تعتبر مدعومة. على سبيل المثال ، في Entitas ، يتم تمثيل الكيان بكائن كامل (مثل GameObject في الوحدة).
  • الانضمام عن طريق ID O (N + M) - دعم أخذ عينات سريعة نسبيًا من نوعين من المكونات. على سبيل المثال ، عندما تحتاج إلى الحصول على جميع الكيانات بمكونات من نوع الضرر (على سبيل المثال ، قطع N) والصحة (قطع M) لحساب الضرر وإحداثه. كان هناك دعم كامل في أرتميس. في Entitas و Ash.NET فهي أسرع من O (N²) ، ولكنها أبطأ من O (N + M). لا أتذكر التقييم الآن.
  • الانضمام عن طريق مرجع المعرف O (N + M) - نفس الشيء المذكور أعلاه فقط عندما يكون لمكون من كيان واحد رابط إلى آخر ، ويحتاج الأخير إلى الحصول على مكون آخر (في مثالنا ، يشير عنصر الضرر في الكيان المساعد إلى كيان اللاعب الضحية ومن هناك تحتاج إلى الحصول على مكون الصحة ). لا يدعمه أي من الحلول التي تم النظر فيها.
  • لا يوجد تخصيص استعلام - لا توجد تخصيصات ذاكرة إضافية عند الاستعلام عن المكونات والكيانات من حالة العالم. في Entitas ، كان ذلك في بعض الحالات ، لكنه غير مهم بالنسبة لنا.
  • طاولات البلياردو - تخزين البيانات العالمية في البرك ، والقدرة على إعادة استخدام الذاكرة ، والتخصيص فقط عندما تكون البركة فارغة. كان هناك دعم "بعض" في Entitas و Artemis ، وغياب كامل في Ash.NET.
  • المقارنة بالمعرف (إضافة ، حذف) - دعم مضمن لأحداث إنشاء / تدمير الكيانات والمكونات بواسطة المعرف. من الضروري أن يقوم مستوى العرض (عرض) بإظهار / إخفاء الكائنات وتشغيل الرسوم المتحركة والتأثيرات. لا يدعمه أي من الحلول التي تم النظر فيها.
  • ization التسلسل (الكميات ، التخطي) - ضغط دلتا مضمن لتسلسل حالة العالم (على سبيل المثال ، لتقليل حجم البيانات المرسلة عبر الشبكة). من خارج الصندوق لم يكن مدعومًا في أي من الحلول.
  • الاستيفاء هو آلية استيفاء مدمجة بين دول العالم. أيا من الحلول المدعومة.
  • إعادة استخدام نوع المكون - القدرة على الاستخدام بمجرد كتابة نوع المكون في أنواع مختلفة من الكيانات. دعم Entitas فقط .
  • ترتيب صريح للأنظمة - القدرة على تعيين أنظمة ترتيب المكالمات الخاصة بك. جميع القرارات مدعومة.
  • محرر (وحدة / خادم) - دعم لعرض وتحرير الكيانات في الوقت الفعلي ، لكل من العميل والخادم. دعم Entitas فقط القدرة على عرض وتحرير الكيانات والمكونات في محرر الوحدة.
  • نسخ / استبدال سريع - القدرة على نسخ / استبدال البيانات بثمن بخس. أيا من الحلول المدعومة.
  • المكون كنوع القيمة (البنية) - المكونات كنوع القيمة. من حيث المبدأ ، أردت تحقيق أداء جيد بناءً على ذلك. لم يتم دعم نظام واحد ؛ كانت فئات المكونات في كل مكان.

متطلبات اختيارية ( لم يدعمها أي من الحلول في ذلك الوقت ):

  • المؤشرات - فهرسة البيانات كما هو الحال في قاعدة البيانات.
  • مفاتيح مركبة - مفاتيح معقدة للوصول السريع إلى البيانات (كما هو الحال في قاعدة البيانات).
  • التحقق من السلامة - القدرة على التحقق من سلامة البيانات في حالة من العالم. مفيد لتصحيح الأخطاء.
  • الضغط المدرك للمحتوى هو أفضل ضغط للبيانات بناءً على معرفة طبيعة البيانات. على سبيل المثال ، إذا كنا نعرف الحجم الأقصى للخريطة أو الحد الأقصى لعدد العناصر في العالم.
  • حدود الأنواع / الأنظمة - تقييد عدد أنواع المكونات أو الأنظمة. في أرتميس في ذلك الوقت ، كان من المستحيل إنشاء أكثر من 32 أو 64 نوعًا من المكونات والأنظمة .

كما يتبين من الجدول ، أردنا بمفردنا تنفيذ جميع المتطلبات ، باستثناء المتطلبات الاختيارية. في الواقع ، في الوقت الحالي لم نقم بما يلي:

  • الانضمام بالمعرف O (N + M) والانضمام بمرجع المعرف O (N + M) - لا يزال التحديد لمكونين مختلفين يشغل O (N²) (في الواقع ، متداخل للحلقة). من ناحية أخرى ، لا يوجد الكثير من الكيانات والمكونات للمباراة.
  • مقارنة بالمعرف (إضافة ، حذف) - غير مطلوب على مستوى الإطار. قمنا بتنفيذ هذا على مستوى أعلى في أفضل لاعب.
  • نسخ / استبدال سريع ومكون كنوع القيمة ( هيكل ) - في مرحلة ما أدركنا أن العمل مع الهياكل لن يكون مناسبًا كما هو الحال مع الفصول ، واستقرنا في الفصول الدراسية - فضلنا راحة التطوير بدلاً من الأداء الأفضل. بالمناسبة ، فعل مطورو Entitas الشيء نفسه في النهاية .

في الوقت نفسه ، أدركنا مع ذلك أحد المتطلبات التي كانت اختيارية في البداية في رأينا:

  • الضغط الواعي للمحتوى - بسبب ذلك تمكنا من تقليل حجم الحزمة المرسلة عبر الشبكة بشكل ملحوظ (عشرات المرات). بالنسبة لشبكات البيانات المحمولة ، من المهم جدًا احتواء حجم الحزمة في MTU حتى لا يتم "تقسيمها" إلى أجزاء صغيرة قد تضيع ، وترتيب مختلف ، ثم تحتاج إلى تجميعها في أجزاء. على سبيل المثال ، في الفوتون ، إذا كان حجم البيانات لا يتناسب مع مكتبة MTU ، فإنه يقسم البيانات إلى حزم ويرسلها على أنها موثوقة (مع التسليم المضمون) ، حتى إذا قمت بإرسالها على أنها "غير موثوق بها" من الأعلى. تم اختباره بألم مباشر.

ملامح تطورنا في ECS


  • نحن في ECS نكتب منطق الأعمال حصريًا . لا يعمل مع الموارد ، وجهات النظر ، إلخ. نظرًا لأن رمز منطق ECS يعمل في نفس الوقت على العميل في Unity وعلى الخادم ، يجب أن يكون مستقلاً قدر الإمكان عن المستويات والوحدات الأخرى.
  • نحن نحاول تقليل المكونات والأنظمة . عادة ، لكل مهمة جديدة ، نبدأ مكونات وأنظمة جديدة. ولكن في بعض الأحيان يحدث أن نقوم بتعديل القديمة ، وإضافة بيانات جديدة إلى المكونات ، و "تضخيم" الأنظمة.
  • في تنفيذ ECS ، لا يمكنك إضافة العديد من المكونات من نفس النوع إلى كيان واحد . لذلك ، إذا تم ضرب لاعب عدة مرات في علامة واحدة (على سبيل المثال ، العديد من المعارضين) ، فإننا عادة ما نقوم بإنشاء كيان جديد لكل ضرر وإضافة عنصر ضرر إليه.
  • في بعض الأحيان ، لا يكون العرض التقديمي كافيًا للمعلومات الموجودة في GameState . ثم عليك إضافة مكونات خاصة أو بيانات إضافية غير متضمنة في المنطق ، ولكن هذا العرض يحتاج. على سبيل المثال ، اللقطة فورية على الخادم ، وعلامة واحدة تعيش ، وبصريًا ، تكون أطول على العميل. لذلك ، بالنسبة للعميل ، يتم إضافة اللقطة إلى المعلمة "عمر اللقطة".
  • نقوم بتنفيذ الأحداث / الطلبات من خلال إنشاء مكونات خاصة . على سبيل المثال ، إذا مات لاعب ، فإننا نعلق عليه مكونًا بدون بيانات Dead ، وهو حدث للأنظمة الأخرى ومستوى العرض الذي مات فيه اللاعب. أو إذا احتجنا إلى إعادة إحياء اللاعب في النقطة مرة أخرى ، فإننا ننشئ كيانًا منفصلاً مع مكون Respawn بمعلومات إضافية حول من يجب إحياءه. يمر نظام RespawnSystem منفصل في بداية دورة اللعبة من خلال هذه المكونات ويخلق بالفعل جوهر اللاعب. على سبيل المثال في الواقع ، الكيان الأول هو طلب لإنشاء الثاني.
  • لدينا مكونات / كيانات "سينجلتون" خاصة . على سبيل المثال ، لدينا كيان بمعرف = 1 ، حيث يتم تعليق المكونات الخاصة - إعدادات اللعبة.

مكافأة


في عملية تقرير ما إذا كان هابرو بحاجة إلى مقال حول ECS ، أجريت القليل من البحث. كما وعدت في البداية ، إليك استعراضي القصير للمقالات حول هذا الموضوع ، وأنت تقرر ما إذا كنت تقرأ أم لا:

Source: https://habr.com/ru/post/ar413729/


All Articles