Buch lesen: "ГОНКА КАК КОНСТРУКТОР. Собери и опубликуй свою игру на Unity"

Дмитрий Денисов
Schriftart:

Table of Contents

Введение

Эта книга — о том, как собрать свою первую гоночную игру и довести её до публикации в браузере. Не «когда-нибудь, после трёх лет обучения», а за несколько вечеров — с работающим результатом, которым можно поделиться ссылкой.

Есть распространённый миф: чтобы сделать игру, нужно сначала выучить программирование «от и до», написать всё с нуля и только потом, может быть, увидеть результат. Индустрия так не работает. Веб-разработчики опираются на фреймворки, мобильные — на готовые SDK, а в геймдеве мы используем движки, плагины и шаблоны. Игра по своей сути — это конструктор: вы собираете проект из деталей — сцен, префабов, материалов, скриптов и интерфейса.

Поэтому мы пойдём не «с нуля», а от готового проекта. Перед вами Pursuit (на заставке она представляется полным именем Petrol Pursuit, но мы для краткости будем звать её просто Pursuit) — казуальная гонка: машина мчится по бесконечной дороге, топливо тает, дорогу приходится делить с грузовиками и автобусами, а на обочинах ждут спасительные канистры. В гараже — три машины на выбор, для смелых — сложный режим со встречным движением. Игра уже работает: откройте https://sciencepub.ru/carcasual/ и проедьте пару заездов прямо в браузере. Это цель, к которой мы пойдём, — только теперь вы будете не игроком, а автором.


Рис. В.1. Pursuit в браузере: машина, дорога, счётчики топлива и дистанции.

Чем эта книга отличается от видеокурсов и справочников? Тем, что мы разбираем настоящий, живой проект — целиком, до последнего скрипта. В Pursuit всего пятнадцать коротких скриптов и шесть сцен: этого достаточно, чтобы игра ощущалась настоящей, и достаточно мало, чтобы вы поняли каждую строчку. К концу книги в проекте не останется ни одного «чёрного ящика».

Эта книга продолжает линейку практикумов «…как конструктор»: в «Шутере как конструкторе» мы собирали космический шутер Deep Space, в «Ловце Драконов» — аркаду с нуля. Читать их перед этой книгой не обязательно: каждый практикум самодостаточен. Но если вы уже прошли один из них — здесь вы увидите знакомый путь на новом жанре, и это лучший способ закрепить навык.

Вот наш маршрут:

В первой главе — быстрый старт: установим Unity, откроем проект и запустим игру в редакторе. Уже через час у вас на экране будет собственная работающая гонка.

Во второй разберём «скелет» игры: шесть сцен и переходы между ними — меню, заезд, гараж, экран поражения. Познакомимся с менеджером игры и паузой.

В третьей сядем за руль: разберём контроллер машины — физику, управление, расход топлива и звук двигателя. Это сердце игры.

В четвёртой построим бесконечную дорогу: узнаем, как игра создаёт полотно трассы впереди машины и убирает его за спиной, не съедая память.

В пятой выпустим на трассу препятствия: случайные машины, взрывы при столкновении и «замирание» мира в момент аварии.

В шестой займёмся ресурсами и интерфейсом: канистры с топливом, счётчик дистанции и передача рекорда между сценами.

В седьмой откроем гараж: выбор из трёх машин — и первая серьёзная самостоятельная доработка проекта.

В восьмой посмотрим на игру глазами гейм-дизайнера: цель, ядро геймплея, уникальность, мини-диздок и баланс — то, что превращает «работает» в «интересно».

В девятой займёмся развитием: сложный режим со встречным движением, рекорды, новые механики — от нитро до полиции на хвосте — и разговор о монетизации.

В десятой впряжём в работу нейросети: как ставить задачи ИИ-ассистенту, проверять его код и отлаживать игру вдвоём с машиной — так, чтобы ускоряться, а не терять контроль.

В одиннадцатой — публикация: соберём браузерную WebGL-версию, разберём плагин публикации на Яндекс.Играх и посмотрим, как игра живёт на собственном сайте с рейтингом игроков.

Важно: все материалы книги — стартовые файлы проекта Pursuit — скачиваются бесплатно на сайте автора. Где именно и как — подробно написано в Приложении «Материалы к книге» в конце; короткая версия: откройте https://sciencepub.ru/carcasual/ и нажмите кнопку «Стартовые файлы проекта · бесплатно».

Совет: читайте эту книгу за компьютером с открытым Unity. Практикум работает только тогда, когда вы повторяете шаги руками: глазами код не выучивается.

Игра, которую мы разбираем, намеренно написана просто — так, как пишут первые проекты. Кое-где вы встретите решения, которые опытный разработчик сделал бы иначе, — и мы будем честно на них останавливаться: почему так написано, чем это грозит и как улучшить. Умение читать и дорабатывать чужой несовершенный код — это, пожалуй, самый востребованный навык программиста вообще.

Поехали!

Глава 1. Быстрый старт: Unity и первый запуск

Дорога начинается не с первого километра, а с поворота ключа.

Введение

Задача этой главы — максимально короткая: к её концу игра должна работать у вас на компьютере. Мы установим Unity, откроем проект Pursuit, дождёмся импорта и нажмём Play. Никакой теории сверх необходимого — всё интересное начнётся, когда перед глазами будет живая игра, которую можно «пощупать».

1.1 Устанавливаем Unity Hub и редактор

Unity ставится в два шага: сначала Unity Hub — небольшая программа-менеджер, которая управляет версиями редактора и списком проектов, затем через неё — сам редактор.

Скачайте Unity Hub с официального сайта https://unity.com/download и установите его.


Запустите Hub и войдите в аккаунт Unity (или создайте новый — это бесплатно). Для некоммерческого обучения и небольших проектов действует бесплатная лицензия Unity Personal.

Во вкладке Installs нажмите Install Editor и выберите версию ветки Unity 6 LTS — проект из этой книги собран на версии 6000.3.18f1. Подойдёт и более свежая версия 6000.3.x.

В списке модулей отметьте WebGL Build Support — он понадобится в одиннадцатой главе для сборки браузерной версии. Остальные модули можно не ставить.

Совет: установка редактора занимает несколько гигабайт и заметное время. Запустите её и налейте чаю — торопить этот процесс бесполезно.

1.2 Открываем проект

Проект Pursuit распространяется архивом (как вы его получили — по ссылке из книги или купив готовый проект — не важно, содержимое одно и то же).

Распакуйте архив в удобную папку. Надёжнее всего — путь без кириллицы и пробелов, например C:\Games\Pursuit или ~/Games/Pursuit.


В Unity Hub во вкладке Projects нажмите Add → Add project from disk и укажите распакованную папку. Правильная папка — та, внутри которой лежат Assets, Packages и ProjectSettings.

Кликните по проекту в списке. Первый запуск идёт несколько минут: Unity строит служебную папку Library и импортирует все ассеты. Это разовая операция — дальше проект будет открываться быстро.

Важно: папки Library, Temp и Logs — это кэш. Их не бывает в архиве, их не нужно копировать при переносе проекта и не нужно жалеть при удалении: Unity пересоздаст их автоматически.

1.3 Экскурсия по редактору



Рис. 1.1. Редактор Unity с проектом Pursuit: слева Hierarchy, в центре Scene с выделенной машиной, справа Inspector с её компонентами, внизу Project.

Первое впечатление от Unity у всех одинаковое: слишком много окон, и все чем-то заняты. Хорошая новость — рабочих окон всего пять, и каждое отвечает ровно на один вопрос. Пять минут ориентирования сейчас сэкономят вам полчаса растерянности потом.

Hierarchy (слева) — что есть в сцене. Обычный список всех объектов открытой сцены: камера, свет, дорога, машина, интерфейс. Треугольники слева от имён разворачивают вложенность: объекты в Unity живут семьями — родитель и дети.

Scene (в центре) — взгляд режиссёра. Свободная камера, которой можно облететь мир с любой стороны, выделить объект и подвинуть его мышкой. Здесь вы собираете сцену; игрок этого вида никогда не увидит.

Game (вкладка рядом со Scene) — взгляд игрока: ровно то, что видит игровая камера. Именно эта картинка появится в браузере или на телефоне.

Inspector (справа) — настройки выделенного. Выделили объект в Hierarchy — справа выпал список его компонентов и всех их полей. Здесь живут те самые значения, которые побеждают код: если в скрипте написано одно, а в инспекторе выставлено другое, работать будет инспектор. Мы будем возвращаться к этому правилу всю книгу.

Project (внизу) — все файлы игры: сцены, скрипты, модели, звуки, префабы. Это проводник по папке Assets, только внутри редактора.

Есть и шестое окно, которое стоит открыть сразу же: Console (Window → General → Console). Это место, куда игра жалуется — ошибки компиляции, предупреждения и ваши собственные сообщения. Пока в консоли красная строка, кнопка Play работать не будет, поэтому привычка заглядывать туда первым делом окупается с первой же ошибки.

Как летать в окне Scene. Колесо мыши — зум. Зажатая правая кнопка — поворот взгляда, и, не отпуская её, можно лететь клавишами WASD, как в шутере. Выделили объект и потеряли его из виду — нажмите F, камера подлетит вплотную. Alt с зажатой левой кнопкой — облёт выделенного по орбите. Всё, этого набора хватит на всю книгу.

Теперь — живой пример, чтобы почувствовать, как окна связаны между собой. Откройте в окне Project сцену Assets/Scenes/Game.unity.

В Hierarchy разверните объект GameWorld и кликните по newRoads. В Inspector появится компонент Road Controller — генератор дороги, а в нём числа: Road Length 14, Despawn Offset 30, Spawn Offset 42, Despawn Distance 56. Дорога в Pursuit бесконечная, и вот эти четыре значения задают, какими кусками она склеивается. В четвёртой главе мы разберём их по косточкам.


Разверните объект carPrefab и кликните по player_purple — это машина игрока. В инспекторе выстроился её «паспорт»: Rigidbody (физическое тело — масса, торможение, ограничения), Box Collider (невидимая коробка, которой машина касается мира), Car Controller (управление и топливо) и Obstacle (Script) (обработка аварии; да, имя странное — это отдельная история из пятой главы).

Нажмите F — окно Scene подлетит к машине. Переключитесь на вкладку Game и обратно на Scene: одна и та же сцена, два разных взгляда.

Вот и весь приём осмотра: выделил в Hierarchy — прочитал в Inspector. Им мы будем пользоваться в каждой главе, разбирая чужой проект по частям.

Совет: окна редактора можно таскать за заголовки, как вкладки браузера, — собирайте раскладку под себя, это нормально. Если в процессе всё разъехалось и половина окон потерялась, есть кнопка «как было»: Window → Layouts → Default. Ничего в проекте при этом не ломается — раскладка окон и содержимое игры никак не связаны.

Задание: откройте сцену Menu и найдите в Hierarchy объект ButtonPlay (внутри него — дочерний play, это просто надпись-картинка). Выделите ButtonPlay и найдите в инспекторе компонент Button, а в нём — блок On Click (). Пока просто посмотрите, что там лежит: во второй главе мы выясним, почему кнопка Play дёргает вовсе не тот скрипт, который можно было бы ожидать.

1.4 Что лежит в проекте

Теперь заглянем в окно Project поближе — это файловая система игры. Пройдёмся по главным папкам внутри Assets:

Scenes — шесть сцен игры, от меню до экрана поражения; их карту мы разберём во второй главе.

Scripts — вся логика игры: пятнадцать коротких скриптов на C#. К концу книги вы будете знать каждый из них в лицо.

Player, Road, Obstacle — префабы и модели машин, дороги и препятствий.

Meshtint Free City Lite Pack Mega Toon, 48 Particle Effect Pack, 2D Casual UI, Vehicle_Essentials — готовые наборы: модели города, эффекты взрывов, элементы интерфейса и большая библиотека автомобильных звуков (лежит про запас — в сценах пока звучит один carSound.wav из папки Audio). Типичный пример «конструкторного» подхода: готовые паки снимают вопрос арта, чтобы сосредоточиться на механике.

Audio — звуки: двигатель, меню, столкновение.

YandexGame и WebGLTemplates — плагин PluginYG для публикации на Яндекс.Играх; о нём поговорим в одиннадцатой главе.

Editor — служебный скрипт сборки WebGL (тоже дождётся одиннадцатой главы).

1.5 Первый заезд

Пора за руль.

В окне Project откройте Assets/Scenes/Menu.unity двойным кликом.


Нажмите кнопку Play над сценой. Появится главное меню игры (рис. 1.2).

Кнопка Play в меню запускает заезд. Управление — стрелки или WASD: вверх — газ, влево и вправо — перестроение между полосами. Следите за счётчиком топлива: он тает на ходу, а пополняется канистрами, которые появляются на дороге.

Врезались в машину или сожгли всё топливо — увидите экран Game Over с пройденной дистанцией. Это и есть ваш счёт.



Рис. 1.2. Главное меню Pursuit: Play, Garage, Options.

Задание: сыграйте три заезда и запишите лучшую дистанцию. А теперь загляните в гараж (кнопка Garage в меню) и попробуйте другую машину. Заодно найдите в игре сложный режим — и почувствуйте разницу.

Важно: остановить игру в редакторе — повторное нажатие Play. Пока игра запущена, кнопка подсвечена, а любые правки в сцене живут только до остановки — Unity откатит их. Это классическая ловушка новичка: полчаса настраивать сцену в режиме Play и потерять всё при остановке.

Выводы

Unity ставится через Unity Hub; для этой книги нужна ветка Unity 6 LTS и модуль WebGL Build Support.

Проект открывается через Add project from disk; правильная папка содержит Assets, Packages, ProjectSettings.

Library — пересоздаваемый кэш, его не копируют и не хранят в архивах.

Рабочих окон редактора всего пять: Hierarchy (что есть в сцене), Scene (взгляд режиссёра), Game (взгляд игрока), Inspector (настройки выделенного), Project (файлы игры); шестое — Console — открывается через Window → General → Console.

Вся игра — шесть сцен и пятнадцать скриптов в Assets/Scenes и Assets/Scripts; арт и эффекты — из готовых наборов.

Запуск и остановка игры в редакторе — кнопка Play; правки в режиме Play не сохраняются.

Итог: игра запускается у вас на компьютере — значит, инструмент настроен и можно разбираться, как всё устроено. В следующей главе мы посмотрим на «скелет» Pursuit: как шесть сцен связаны в единое целое и кто управляет переходами между ними.

Глава 2. Скелет игры: сцены и переходы

Игра — это не одна комната, а квартира. Главное — знать, где двери.

Введение

Прежде чем лезть в физику машины и генерацию дороги, разберёмся, из каких «комнат» состоит Pursuit и как игрок между ними перемещается. В Unity такие комнаты называются сценами: каждая — отдельный мир со своими объектами, камерой и интерфейсом. Понимание карты сцен — это половина понимания архитектуры любой небольшой игры.

2.1 Шесть сцен Pursuit

Откройте папку Assets/Scenes:

Menu — главное меню: кнопки Play, Garage, Options.

Game — обычный заезд: весь трафик едет попутно.

HardGame — сложный режим: к попутному трафику добавляется встречный.

Garage — выбор одной из трёх машин.

GameOver — экран поражения с пройденной дистанцией.

SampleScene — пустая сцена-заготовка из шаблона Unity; в игре не используется, её можно смело игнорировать (или удалить — это ни на что не повлияет).

Схема переходов проста: из меню — в заезд или гараж; из гаража — в заезд; из заезда — на экран поражения (или через паузу назад в меню); с экрана поражения — снова в меню. Круг замкнулся.

2.2 Кто переключает сцены: MenuController

За навигацию из меню отвечает крошечный скрипт MenuController.cs — он висит на объекте SceneManager сцены Menu:

using UnityEngine;


using UnityEngine.SceneManagement;



public class MenuController : MonoBehaviour


{


public void PlayGame()


{


SceneManager.LoadScene("Game"); // загружаем сцену "Game"


}



public void OpenGarage()


{


SceneManager.LoadScene("Garage"); // загружаем сцену "Garage"


}


public void OpenMenu()


{


SceneManager.LoadScene("Menu"); // загружаем сцену "Menu"


}


}

Вся магия — в одной строке: SceneManager.LoadScene("имя") выгружает текущую сцену и загружает названную. Обратите внимание: сцена вызывается по имени. Отсюда два практических следствия. Первое — имена сцен нельзя менять бездумно: строка "Game" в коде не узнает, что файл переименован. Второе — каждая сцена должна быть добавлена в список сборки (File → Build Profiles → Scene List), иначе при загрузке игра «не найдёт дверь».

А как кнопки узнают об этих методах? Выделите кнопку в сцене и посмотрите в инспекторе на блок On Click () компонента Button: туда перетаскивается объект с нужным скриптом, а в выпадающем списке выбирается метод. Это стандартный приём UI в Unity: кнопка — отдельно, логика — отдельно, связываются они в инспекторе.

И вот тут проект преподносит первый сюрприз — посмотрите внимательно, что именно подключено к кнопкам меню. Кнопка ButtonGarage честно вызывает MenuController.OpenGarage. А вот кнопка Play идёт в обход MenuController: в её On Click перетащен объект GarageManager (да, он есть и в сцене меню) с методом StartGame — тем самым, который мы разберём в главе про гараж: он проверяет, какая машина выбрана, и только потом загружает заезд. Метод MenuController.PlayGame(), который мы только что прочитали, не вызывается ниоткуда — это мёртвый код, оставшийся от ранней версии игры. А кнопка ButtonOptions и вовсе без обработчика: нажимайте сколько угодно — ничего не произойдёт. Такие «окаменелости» есть почти в любом реальном проекте; уметь их замечать — часть профессии.



Рис. 2.1. Инспектор кнопки ButtonPlay: в блоке On Click () вместо MenuController — GarageManager.StartGame.

Задание: добавьте в меню четвёртую кнопку — «Hard», — которая запускает сложный режим. Вам понадобится новый метод в MenuController с LoadScene("HardGame") и копия кнопки Play с новым обработчиком On Click — на этот раз подключите её к MenuController сами, как положено. А если хотите размяться — оживите заодно кнопку Options: пусть она хотя бы открывает гараж.

2.3 Менеджер игры и пауза

В сцене заезда живёт второй управляющий скрипт — GameManager.cs. Посмотрим на его ключевые части:

public class GameManager : MonoBehaviour


{


public static GameManager instance;



public Button PauseButton;


public Button ResumeButton;


public Button ExitButton;


private bool isPaused = false;



private void Awake()


{


instance = this;


}



public void PauseGame()


{


Time.timeScale = 0;


isPaused = true;


PauseButton.gameObject.SetActive(false);


ResumeButton.gameObject.SetActive(true);


}



public void ResumeGame()


{


Time.timeScale = 1;


isPaused = false;


PauseButton.gameObject.SetActive(true);


ResumeButton.gameObject.SetActive(false);


}


}

Здесь три важных приёма.

Синглтон. Строка public static GameManager instance и присваивание instance = this в Awake() — это упрощённый паттерн «одиночка»: любой скрипт игры может обратиться к менеджеру через GameManager.instance, не разыскивая его по сцене. Для маленькой игры — то, что нужно.

Пауза через время. Time.timeScale = 0 останавливает игровое время целиком: физика замирает, Time.deltaTime становится нулём, всё, что двигалось, — останавливается. timeScale = 1 — обычная скорость. Одна строка — и честная пауза готова.

Важно: Time.timeScale — глобальная настройка, она переживает загрузку сцен. Поэтому в методах перехода (GameOver(), ExitMenu()) менеджер обязательно возвращает timeScale = 1: забудете — и следующая сцена встретит вас «замороженной». Это реальная ошибка, на которую натыкается почти каждый новичок.

2.4 Кто связывает кнопки: GameUI

В проекте есть и второй способ связать кнопки с логикой — кодом. Для этого написан скрипт GameUI.cs:

public class GameUI : MonoBehaviour


{


private void Awake()


{


GameManager gameManager = GameManager.instance;


Transform gameUI = transform;


gameManager.PauseButton = gameUI.Find("Button_Pause").GetComponent<Button>();


gameManager.ResumeButton = gameUI.Find("Button_Resume").GetComponent<Button>();


gameManager.ExitButton = gameUI.Find("Button_Exit").GetComponent<Button>();



// Настроить обработчики событий для кнопок


gameManager.PauseButton.onClick.AddListener(gameManager.PauseGame);


gameManager.ResumeButton.onClick.AddListener(gameManager.ResumeGame);


gameManager.ExitButton.onClick.AddListener(gameManager.ExitMenu);


}


}

transform.Find("имя") ищет дочерний объект по имени, а onClick.AddListener(метод) подписывает метод на нажатие — то же самое, что блок On Click в инспекторе, только из кода. Кодовый способ удобнее, когда кнопок много или интерфейс создаётся на лету.

А теперь — вторая окаменелость этой главы, и она выразительнее первой. Проверьте, на каком объекте висит GameUI.cs. Ответ: ни на каком — во всём проекте нет ни одного объекта с этим компонентом. Кнопки паузы в сцене заезда называются Pause, Resume и ExitMenu и давно подключены к GameManager через инспектор, как и кнопки меню. А скрипт ищет объекты Button_Pause, Button_Resume, Button_Exit — таких имён в сцене больше нет: повесь его сейчас — он упадёт с NullReferenceException прямо в Awake. Судя по всему, автор начал с кодовой привязки, потом переподключил кнопки мышкой и переименовал их, а скрипт остался лежать. Урок тот же, что с PlayGame(): наличие кода в проекте ещё не значит, что он работает — проверяйте, кто и откуда его вызывает.

Совет: поиск по строковому имени (Find("Button_Pause")) хрупок так же, как загрузка сцены по имени: переименуете кнопку — получите ошибку уже во время игры, а не при компиляции. Привыкайте читать такие строки как «места повышенного риска» проекта.

Die kostenlose Leseprobe ist beendet.

Altersbeschränkung:
12+
Veröffentlichungsdatum auf Litres:
15 August 2026
Datum der Schreibbeendigung:
2026
Umfang:
118 S. 15 Illustrationen
Rechteinhaber:
Автор
Download-Format: