
إنها قصة حول كيفية كتابة مكون إضافي لمتجر Unity Asset Store ، واتخاذ إجراءات صارمة لحل المشكلات المعروفة في الألعاب ، وكسب القليل من المال من ذلك ، وكذلك لفهم مدى قابلية محرر Unity القابل للتوسيع. الصور ، رمز ، الرسوم البيانية والأفكار في الداخل.
مقدمة
لذلك ، كانت ليلة واحدة عندما اكتشفت أنه لم يكن لدي شيء أقوم به. لم تكن السنة القادمة واعدة حقًا في حياتي المهنية (على عكس الشخصية ، ولكن هذه قصة غير معروفة تمامًا). على أي حال ، حصلت على هذه الفكرة لأكتب شيئًا ممتعًا من أجل العصور القديمة ، سيكون ذلك شخصيًا جدًا ، شيئًا خاصًا بي ، ولكن لا يزال لدي ميزة تجارية صغيرة (أحب هذا الشعور الدافئ عندما يكون مشروعك ممتعًا لشخص آخر ، باستثناء لصاحب العمل الخاص بك). كل هذا يسير جنبا إلى جنب مع حقيقة أنني كنت أنتظر منذ فترة طويلة للتحقق من إمكانيات امتداد محرر Unity ومعرفة ما إذا كان هناك أي شيء جيد في برنامجه لبيع ملحقات المحرك الخاصة.
كرست يومًا واحدًا لدراسة متجر الأصول: النماذج والبرامج النصية والتكامل مع الخدمات المختلفة. أولاً ، بدا الأمر وكأن كل شيء قد تمت كتابته وتكامله بالفعل ، مع وجود عدد من الخيارات ذات مستويات الجودة والتفاصيل المختلفة ، تمامًا مثل الأسعار والدعم. لقد ضاقت الأمر على الفور إلى:
- الرمز فقط (بعد كل شيء ، أنا مبرمج)
- 2D فقط (بما أنني أحب 2D فقط وقدموا دعمًا لائقًا جيدًا لذلك في Unity)
ثم تذكرت كم عدد الصبار التي أكلتها وكم عدد الفئران التي ماتت عندما صنعنا لعبة متساوي القياس من قبل. لن تصدق كم من الوقت الذي قضيناه في البحث عن حلول قابلة للتطبيق وعدد النسخ التي كسرناها في محاولات لفرز هذه المقاييس ورسمها. لذلك ، وأنا أجد صعوبة في الحفاظ على يدي ثابتة ، لقد بحثت عن طريق كلمات مختلفة وليست كلمات رئيسية ولم أجد شيئًا سوى كومة ضخمة من الفن متساوي القياس ، حتى قررت أخيرًا إنشاء ملحق إضافي متساوي القياس من الصفر.
تحديد الأهداف
أول ما أحتاج إليه هو أن أصف باختصار المشكلات التي كان من المفترض أن يحلها هذا البرنامج المساعد وما فائدة مطور الألعاب المتساوي القياس. لذلك ، فإن مشاكل القياس هي كما يلي:
- فرز الكائنات عن بعد من أجل رسمها بشكل صحيح
- امتداد لإنشاء وتحديد المواقع وإزاحة الكائنات متساوي القياس في المحرر
وبالتالي ، مع الأهداف الرئيسية للنسخة الأولى وضعت ، حددت نفسي 2-3 أيام الموعد النهائي لمسودة الإصدار الأول. هكذا لا يمكن تأجيله ، كما ترى ، لأن الحماس أمر هش وإذا لم يكن لديك شيء جاهز في الأيام الأولى ، فهناك فرصة كبيرة لتدميره. وأيام رأس السنة الجديدة ليست طويلة كما قد يبدو ، حتى في روسيا ، وأردت إصدار الإصدار الأول في غضون عشرة أيام ، على سبيل المثال.
الفرز
باختصار ، القياس المتماثل هو محاولة تقوم بها العفاريت ثنائية الأبعاد لتبدو وكأنها نماذج ثلاثية الأبعاد. هذا ، بالطبع ، يؤدي إلى عشرات المشاكل. العنصر الرئيسي هو أنه يجب فرز العفاريت بالترتيب الذي تم به ترتيبها لتجنب مشاكل التداخل المتبادل.

في لقطة الشاشة ، يمكنك رؤية كيف يتم رسم العفريت الأخضر أولاً (2،1) ، ثم ينتقل اللون الأزرق (1،1)

تعرض لقطة الشاشة الفرز غير الصحيح عندما يتم رسم العفريت الأزرق أولاً
في هذه الحالة البسيطة ، لن يكون الفرز مشكلة ، وستكون هناك خيارات ، على سبيل المثال:
- الفرز حسب موضع Y على الشاشة ، وهو * (isoX + isoY) 0.5 + isoZ **
- الرسم من بعد خلية الشبكة متساوي القياس من اليسار إلى اليمين ، من أعلى إلى أسفل [(3،3) ، (2،3) ، (3،2) ، (1،3) ، (2،2) ، (3 ، 1) ، ...]
- ومجموعة كاملة من طرق أخرى مثيرة للاهتمام وغير مثيرة للاهتمام حقا
إنها جميعها جيدة وسريعة وعاملة ، ولكن فقط في حالة وجود كائنات أو خلايا أحادية الخلية ممتدة في اتجاه isoZ :) بعد كل شيء ، كنت مهتمًا بحل أكثر شيوعًا يعمل من أجل الكائنات الممتدة في اتجاه إحداثي واحد ، أو حتى "الأسوار" التي ليس لها أي عرض على الإطلاق ، ولكنها ممتدة في نفس اتجاه الارتفاع الضروري.

تُظهر لقطة الشاشة الطريقة الصحيحة لفرز الكائنات الموسعة 3 × 1 و 1 × 3 باستخدام "الأسوار" بقياس 3 × 0 و 0x3
وهنا تبدأ مشاكلنا وتضعنا في مكان يجب أن نقرر فيه الطريق إلى الأمام:
اخترت الخيار الثاني ، مع عدم وجود رغبة خاصة للدخول في معالجة صعبة لكل كائن ، إلى القطع (حتى التلقائي) ، والمنهج الخاص للمنطق. للسجل ، استخدموا الطريقة الأولى في عدد قليل من الألعاب الشهيرة مثل Fallout 1 و Fallout 2 . يمكنك بالفعل رؤية هذه الشرائط إذا وصلت إلى بيانات الألعاب.
لذلك ، فإن الخيار الثاني لا يعني أي معايير الفرز. هذا يعني أنه لا توجد قيمة محسوبة مسبقًا يمكنك بواسطتها فرز الكائنات. إذا كنت لا تصدقني (وأعتقد أن العديد من الأشخاص الذين لم يسبق لهم العمل مع الإيزوميتري لا يفعلون ذلك) ، خذ قطعة من الورق وارسم أشياء صغيرة بحجم 2 × 8 ، على سبيل المثال ، 2 × 2 . إذا تمكنت بطريقة ما من معرفة قيمة لحساب عمقها وفرزها - فما عليك سوى إضافة كائن 8 × 2 ومحاولة فرزها في مواضع مختلفة بالنسبة لبعضها البعض.
لذلك ، لا توجد قيمة من هذا القبيل ، ولكن لا يزال بإمكاننا استخدام التبعيات بينها (بمعنى الكلمة تقريبًا ، أيها تتداخل أي منها) للفرز الطوبوغرافي . يمكننا حساب تبعيات الكائنات باستخدام توقعات الإحداثيات متساوية القياس على المحور متساوي القياس.

لقطة شاشة توضح أن المكعب الأزرق يعتمد على الأحمر

لقطة شاشة توضح أن المكعب الأخضر يعتمد على المكعب الأزرق
الكود الكاذب لتحديد التبعية لمحورين (نفس العمل مع المحور Z):
bool IsIsoObjectsDepends(IsoObject obj_a, IsoObject obj_b) { var obj_a_max_size = obj_a.position + obj_a.size; return obj_b.position.x < obj_a_max_size.x && obj_b.position.y < obj_a_max_size.y; }
مع مثل هذا النهج ، نبني التبعيات بين جميع الكائنات ، مروراً بينها بشكل متكرر ووضع علامة على تنسيق العرض Z. هذه الطريقة عالمية تمامًا ، والأهم من ذلك أنها تعمل. يمكنك قراءة وصف مفصل لهذه الخوارزمية ، على سبيل المثال ، هنا أو هنا . كما أنها تستخدم هذا النوع من النهج في مكتبة فلاش متساوي القياس شعبية ( as3isolib ).
وكان كل شيء رائعًا فقط باستثناء أن التعقيد الزمني لهذا النهج هو O (N ^ 2) حيث يتعين علينا مقارنة كل كائن مع كل كائن آخر لإنشاء التبعيات. لقد تركت التحسينات للإصدارات الأحدث ، حيث قمت بإضافة عمليات إعادة الفرز البطيئة حتى لا يتم فرز أي شيء حتى يتحرك شيء ما. لذلك سنتحدث عن التحسين لاحقًا.
تمديد المحرر
من الآن فصاعدًا ، كان لدي الأهداف التالية:
- فرز الأشياء كان يجب أن يعمل في المحرر (ليس فقط في لعبة)
- يجب أن يكون هناك نوع آخر من أدوات Gizmos-Arrow (أسهم للأجسام المتحركة)
- اختياريا ، سيكون هناك محاذاة مع البلاط عند نقل الكائن
- سيتم تطبيق أحجام البلاط وتعيينها في مفتش العالم متساوي القياس تلقائيا
- يتم رسم الكائنات AABB وفقًا لأحجامها متساوية القياس
- إخراج الإحداثيات متساوي القياس في مفتش الكائن ، عن طريق تغيير الذي سوف نغير موضع الكائن في عالم اللعبة
وقد تم تحقيق كل هذه الأهداف. الوحدة تسمح حقًا بتوسيع محررها بشكل كبير. يمكنك إضافة علامات تبويب جديدة ونوافذ وأزرار وحقول جديدة في مفتش الكائنات. إذا أردت ، يمكنك إنشاء مفتش مخصص لمكون من النوع الدقيق الذي تحتاجه. يمكنك أيضًا إخراج معلومات إضافية في نافذة المحرر (في حالتي ، على كائنات AABB) ، واستبدال أدوات نقل الكائنات القياسية أيضًا. تم حل مشكلة الفرز داخل المحرر من خلال علامة ExecuteInEditMode السحرية هذه ، والتي تسمح بتشغيل مكونات الكائن في وضع المحرر ، وهي أن تفعل ذلك بنفس طريقة اللعب.
كل هذا تم ، بالطبع ، ليس من دون صعوبات وحيل من جميع الأنواع ، ولكن لم تكن هناك مشكلة واحدة قضيت فيها أكثر من ساعتين على (جوجل والمنتديات والمجتمعات ساعدتني بالتأكيد على حل جميع المشكلات نشأت والتي لم تذكر في الوثائق).

لقطة شاشة توضح أدواتي لأجسام الحركة داخل العالم متساوي القياس
الإصدار
لذلك ، حصلت على النسخة الأولى جاهزة ، أخذت لقطة الشاشة. حتى أنني وجهت أيقونة وكتبت وصفًا. لقد حان الوقت. لذلك ، حددت سعرًا رمزيًا قدره 5 دولارات ، وقم بتحميل المكون الإضافي في المتجر وانتظر موافقة Unity عليه. لم أفكر في السعر كثيرًا ، حيث أنني لم أرغب حقًا في كسب أموال كبيرة حتى الآن. كان هدفي هو معرفة ما إذا كان هناك طلب عام ، وإذا كان الأمر كذلك ، أود تقدير ذلك. أردت أيضًا أن أساعد مطوري الألعاب متساوي القياس الذين حرموا تمامًا من الفرص والإضافات.
في 5 أيام مؤلمة إلى حد ما (قضيت حوالي الوقت نفسه في كتابة الإصدار الأول ، لكنني كنت أعرف ما كنت أفعله ، دون المزيد من التساؤل والتفكير ، مما أعطاني السرعة العالية مقارنةً بالأشخاص الذين بدأوا للتو العمل باستخدام القياس المتماثل) تلقيت ردًا من Unity قائلًا إنه تمت الموافقة على المكون الإضافي ويمكن أن أراه بالفعل في المتجر ، وكذلك مبيعاته الصفرية (حتى الآن). قام بتسجيل الدخول في المنتدى المحلي ، وقام بتكوين Google Analytics في صفحة البرنامج المساعد في المتجر وأعد نفسي لانتظار نمو العشب.
لم يستغرق الأمر وقتًا طويلاً قبل المبيعات الأولى ، تمامًا كما ظهرت التعليقات على المنتدى والمتجر. بالنسبة للأيام المتبقية من 12 كانون الثاني (يناير) ، تم بيع نسخ من البرنامج المساعد ، وهو ما اعتبرته علامة على مصلحة الجمهور وقررت الاستمرار فيه.
التحسين
لذلك ، كنت غير راض عن شيئين:
- التعقيد الزمني للفرز - O (N ^ 2)
- مشاكل مع جمع القمامة والأداء العام
خوارزمية
امتلك 100 كائن و O (N ^ 2) كان لدي 10000 تكرار لأجري فقط للعثور على التبعيات ، وأيضًا يجب علي تمريرها جميعًا ووضع علامة على العرض Z للفرز. يجب أن يكون هناك بعض الحل لذلك. لذلك ، حاولت عددًا كبيرًا من الخيارات ، لم أستطع النوم بالتفكير في هذه المشكلة. على أي حال ، لن أخبركم عن جميع الأساليب التي جربتها ، لكنني سأصف الطريقة التي وجدت الأفضل حتى الآن.
أول شيء أولاً ، بالطبع ، نحن فرز الكائنات المرئية فقط. ما يعنيه ذلك هو أننا نحتاج دائمًا إلى معرفة ما لدينا. إذا كان هناك أي كائن جديد ، فعلينا إضافته في عملية الفرز ، وإذا كان أحد الكائنات القديمة قد اختفى - تجاهله. الآن ، لا تسمح الوحدة بتحديد Bounding Box الخاص بالكائن مع أطفاله في شجرة المشهد. المرور عبر الأطفال (في كل مرة ، بالمناسبة ، حيث يمكن إضافتهم وإزالتهم) لن ينجح - بطيئًا جدًا. لا يمكننا أيضًا استخدام OnBecameVisible وأحداث أخرى لأن هذه تعمل فقط للكائنات الأصل. ولكن يمكننا الحصول على جميع مكونات Renderer من الكائن الضروري وأطفاله. بالطبع ، لا يبدو الخيار الأفضل لدينا ، لكنني لم أجد طريقة أخرى ، وهي عالمية ومقبولة من حيث الأداء.
List<Renderer> _tmpRenderers = new List<Renderer>(); bool IsIsoObjectVisible(IsoObject iso_object) { iso_object.GetComponentsInChildren<Renderer>(_tmpRenderers); for ( var i = 0; i < _tmpRenderers.Count; ++i ) { if ( _tmpRenderers[i].isVisible ) { return true; } } return false; }
هناك خدعة صغيرة من استخدام دالة GetComponentsInChildren التي تسمح بالحصول على مكونات دون تخصيصات في المخزن المؤقت الضروري ، بخلاف الأخرى التي تُرجع مجموعة جديدة من المكونات
ثانياً ، لا يزال يتعين علي فعل شيء حيال O (N ^ 2) . لقد جربت عددًا من تقنيات تقسيم المساحة قبل أن أتوقف عند شبكة بسيطة ثنائية الأبعاد في مساحة العرض حيث أعرض أجسدي متساوي القياس. يحتوي كل قطاع على قائمة بالكائنات متساوية القياس التي تعبرها. لذا ، فإن الفكرة بسيطة: إذا لم يتم تقاطع إسقاطات الكائنات ، فلا فائدة من بناء التبعيات بين الكائنات على الإطلاق. ثم نقوم بتمرير جميع الكائنات المرئية وبناء التبعيات فقط في القطاعات التي يكون ذلك ضروريًا ، مما يقلل من تعقيد وقت الخوارزمية ويزيد من الأداء. نحسب حجم كل قطاع كمتوسط بين أحجام جميع الكائنات. لقد وجدت النتيجة أكثر من مرضية.
بالطبع ، يمكنني أن أكتب مقالة منفصلة عن هذا ... حسنًا ، دعنا نحاول أن نجعل هذا الأمر قصيرًا. أولاً ، نقوم بصرف المكونات (نستخدم GetComponent للعثور عليها ، وهي ليست سريعة). أوصي الجميع بأن يشاهدوا أنفسكم عند العمل مع أي شيء له علاقة بالتحديث . يجب عليك دائمًا أن تضع في اعتبارك أن هذا يحدث لكل إطار ، لذلك عليك أن تكون حذرًا جدًا ، وتذكر أيضًا جميع الميزات المثيرة للاهتمام مثل custom == operator . هناك الكثير من الأشياء التي يجب وضعها في الاعتبار ، ولكن في النهاية ستتعرف على كل واحد منها في الملف التعريفي المدمج. يجعلها أسهل بكثير لحفظها واستخدامها :)
كما يمكنك أن تفهم حقا آلام جامع القمامة. هل تحتاج إلى أداء أعلى؟ ثم ننسى أي شيء يمكن أن يخصص الذاكرة ، والتي في C # (خاصة في برنامج التحويل البرمجي Mono القديم) يمكن أن يتم بأي شيء ، بدءًا من foreach (!) إلى lambdas الناشئة ، ناهيك عن LINQ المحظور عليك الآن حتى في أبسط الحالات. في النهاية ، بدلًا من C # مع السكر النحوي ، تحصل على ما يشبه C بقدرات سخيفة.
هنا سأقدم بعض الروابط حول الموضوع الذي قد تجده مفيدًا:
Part1 ، Part2 ، Part3 .
النتائج
لم أكن أعرف أي شخص يستخدم تقنية التحسين هذه من قبل ، لذلك كنت سعيدًا جدًا برؤية النتائج. وإذا احتاج الأمر في الإصدارات الأولى إلى 50 كائنًا متحركًا للعبة لتحويلها إلى عرض شرائح ، فهي تعمل الآن بشكل جيد حتى عندما يكون هناك 800 كائن في إطار: كل شيء يدور بأقصى سرعة وإعادة الفرز لمجرد 3-6 مللي ثانية وهو أمر جيد جدًا لهذا العدد من الكائنات في القياس. علاوة على ذلك ، بعد التهيئة ، لم يتم تخصيص ذاكرة تقريبًا لإطار :)
المزيد من الفرص
بعد أن قرأت التعليقات والاقتراحات ، كانت هناك بعض الميزات التي أضفتها في الإصدارات السابقة.
2D / 3D خليط
يعد خلط ثنائي الأبعاد وثلاثي الأبعاد في الألعاب متساوية القياس فرصة مثيرة للاهتمام تتيح تقليل رسم خيارات الحركة والتناوب المختلفة (على سبيل المثال ، النماذج ثلاثية الأبعاد للشخصيات المتحركة). إنه ليس بالأمر الصعب حقًا القيام به ، لكنه يتطلب الاندماج في نظام الفرز. كل ما تحتاجه هو الحصول على Bounding Box من الطراز مع جميع أبنائه ، ثم تحريك النموذج على طول الشاشة Z حسب عرض الصندوق.
Bounds IsoObject3DBounds(IsoObject iso_object) { var bounds = new Bounds(); iso_object.GetComponentsInChildren<Renderer>(_tmpRenderers); if ( _tmpRenderers.Count > 0 ) { bounds = _tmpRenderers[0].bounds; for ( var i = 1; i < _tmpRenderers.Count; ++i ) { bounds.Encapsulate(_tmpRenderers[i].bounds); } } return bounds; }
هذا مثال على كيفية الحصول على Bounding Box من النموذج مع جميع أطفاله

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

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

مجموعة أدوات المادية GIF التجريبي
الخاتمة والاستنتاجات
بعد أن قمت بتنفيذ جميع الاقتراحات المقدمة من المنتدى ، قررت رفع السعر إلى 40 دولارًا ، لذلك لن يبدو وكأنه مجرد مكون إضافي آخر بخمس سطور من الكود :) سأكون سعيدًا جدًا للإجابة على الأسئلة و الاستماع إلى النصائح الخاصة بك. بما أنها المرة الأولى التي أكتب فيها شيئًا عن هبر ، أرحب بجميع أنواع النقد ، شكرًا لك! والآن ، الشيء الذي كنت ادخره هو آخر إحصائيات المبيعات لهذا الشهر:
شهر | 5 دولارات | 40 دولار |
---|
يناير | 12 | 0 |
فبراير | 22 | 0 |
مارس | 17 | 0 |
ابريل | 9 | 0 |
مايو | 9 | 0 |
يونيو | 9 | 0 |
يوليو | 7 | 4 |
أغسطس | 0 | 4 |
سبتمبر | 0 | 5 |
Unity Asset Store رابط الصفحة: Isometric 2.5D Tools