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

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

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

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

Відвідати персоналізований сайт
Показано 5 з 5 послугУсі 242 послуг
Розробка базового контролера гравця
Середня
від 2 робочих днів до 2 тижнів
Розробка систем виявлення зіткнень та тригерів
Середня
від 2 робочих днів до 2 тижнів
Часті питання

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

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

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

  • image_games_mortal_motors_495_0.webp
    Розробка гри для компанії Mortal Motors
    683
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    Покрокова стратегія у фентезі сеттингу With Fire And Sword
    862
  • image_games_second_team_604_0.webp
    Розробка ігри для компанії Second term
    491
  • image_games_phoenix_ii_606_0.webp
    3D-анімація – тизер для гри phoenix 2.
    533

Геймплейне програмування

Розробник передає проект зі словами «там архітектура дещо дивна, але працює». Відкриваєте — і бачите: контроллер персонажа на 2000 строк, де фізика, анімація, UI та звук перемішані в одному MonoBehaviour. У Update() — перевірки стану через десяток boolean-флагів. Збереження через PlayerPrefs з ключами типу "player_hp_current_value_int". Це не гіпотетика — це типовий стан коду в проектах, які росли органічно без архітектурного рішення на початку.

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

Контроллер персонажа

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

Базовий вибір: Character Controller або Rigidbody?

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

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

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

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

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

Кілька правил, які заощаджують час:

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

Глибше: архітектура ШІ противників

Це та область, де різниця між «працює» і «працює добре» найбільш відчутна. Поганий 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 (BT) — наступний рівень. Дерево поведінки описує логіку агента через ієрархію задач: Sequence, Selector, Decorator, Leaf.

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

У 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 (GOAP) — підхід для дійсно складного AI, де агент повинен планувати послідовність дій для досягнення мети з урахуванням поточного стану світу.

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

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

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

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

  • Agent Radius та Agent Height повинні точно відповідати коллайдеру персонажа
  • Stopping Distance потребує налаштування під дальність атаки кожного типу ворога
  • NavMesh Obstacle з Carve: true для динамічних перешкод (падаючі ящики, закриваючі двері) — інакше агенти будуть намагатися пройти крізь них
  • Для великих відкритих світів — NavMesh Links для з'єднання окремих сегментів та Off-Mesh Links для стрибків і спусків

Глибше: система збережень

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

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-события. Проект стає значно легше тестувати та розширювати.

Що входить у послугу

  • Проектування та реалізація контроллера персонажа (CharacterController або Rigidbody залежно від жанру)
  • Налаштування фізики та системи колізій
  • Реалізація AI: State Machine, Behaviour Tree, GOAP — залежно від складності ворогів
  • Налаштування навігації: NavMesh, NavMeshAgent, динамічні перешкоди
  • Проектування та реалізація системи збережень з версійністю
  • Аудит існуючого коду: виявлення архітектурних проблем, рефакторинг вузьких місць
  • Code review та написання документації по ігровим системам