Послуги з програмування геймплею та ігрових механік

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

Від імерсивних застосунків до ігрових світів і 3D-сцен

Наша виділена команда для VR/AR/MR-розробки, Unity-продакшну і 3D-моделювання та анімації — з власними кейсами і презентаціями.

Відвідати персоналізований сайт
Показано 5 з 5Усі 242 послуг

Наші компетенції

Які етапи розробки гри?

Останні роботи

  • image_games_mortal_motors_495_0.webp
    Розробка гри для компанії Mortal Motors
    1434
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    Покрокова стратегія у фентезі сеттингу With Fire And Sword
    972
  • image_games_second_team_604_0.webp
    Розробка ігри для компанії Second term
    586
  • image_games_phoenix_ii_606_0.webp
    3D-анімація – тизер для гри phoenix 2.
    651
  • image_training-quizzes_kids_shopping_quiz_614_0.webp
    Навчальна вікторина для дітей «Покупки в магазині»
    12

Програмування ігор: геймплей та системна логіка

До нас приходить проєкт з хаотичною архітектурою. Розробник каже: «працює, не чіпай», але на ділі кожен новий рівень потребує окремого виправлення. Типова картина: контролер персонажа на 2000 рядків, де фізика, анімація, UI та звук перемішані в одному MonoBehaviour. Збереження через PlayerPrefs з ключами типу "player_hp_current_value_int". Це не гіпотетика — прямий результат відсутності архітектурного рішення на початку. Ми займаємося геймплейним програмуванням понад 7 років, реалізували понад 15 проєктів — від мобільних гіперказуалок до PC-шутерів. Гарантуємо, що після нашого втручання проєкт перестає бути «чорною скринькою». Якщо впізнали свою ситуацію — замовте безкоштовний аудит коду, оцінимо стан за 2 дні.

Ми беремо такий код, проводимо аудит і реорганізуємо його в модульну систему. Геймплейне програмування — серце гри. Тут закладається відчуття від керування, інтелект противників, чесна фізика та надійна система прогресу. Зроблено погано — жодний арт не врятує.

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

Який контролер персонажа обрати?

Контролер персонажа задає тон усій грі. Перше, з чим стикається гравець — керування. Затримки, слизький рух, «залипання» на перешкодах — все це зчитується миттєво та псує враження раніше, ніж гравець встигає побачити геймплей. Базовий вибір зводиться до двох варіантів: CharacterController або Rigidbody.

CharacterController — вбудований компонент Unity, спеціалізований для персонажів. Ігнорує фізичний рушій для руху, але коректно обробляє сходи, похилі поверхні та перешкоди. Рекомендований для екшн-ігор, платформерів, шутерів від першої особи — там, де потрібен точний передбачуваний відгук.

Rigidbody — фізичний об'єкт. Необхідний, коли персонаж повинен взаємодіяти з фізичними об'єктами: штовхати ящики, реагувати на вибухи, бути підкинутим. Вимагає акуратної роботи через FixedUpdate та обережного вимкнення гравітації або тертя, щоб керування не здавалося «плаваючим».

Для більшості 3D-проєктів ми використовуємо CharacterController з кастомним обробником гравітації — це дає контроль без артефактів фізичного рушія. Для 2D — Rigidbody2D з constraints на обертання та ретельно налаштованим Collision Detection Mode: Continuous. Кожне рішення приймаємо під жанр та цільову платформу.

Фізика та колізії

Rigidbody та колайдери — джерело регулярних проблем, якщо їх не налаштувати правильно з самого початку. Декілька правил, які економлять час:

  • Collision Detection: Continuous для швидких об'єктів (кулі, снаряди) — інакше вони «пролітають» крізь тонку геометрію
  • Складні меш-колайдери замінюємо складовими примітивами (Box + Capsule + Sphere) — це дешевше для фізики на 70-80%
  • Шари (Physics Layers) та матриця колізій в Physics Settings налаштовуються на початку проєкту — потім додати їх без рефакторингу дуже боляче
  • Всі фізичні обчислення — в FixedUpdate, не в Update. Інакше поведінка залежить від FPS

Дотримання цих правил знижує кількість багів з колізіями на 90% вже на етапі прототипу.

Типові помилки при налаштуванні фізики:

  • Частинки або UI-об'єкти з колайдерами — невидимі перешкоди для куль
  • Тригери, що висять на об'єктах без Rigidbody — події не спрацьовують
  • Швидкість кулі > 100 м/с без Continuous Dynamic — наскрізні прольоти
  • Один колайдер на складну сітку замість композитних — падіння FPS на 40-50%

Як архітектура ШІ впливає на ігровий досвід?

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

Кінцеві автомати (State Machines)

Найпоширеніший підхід — ієрархічний кінцевий автомат (HSM). Кожен стан: Idle, Patrol, Chase, Attack, Dead — це клас або метод з входом, оновленням та виходом. Вікіпедія описує кінцевий автомат як математичну модель поведінки з фіксованим набором станів і переходів.

public enum EnemyState { Idle, Patrol, Chase, Attack, Dead }

private void UpdateStateMachine() {
    switch (_currentState) {
        case EnemyState.Patrol:
            UpdatePatrol();
            if (CanSeePlayer()) TransitionTo(EnemyState.Chase);
            break;
        case EnemyState.Chase:
            _navMeshAgent.SetDestination(_player.position);
            if (InAttackRange()) TransitionTo(EnemyState.Attack);
            if (!CanSeePlayer() && _lostSightTimer > 5f) TransitionTo(EnemyState.Patrol);
            break;
        // ...
    }
}

State Machine добре працює для противників з невеликою кількістю станів (5-8). При зростанні складності — вибухове зростання переходів між станами, код стає важко читати та тестувати.

Behaviour Trees

Behaviour Tree — наступний рівень. Дерево поведінки описує логіку агента через ієрархію задач: Sequence, Selector, Decorator, Leaf. Вікіпедія визначає Behaviour Tree як граф, що дозволяє модульно будувати складну поведінку.

Перевага перед State Machine: кожен вузол атомарний і перевикористовуваний. Вузол CheckLineOfSight написаний один раз і використовується в десяти деревах. Додати нову поведінку — означає додати гілку в дерево, не рефакторити існуючу логіку. Для проєктів з понад 5 типами ворогів BT скорочує час на додавання нового типу в 2-3 рази порівняно зі State Machine.

У Unity BT реалізується через асети (NodeCanvas, Behaviour Designer) або кастомну реалізацію. Для великих проєктів з кількома типами ворогів це окупається вже на етапі другого типу противника. Приклад структури дерева для патрульного противника:

Root
└── Selector
    ├── Sequence (Combat)
    │   ├── IsPlayerVisible
    │   ├── IsPlayerInRange
    │   └── AttackPlayer
    ├── Sequence (Alert)
    │   ├── HeardSound
    │   └── InvestigatePosition
    └── Sequence (Patrol)
        ├── HasPatrolRoute
        └── FollowPatrolRoute

GOAP — коли BT недостатньо

Goal-Oriented Action Planning — підхід для дійсно складного AI, де агент повинен планувати послідовність дій для досягнення мети з урахуванням поточного стану світу. Класичний приклад — противник, якому потрібно «вбити гравця». Якщо в нього немає зброї, він шукає зброю. Якщо немає боєприпасів, він шукає патрони. Якщо гравець сховався, він шукає обхідний маршрут. GOAP дозволяє задати ці дії та їх передумови/постумови, а планувальник будує ланцюжок автоматично.

GOAP значно складніший у реалізації, ніж BT, і виправданий не завжди. Для платформерів і казуальних ігор це надмірно. Для тактичних ігор, симуляторів виживання, стелс-екшн — може бути правильним вибором.

NavMeshAgent та навігація

NavMeshAgent — стандартний інструмент для навігації в Unity. Працює коректно при правильному налаштуванні NavMesh та агентів:

  • Agent Radius та Agent Height повинні точно відповідати колайдеру персонажа
  • Stopping Distance потрібно налаштовувати під дальність атаки кожного типу ворога
  • NavMesh Obstacle з Carve: true для динамічних перешкод (ящики, що падають, двері, що зачиняються) — інакше агенти будуть намагатися пройти крізь них
  • Для великих відкритих світів — NavMesh Links для з'єднання окремих сегментів та Off-Mesh Links для стрибків і спусків
Критерій State Machine Behaviour Tree
Перевикористання логіки Низьке (стани прив'язані до контексту) Високе (вузли незалежні)
Масштабування Вибух переходів при 10+ станів Лінійне зростання дерева
Налагодження Складне (потрібен повний стейт-трекер) Просте (видно поточний вузол)
Складність впровадження Низька (старт за 1 день) Середня (3-5 днів на налаштування)
Рекомендований обсяг До 6 типів ворогів Від 6 типів ворогів

Чому система збережень потребує версіонування?

Друга область, де архітектурні рішення на початку критично впливають на все подальше. Збереження, додані в кінці розробки «за тиждень», майже завжди ламаються при зміні структури даних. Розглянемо інструменти.

PlayerPrefs — коли підходить і коли ні

PlayerPrefs — це сховище простих ключ-значення (string, int, float). Підходить суворо для налаштувань (гучність, керування, мова). Використовувати його для зберігання стану ігрового світу — помилка: немає типізації, немає версіонування, немає зручного дебагу.

JSON-серіалізація

Робочий підхід для більшості проєктів — серіалізація даних у JSON через JsonUtility (вбудований, швидкий, але обмежений) або Newtonsoft.Json (повноцінний, підтримує словники, успадкування, nullable типи). Структура системи збережень:

[Serializable]
public class SaveData {
    public int version = 1;          // версіонування
    public PlayerSaveData player;
    public WorldSaveData world;
    public SettingsSaveData settings;
}

public class SaveSystem : MonoBehaviour {
    private const string SAVE_FILE = "/save.json";

    public void Save(SaveData data) {
        string json = JsonConvert.SerializeObject(data, Formatting.Indented);
        File.WriteAllText(Application.persistentDataPath + SAVE_FILE, json);
    }

    public SaveData Load() {
        string path = Application.persistentDataPath + SAVE_FILE;
        if (!File.Exists(path)) return new SaveData();
        string json = File.ReadAllText(path);
        return JsonConvert.DeserializeObject<SaveData>(json);
    }
}

ScriptableObject як контейнер даних

ScriptableObject — недооцінений інструмент для зберігання ігрових даних. Конфігурації предметів, характеристики противників, параметри рівнів — все це зручніше тримати в ScriptableObject, ніж у JSON або константах у коді. Для збережень ScriptableObject використовується в патерні Runtime Set і Variable: значення зберігаються в ScriptableObject, збереження записує лише дельту відносно дефолтних значень.

Версіонування збережень

Поле version в корені SaveData — не бюрократія, а необхідність. Коли після релізу додається нова механіка з новими полями, потрібно коректно мігрувати старі збереження. Міграційний метод:

private SaveData MigrateSaveData(SaveData data) {
    if (data.version < 2) {
        data.player.newField = defaultValue;
        data.version = 2;
    }
    if (data.version < 3) {
        // наступна міграція
        data.version = 3;
    }
    return data;
}

Без версіонування доводиться вибирати між зламаними збереженнями у гравців або відмовою від зміни структури даних.

Що дає ScriptableObject-архітектура?

Для середніх і великих проєктів ми використовуємо підхід, популяризований Ryan Hipple на GDC: ScriptableObject як шина подій та сховище спільного стану.

// Змінна-подія
[CreateAssetMenu]
public class GameEvent : ScriptableObject {
    private List<GameEventListener> _listeners = new();

    public void Raise() {
        for (int i = _listeners.Count - 1; i >= 0; i--)
            _listeners[i].OnEventRaised();
    }
}

Це дозволяє системам у грі взаємодіяти без прямих посилань одна на одну. PlayerHealth не знає про UI, UI не знає про GameManager — всі вони знають тільки про ScriptableObject-події. Проєкт стає значно легше тестувати та розширювати.

Як ми працюємо: етапи та результат

Кожен проєкт проходить через п'ять етапів. Нижче — орієнтовні терміни та ключові артефакти. Вартість кожного етапу розраховується індивідуально.

Етап Тривалість (робочі дні) Результат
Аудит поточної архітектури 1-3 Документ з проблемами та рекомендаціями
Проектування систем 2-5 Архітектурна схема, опис модулів
Реалізація (ітерації) від 10 Робочий код, протестований у зв'язці з артом
Код-рев'ю та рефакторинг 2-4 Чиста кодова база, коментарі до складних ділянок
Документація та здача 1-2 Гайд для команди, опис налаштувань (Physics Layers, NavMesh тощо)

Терміни варіюються залежно від обсягу legacy-коду та складності механік. Отримайте консультацію інженера до старту робіт — переконаємося, що підхід підходить саме вашому проєкту.

Що входить у роботу

Після завершення ви отримуєте:

  • Архітектурну документацію ігрових систем (контролер, AI, збереження)
  • Вихідний код з коментарями, готовий до подальшої розробки
  • Налаштування інструментів: Physics Layers, NavMesh, проєкт ScriptableObject-подій
  • Код-рев'ю існуючих модулів (якщо проєкт не з нуля)
  • Гарантію: безкоштовна підтримка на етапі інтеграції протягом 2 тижнів після здачі

Зв'яжіться з нами, щоб обговорити деталі. Замовте аудит архітектури — це безкоштовно та займе не більше 3 робочих днів.