Як організувати ігровий движок на C ++? Чи корисне використання моєї спадщини?


11

Я початківець і в розробці ігор, і в програмуванні.

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

Тому я подумав, що мій ігровий движок повинен контролювати це:

- Moving the objects in the scene
- Checking the collisions
- Adjusting movements based on collisions
- Passing the polygons to the rendering engine

Я сконструював свої об'єкти так:

class GlObject{
private:
    idEnum ObjId;
    //other identifiers
public:
    void move_obj(); //the movements are the same for all the objects (nextpos = pos + vel)
    void rotate_obj(); //rotations are the same for every objects too
    virtual Polygon generate_mesh() = 0; // the polygons are different
}

і у мене в грі є 4 різні об'єкти: літак, перешкода, гравець, куля, і я створив їх так:

class Player : public GlObject{ 
private:
    std::string name;
public:
    Player();
    Bullet fire() const; //this method is unique to the class player
    void generate_mesh();
}

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

Це гарна ідея?

class GameEngine{
private:
    std::vector<GlObject*> objects; //an array containg all the object present
    Player* hPlayer; //hPlayer is to be read as human player, and is a pointer to hold the reference to an object inside the array
public:
    GameEngine();
    //other stuff
}

конструктор GameEngine буде таким:

GameEngine::GameEngine(){
    hPlayer = new Player;
    objects.push_back(hPlayer);
}

Справа в тому, що я використовую вказівник, тому що мені потрібно викликати те, fire()що є унікальним для об’єкта Player.

Тож моє запитання: чи це гарна ідея? Чи моє використання спадщини тут неправильне?



Я бачив їх після публікації, і насправді композиція - чудова ідея! Я завжди побоювався в кінцевому підсумку використовувати c з класами, а не мати справжній дизайн c ++!
Pella86

Я думаю, що причин використовувати композицію набагато більше, ніж успадкування ... У моїй відповіді наведено кілька прикладів щодо переваг гнучкості, які вона надає. Для гарного прикладу реалізації перевірте статті, які я пов’язав.
Койот

Відповіді:


12

Залежить від того, що ви маєте на увазі під «добрим». Це, безумовно, виконає роботу. Він має велику кількість боків вгору та вниз. Ви їх не знайдете, поки ваш двигун не стане набагато складнішим. Дивіться це питання на SO , для обговорення з цього питання.

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

Взагалі, Скотт Мейєрс може сказати про спадщину кілька чудових речей. Якщо ви все ще знаходитесь на цьому етапі розвитку, прочитайте цю книгу (Ефективна C ++), перш ніж продовжувати. Це фантастична книга і є вимогою для будь-яких кодерів, що працюють у нашій компанії. Але підводячи підсумки поради: якщо ви пишете клас і плануєте використовувати спадщину, будьте абсолютно зрозумілі, що відносини між класом та його предком є ​​відносинами "є". Тобто, кожен B - це А. Не використовуйте успадкування як простий спосіб надати B доступ до функціоналу, який забезпечує A, такий спосіб призведе до серйозних архітектурних проблем, і є інші способи зробити це.

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


Я другий на думку про спадщину. Моє особисте правило: не робіть цього, якщо вам не доведеться робити це будь-яким іншим способом, було б дурно. Іншими словами, успадкування - це погана ідея у 99% випадків (принаймні :)). Крім того, може бути добре відокремити логіку гри (поворот / переміщення) від візуалізації (create_mesh ()). Інша справа - fire () - розгляньте наявність списку дій та родової функції (моя операція) - таким чином ви не отримаєте базільйон різних функцій, розподілених по всіх класах. У будь-якому випадку, будьте простими та немилосердними, коли у вас виникнуть проблеми.
Ґілеад

8

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

Також ця стаття про системи сутностей надихнула мене, коли я вперше прочитав її.

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

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

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

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

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

Гнучкість у часі : Компоненти можна використовувати для збереження сумісності між версіями. Наприклад, коли вносяться зміни між випусками гри, ви можете (не завжди) просто додавати нові компоненти, які реалізують нову поведінку та зміни. Тоді гра інстанціює потрібні компоненти, приєднані до ваших об'єктів, залежно від завантаженого файлу збереження, підключення до однорангових служб або сервера, до якого гравець приєднується. Це надзвичайно зручно в сценаріях, коли ви не можете контролювати дати випуску нової версії на всіх платформах (Steam, Mac App Store, iPhone, Android ...).

Для кращого розуміння погляньте на теги [на основі компонентів] та [сутність-системи] .


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.