"Оптимальний" ігровий цикл для двовимірної скроллера


14

Чи можна описати "оптимальну" (з точки зору продуктивності) макет для ігрового циклу 2D бічного скролера? У цьому контексті "ігровий цикл" приймає дані користувача, оновлює стани ігрових об'єктів та малює ігрові об'єкти.

Наприклад, базовий клас GameObject з глибокою ієрархією успадкування може бути корисним для обслуговування ... Ви можете зробити щось на кшталт наступного:

foreach(GameObject g in gameObjects) g.update();

Однак я думаю, що такий підхід може створити проблеми з ефективністю.

З іншого боку, дані та функції всіх ігрових об'єктів можуть бути глобальними. Це може бути головним болем, але може бути ближчим до оптимально працюючого циклу гри.

Будь-які думки? Мене цікавлять практичні програми майже оптимальної структури ігрової петлі ... навіть якщо я отримую головний біль в обслуговуванні в обмін на велику продуктивність.


Відповіді:


45

Наприклад, базовий клас GameObject з глибокою ієрархією успадкування може бути корисним для обслуговування ...

Насправді глибокі ієрархії, як правило, гірші для ремонту, ніж дрібні, а сучасний архітектурний стиль для ігрових об'єктів має тенденцію до мілководних підходів, заснованих на агрегації .

Однак я думаю, що такий підхід може створити проблеми з ефективністю. З іншого боку, дані та функції всіх ігрових об'єктів можуть бути глобальними. Це може бути головним болем, але може бути ближчим до оптимально працюючого циклу гри.

Цикл, який ви показали, потенційно може мати проблеми з продуктивністю, але не, як мається на увазі у вашому наступному викладі, оскільки у вас є дані екземпляра та функції учасника в GameObjectкласі. Скоріше, проблема полягає в тому, що якщо ви ставитесь до кожного об'єкта в грі як абсолютно однаковий, ви, ймовірно, не групуєте ці об'єкти інтелектуально - вони, ймовірно, випадковим чином розкидані по всьому списку. Потенційно, тоді кожен виклик методу оновлення цього об’єкта (незалежно від того, чи є цей метод глобальною функцією чи ні, і чи є об'єкт даними екземпляра або "глобальними даними", що плавають навколо в таблиці, яку ви індексуєте, або ін.) відрізняється від виклику оновлення в останніх ітераціях циклу.

Це може спричинити посилення тиску на систему, оскільки вам може знадобитися сторінка пам'яті відповідною функцією в пам'яті та поза нею та частіше повторно заповнювати кеш інструкцій, що призводить до уповільнення циклу. Від того, чи це можна спостерігати неозброєним оком (або навіть у профілера), залежить саме те , що вважається "ігровим об'єктом", скільки їх існує в середньому і що ще відбувається у вашій програмі.

Орієнтовані на компоненти об'єктні системи є популярною тенденцією зараз, використовуючи філософію, що агрегування є переважним у спадок . Такі системи потенційно дозволяють розділити логіку "оновлення" компонентів (де "компонент" приблизно визначається як деяка одиниця функціональності, наприклад, річ, яка представляє фізично модельовану частину об'єкта, яка обробляється фізичною системою ) на декілька потоків - дискримінують за типом компонента - якщо це можливо та бажано, що може призвести до підвищення продуктивності. Принаймні, ви можете організувати компоненти таким чином, що всі компоненти даного типу оновлюються разом , оптимально використовуючи кеш процесора. Приклад такої системи, орієнтованої на компоненти, обговорюється в цій темі .

Такі системи часто сильно роз'єднуються, що також є користю для технічного обслуговування.

Дизайн, орієнтований на дані, - це пов'язаний підхід - це орієнтація на себе навколо даних, необхідних для об'єктів, як першочергового завдання, щоб ці дані могли ефективно оброблятися масово (наприклад). Це, як правило, означає організацію, яка намагається зберегти дані, які використовуються для одного кластеру цілі, разом і оперувати ними відразу. Це принципово не сумісне з дизайном OO, і ви можете знайти деякі балаканини на цю тему тут, на GDSE, у цьому запитанні .

Насправді, більш оптимальним підходом до циклу гри був би замість оригіналу

foreach(GameObject g in gameObjects) g.update();

щось більше схоже

ProcessUserInput();
UpdatePhysicsForAllObjects();
UpdateScriptsForAllObjects();
UpdateRenderDataForAllObjects();
RenderEverything();

У такому світі, кожен GameObjectможе мати покажчик або посилання на свій власний PhysicsDataабо Scriptабо RenderData, за винятком випадків , коли вам може знадобитися , щоб взаємодіяти з об'єктами на індивідуальній основі, але фактична PhysicsData, Scripts, RenderDataі так далі були б всі власністю їх відповідних підсистем (фізичний симулятор, середовище хостингу сценаріїв, візуалізація) та обробляється оптом, як зазначено вище.

Це дуже важливо відзначити , що цей підхід не є чудодійним і не завжди дає приріст продуктивності (хоча це , як правило , краще дизайн , ніж глибоке дерево успадкування). Особливо ймовірно, ви помітите, що різниця в продуктивності практично не має, якщо у вас дуже мало об'єктів або дуже багато об'єктів, з якими ви не можете ефективно паралелізувати оновлення.

На жаль, не існує такого магічного циклу, який є найбільш оптимальним - кожна гра є різною і може потребувати налаштування продуктивності по-різному. Тому дуже важливо виміряти (профілювати) речі, перш ніж сліпо піти приймати поради якогось випадкового хлопця в Інтернеті.


5
Добрий пане, це чудова відповідь.
Raveline

2

Ігровий об’єктний стиль програмування чудово підходить для менших проектів. Оскільки проект стане справді величезним, ви отримаєте глибоку ієрархію спадкування, і база ігрових об'єктів стане дійсно важкою!

Як приклад ... Припустимо, ви почали створювати базу ігрових об'єктів з мінімальними атрибутами, наприклад, положення (для x та y), displayObject (це стосується зображення, якщо його є елементом малювання), шириною, висотою тощо ...

Тепер пізніше, якщо нам потрібно додати підклас, який матиме стан анімації, ви, ймовірно, перемістите «Код управління анімацією» (скажімо currentAnimationId, Кількість кадрів для цієї анімації тощо) до GameObjectBase, думаючи, що більшість об’єкти матимуть анімацію.
Пізніше, якщо ви хочете додати жорсткий корпус, який є статичним (не має анімації), вам потрібно перевірити "код управління анімацією" у функції оновлення GameObject Base (щонайменше, перевірити, чи є анімація чи ні). .. це має значення!) На відміну від цього, якщо ви будете слідувати структурі на основі компонентів, у вас буде приєднаний "компонент управління анімацією" до вашого об'єкта. Якщо це буде потрібно, ви прикріпите його. Для статичного тіла ви не будете приєднувати цей компонент. Це Таким чином, ми можемо уникнути непотрібних перевірок.

Отже, вибір стилю залежить від розміру проекту. Структуру GameObject легко освоїти лише для менших проектів. Надіюсь, це трохи допомагає.


@Josh Petrie - Дійсно чудова відповідь!
Айяппа

@Josh Petrie - Дійсно хороша відповідь з корисними посиланнями (sry не знаю, як розмістити цей коментар поруч із твоєю публікацією: P)
Айяппа

Як правило, ви можете натиснути посилання "Додати коментар" під моєю відповіддю, але це вимагає більшої репутації, ніж у вас зараз (див. Gamedev.stackexchange.com/privileges/comment ). І дякую!
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.