Як я писав стратегію на Unity без досвіду, використовуючи ChatGPT. Частина 1

Привіт, спільното! Мене звати Кирило, я займаюся web та .NET розробкою останні 4 роки.

У цьому блозі я хочу поділитися своїм досвідом створення покрокової стратегії на Unity з використанням ChatGPT та описати принципи застосованих механік.

Це перша частина з циклу статей.

Натхнення та вибір інструментів

Я з дитинства полюбляв грати у комп’ютерні стратегії по типу Heroes of Might and Magic, серій Total War та Civilization, глобальні стратегії від Paradox, та інші симулятори полководців. Та останніми роками став відчувати тривожність при просиджуванні часу за катками за якусь Скіфію у Rome 2 або будуванні морської імперії у 6-й Цивілізації. Мене стали долати думки про марно витрачений час, на протязі якого я не роблю щось корисне або не розвиваюся (так, це схоже на невеличкі психологічні проблеми). Доречно буде додати, що я працюю розробником останні декілька років та майже весь свій час проводжу за комп’ютером.

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

В мене давно був сформований концепт гри, яку б я хотів створити, це повинна була бути покрокова стратегія с механіками «Героїв» 5-х та 6-х, Total War та їх, нажаль загиблого, проекту Arena у часи класичної античності. Тож я з радістю та ентузіазмом почав... Почав розуміти, що для написання гри мені потрібно буде розібратися з ігровим двіжком. Я знав, що існують 2 основні engine для геймдеву — Unity та UE. Знав також, що перший використовують для створення більш примітивних ігор, а другий для більш серйозних проектів. Вибір мені було зробити елементарно, бо я 2 роки працював C# розробником, а це як раз мова розробки на Unity.

Чудово, вибір інструменту зроблено, з палаючими очима приступив до перегляду безкоштовних курсів по Unity. Як для початківця мені сподобалися курси на каналі itProger aka Гоша Дударь aka Школа програмування (українською). Основні концепти, такі як скрипти, префаби, об’єкти та компоненти пізнаються досить легко та за пару годин, враховуючи, що програмування ти вже знаєш. Але для моєї задумки із стратегією, там не було прикладів ні коду ні механік самого Unity.

Саме по стратегіям, на YouTube немає якісного матеріалу, тому я пішов у наступне місце для пошуку інформації — Google. Готових рішень не знайшлося і там, але я знайшов декілька корисних статей з написання шахів, адже для покрокової стратегії, це майже одне й те саме — виділення юніту, переміщення по клітинкам полю бою, атака ворожих фігурок-юнітів.

Початок розробки

На основі отриманих знань я накидав ігрове поле, виділення та переміщення юнітів, підсвітку клітинок.

А далі треба було подумати над такими речами, як:

  • Швидкість юніту (кількість клітинок на яку він може рухатися);
  • Створення подоби місцевості на карті;
  • Урахування перешкод та інших юнітів у зоні переміщення;
  • Урахування діагонального переміщення;
  • Алгоритм пошуку шляху, щоб виключити імітацію перельоту через перешкоди та інших юнітів;
  • Створення ворожої команди, та взаємодія із ворожими юнітами;
  • Різноманітні характеристики юнітів;

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

Мапу зробив із префабів клітинок з різним забарвленням та іменами.

Для логіки розрахунку доступного переміщення використовувався алгоритм на основі пошуку шляху А* (можете окремо пошукати інформацію), який по суті перебирає всі можливі зв’язки клітинок між стартовою та цільовою клітинками. Відмінності від оригінального варіанту полягали у можливості переміщення по діагоналі з урахуванням «ціни» такого переміщення, як 1,5 від переміщення по прямій. Думаю в Героях використовували схожу логіку.

Для коректного написання коду з використанням стандартних методів Unity, які за пару годин вивчання я не запам’ятав та не повністю зрозумів, мені дуже допомагав GPT. Чесно кажучи, код на C# написаний для Unity, доволі відрізняється від класичного та звичного мені стилю написання на .NET. Мені він віддає чимось схожим на JS.

Настав етап, коли треба було спавнити свою та ворожу команду із обраними юнітами, а не одного тестового. Загони із набором юнітів для кожної команди уявляли із себе пусті батьківські елементи із набором юнітів:

Які передавалися в контроллер / менеджер ігровогу процесу (можливо, архітектурно, в Unity це виглядає жахливо).

Основи ігрової механіки

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

void Start()
{
    InitializeBoard();
    InitializeSquads();
    SpawnUnits(Team.Allies);
    SpawnUnits(Team.Enemies);
}

Також, трохи пофотошопив, я домалював поле, що б виглядало приємніше.

Наступними кроками були створення моделі різних юнітів (маю на увазі класи, а не 3-д моделі) та реалізація їх бойової механіки, тут доволі просто:

  • Швидкість;
  • Кількість бійців;
  • Дамаг;
  • Броня;

Тут формула розрахунку пошкоджень при атаці:

public class GameUnit : MonoBehaviour, IUnitActions
{
    private Cell currentCell;
    public int speed;
    public int troopStrength;
    public float armor;
    public float damage;
    public Enums.Team team;
    public Enums.UnitCategory category;
    public void SetCurrentCell(Cell cell)
    {
        currentCell = cell;
        transform.position = cell.transform.position;
    }
    public Cell GetCurrentCell()
    {
        return currentCell;
    }
    public virtual void Attack(GameUnit target)
    {
    }
    public virtual void Defend()
    {
    }
    public void TakeDamage(float damage, float armor, int attackersTroopStrength)
    {
        float breakThrough = damage - armor;
        int finalDamage = Mathf.RoundToInt(damage* attackersTroopStrength);
        if (breakThrough > 0)
        {
            finalDamage += Mathf.RoundToInt(finalDamage * breakThrough);
        }
        if (breakThrough < 0)
        {
            finalDamage += Mathf.RoundToInt(finalDamage * breakThrough);
        }
        troopStrength -= finalDamage;
        if (troopStrength <= 0)
        {
            Die();
        }
    }
    public void Die()
    {
        if (currentCell != null)
        {
            currentCell.SetUnit(null);
        }
        Destroy(gameObject);
    }
}

Та приклади моделей юнітів:

public class CarthageArmoredInfantry : GameUnit
{
    void Start()
    {
        speed = 2;
        troopStrength = 150;
        armor = 0.3f;
        damage = 0.2f;
        category = Enums.UnitCategory.ArmoredInfantry;
    }
}
public class RomanLegioner : GameUnit
{
    void Start()
    {
        speed = 2;
        troopStrength = 150;
        armor = 0.4f;
        damage = 0.2f;
        category = Enums.UnitCategory.ArmoredInfantry;
    }
}

Після цього необхідно було зробити систему ходу одного та іншого гравця:

void Start()
{
    InitializeBoard();
    InitializeSquads();
    SpawnUnits(Team.Allies);
    SpawnUnits(Team.Enemies);
    endTurnButton.onClick.AddListener(EndTurn);
}
public void EndTurn()
{
    currentTeam = (currentTeam == Team.Allies) ? Team.Enemies : Team.Allies;
}

Простий перемикач на кнопці.

Також добавив підсвітку для поточної команди.

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

public class CameraController : MonoBehaviour
{
    private Vector3 startPosition;
    public float panSpeed = 20f;
    public float panBorderThickness = 10f;
    public float zoomSpeed = 2f;
    public float minZoom = -3f;
    public float maxZoom = -15f;
    private BattlefieldBoardManager boardManager;
    private Vector3 lastMousePosition;
    void Start()
    {
        boardManager = FindObjectOfType<BattlefieldBoardManager>();
        startPosition = transform.position;
    }
    void Update()
    {
        HandleCameraMovement();
        HandleCameraZoom();
    }
    void HandleCameraMovement()
    {
        if (Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2))
        {
            lastMousePosition = Input.mousePosition;
        }
        if (Input.GetMouseButton(1) || Input.GetMouseButton(2))
        {
            Vector3 delta = Input.mousePosition - lastMousePosition;
            Vector3 move = new Vector3(-delta.x * panSpeed * Time.deltaTime,
            -delta.y * panSpeed * Time.deltaTime, 0);
            transform.Translate(move, Space.World);
            lastMousePosition = Input.mousePosition;
        }
        Vector3 pos = transform.localPosition;
        pos.x = Mathf.Clamp(pos.x, startPosition.x - panBorderThickness, 
        startPosition.x + boardManager.columns * boardManager.cellSize + panBorderThickness);
        pos.y = Mathf.Clamp(pos.y, startPosition.y - panBorderThickness,
        startPosition.y + boardManager.rows * boardManager.cellSize + panBorderThickness);
        transform.localPosition = pos;
    }
    void HandleCameraZoom()
    {
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (scroll != 0.0f)
        {
            Vector3 pos = transform.position;
            pos.z = Mathf.Clamp(pos.z + scroll * zoomSpeed, maxZoom, minZoom);
            transform.position = pos;
        }
    }
}

Ось так в мене вийшла заготовка для подальшої розробки та наповнення гри, з цим вже можна працювати.

Загального витраченого часу на створення прототипу гри — приблизно 12-16 годин + годин 8-10 на вивчення Unity та прикладів ігрових механік.

У наступних частинах розповім про розвиток гри, поодинокий режим та мультіпреєр.

Підписуйтеся на Telegram-канал @gamedev_dou, щоб не пропустити найважливіші статті і новини

👍ПодобаєтьсяСподобалось8
До обраногоВ обраному4
LinkedIn


Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Бажаю успіху в вашому проєкті)
Сам на початку місяця почав за допомогою gpt робити іграшку для андроїд в юніті. В останній раз C бачив ще на програмуванні в академії і важкувато дається тепер все це))

Так а де тут chatgpt?
По факту, стаття говорить про «як я почав будувати гру на unity маючи досвід роботи C# розробником»

Для коректного написання коду з використанням стандартних методів Unity, які за пару годин вивчання я не запам’ятав та не повністю зрозумів, мені дуже допомагав GPT.

По факту, ті, хто зараз тільки входять у геймдев і не мають достатніх знанть у тому, чи іншому рушії, так або інакше юзать чат.

В TakeDamage дубляж коду, є умови що ні на що не впливають. Можливо механічна помилка, можливо лишилось не відрефакотореним)

Так, ви праві, не відрефакторено, буду писати далі, викладати, буду намагатись більше уваги приділяти рефакторінгу)

Бажаю успіху, все вийде у тих, хто наполегливий.

Підписатись на коментарі