محرك Koala هو محرك ألعاب يتم تنفيذه بالكامل كنظام مكون كيان (ECS).
ويستند المحرك على entt. يجب أن يكون التكامل مع أجزاء أخرى من البرامج باستخدام EnTT واضحًا. تفترض هذه الوثائق على الأقل معرفة أساسية بـ EnTT ومصطلحاتها ( entity ، registry ، handle ...).

يعرض المثال المثال بعض الميزات الأساسية. يجب أن يعطيك فكرة عن دعم المحرك للانعكاس ووقت التشغيل الذي يوفره وقت التشغيل.
يستخدم المحرك عروضًا فرعية GIT ، وبالتالي يجب استنساخها بشكل متكرر مع
git clone https://github.com/phisko/kengine --recursive
تم اختبار المحرك على Windows مع MSVC و Mingw.
يعمل مجموعة Linux مع GCC. في وقت كتابة هذا التقرير ، لا يدعم Clang C ++ 20 من constexpr std::string and std::vector .
يتطلب المحرك مترجم C ++ 20 .
بدأ المحرك كمشروع شغف/طالب في 2016-17. أصدقائي/زملائي ، كان لديّ تنفيذ ECS من الألف إلى الياء. لقد أردنا سلامة ووضوح من النوع المطلق ، وبينما كنا نتحرك بالفعل باستخدام القالب المتصاعد ، كانت هذه فرصة لنا لمعرفة المزيد عنها.
بمجرد أن تعمل ECS الأساسية ، تحول المحرك إلى ملعب لمعرفة المزيد عن تطوير اللعبة. لقد تعلمت عرض OpenGL ، وكيفية استخدام Navmeshes مع إعادة صياغة/إزاحة ، وإعداد الفيزياء بالرصاص ... مع تطوير مساعدين مفيدين ومرافق انعكاس وقت التشغيل المتوافقة مع ECS.
بعد أكثر من 5 سنوات من العمل على المحرك ، أدركت أن ECS الأساسية نفسها لم تعد محور هذا المشروع. تقدم المكتبات الأخرى ، و EnTT على وجه الخصوص ، واجهة برمجة تطبيقات مماثلة للغاية ، مع ميزات أكثر تقدمًا. لذلك أخذت الوقت الكامل لإخراج ECS الداخلية بالكامل واستبدالها بـ EnTT . تبقى جميع الميزات كما هي ، وهذا سيعطيني المزيد من الوقت للعمل على مساعدين مفيدين ، والتي يمكن أن تعمل مع مشاريع EnTT الأخرى.
تستفيد أجزاء كثيرة من المحرك (مثل أنظمة البرمجة النصية أو محرر Emgui Entity) من واجهة برمجة تطبيقات انعكاس putils . وبالتالي يتم تعريف معظم المكونات في العينات التالية على أنها عكسية.
تم تنظيم رمز المحرك في ثلاث فئات:
لاحظ أن systems ليست كائنات لفئة معينة. الأنظمة هي ببساطة كيانات ذات مكون تنفيذ (أو أي شيء آخر يحتاجون إليه للقيام بعملهم). ثم يعيش الكيان في registry مع بقية حالة اللعبة. هذا يتيح للمستخدمين إدخال الأنظمة أو إضافة سلوك لهم تمامًا مثل أي كيان آخر.
يتم تقسيم هذه الفئات الثلاث إلى مكتبات مختلفة ، على سبيل المثال:
لاحظ أن بعض المكتبات تحتوي على ألياف فرعية ، على سبيل المثال:
يذهب قسم CMAKE إلى مزيد من التفاصيل حول كيفية العمل مع هذه المكتبات.
يأتي المحرك مع عدد (كبير إلى حد ما) من المكونات التي تم إنشاؤها مسبقًا والتي يمكن استخدامها لتمهيد اللعبة ، أو ببساطة كأمثلة يمكنك من خلالها بناء تطبيقاتك الخاصة.
هذه المكونات تتناسب مع ثلاث فئات:
مكونات البيانات تحتوي على بيانات حول كيانها.
مكونات البيانات هي ما يتبادر إلى الذهن أولاً عند التفكير في مكون ، مثل التحويل أو الاسم.
يمكن أن تحتوي مكونات البيانات في بعض الأحيان على وظائف:
تحتوي مكونات الوظائف على وظائف للاستعلام أو تغيير أو إخطار كيانها.
مكونات الوظائف هي ببساطة حاملين للمساحات التي يمكن إرفاقها كمكونات للكيانات. يمكن استخدام هذا الميكانيكي إلى:
مكونات الوظائف هي أنواع ترث من base_function ، مما يعطيها توقيع الوظيفة كمعلمة قالب.
لاستدعاء مكون دالة ، يمكن للمرء استخدام operator() أو وظيفة call الخاصة به.
entt::registry r;
const auto e = r.create();
r.emplace<main_loop::execute>(e,
[]( float delta_time) { std::cout << " Yay! " << std::endl; }
);
const auto & execute = r.get<main_loop::execute>(e); // Get the function
execute ( 0 .f); // Call it with its parameters
execute.call( 42 .f); // Alternativelyمكونات التعريف هي مكونات للمكونات.
يستخدم المحرك "كيانات الكتابة" للاحتفاظ بمعلومات حول المكونات المختلفة المستخدمة. يمثل كل كيان نوع نوع مكون مختلف ، ويمكن استخدامه للاستعلام عن خصائص المكون في وقت التشغيل.
يتم إرفاق مكونات META بـ "الكيانات النوع" ، وتعقد تنفيذ وظيفة عامة لهذا النوع المحدد. لأنها تحمل وظائف ، فهي تشبه إلى حد كبير مكونات الوظائف.
مثال على ذلك يجعل هذا أكثر وضوحًا: Meta :: Imgui :: Edit هو مكون Meta ، عند استدعاء ، سوف يرسم خصائص "مكون الوالد" باستخدام IMGUI للكيان المعطى. سيعرض الرمز التالي نافذة لتحرير مكون اسم e
// r is a registry with the "type entity" for `name` already setup
const auto e = r.create();
r.emplace<core::name>(e);
const auto type_entity = type_helper::get_type_entity<core::name>(r);
const auto & edit = r.get<meta::imgui::edit>(type_entity);
if (ImGui::Begin( " Edit name " ))
edit ({ r, e });
ImGui::End ();إذا قمت بتعميم هذا ، فيمكنك تحرير جميع مكونات الكيان مع الكود التالي:
// r is a registry with the "type entities" for all used components already setup
// e is an entity with an unknown set of components
if (ImGui::Begin( " Edit entity " ))
for ( const auto & [type_entity, edit] : r.view<meta::imgui::edit>()) {
edit ({ r, e });
}
ImGui::End ();انظر Cmake للحصول على تعليمات حول كيفية تمكين كل مكتبة.
يتم توفير البرنامج النصي Python Python cliregh_type_registration ، والذي يمكن استخدامه لإنشاء ملفات C ++ التي تحتوي على وظائف ستقوم بتسجيل مجموعة من أنواع معينة مع المحرك.
هذا ليس إلزاميا على الإطلاق .
يستخدم المحرك Cmake كنظام بناء. تم وضع إطار مخصص لتبسيط إنشاء المكتبات. يتكرر Cmakelists الجذر على المخرجين الفرعيين ويضيفونها تلقائيًا كمكتبات إذا كانت تتطابق مع بعض الشروط.
يتم إنشاء مكتبة Base kengine Interface التي تربطها جميع المكتبات الممكّنة ، لذلك يمكن للعملاء ببساطة الارتباط مع ذلك.
يتم كشف خيارات CMAKE التالية.
KENGINE_TESTSيجمع الاختبار التنفيذيين للمكتبات التي تنفذ الاختبارات.
KENGINE_NDEBUGيعطل رمز التصحيح.
KENGINE_TYPE_REGISTRATIONسوف يولد رمز تسجيل النوع لأنواع المحرك. هذا أمر أساسي للعديد من قدرات انعكاس المحرك ، حيث يوفر تنفيذ مكونات META.
KENGINE_GENERATE_REFLECTIONسيتم تحديث رؤوس الانعكاس لأنواع المحرك. يتم إنشاؤها مسبقًا ، لذلك ما لم تقم بتعديل رمز مصدر المحرك ، فلن تحتاج إلى تمكين ذلك.
يتم تعطيل جميع المكتبات افتراضيًا ، لتجنب بناء تبعيات غير مرغوب فيها. يمكن تمكين كل مكتبة بشكل فردي عن طريق ON خيار CMake الخاص بها. انظر تسمية المكتبة لاسم الخيار.
بدلاً من ذلك ، يمكن تمكين جميع المكتبات باستخدام خيار KENGINE_ALL_SYSTEMS .
لاحظ أن المشاريع الفرعية تحتاج إلى تمكين مكتبة الأصل الخاصة بهم: يتطلب kengine_imgui_entity_editor kengine_imgui.
تتم تسمية المكتبات اعتمادًا على مسارها النسبي إلى جذر المحرك. يتم استبدال القطع في المسار ببساطة بالرسوم السفلية ، على سبيل المثال:
kengine_corekengine_imgui_toolهذه الأسماء هي:
KENGINE_CORE لـ kengine_core )KENGINE_CORE_EXPORT لـ kengine_core )من الممكن اختبار وجود مكتبة أثناء التجميع بفضل C ++ تحديد وحدات الماكرو. هذه لها نفس اسم خيارات CMake ، على سبيل المثال:
# ifdef KENGINE_CORE
// The kengine_core library exists
# endifتستخدم بعض المكتبات VCPKG لإدارة التبعية.
نظرًا لأن المكتبات يتم اكتشافها تلقائيًا بواسطة CMakeLists.txt root ، فإن إنشاء مكتبة جديدة أمر سهل إلى حد ما.
ترتبط المكتبات تلقائيًا مع kengine_core ، لأنها توفر للمساعدين الذين يجب استخدامه من قبل جميع المكتبات (مثل Log_Helper و Plugiling_Helper).
الربط الفرعي تلقائيًا مع الوالدين. على سبيل المثال ، kengine_imgui_entity_editor يرتبط تلقائيًا مقابل kengine_imgui.
تتم إضافة ملفات المصدر من helpers systems للمكتبة تلقائيًا. إذا لم يتم العثور على أي منها ، فستكون المكتبة مكتبة واجهة Cmake.
قد يتم إنشاء كود التسجيل ورمز الانعكاس تلقائيًا للمكونات. افتراضيًا ، سيتم نقل جميع الرؤوس في data functions المكتبة إلى البرامج النصية للجيش.
على غرار ملفات المصدر ، إذا تم العثور على ملفات *.tests.cpp في helpers/tests أو systems/tests للمكتبة ، سيتم تلقائيًا إضافة قابلة للتنفيذ تلقائيًا.
CMakeLists.txt المخصص يجب ألا تحتاج المكتبات الأساسية CMakeLists.txt ، لأن ملفات المصدر الخاصة بها ستكون تلقائيًا. ومع ذلك ، إذا كانت المكتبة تحتاج إلى سلوك مخصص (على سبيل المثال ، لإضافة مصادر إضافية أو الارتباط مع مكتبة طرف ثالث) ، فقد تضيف CMakeLists.txt . سيتم استدعاء CMakeLists.txt بعد المكالمة لإضافة add_library .
يتم تعريف المتغيرات والوظائف التالية قبل استدعاء CMakeLists.txt :
kengine_library_name : اسم المكتبةkengine_library_tests_name : اسم غوغليست في المكتبةlink_type : نوع ارتباط المكتبة ( PUBLIC أو INTERFACE ، اعتمادًا على ما إذا تم العثور على مصادر أم لا)kengine_library_link_public_libraries(libraries) : روابط ضد المكتبات الأخرى (علنًا)kengine_library_link_private_libraries(libraries) : روابط ضد المكتبات الأخرى (خاصة)register_types_from_headers(headers) : يضيف رؤوسًا يمكن إنشاء تسجيل النوع ورؤوس الانعكاسsubdirectory_is_not_kengine_library(path) : يشير إلى CMakeLists.txt الجذر path