Туториал 3: 2Д спейс шутер (новичок)

Вступление:

Я сейчас использую Юнити 5.5. Скриншоты были сделаны ещё на версии 5.3. Некоторые мелочи будут немножко отличаться, но не критично. Готовый проект для скачивания тоже был сделан на версии 5.5.
В этом туториале рассказывается как сделать небольшую космическую стрелялку в 2Д. Тут используется встроенная в Юнити 2Д физика. Для туториала понадобится несколько картинок. Можете скачать их тут. * Так же понадобятся звуковые файлы. Zip архив можно скачать тут.
*возможно некотрые браузеры просто откроют картинку вместо скачивания, сделайте правый клик на картинку и выберите сохранить. Или правый клик на линке и выбрать сохранить цель.

1. Шаг

Создайте новый Юнити 2D project. Импортируйте скачанную картинку spaceImage (можете просто перетащить её в Assets или правый клик в Assets - нажать Import New Asset - выбрать скачанную картинку и нажать импорт Import). Выделить spaceImage в Assets и изменить Sprite Mode на Multiple в Inspector. Нажмите Apply.
импортировать рессурсы

Нажмите теперь на Sprite Editor. Кликните на Slice. Проверьте что Type установлен на Automatic и нажмите кнопку Slice. Кликните на Apply и закройте редактор спрайтов.
редактор спрайта

2. Шаг

Сначала сделаем корабль для игрока. В Catch Game 2D мы использовали клавиатуру для движения. Теперь для движения будем использовать мышь. Наш корабль будет лететь к точке, где мы кликнули по экрану. Перетащите картинку корабля в сцену. Юнити автоматически создаст новый GameObject. Смените его имя на Player. Смените тэг на Player.
создать корабль игрока

Мы будем использовать встроенную 2Д физику. Добавьте к игроку Player: Box Collider 2D и Rigidbody 2D (Add Component - Physics 2D - ...). Поставьте Gravity Scale на 0 (нам не нужна гравитация) и Interpolate to Interpolate (для более плавного движения). Включите Freeze Rotation Z в Constraints (что-бы избежать вращения корабля).
компоненты корабля

3. Шаг

Теперь можем сделать скрипт движения для игрока. Создайте новый C# script (правый клик в Assets и выбрать create - C# script). Назовите PlayerMove. Небольшое поясение, что мы будем делать:

Векторное вычитание:

С векторным вычитанием мы можем получить направление и дистанцию от одного объекта до другого. Смотрите Unity Manual для подробностей. Получившийся при вычитании новый вектор можно использовать как направление от корабля к точке клика. Мы можем изменить velocity (направление и скорость движения) на этот вектор и корабль полетит в сторону клика.* Длина вектора равна расстоянию между кораблем и точкой клика. Длину вектора можно получить из функции Vector3.magnitude. В ней используется вычисление квадратного корня, это довольно процессоро-зависимо. Если длина нужна просто для сравнения величин, то пользуйтесь Vector3.sqrMagnitude и сравнивайте с величиной в квадрате. Это, конечно, при действительно больших вычислениях, так что не особо забивайте голову. Смотрите Unity Manual для инфо.
* Часто использование этого вектора напрямую это не лучший вариант. Корабль будет лететь быстро при удалённом клике и медленно при близком. Так как вам нужно только направление и своя скорость, то используйте normalized вектор. У такого вектора сохраняется направление, но длина будет равна 1. Получить такой вектор из вашего вектора можно через функцию Vector3.Normalize. Потом вы просто умножите это вектор на скорость, что-бы получить нужную постоянную скорость. Но для туториала нам подходит и просто результат векторного вычитания.
векторы в Юнити
Позицию нашего корабля можем получить из transform.position. Ещё нужна позиция клика в игровом мире. Позиция курсора выражается в разрешении монитора. Нам надо трансформировать её в позицию соответствующей в игровом мире. Это можно сделать через функцию: Camera.ScreenToWorldPoint.

Camera.ScreenToWorldPoint

Эта функция трнсформирует экранную (считается в пикселях) позицию в позицию игрового мира. Осторожно с Z-координатой, она считается от местоположения камеры (точнее от начала отсчёта видимости). Смотрите Unity Manual для деталей.
В нашей сцене есть по умолчанию Main Camera. C# строчка будет: clickPos = Camera.main.ScreenToWorldPoint (Input.mousePosition);. И мы сможем посчитать вектор направления clickPos - transform.position. И использовать этот вектор как направление движения для нашего корабля. Создайте новый C# script и назовите его PlayerMove. Скрипт получится такой:
using UnityEngine;
using System.Collections;

public class PlayerMove : MonoBehaviour {

//переменная для позиции клика
Vector3 clickPos;
//переменная для вектора движения
Vector3 move;
//переменная для скорости движения
public float speed = 1;
//переменная для ссылки на Rigidbody2D
Rigidbody2D rb;

//выполнится один раз при старте скрипта
void Start () {
//делаем ссылку на компонент Rigidbody2D
rb = GetComponent < Rigidbody2D > ();
//что-бы корабль остался на месте и не полетел к точке (0,0,0)
clickPos = transform.position;
}

//исполняется каждый новый кадр
void Update () {
//проверка, нажата-ли левая кнопка мыши
if (Input.GetMouseButton (0)) {
//трансформировать координаты курсора в координаты игрового мира
clickPos = Camera.main.ScreenToWorldPoint (Input.mousePosition);
}
//вычислить новый вектор движения
move = clickPos - transform.position;
}

//выполняется через определенные периоды (0.02 по умолчанию). Используйте для вычислений физики
void FixedUpdate () {
//измените вектор движения
//z останется 0, что-бы корабль не двигался по z-оси
rb.velocity = new Vector2 (move.x, move.y) * speed;
}
}
Добавьте этот скрипт к Player. Поставьте скорость на 1. Запустите сцену и проверьте движение.
добавить скрипт к кораблю

4. Шаг

Наш корабль будет стрелять лазером. Перетащите картинку лазера в сцену. Юнити создаст новый игровй объект. Назовите его Laser. Измените Order in Layers на -10. Лазер будет отрисовываться под кораблем (могли-бы поставить и -1, но может этот слой понадобится для других объектов). Сделайте его немножко меньше (scale 0.5 должен подойти). Добавьте Box Collider 2D к нему. Включите галочку Is Trigger (будем использовать позже для проверки касаний). Добавьте Rigidbody2D. Измените Gravity на 0 и Interpolate на Interpolate.
создать лазер

5. Шаг

Лазер будет лететь вдоль Y-оси. Нам нужен скрипт для этого. Создайте новый C# script. Назовите его LaserShot. Теперь мы будем двигать объект с помошью AddForce (приложения силы). Нам нужен линк на Rigidbody2D и потом мы применим силу по Y-оси. Нам лучше уничтожить лазер, после того как он вылетел за пределы экрана. Он не нужен нам за пределами экрана (так как движок будет тратить рессурсы на него). Мы можем использовать вызов OnBecameInvisible для запуска функции уничтожения. Скрипт получится такой:
using UnityEngine;
using System.Collections;

public class LaserShot : MonoBehaviour {

//переменная для линка на Rigidbody2D
Rigidbody2D rb;
//переменная величины силы
public float force;

//выполнится один раз
void Start () {
//делаем линк на Rigidbody2D
rb = GetComponent < Rigidbody2D > ();
//высчитываем вектор приложения силы
Vector3 directon = new Vector3 (0, force, 0);
//толкаем объект по Y-оси
rb.AddForce (directon, ForceMode2D.Impulse);
}
//выполнится, если объект перестанет рисоваться камерой
void OnBecameInvisible () {
//удаляем объект из сцены
Destroy (gameObject);
}
}

Добавьте этот скрипт к объекту Laser. Измените force на 5 (это величина силы импульса).
Мы будем спавнить много лазеров. Нам нужен prefab (образец) для этого. Перетащите Laser в Asstes. Юнити создаст новый prefab автоматически. Можете удалить Laser из Hierarchy, если вы сделали laser prefab.
создать префаб лазера

6. Step

Лазер может теперь двигаться. Нсли мы хотим стрелять, то можем просто спавнить Laser на позиции корабля. Мы можем использовать Instantiate для спавна. Будем использовать правую кнопку мыши для стрельбы. Наш скрипт будет проверять нажата-ли кнопка и спавнить лазеры в этом случае. Такая проверка будет производится каждый кадр, поэтому нужно сделать задержку между выстрелами. Для задержки можно использовать функцию с паузой при выполнении Coroutine. Смотрите Unity Manual. Создайте новый C# script м назовите его PlayerShoot. Скрипт получится такой:
using UnityEngine;
using System.Collections;

public class PlayerShoot : MonoBehaviour {

//переменная для ссылки на префаб лазера
public GameObject laser;
//задержка между выстрелами
public float delayTime;
//бинарная переменная для проверки возможности стрельбы
bool canShoot = true;

//выполняется каждый кадр
void Update () {
//проверка: нажата-ли правая кнопка и можно-ли стрелять
if (canShoot && Input.GetMouseButton(1)) {
//отключить возможность стрельбы для следующей проверки
canShoot = false;
//спавн лазера на позиции корабля
Instantiate (laser, transform.position, transform.rotation);
//запуск функции дающей разрешение на стрельбу
StartCoroutine (NoFire());
}
}

//корутина, такую функцию можно поставить на паузу
IEnumerator NoFire () {
//пауза этой функции, возврат через заданное время
yield return new WaitForSeconds (delayTime);
//влючить возможность стрельбы
canShoot = true;
}
}

Добавьте этот скрипт к кораблю. Поместите Laser префаб в поле Laser. Смените Delay Time на 0.5. Запустите сцену и проверьте стрельбу.
добавить лазер к кораблю

7. Шаг

Теперь можем сделать астероиды. Перетащите картинку астероида в сцену. Назовите созданный объект Asteroid. Смените Order in Layer в его Sprite Renderer на -5 (рисуется на слое ниже игрока, но выше лазера). Добавьте к нему Circle Collider 2D. Добавьте Rigidbody2D. Измените Gravity Scale на 0 и Interpolate на Interpolate. Добавьте новый тэг Enemy и смените тэг у Asteroid на Enemy.
создать астероид

8. Step

Asteroid нужен скрипт движения. Создайте новый C# script и назовите его AsteroidMove. Мы будем менять velocity по Y-оси. И будем уничтожать астероиды вылетевшие за пределы экрана. Так-же будем проверять попали-ли астероиды в игрока. Для проверки будем использовать OnCollisionEnter2D. Эта функция запустится если Collider2D коснулся другого Collider2D. Тогда мы проверим Tag коснувшегося объекта и уничтожим его, если это былPlayer. Если игрок уничтожен, то перезапустим сцену. Нам надо использовать SceneManager для управления сценами.
Создайте новый C# script и назовите его AsteroidMove.
using UnityEngine;
using System.Collections;
//библиотека для управления сценами
using UnityEngine.SceneManagement;

public class AsteroidMove : MonoBehaviour {

//переменная скорости движения
public float speed;
//переменная для ссылки на Rigidbody2D
Rigidbody2D rb;

//выполнится 1 раз при старте скрипта
void Start () {
//делаем ссылку на Rigidbody2D
rb = GetComponent < Rigidbody2D > ();
//декларируем новый вектор с инициализацией направления движения
Vector3 move = new Vector3 (0, -1, 0);
//меняем вектор движения
rb.velocity = move * speed;
}

//исполнится если объект перестал отображаться на экране
void OnBecameInvisible () {
//уничтожить объект
Destroy (gameObject);
}

//выполнится, если другой объект с коллайдером2Д коснулся этот объект
void OnCollisionEnter2D (Collision2D something) {
//проверка тэга коснувшегося объекта
if (something.gameObject.tag == "Player") {
//удалить объект из сцены
Destroy (gameObject);
//перезапустить сцену
SceneManager.LoadScene (SceneManager.GetActiveScene().name);
}
}
}

Добавьте этот скрипт к Asteroid. Измените Speed на 1. Создайте префаб из Asteroid (перетащите Asteroid из Hierarchy в Asset). Удалите Asteroid из Hierarchy.
создать префаб астероида

9. Шаг

Нам нужен спавнер для случайного спавна астероидов. Нажмите на GameObject - Create Empty. Empty GameObject будет создан в сцене. Назовите его AsteroidSpawner. Выьерите Icon для отображения в сцене (смотрите скриншот). Передвиньте его налево над Camera View. Поставьте Z-Position на 0 (если отличается от 0).
левая позиция спавна
Выберите AsteroidSpawner в Hierarchy. Сделайте копию (правый клик на него и нажмите duplicate). Смените имя дубликата на RightPosition. Сделайте RightPosition child (дочерний объект) от AsteroidSpawner (перетащите RightPosition на AsteroidSpawner в Hierarchy). Передвиньте RightPosition на правую сторону и измените Z-Position на 0 (если отличатеся от 0).
правая позиция спавна
Создайте новый C# script и назовите его ObjectSpawner. В этом скрипте мы будем периодично вызывать функцию спавна. Используем для этого InvokeRepeating. Скрипт получится такой:
using UnityEngine;
using System.Collections;

public class ObjectSpawner : MonoBehaviour {

//переменная для ссылки на позицию, которую будем использовать как правую границу спавна
public Transform RightPosition;
//задержка между спавном
public float spawnDelay;
//переменная для ссылки на объект для спавна
public GameObject Item;

//выполнится один раз
void Start () {
// эта функция запустит периодичное выполнение функции "Spawn"
InvokeRepeating ("Spawn", spawnDelay, spawnDelay);
}

//функция спавна
void Spawn () {
//случайная позиция между спавном и правой границей
Vector3 spawnPos = new Vector3 (Random.Range (transform.position.x, RightPosition.position.x), transform.position.y, 0);
//спавн объекта
Instantiate (Item, spawnPos, transform.rotation);
}
}

Выберите AsteroidSpawner в Hierarchy. Добавьте этот скрипт к нему. Перетащите объект Right Position в поле RightPosition (или добавьте через список выбора (точка справа от поля RightPosition)). Установите Spawn Delay на 5. Перетащите префаб Asteroid в поле Item. Запустите сцену и проверьте спавн астероидов.

10. Step

Наши лазеры пока пролетают сквозь астероиды. Нужен скрипт для проверки попаданий и нанесения повреждений. Мы можем добавить объектам жизненные очки. За жизнь будет отвечать переменная hp. Для урона мы можем сделать функцию, которая будет вычитать урон из жизни. Создайте новый C# script и назовите его HpController.
using UnityEngine;
using System.Collections;

public class HpController : MonoBehaviour {

//переменная для жизненных очков
public int hp;

//функция для уменьшения жизни при уроне (значение урона будем получать от других функций)
void MakeDamage (int damage) {
//уменьшение жизни на урон
hp = hp - damage;
//проверка: жизнь равна 0 или меньше 0
if (hp <= 0) {
//уничтожение объекта
Destroy (gameObject);
}
}
}

Выберите префаб Asteroid в Assets и добавьте скрипт HpController к нему. Измените HP на 3.
префаб астероида
Теперь откройте скрипт LaserShot. Добавим проверку коллизий. У Laser есть Collider2D как Trigger. Можем использовать: OnTriggerEnter2D для проверки соприкосновений. Если у соприкоснувшегося объекта будет тэг Enemy, то мы воспользуемся функцией SendMessage. Эта функция проверяет все компоненты в поиске функции с определённым именем и запускает их, если найдет. Смотрите Unity Manual.
using UnityEngine;
using System.Collections;

public class LaserShot : MonoBehaviour {

//переменная для ссылки на Rigidbody2D
Rigidbody2D rb;
//переменная для урона
public int damage;
//переменная величины силы
public float force;

//выполнится 1 раз
void Start () {
//делаем ссылку на Rigidbody2D
rb = GetComponent < Rigidbody2D > ();
//декларируем Vector3 с нужным направлением движения (Y-ось)
Vector3 directon = new Vector3 (0, force, 0);
//применяем силу на rigidbody2D для движения по Y-оси
rb.AddForce (directon, ForceMode2D.Impulse);
}
//выполняется, если объект пропал с экрана
void OnBecameInvisible () {
//уничтожить объект
Destroy (gameObject);
}
//выполняется если другой объект с коллайдером2Д попал в триггер
void OnTriggerEnter2D (Collider2D other) {
//проверить на тэг Enemy
if (other.gameObject.tag == "Enemy") {
//попытаться выполнить функцию MakeDamage на другом объекте, damage передаётся как параметр
other.gameObject.SendMessage ("MakeDamage", damage, SendMessageOptions.DontRequireReceiver);
//уничтожить объект лазер
Destroy (gameObject);
}
}
}

Выберите префаб Laser в Assets. Скрипт LaserShot имеет теперь новую переменную damage. Измените её на 1.
изменить урон лазера
Запустите сцену. Asteroids должны уничтожаться тремя попаданиями.

11. Шаг

Добавим теперь врага. Для ускорения процесса просто переделаем клон Asteroid. Перетащите префаб Asteroid в сцену из Assets. Выьерите его в Hierarchy и смените имя на Enemy. Измените HP переменную на 1. Измените картинку Asteroid на картинку врага. удалите Circle Collider 2D (правый клик на Circle Collider 2D и выбрать Remove Component). Добавьте новый Box Collider 2D. Сделайте префаб Enemy (перетащите Enemy из Hierarchy в Assets). Удалите Enemy из Hierarchy после того как сделали префаб.
создание врага

12. Step

Врагу надо скрипт для стрельбы. Враг будет проверять есть-ли игрок в сцене и стрелять в этом случае. Для поиска игровых объектов мы можем использовать GameObject.FindWithTag ("Tag");. При выстреле враг будет спавнить пулю, которая сама найдет местоположение игрока и полетит в эту точку. Для начала сделаем эту пулю. Перетащите картинку пули в сцену. Выберите созданный объект в Hierarchy. Смените имя на Bullet. Измените Order in Layer на -6. Добавьте Rigidbody2D. Измените Gravity на 0 и Interpolate на Interpolate. Добавьте Box Collider 2D и сделайте его триггером. Теперь сделаем скрипт для управления пулей. Создайте новый C# script и назовите его EnemyBullet.
using UnityEngine;
using System.Collections;

public class EnemyBullet : MonoBehaviour {

//переменная для ссылки на объект игрока
GameObject player;
//переменная для ссылки на Rigidbody2D
Rigidbody2D rb;
//переменная для величины силы
public float force;
//переменная для величины урона
public int damage;

//выполнится при старте скрипта
void Start () {
//сделаем ссылку на Rigidbody2D
rb = GetComponent < Rigidbody2D > ();
//ищем игрока в сцене и делаем ссылку на него
player = GameObject.FindWithTag ("Player");
//проверка: нашли-ли игрока в сцене
if (player != null) {
//считаем вектор направления к игроку
Vector3 dir = player.transform.position - transform.position;
//высчитываем угол между игроком и Х-осью
float angle = Mathf.Atan2 (dir.y, dir.x) * Mathf.Rad2Deg;
//поворачиваем пулю в сторону игрока
transform.Rotate (0, 0, angle);
//толкаем пулю вдоль её локальной Х-оси, куда смотрит картинка (может не совпадать с глобальной)
rb.AddRelativeForce (Vector2.right * force, ForceMode2D.Impulse);
//иначе, если игрок не был найден в сцене
} else
//уничтожаем пулю
Destroy (gameObject);
}

//выполнится, если объект с коллайдером 2Д попадёт в тригер
void OnTriggerEnter2D (Collider2D other) {
//проверка на тэг Player
if (other.gameObject.tag == "Player") {
//попытаться выполнить функцию "MakeDamage" с параметром "damage" на другом объекте
other.gameObject.SendMessage ("MakeDamage", damage, SendMessageOptions.DontRequireReceiver);
//уничтожить пулю
Destroy (gameObject);
}
}

//Выполняется если пуля вылетела за экран (out of screen)
void OnBecameInvisible () {
//уничтожить пулю
Destroy (gameObject);
}
}

Добавьте этот скрипт к Bullet. Измените Force на 3 и Damage на 1. Сделйте префаб для Bullet (перетащите Bullet из Hierarchy в Assets). Удалите Bullet из Hierarchy, после того как сделали префаб.
пуля для врага

12. Step

Теперь можем сделать скрипт стрельбы для Enemy. Enemy будет проверять есть-ли Player в сцене и периодично спавнить Bullet (котрая сама найдет Player и полетит в его сторону). Создайте новый C# script и назовите его EnemyShoot.
using UnityEngine;
using System.Collections;

public class EnemyShoot : MonoBehaviour {

//переменная для префаба пули
public GameObject bullet;
//задержка между выстрелами
public float fireDelay;
//переменная для ссылки на игрока
GameObject player;
//бинарная переменная для разрешения стрельбы
bool canShoot = true;

//выполнится один раз
void Start () {
//поиск объекта с тэгом "Player" и создание ссылки на него
player = GameObject.FindWithTag ("Player");
}

//выполняется каждый кадр
void Update () {
//проверка: есть-ли игрок в сцене и можно-ли стрелять
if (canShoot && player != null) {
//запрет стрельбы для следующей проверки
canShoot = false;
//спавн пули на позиции врага
Instantiate (bullet, transform.position, Quaternion.identity);
//запуск функции задержки стрельбы
StartCoroutine (firePause());
}
}

//старт корутины
IEnumerator firePause () {
//пауза исполнения на указанное время
yield return new WaitForSeconds (fireDelay);
//возможность стрельы при следующей проверке
canShoot = true;
}
}

Выберите префаб Enemy в Assets и добавьте скрипт EnemyShoot. Добавьте префаб Bullet в поле этого скрипта. Измените Fire Delay на 2.
добавит пулю к врагу

13. Шаг

Префаб Enemy теперь готов. Так-же как и астероиды мы можем спавнить их в сцене. Переделаем немножко наш ObjectSpawner скрипт (могли-бы конечно сделать и новый объект для спавна с этим скриптом). Добавим массив для GameObjects и будем случайным образом спавнить объекты из этого массива. Откройте ObjectSpawner C# script и измените его:
using UnityEngine;
using System.Collections;

public class ObjectSpawner : MonoBehaviour {

//переменная для ссылки на позицию, которую будем использовать как правую границу спавна
public Transform RightPosition;
//задержка между спавнами
public float spawnDelay;
//массив для объектов спавна
public GameObject [] Item;

//выполняется один раз
void Start () {
//запуск периодичного вызова функции для спавна
InvokeRepeating ("Spawn", spawnDelay, spawnDelay);
}

//функция спавна
void Spawn () {
//случайная позиция между AsteroidSpawner и RighPosition
Vector3 spawnPos = new Vector3 (Random.Range (transform.position.x, RightPosition.position.x), transform.position.y, 0);
//случайное int значение от 0 до максимальной длины массива (количество объектов в нём)
int i = Random.Range (0, Item.Length);
//поместить оьъект на случайную позицию
Instantiate (Item [i], spawnPos, transform.rotation);
}
}

Выберите AsteroidSpawner в Hierarchy. Нам надо заполнить массив. Установите Size от Item на 2 (пока у нас есть Asteroid и Enemy). Переташите префабы Asteroid и Enemy в поля массива. Запустите сцену для теста.
добавить врага в спавнер

14. Шаг

У Player ещё нет очков здоровья. Сделаем скрипт для этого. Ещё можем добавить простую UI для отображения здоровья на экране. Можем сделать полосу здоровья в Unity Interface.Нажмите на GameObject -> UI -> и выберите Image. Canvas и Image будут созданны. Переименуйте Image как HealthBar. Выберите Canvas и установите UI Scale Mode на Scale with Screen Size.
картинка интерфейса
Сделайте двойной клик на Canvas.Камера сфокусируется на белом прямоугольнике. Это показываются границы интерфейса на экране. Распологайте части интерфейса в этих границах. Сейчас у нас есть там белый квадрат (HealthBar (Image)). Включите Rectangle Tool (если не включен) и растяните HealthBar как полосу здоровья.
полоска жизни
Выберите HelathBar в Hierarchy. Смените Source Image на UISprite (нам просто нужна картинка для полосы). Можете сменить цвет. Поменяйте Image Typ на Filled. Fill Method на Horizontal. Fill Origin на Left (можете попробовать что-то другое FillMethod и Fill Origin, но Image Typ должен быть Filled). Теперь можете менять Fill Amount и увидите как меняется полоска.
настройка полоски жизни

15. Шаг

Теперь сделаем C# script для управления здоровьем у игрока. Создайте новый C# script и назовите его PlayerHP.
using UnityEngine;
using System.Collections;
//библиотека для доступа к UI
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class PlayerHP : MonoBehaviour {

//переменная для ссылки на полосу здоровья
public GameObject HealthBar;
//переменная для ссылки на компонент Image
Image img;
//переменная для текущего здоровья
public int hp;
//максимальное здоровье, используется для вычисления соотношения в процентах
float maxHp;

//выполняется один раз
void Start () {
//ссылка на UI компонент Image от объекта HealthBar
img = HealthBar.GetComponent < Image > ();
//инициализируем максимальное здоровье
maxHp = hp;
//изменяем процент заполнения полоски (пока заполнится на 100%)
img.fillAmount = hp / maxHp;
}

//вызывается другими скриптами при нанесении урона
void MakeDamage (int damage) {
//уменьшить здоровье
hp = hp - damage;
//проверка: не стало-ли здоровье меньше или равно 0
if (hp <= 0) {
//перезапустить сцену (так как игрок погиб)
SceneManager.LoadScene (SceneManager.GetActiveScene().name);
}
//изменить заполнение полоски в процентах
img.fillAmount = hp / maxHp;
}
}

Двойной клик на Main Camera в Hierarchy что-бы вернтуься на первоначальный вид сцены. Выберите Player. Добавьте скрипт PlayerHP к Player. Поместите HealthBar в поле Health Bar (раскройте Canvas если не видно), или нажмите на точку выбора рессурсов справа от Healt Bar поля и выберите HealthBar). Измените HP на 10.
добавление полоски жизни в скрипт
В целом игра готова, добавим пару мелочей.

16. Шаг

Выберите Main Camera. Кликните на Background в Inspector и смените цвет на чёрный.
цвет фона

17. Шаг

Добавим звезды для имитации движения. Unity партикли подходят для этого. Нажмите на GameObject и выберите Particle System. Выделите её в Hierarchy и назовите Stars. Поставьте Z-Position на 10. И смените X-Rotation на 90. Передвиньте систему наверх за камеру Camera View. Включите Prewarm. Настройте Start Lifetime (партикли должны пролетать через всю камеру и пропадать). Настройте Start Speed по желанию. Установите Start Size на 0.2 (как нравится).Нажмите на меню Shape и установите Shape на Box. Измените значение X на 15 (размер области спавна должен быть больше камеры). Нажмите на Renderer и измените Order in Layer на -100.
создание звезд

18. Шаг

Распакуйте скачанный SpaceSound архив (можно скачать тут ). Импортируйте 3 звуковых файла в Юнити (можете перетащить их в Assets или правый клик в Assets -> Import New Asset ... выберите файлы в вашей папке и нажмите Import). Теперь сделаем фоновую музыку. Выберите Main Camera. Добавьте Space Music к ней. Юнити создаст новый компонент Audio Sorce (или добавьте сами Audio Sorce к камере и потом смените аудиофайл в поле Audio Clip на SpaceMusic). Включите Loop.
фоновая музыка

18. Шаг

Теперь сделаем звуки стрельбы. Можем проигрывать звуки при спавне Bullet. Откройте EnemyBullet script. Для начала надо декларировать AudioClip. И потом сможем воспроизвести клип с помощью AudioSource.PlayClipAtPoint.
using UnityEngine;
using System.Collections;

public class EnemyBullet : MonoBehaviour {

//переменная для ссылки на объект игрока
GameObject player;
//переменная для ссылки на Rigidbody2D
Rigidbody2D rb;
//переменная для величины силы
public float force;
//переменная для величины урона
public int damage;
//переменная для звука стрельбы
public AudioClip BulletSound;

//выполнится при старте скрипта
void Start () {
//сделаем ссылку на Rigidbody2D
rb = GetComponent < Rigidbody2D > ();
//ищем игрока в сцене и делаем ссылку на него
player = GameObject.FindWithTag ("Player");
//проверка: нашли-ли игрока в сцене
if (player != null) {
//считаем вектор направления к игроку
Vector3 dir = player.transform.position - transform.position;
//высчитываем угол между игроком и Х-осью
float angle = Mathf.Atan2 (dir.y, dir.x) * Mathf.Rad2Deg;
//поворачиваем пулю в сторону игрока
transform.Rotate (0, 0, angle);
//толкаем пулю вдоль её локальной Х-оси, куда смотрит картинка (может не совпадать с глобальной)
rb.AddRelativeForce (Vector2.right * force, ForceMode2D.Impulse);
//проигрываем звук (создаётся новый объект, который воспроизведёт клип и уничтожится)
AudioSource.PlayClipAtPoint (BulletSound, transform.position);
//иначе, если игрок не был найден в сцене
} else
////уничтожаем пулю
Destroy (gameObject);
}

//выполнится, если объект с коллайдером 2Д попадёт в тригер
void OnTriggerEnter2D (Collider2D other) {
//проверка на тэг Player
if (other.gameObject.tag == "Player") {
//попытаться выполнить функцию "MakeDamage" с параметром "damage" на другом объекте
other.gameObject.SendMessage ("MakeDamage", damage, SendMessageOptions.DontRequireReceiver);
//уничтожить пулю
Destroy (gameObject);
}
}

//Выполняется если пуля вылетела за экран (out of screen)
void OnBecameInvisible () {
//уничтожить пулю
Destroy (gameObject);
}
}

Выберите префаб Bullet в Assets. В скрипте EnemyBullet теперь есть новое поле для звукового файла Bullet Sound. Поместите LaserSound в него.
звук выстрела врага
Сделаем теперь и звук для лазера. Откройте "LaserShot" скрипт.
using UnityEngine;
using System.Collections;

public class LaserShot : MonoBehaviour {

//переменная для ссылки на Rigidbody2D
Rigidbody2D rb;
//переменная для урона
public int damage;
//переменная величины силы
public float force;
//переменная для звукового клипа
public AudioClip BulletSound;

//выполнится 1 раз
void Start () {
//делаем ссылку на Rigidbody2D
rb = GetComponent < Rigidbody2D > ();
//декларируем Vector3 с нужным направлением движения (Y-ось)
Vector3 directon = new Vector3 (0, force, 0);
//применяем силу на rigidbody2D для движения по Y-оси
rb.AddForce (directon, ForceMode2D.Impulse);
//проигрываем звук (создаётся новый объект, который воспроизведёт клип и уничтожится)
AudioSource.PlayClipAtPoint (BulletSound, transform.position);
}
//выполняется, если объект пропал с экрана
void OnBecameInvisible () {
//уничтожить объект
Destroy (gameObject);
}
//выполняется если другой объект с коллайдером2Д попал в триггер
void OnTriggerEnter2D (Collider2D other) {
//проверить на тэг Enemy
if (other.gameObject.tag == "Enemy") {
//попытаться выполнить функцию MakeDamage на другом объекте, damage передаётся как параметр
other.gameObject.SendMessage ("MakeDamage", damage, SendMessageOptions.DontRequireReceiver);
//уничтожить объект лазер
Destroy (gameObject);
}
}
}

Выделите префаб Laser. Поместите LaserSound в поле Bullet Sound.
звук выстрела лазера

19. Шаг

Теперь добавим звуки взрывов. Откройте скрипт "HpController".
using UnityEngine;
using System.Collections;

public class HpController : MonoBehaviour {

//переменная для жизненных очков
public int hp;
//переменная для звкового клипа
public AudioClip ExplosionsSound;

//функция для уменьшения жизни при уроне (значение урона будем получать от других функций)
void MakeDamage (int damage) {
//уменьшение жизни на урон
hp = hp - damage;
//проверка: жизнь равна 0 или меньше 0
if (hp <= 0) {
//проигрываем звук (создаётся новый объект, который воспроизведёт клип и уничтожится)
AudioSource.PlayClipAtPoint (ExplosionsSound, transform.position);
//уничтожение объекта
Destroy (gameObject);
}
}
}

Выделите префаб Asteroid и поместите ExplosionsSound в новое поле Explosions Sound. Выделите префаб Enemy и поместите ExplosionsSound в новое поле Explosions Sound.
звук взрыва

20. Шаг

Теперь добавим сам взрыв, если Enemy (или Asteroid) были уничтожены. Particle Effects хорошо подзолят для этого. Нажмите на GameObject -> select Particle System. Переименуйте созданную Particle System на Explosion. Поставье все занчения ротаций Rotation на 0. Duration на 1. Отключите Looping. Start Lifetime на 1. Start Speed на 2. Start Size на 2. Max Particles на 30. Нажмите на Emission и кликните на +. Поставьте Min и Max Burst в Time 0.0 на 30. Кликните на Shape и измените Shape на Circle с Radius 0.1.
создание взрыва
Включите Color over Time.Нажмите на него. Нажмите на поле Color. Откроется временная линия цвета. Сверху is alfa прозрачность, снизу это цвет. Измените последний пункт прозрачности на 0. Измените первый и последний пункты цветов по желанию. Нажмите Renderer и измените Order in Layer на 10.

Это просто пример взрыва, тут границы это только ваша фантазия. Сейчас Particle System постоянно улучшается. На скринах ещё старая система.
цвет взрыва
Теперь нужен скрипт для уничтожения проигравших Explosion из сцены. Моздайте новый C# script и назовите его TimeDestroyer.
using UnityEngine;
using System.Collections;

public class TimeDestroyer : MonoBehaviour {

//переменная для длительности нахождения в сцене
public float timeToDestroy;

//выполнится один раз при старте скрипта
void Start () {
//унитожить объект через заданное время
Destroy (gameObject, timeToDestroy);
}
}

Выберите Explosion и добавьте TimeDestroyer скрипт. Поставьте Time to Destroy на 2. Сделайте префаб Explosion (перетащите Explosion из Hierarchy в Assets). Удалите Explosion из Hierarchy.
префаб взрыва

21. Step

Теперь у нас есть префаб Explosion. Будем спавнить его на месте уничтоженного Enemy или Asteroid. Откройте скрипт HpController .
using UnityEngine;
using System.Collections;

public class HpController : MonoBehaviour {

//переменная для жизненных очков
public int hp;
//переменная для звукового клипа
public AudioClip ExplosionsSound;
//переменная для взрыва
public GameObject Explosion;

//функция для уменьшения жизни при уроне (значение урона будем получать от других функций)
void MakeDamage (int damage) {
//уменьшение жизни на урон
hp = hp - damage;
//проверка: жизнь равна 0 или меньше 0
if (hp <= 0) {
//проигрываем звук (создаётся новый объект, который воспроизведёт клип и уничтожится)
AudioSource.PlayClipAtPoint (ExplosionsSound, transform.position);
//спавним взрыв на месте этого объекта
Instantiate (Explosion, transform.position, Quaternion.identity);
//уничтожение объекта
Destroy (gameObject);
}
}
}

Выберите префаб Asteroid. В скрипте HpController появилось новое поле Explosion. Поместите префаб Explosion в это поле. Сделайте тоже самое с префабом Enemy. Выберите префаб Enemy и добавьте префаб Explosion в поле Explosion.
добавление префаба взрыва

22. Шаг

Теперь добавим пламя двигателя к Player. Нажмите на GameObject и выберите Particle System. Переименуйте как EngineFire. Передвиньте на корабль. И сделайте дочерним объектом от Player (просто претащите EngineFire на Player в Hierarchy). Поставьте Z_Position на 0. X_Rotation На 90. Duration на 1. Включите Prewarm. Start Lifetime на 0.5. Start Size на 0.5. Simulation Space на World. Max Particles на 50. Нажмите на Emission и поставьте Rate на 50. Измените Shape на Box с нулевыми размерами.
левое пламя
Включите Color over Lifetime. Нажмите на поле цвета и настройте по желанию. Включите Size over Lifetime. Настройте size curve от 1 до 0. Нажмите на Renderer. Поставьте Order in Layer на -1. Один EngineFire теперь готов. Сделайте правый клик на EngineFire и нажмите на Duplicate. Передвиньте созданный дубликат огня на правую сторону.
правое пламя

23. Шаг

И думаю, пока хватит для простой игры. Можете собрать игрушку для какой-то платформы. Нажмите File -> Build Settings. Выберите нужную платформу Platform (вин наверно для начала) и нажмите Build.

юнити пакет проекта скачать готовый проект этого туториала.

К сожалению на некоторых версиях Юнити бывает ошибка при импорте проекта. Tag у GameObject может сброситься на undefinied. Надо добавить Enemy тэг. И сменить тэги у префабов Asteroid и Enemy на Enemy.