Твоя первая игра: как сделать Pong на Unity без опыта разработки

Создание своей первой игры может казаться долгой и сложной задачей — но это не так. Мы вместе с вами с нуля освоим основы работы с объектами, базовый скриптинг и взглянем на встроенный физический движок Unity. По итогу у вас будет своя копия первой коммерчески успешной игры в истории — аркады Pong.

Гайд написан по ролику ютубера Zigurous.

Для создания Pong понадобятся две программы:

Игровой движок Unity. Вместе с ним установится UnityHub, который пригодится для установки расширений

Microsoft Visual Studio (MVS) или Microsoft Visual Code (MVC) — они оба доступны здесь. Для гайда использовался MVCМодули вы можете добавить через это всплывающее меню

Модули вы можете добавить через это всплывающее меню

Для MVS нужно скачать модуль в UnityHub. В нём выберите свою версию Unity, нажмите на шестерёнку и там увидите нужный модуль.

Для MVC нужно просто создать любой проект. Для этого зайдите в Edit, выберите Preferences и через опцию Browse укажите путь к файлу, который запускает программу.

Если у вас нет расширения или вы не указывали путь к Visual Studio Code — будут только две опции: Open by file extension и Browse…, вам нужно выбрать вторую

Выберите в меню All templates ядро 2D — в нём делаются все проекты, использующие плоские спрайты.

Назначьте проекту папку и дайте имя, после чего нажмите Create Project, чтобы создать проект, и дождитесь его загрузки.

Меню создания проекта

Разработка начинается с создания основных объектов, изменения сцены и добавлению к ним компонентов. Для создания Pong понадобится базовая сцена.

В левом углу Unity расположены все объекты игры, по умолчанию там будет только камера.

Цвет камеры меняется через опцию Color. Она находится справа, в Inspector камеры. Каноничный Pong чёрный, поэтому мы выбрали этот цвет, но вы можете поэкспериментировать.

Создаем объекты и выбираем цвет камеры

Создайте мяч как игровой объект. Для этого кликните правой кнопкой мыши по пустому месту в меню объектов и выберите 2D Object → Sprites → Square.

По умолчанию объект будет в центре проекта.

Embedded Image
Embedded Image

Созданный мяч и ваш левый угол. Мяч можете назвать по своему усмотрению.

Для удобства можете переименовать объект в «Ball» или как-то иначе. Для этого нажмите правой кнопкой мыши по объекту и выберите Rename.

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

Далее расположите мяч в центре. Для этого в Inspector выберите опцию Transform, кликните на три точки и нажмите Reset. Это также сбросит все настройки.

Обнуление настроек с помощью Reset

У вас есть две вкладки:

Scene — в ней видны сетки объектов, освещения и так далее

И Game — в ней отображаются объекты так, как они будут выглядеть непосредственно в игре. Для наглядности мы будем работать в Game

Созданный вами мяч будет больше, чем изображенный на картинке. Чтобы это исправить, в Transform поменяйте параметры в графе Scale, например, на 0.25.

Embedded Image
Embedded Image

Мяч и его параметры

Чтобы наделить игровой объект физическими или иными свойствами, нужны компоненты. Мы делаем 2D игру, поэтому нужен Rigidbody 2D. Добавляются компоненты через опцию Add Component, которая находится в Inspector, при выборе объектов.

Найти Rigidbody 2D можно вбив его название в поиске выпадающего меню. Он сделает ваш объект твёрдым телом и позволит физически взаимодействовать с другими твёрдыми объектами окружения.

В меню компонента много параметров, мы вносим изменения в эти:

Gravity Scale — гравитация. Для мяча она не нужна, поэтому ставим 0, иначе его будет тянуть вниз сила притяжения

Angular Drag — поставьте 0. Этот параметр отвечает за кручение объекта. Если вы зададите ему другой параметр, у вас может выйти необычная модификация каноничного Pong

В подменю Constraints выберите Freeze Rotation Z, чтобы мяч точно не выдавал никаких ненужных кручений

В параметре Mass оставьте единицу. Если хотите, чтобы мяч ощущался иначе, можете изменить значение

Значение Rigidbody 2D для мяча

После добавьте компонент Box Collider 2D к мячу — его можно тоже найти через поиск. Он добавит коллизию объекта — своего рода хитбокс, который будет определять невидимые границы для взаимодействия с другими физическими объектами. Значения менять не надо, для нас подходит вариант по умолчанию. Визуальные границы можно проверить с помощью опции Gizmos.

Проверяем границы объекта с помощью опции Gizmos

Теперь создайте ворота для игрока — повторите аналогичные созданию мяча действия: ПКМ по списку объектов в левом углу → 2D Object → Sprites → Square.

Снова разместите объект в центре через функцию Reset параметров в меню Transform, потом измените размер (Scale): X — 0.2, Y —1.5. Сам объект переместите на другую сторону экрана через Position, задав значение параметра X на -8.

Embedded Image
Embedded Image

Делаем ворота и задаем параметры Rigidbody 2D

Ворота, как и мяч, должны быть физическим объектом, поэтому добавьте компонент Rigidbody 2D. Выставите для Angular Drag значение 0. Ворота нужно заморозить по осям X и Z — нужна только ось Y, так как они двигаются только вверх и низ.

Также измените массу (Mass), чтобы ворота ощущались достаточно легко — можете поиграть с этим значением. Linear Drag в нашем случае отвечает за то, будет ли у ворот остаточное движение после остановки. Чем выше параметр, тем резче будет остановка — поставьте 2.

Воротам тоже нужен Box Collider 2D — добавьте его, но параметры не меняйте.

Чтобы создать ворота, за которые будет играть компьютер, нажмите ПКМ по объекту с воротами игрока и выберите опцию Duplicate.

Для удобства переименуйте их и измените в компоненте Transform значение X с 8 на -8.

Embedded Image
Embedded Image

Параметр расположения ворот и то, как это должно выглядеть

Чтобы мячу было от чего отлетать, если он не попал в ворота, нужны стены. Они тоже будут объектом, но в этот раз нужно выбрать Сreate Empty. Через Trasnform сделайте Reset, назовите объект «верхней стеной». Добавьте Box Collider 2D и перейдите в меню сцены, чтобы отрегулировать коллизию стены правильно.

Включите Gizmos для удобства и выставите необходимые параметры Position для вашей стены — подходит значение 5.5 по оси Y. Ось X параметра Scale можно сделать любой, но при этом она должна быть чуть больше вашей сцены, чтобы не было пустых углов.

Несколько объектов можно выделить поочередно нажав на них с зажатым Ctrl. Если включить Gizmos, можно увидеть границы Box Colider, даже если объект невидимый

Продублируйте верхнюю стену, чтобы добавить нижнюю. Переименуйте её для удобства и измените значение Y с 5.5 на -5.5.

Чтобы сделать стены по бокам, продублируйте верхнюю и нижнюю.

Границы стен

Для левой стены проставьте параметры Position по оси X -10.5, а Scale по оси Y 10. Для правой Position по X укажите 10.5, а Scale — 10 по Y.

Ещё один объект — пунктирная линия, разделяющая стороны игроков. Она создаётся как и другие объекты, но выбирать нужно Create Empty.

Сбросьте позицию объекта через вкладку Transform и Reset Position и добавьте компонент через Add Component — нужен Line Renderer. Длину измените через таблицу Line Renderer, по осям Y для 0 — -10, а для 1 — 10.

Тени в этом проекте не нужны, поэтому отключите все параметры в Lighting.

Теперь к линии нужно применить материал. Для этого создайте папку под материалы в нижнем меню ассетов: ПКМ → Create → Material. Нажмите ЛКМ по материалу и измените Shader на Unlit/Transparent. Перетащите его в опцию Materials компонента Line Renderer, который применяли к разделяющей линии.

Embedded Image
Embedded Image

Применяем материал. Если перейдёте во вкладку Game, увидите как всё это сейчас играется

Линия пока не пунктирная. Проще всего будет скачать нужный спрайт тут. Под спрайт можно сделать отдельную папку в меню ассетов. Загрузите его в проект простым перетаскиванием из загрузок в Unity.

Выберите спрайт, найдите опцию Wrap Mode и в ней задайте Repeat — так текстура будет постоянно повторяться. В Filter Mode выберите Point (no filter) — так текстура будет выглядеть как олдскульные пиксели, без сглаживания углов.

Итоговые настройки спрайта, который сделает из непрерывной линии пунктирную

После этого выберите материал и перетащите его в «Texture».

Пошаговый видео-гайд по созданию материала

Если вам вдруг сложно создать скрипт

Перед написанием кода нужно создать папку для скриптов, как делали для объектов: в меню ассетов через ПКМ. И создайте сами скрипты — это почти как создать объект, только пункт нужно выбрать другой. Сначала создайте три скрипта:

общий для ворот

уникальный для ворот игрока

уникальный для ворот компьютера — у нас будет простенький, но способный быть соперником

Для удобства удалите лишние строчки кода. То же самое сделайте в скрипте ворот игрока и ворот компьютера. Затем измените public class в воротах игрока и воротах компьютера, чтобы связать эти скрипты с классом ворот.

Это делать необязательно, но так будет удобней, а код будет выглядеть чище

Код ворот игрока на данном этапе:

1using UnityEngine;
2public class VorotaIgroka : Vorota
3{
4}
Код ворот компьютера на данном этапе:

1using UnityEngine;
2public class VorotaAI : Vorota
3{
4}
К коду для искусственного интеллекта нужно будет вернуться позже. Сейчас сосредоточьтесь на скриптах для ворот игрока и общего скрипта для ворот.

Сначала нужно связать код с объектом ворот игрока, для этого перетащите скрипт ворот игрока на объект. То же самое можете предварительно проделать со скриптом ворот компьютера.

Связываем скрипт и объект

Создайте переменную для Rigidboddy 2D в общий скрипт для ворот — это родительский класс, поэтому его элементы будут проецироваться на скрипты других ворот. Не забывайте отделять разные элементы кода круглыми скобками.

Чтобы у ворот игрока и ворот компьютера был доступ к этой команде, не ставьте private, а используйте protected.

Код общего скрипта ворот:

1using UnityEngine;
2
3public class Vorota : MonoBehaviour
4{
5 protected Rigidbody2D _rigidbody;
6}
Код общего скрипта ворот

Теперь нужно добавить функцию Awake — она будет запускать инициализацию процесса до начала игры. Свяжите её с функцией, которая запустит в скрипте Rigidbody 2D и сделает ваш объект физическим.

Для этого используйте этот код:

1using UnityEngine;
2public class Vorota : MonoBehaviour
3{
4protected Rigidbody2D _rigidbody;
5private void Awake()
6{
7_rigidbody = GetComponent<Rigidbody2D>();
8}
9}
Добавьте логику для ворот игрока — по сути, вы добавите управление. Для этого нужна команда Input, к которой привязываются клавиши через Keycode. Альтернативный ввод создаём через оператор ||.

В этой логике используется два направления движения: верх (Vector2.up) и низ (Vector2.down). Не забывайте о функции private void Update, которую нужно располагать выше — она инициализирует этот скрипт на протяжении каждого фрейма игры.

Текстовая версия кода:

1using UnityEngine;
2public class VorotaIgroka : Vorota
3{
4private Vector2 _direction;
5private void Update()
6{
7if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) {
8_direction = Vector2.up;
9} else if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) {
10_direction = Vector2.down;Добавьте состояние покоя для ворот. Для этого на 13 строку добавьте оператор условия else, а на 14 задайте нулевую позицию для Vector2.

Текстовая версия кода:

1using UnityEngine;
2public class VorotaIgroka : Vorota
3{
4private Vector2 _direction;
5private void Update()
6{
7if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) {
8_direction = Vector2.up;
9} else if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) {
10_direction = Vector2.down;
11} else {
12_direction = Vector2.zero;
13}
14}
15}
Теперь нужно добавить отталкивание мяча от ворот игрока. Используйте для этого функцию FixedUpdate — она позволит обновлять физику постоянно. Через оператор if введите условие, при котором будет добавляться скорость, в коде это выглядит так:

1using UnityEngine;
2public class VorotaIgroka : Vorota
3{
4private Vector2 _direction;
5private void Update()
6{
7if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) {
8_direction = Vector2.up;
9} else if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) {
10_direction = Vector2.down;
11} else {
12_direction = Vector2.zero;
13}
14}
15private void FixedUpdate()
16{
17if (_direction.sqrMagnitude != 0) {
18_rigidbody.AddForce(_direction * this.speed);
19}
20}
21}
Вводя переменную this.speed, нужно выполнить два условия: задать ей значение и сделать её одинаковой для всех ворот.

Поэтому перейдите в общий скрипт ворот и после класса через public float введите общее значение: 10.0f. Если хотите быстрее — напишите большее число. В коде это выглядит так:

1using UnityEngine;
2public class Vorota : MonoBehaviour
3{
4public float speed = 10.0f;
5protected Rigidbody2D _rigidbody;
6private void Awake()
7{
8_rigidbody = GetComponent<Rigidbody2D>();
9}
10}
Сохраните результат через вкладку File, выбрав Save All. Если всё сделано верно, в компоненте скрипта появится значение Speed. Его можно менять прямо в Unity, не изменяя код.

Embedded Image
Embedded Image

Когда есть что сохранять, кнопка светится белым. На втором изображении — новое значение, которое должно появиться в добавленном к объекту скрипту

Чтобы управления воротами было отзывчивым и они не падали вниз понижаем параметр Gravity Scale до 0 в Rigbody 2D. Не повторяйте ошибок автора и не забудьте сделать это и для ворот компьютера.

Автор забыл убрать значение гравитации для ворот компьютера. Вы не забывайте.

Заставляем мяч двигаться

Скрипт мяча создайте так же, как другие: в папке скриптов и перенеся его на соответствующий объект — на мяч.

Используйте функцию private void Start() — этот элемент кода похож на Awake(), но запускается единожды.

1using UniteEngine;
2public class Ball : MonoBehaviour
3{
4private void Start()
5{
6}
7}
На 7 строку кода добавьте функцию, которая добавит стартовую скорость мяча. А ниже задайте ей значения по оси X.

Так как ось X — это сторона, в которую полетит мяч, мы будем решать кому полетит мяч броском монетки: -1 — к игроку, 1 — к компьютеру. В виде кода это выглядит так:

1using UniteEngine;
2public class Ball : MonoBehaviour
3{
4private void Start()
5{
6AddStartingForce();
7}
8private voud AddStartingForce()
9}
10float x =Random.value < 0.5f ? -1.0f : 1.0f;
11}
12}
Для оси Y нужны случайные значения, которые при этом не будут равны нулю — иначе ваш мяч полетит по прямой траектории. Код выглядит так:

1using UniteEngine;
2public class Ball : MonoBehaviour
3{
4private void Start()
5{
6AddStartingForce();
7}
8private voud AddStartingForce()
9}
10float x =Random.value < 0.5f ? -1.0f : 1.0f;
11float y = Random.value < 0.5F ? Random.Range(-1.0f, -0.5f) :
12Random.Range(0.5f, 1.0f);
13}
14}
Чтобы установить скорость, необходимо ввести Rigidboddy 2D в скрипт мяча. Для этого сместите код и на пятую строку добавьте функции: Awake() и GetComponent.

1using UniteEngine;
2public class Ball : MonoBehaviour
3{
4private RigidBody2D _rigidbody;
5private void Awake()
6{
7_rigidbody = GetComponent<Rigidbody2D>();
8private void Start()
9{
10AddStartingForce();
11}
12private voud AddStartingForce()
13}
14float x =Random.value < 0.5f ? -1.0f : 1.0f;
15float y = Random.value < 0.5F ? Random.Range(-1.0f, -0.5f) :
16Random.Range(0.5f, 1.0f);
17}
18}
Задайте параметры движения и заранее пропишите скорость по аналогии со скриптом для ворот.

У вас будут подчёркнуты ошибки, но они исчезнут, когда на пятую строку кода для мяча, введёте фиксированное значение скорости с помощью команды public float speed = 200.0f; После этого сохраните результат.

Код мяча на данном этапе выглядит так:

1using UnityEngine;
2public class Ball : MonoBehaviour
3{
4public float speed = 200.0f;
5private Rigidbody2D _rigidbody;
6private void Awake()
7{
8_rigidbody = GetComponent<Rigidbody2D>();
9}
10private void Start()
11{
12AddStartingForce();
13}
14private void AddStartingForce()
15{
16float x = Random.value < 0.5f ? -1.0f : 1.0f;
17float y = Random.value < 0.5F ? Random.Range(-1.0f, -0.5f) :
18Random.Range(0.5f, 1.0f);
19Vector2 direction = new Vector2(x, y);
20_rigidbody.AddForce(direction * this.speed);
21}
Отступы, как на 21 строке, делаются с помощью клавиши пробел

Если вы всё сделали правильно, в Unity у скрипта мяча появится параметр скорости.

Параметр скорости выделен красным

Запустите Unity и проверьте мяч — он работает правильно, но пока не отскакивает от невидимых стен. Чтобы это исправить, хватит внутренних возможностей движка.

Всё работает правильно, но мяч пока не отскакивает

Создаем физический объект

Чтобы вызвать настройки физического материала, нажмите ЛКМ по нему. Параметр Friction, который отвечает за трение, не нужен, а вот в Bounciness поставьте 1.

Физический материал нужно привязать к Rigidbody 2D мяча. Для этого выберите объект мяча и перетащите в строку Material Rigidbody 2D. Теперь мяч будет отскакивать от невидимых стен.

Embedded Image
Embedded Image

Если всё сделано правильно, мяч будет отскакивать от объектов

Создание ИИ

Откройте код ворот компьютера. Чтобы убедиться, что он связался с общим скриптом ворот, в Unity перенесите скрипт ворот компьютера на объект ворот компьютера. Проверьте, чтобы название скрипта было аналогично его классу в коде — иначе выдаст ошибку.

Нужно заставить ворота компьютера следовать за мячом. Для этого нужен void FixedUpdate, так как мы работаем с меняющимися параметрами. Также нужно связать скрипт искусственного интеллекта с RigidBoddy 2D мяча, лучше это сделать сразу после информации о классе скрипта.

1using UnityEngine;
2public class VorotaAI : Vorota
3{
4public Rigidbody2D ball;
5private void FixedUpdate()
6{
7}
8}
Чтобы проверить правильность кода — сохраните и вернитесь в Unity. Там будет новая опция в компоненте скрипта — Ball. Чтобы ИИ узнавал движения мяча, просто перетаскиваем объект мяча в эту строку.

Если непонятно как — показываем наглядно

Теперь добавьте значения, при которых ворота компьютера будут двигаться, а также введите увеличение скорости для ворот ИИ. В коде это выглядит так :

1using UnityEngine;
2public class VorotaAI : Vorota
3{
4public Rigidbody2D ball;
5private void FixedUpdate()
6{
7if (this.ball.velocity.x > 0.0f)
8{
9if (this.ball.position.y > this.transform.position.y) {
10_rigidbody.AddForce(Vector2.up * this.speed);
11} else if (this.ball.position.y < this.transform.position.y) {
12_rigidbody.AddForce(Vector2.down * this.speed);
13}
14}
15}
16}
Эта логика на случай, если мяч летит на ворота компьютера. Теперь добавьте логику, если он летит в сторону игрока:

1using UnityEngine;
2public class VorotaAI : Vorota
3{
4public Rigidbody2D ball;
5private void FixedUpdate()
6{
7if (this.ball.velocity.x > 0.0f)
8{
9if (this.ball.position.y > this.transform.position.y) {
10_rigidbody.AddForce(Vector2.up * this.speed);
11} else if (this.ball.position.y < this.transform.position.y) {
12_rigidbody.AddForce(Vector2.down * this.speed);
13}
14}
15else
16{
17if (this.transform.position.y > 0.0f) {
18_rigidbody.AddForce(Vector2.down * this.speed);
19} else if (this.transform.position.y < 0.0f)
20_rigidbody.AddForce(Vector2.up * this.speed);
21}
22}
23}
24}
То, что написано начиная с 17 строки, заставит ворота игрока следовать за мячом — правильность ввода проверяем в Unity. Не забудьте сохранить изменения.

Проверяем ИИ

Увеличиваем скорость мяча

Любая игра должна развиваться, иначе будет скучно. Чтобы сделать игру более весёлой, добавьте скорость, которая будет увеличиваться исходя из каждого отталкивания мяча от игровых объектов.

Для этого создайте новый скрипт — нужен отдельный исходя из специфики настроек. Задайте разные параметры увеличения скорости для отскока от стен и ворот.

Начните с ввода переменной, условно назовем её bouncestrength:

1Using UnityEngine;
2public class Bounce : MonoBehaviour
3{
4public float bouncestrength;
5}
Ввод кода

Добавьте функцию переменной для коллизии мяча. Когда вы свяжете её с другими объектами, сможете увеличивать скорость. Не забывайте про фигурные скобки:

1using UnityEngine;
2public class Bounce : MonoBehaviour
3{
4public float bouncestrength;
5private void OnCollisionEnter2D(Collision2D collision)
6}
Визуализация кода

Введите в скрипт коллизию мяча, через условие if ≠ null уточните, что всё будет происходить только когда мяч находится на экране. Так это выглядит в коде:

1using UnityEngine;
2public class Bounce : MonoBehaviour
3{
4public float bouncestrength;
5private void OnCollisionEnter2D(Collision2D collision)
6{ Ball ball = collision.gameObject.GetComponent<Ball>();
7if (ball != null)
8{
9}
10}
11}
А так — на практике

Введите условие обычного состояния для коллизии. Для этого GetContact должен иметь индекс (0):

1using UnityEngine;
2public class Bounce : MonoBehaviour
3{
4public float bouncestrength;
5private void OnCollisionEnter2D(Collision2D collision)
6{ Ball ball = collision.gameObject.GetComponent<Ball>();
7if (ball != null)
8{
9Vector2 normal = collision.GetContact(0).normal;
10ball.AddForce(-normal * this.bouncestrength);
11}
12}
13}
Если текстовая форма недостаточно наглядная, показываем как ввод выглядит в динамике

Добавочную силу для мяча нужно прописывать непосредственно в его скрипте. Введите общее условие и добавьте к Rigidbody 2D силу, код мяча должен выглядеть так:

1using UnityEngine;
2public class Ball : MonoBehaviour
3{
4public float speed = 200.0f;
5private Rigidbody2D _rigidbody;
6private void Awake()
7{
8_rigidbody = GetComponent<Rigidbody2D>();
9}
10private void Start()
11{
12AddStartingForce();
13}
14private void AddStartingForce()
15{
16float x = Random.value < 0.5f ? -1.0f : 1.0f;
17float y = Random.value < 0.5F ? Random.Range(-1.0f, -0.5f) :
18Random.Range(0.5f, 1.0f);
19Vector2 direction = new Vector2(x, y);
20_rigidbody.AddForce(direction * this.speed);
21}
22public void AddForce(Vector2 force)
23{
24_rigidbody.AddForce(force);
25}
Рабочий процесс ввода кода

Вернитесь к скрипту усиления силы и введите условие увеличения скорости:

1using UnityEngine;
2public class Bounce : MonoBehaviour
3{
4public float bouncestrength;
5private void OnCollisionEnter2D(Collision2D collision)
6{ Ball ball = collision.gameObject.GetComponent<Ball>();
7if (ball != null)
8{
9Vector2 normal = collision.GetContact(0).normal;
10ball.AddForce(-normal * this.bouncestrength);
11}
12}
13}
Переходим в скрипт мяча и добавляем условия, при котором он будет ускоряться

Сохраните изменения в скриптах и примените на объекты в Unity скрипт, ответственный за ускорение: на ворота игрока, ворота компьютера и стены. Можете задать параметры на своё усмотрение, но в нашем билде игры это 5 для стен и 10 для ворот.

Отскакивание мяча от стен

Скрипт для системы очков

Для системы подсчёта очков создайте отдельный скрипт. Назовите его «GameManager» — у него будет иконка отличная от других скриптов.

Если назовёте скрипт правильно, иконка автоматически станет такой

Сначала сделайте два раздельных подсчета очков: для игрока и компьютера, через команду private int.

1using UnityEngine
2;
3public class GameManager : MonoBehaviour
4{
5private int _playerscore;
6private int _playerscore;
7}
Теперь введите публичное отображение очков и установите возможность их увеличения через двойной плюс на конце значений:

1using UnityEngine;
2public class GameManager : MonoBehaviour
3{
4private int _playerscore;
5private int _playerscore;
6public void PlayerScore()
7{
8_playerscore++;
9}
10public void ComputerScore()
11{
12_computerscore++;
13}
14}
Выстраивая систему подсчета очков, придём к тому, что мяч должен сбрасывать свою позицию после каждого забитого гола. Это нужно сделать не в скрипте GameManager, а в коде мяча. Добавьте сброс позиции с обнулением ускорения: для этого используйте velocity zero и воспроизведение добавления стартовой силы.

1using UnityEngine;
2public class Ball : MonoBehaviour
3{
4public float speed = 200.0f;
5private Rigidbody2D _rigidbody;
6private void Awake()
7{
8_rigidbody = GetComponent<Rigidbody2D>();
9}
10private void Start()
11{
12AddStartingForce();
13}
14private void AddStartingForce()
15{
16float x = Random.value < 0.5f ? -1.0f : 1.0f;
17float y = Random.value < 0.5F ? Random.Range(-1.0f, -0.5f) :
18Random.Range(0.5f, 1.0f);
19Vector2 direction = new Vector2(x, y);
20_rigidbody.AddForce(direction * this.speed);
21}
22public void AddForce(Vector2 force)
23{
24_rigidbody.AddForce(force);
25}
26public void ResetPosition()
27{
28_rigidbody.position = Vector3.zero;
29_rigidbody.velocity = Vector3.zero;
30AddStartingForce();
31}
32}
Вернитесь к скрипту GameManager и введите мяч как объект влияющий на очки, сославшись на него через команду public Ball ball;

Ещё добавьте функцию, которая будет инициировать сброс позиции мяча при увеличении очков. Эти команды отображены на 10 и 15 строчках кода:

1using UnityEngine;
2public class GameManager : MonoBehaviour
3{
4public Ball ball;
5private int _playerscore;
6private int _playerscore;
7public void PlayerScore()
8{
9_playerscore++;
10this.ball.ResetPosition();
11}
12public void ComputerScore()
13{
14_computerscore++;
15this.ball.ResetPosition();
16}
17}
Теперь нужно создать объект для скрипта GameManager через Create Empty. На него нужно перетащить одноименный скрипт и расположить на самом верху объектов, а после тоже перетаскиванием связать с объектом мяча.

Привязываем мяч к геймменджеру

Теперь сделайте новый скрипт для зоны, взаимодействие с которой будет подсчитывать очки. Сразу добавьте его к левой и правой стене, перетаскивая как компонент.

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

1using UnityEngine;
2public class ScoringZone : MonoBehaviour
3{ public float bouncestrength;
4private void OnCollisionEnter2D(Collision2D collision)
5{ Ball ball = collision.gameObject.GetComponent<Ball>();
6if (ball != null)
7{
8}
9}
Процесс копирования и удаления лишнего

Чтобы не писать вручную код для подсчёта очков, необходимо обратиться к eventsystem unity. Добавьте на вторую строку команду, а после public class триггер:

1using UnityEngine;
2using UnityEngine.EventSystems;
3public class ScoringZone : MonoBehaviour
4public float bouncestrength;
5private void OnCollisionEnter2D(Collision2D collision)
6{ Ball ball = collision.gameObject.GetComponent<Ball>();
7if (ball != null)
8{
9}
10}
11}
После if (ball ≠ null) создайте две связанных функции, чтобы очки менялись из-за триггера — их мы видим на 10 и 11 строке. Также не забудьте ввести общий триггер, который отвечает за подсчёт очков — команда, отвечающая за это, на 4 строке:

1using UnityEngine;
2using UnityEngine.EventSystems;
3public class ScoringZone : MonoBehaviour
4{ public EventTrigger.TriggerEvent scoreTrigger;
5public float bouncestrength;
6private void OnCollisionEnter2D(Collision2D collision)
7{ Ball ball = collision.gameObject.GetComponent<Ball>();
8if (ball != null)
9{
10BaseEventData eventData = new BaseEventData(EventSystem.current);
11this.scoreTrigger.Invoke(eventData);
12}
13}
14}
В итоге мы сделали так, чтобы когда мяч сталкивался с левой или правой стеной (scoring zone), триггерился ивент, который возвращает его на стартовую позицию.

Сохраните изменения, перейдите в Unity и примените скрипт на левую и правую стены.

Применение GameManager к объектам невидимых стен

Чтобы точно не было проблем с сбрасыванием позиции мяча, добавьте debug в скрипте GameManager под строками об очках игрока и компьютерного соперника. Учтите, что это действие не обязательно.

Добавляем Debug.Log в скрипт GameManager

Сохраните и проверьте, всё ли работает.

Внизу видны сообщения от команды Debug. Тут все работает правильно

UI начинаем с создания текста: ПКМ → UI → Text.

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

Первым делом займемся текстом для очков игрока — отцентрируйте его по правой стороне.

Показываем наг

Итоговый размер шрифта — 85

Применяем кастомный шрифт

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

Чтобы заставить текст меняться, нужно добавить еще пару строчек кода в скрипт GameManager.

Первым делом внесите использование UI Unity, потом введите значение public для текста компьютера и игрока, а потом, после команд _playerscore и _computerscore, введите переменную.

Финальный код скрипта GameManager будет выглядеть так:

1using UnityEngine;
2using UnityEngine.UI;
3public class GameManager : MonoBehaviour
4{
5public Ball ball;
6public Text playerScoreText;
7public Text computerScoreText;
8private int _playerscore;
9private int _computerscore;
10public void PlayerScore()
11{
12_playerscore++;
13this.playerScoreText.text = _playerscore.ToString();
14Debug.Log(_playerscore);
15this.ball.ResetPosition();
16}
17public void ComputerScore()
18{
19_computerscore++;
20this.computerScoreText.text = _computerscore.ToString();
21Debug.Log(_computerscore);
22this.ball.ResetPosition();
23}
24}
На этом все

Это всё — проект можно сохранять и тестировать. Для сохранения откройте вкладку File → Build Settings, и переместите игру в папку.

Билд созданной нами игры можете скачать тут.

Комментарии: 0