Как указать траекторию движения врага в Unity 2d?
Тостеры, не ругайте строго, Unity открыл только вчера (раньше вообще писал на питоне). В общем нужен скрипт, чтобы враг двигался от одного Transform’а к другому и в их координатах останавливался на некоторое время (именно 2D). И не пишите пожалуйста «учись гуглить» или что-то ещё (гугл выдаёт сложные 3D скрипты, например с погоней за играком, а мне это не нужно (ну или, что вероятно, я не умею гуглить) ), в своё оправдание могу сказать, что с Unity знаком второй день. Большое спасибо.
- Вопрос задан более трёх лет назад
- 4177 просмотров
1 комментарий
Простой 1 комментарий
Разные типы врагов Unity3D
Как правильно делаются разные типы врагов в Unity3D? У врагов должна быть разный массив спрайтов, разная анимация, и разное поведение.
Я сделал скрипт:
using UnityEngine; using System.Collections; [ExecuteInEditMode] [RequireComponent(typeof(SpriteRenderer))] [RequireComponent(typeof(Animator))] [RequireComponent(typeof(BoxCollider2D))] [RequireComponent(typeof(Rigidbody2D))] [RequireComponent(typeof(ChangerLayer))] public class EmenyScript : MonoBehaviour < private SpriteRenderer spriteRenderer; private Animator animator; private BoxCollider2D collider; private Rigidbody2D rigidBody; private ChangerLayer changerLayer; void Awake() < spriteRenderer = GetComponent(); animator = GetComponent(); collider = GetComponent(); rigidBody = GetComponent(); changerLayer = GetComponent(); rigidBody.isKinematic = true; > >
Этот скрипт натягиваю на каждый GameObject врага, потом ручками создаю анимацию, добавляю AnimatorController в Animator, создаю конкретный скрипт конкретного врага и натягиваю на этот GameObject. Потом создаю префаб.
Так это делается? Или есть более лучший способ создание разных врагов?
#1
11:22, 10 авг 2017
bretbas
> Так это делается? Или есть более лучший способ создание разных врагов?
> создаю конкретный скрипт конкретного врага и натягиваю на этот GameObject
если с наследованием от базового класса поведения, то да
или хотябы с общим интерфейсом, чтобы все враги могли впихиваться в одну коллекцию для универсального оперирования ими
#2
14:23, 10 авг 2017
Chupakaber, Код, который я написал выше, уместно использовать для определения общих компонентов всех врагов?
Я не сильно Вас понял. Вы написали:
или хотябы с общим интерфейсом, чтобы все враги могли впихиваться в одну коллекцию для универсального оперирования ими
Но зачем делать общий интерфейс, и где его делать, если:
1. Все враги — это GameObject, и я могу использовать их в одной коллекции соответственно
2. От GameObject наследовать нельзя, поэтому как я сделаю общий интерфейс, как вы написали?
Опять же спрашиваю, если я просто добавлю на каждого врага тот скрипт, который описал выше, и потом уже буду каждого настраивать ручками, добавлять конкретизированные скрипты для того или иного врага и тд, будет ли этот подход правильный? Да, и потом все это в префабы запихну
#3
14:48, 10 авг 2017
bretbas
> 1. Все враги — это GameObject, и я могу использовать их в одной коллекции
> соответственно
> 2. От GameObject наследовать нельзя, поэтому как я сделаю общий интерфейс, как
> вы написали?
Я хз, как в юнити, но суть в чём. Есть интерфейс «враг» со всеми ресурсами, есть наследники, которые эти ресурсы устанавливают и логику описывают.
#4
15:10, 10 авг 2017
bretbas
> Но зачем делать общий интерфейс, и где его делать, если:
> 2. От GameObject наследовать нельзя, поэтому как я сделаю общий интерфейс, как
> вы написали?
наследоваться можно от «MonoBehaviour» , это любой компонент GameObject’а. и в нем есть ссылка на сам GameObject
> 1. Все враги — это GameObject, и я могу использовать их в одной коллекции
> соответственно
если будешь проходиться по коллекции и вызывать поведенческие методы циклические, то придется брать компонент поведения (скрипт/класс) при помощи GetComponent(), не самый эффективный способ
если есть скрипт «EmenyScript» (что бы это ни значило, полагаю буквы местами перепутаны просто), то логичнее именно эти классы в коллекции хранить, и внутри них уже ссылки на GameObject (по умолчанию есть) и графический компонент, который универсален (полагаю что так) для всех врагов
допустим есть враги — 1. лучник, 2. мечник, 3. лекарь, у них есть в поведении одинаковые детали — если нужно дойти до определенной точки маршрута — они пойдут все одинаково
и есть отличающиеся — когда атакует лучник — он начнет это делать с расстояния, и будет стараться держать дистанцию, а мечник будет бегать за врагом, лекарь же сперва проверит. ненадо ли кого полечить. а если нет друзей тогда уже будет отбиваться сам
таким образом часть поведения — общая, другая часть — уникальная
общую часть можно наследовать от базового класса поведения (EnemyBehavior), а уникальную уже переопределять (override для virtual методов) в результирующих классов (ArcherBehavior : EnemyBehavior, SwordsmanBehavior : EnemyBehavior)
либо не наследовать, а использовать интерфейсы, т.е. не будет базового класса как такового и даже абстрактного класса не будет, будут только «правила», определяющие что у этих классов есть общее, а именно — должны быть методы: 1. движения по маршруту, 2. атаки, 3. убегания, 4. всякие реакции на события и т.д.
и коллекция будет уже не по классу а по интерфейсу, это немного не так удобно, но тоже допустимо, и в некоторых случаях более гибко
есть ещё вариант — на все возможные события в игре подписываться каждым уникальным классом, тогда никакого наследования и интерфейсов, никаких коллекций и вообще никаких узких мест, всё супер гибко, но супер-запутано будет думаю, в общем дело вкуса, подпиской на события тоже надо уметь пользоваться
ещё нюанс — вязать отображение (графику — спрайты, модели, материалы) и поведение в один класс не стоит, потеря гибкости и лапшекод нечитаемый. так что с этим всё верно — игровой объект это один класс (компонент Monobehaviour), а поведение — другой
Dimich
> Я хз, как в юнити, но суть в чём. Есть интерфейс «враг» со всеми ресурсами,
> есть наследники, которые эти ресурсы устанавливают и логику описывают.
в юнити всё довольно гибко, можно сделать по человечески, как ты описываешь
главное не зацикливаться на том, что есть уже готовый функционал и не думать что нужно пользоваться только им. его конечно нужно расширять и игнорировать то, что тебе мешает сделать нормально (:
#5
16:22, 10 авг 2017
Chupakaber, Все что ты написал, я понимаю:) Мне просто не привычно наследоваться от компонент, а не от GameObject. В своем движке, когда я писал его, я сделал так, чтобы можно было наследоваться от GameObject и определить в нем заранее нужные для этого GameObject’а компоненты. Плюс я определял нужные данные в этом же наследнике, чтобы не вызывать долгие методы, аля GetComponent<> и тд. Выглядело это примерно так:
GameObject -> Vehicle -> Tank -> Tank1, Tank2, Tank3
Вообщем теперь нужно немного по другому думать. Допустим у меня есть определенное движение у каждого врага, уникальное у каждого — кто-то прыгает, кто-то ходит по зигзагу, кто-то просто идет прямо и тд. Но все они в любом случае перемещаются по одной оси.
Следовательно общий скрипт перемещения по оси будет один. Назовем его MovingEnemyScript.
Также у каждого врага должны быть по любому следующие стандартные компоненты: SpriteRenderer, Animator, BoxCollider2D, RigidBody2D ну и еще может быть какие-то.
Я должен определить базовый класс EnemyController, который будет добавлять(если нет) все компоненты, которые я описал выше: SpriteRenderer, Animator, BoxCollider2D, RigidBody2D, MovingEnemyScript.
Получаем такой код соответственно:
using UnityEngine; using System.Collections; [ExecuteInEditMode] [RequireComponent(typeof(SpriteRenderer))] [RequireComponent(typeof(Animator))] [RequireComponent(typeof(BoxCollider2D))] [RequireComponent(typeof(Rigidbody2D))] [RequireComponent(typeof(ChangerLayer))] [RequireComponent(typeof(MovingEnemyScript))] public class EmenyScript : MonoBehaviour < private SpriteRenderer spriteRenderer; private Animator animator; private BoxCollider2D collider; private Rigidbody2D rigidBody; private ChangerLayer changerLayer; private MovingEnemyScript moveEnemy; void Awake() < spriteRenderer = GetComponent(); animator = GetComponent(); collider = GetComponent(); rigidBody = GetComponent(); changerLayer = GetComponent(); moveEnemy = GetComponent(); rigidBody.isKinematic = true; > >
Затем наследуем от него, и делаем у каждого врага поведения движения(прыжок, ход по зигзагу и тд).
Можно и по другому сделать.
Можно выделить просто базовый класс хотьбы:
using UnityEngine; using System.Collections; public class EmenyMoveScript : MonoBehaviour
И от него уже наследовать движения (прыжок, ход по зигзагу и тд).
А вот этот скрипт уже отдельно на каждый GameObject врага пихать:
using UnityEngine; using System.Collections; [ExecuteInEditMode] [RequireComponent(typeof(SpriteRenderer))] [RequireComponent(typeof(Animator))] [RequireComponent(typeof(BoxCollider2D))] [RequireComponent(typeof(Rigidbody2D))] [RequireComponent(typeof(ChangerLayer))] [RequireComponent(typeof(MovingEnemyScript))] public class EmenyScript : MonoBehaviour < private SpriteRenderer spriteRenderer; private Animator animator; private BoxCollider2D collider; private Rigidbody2D rigidBody; private ChangerLayer changerLayer; private MovingEnemyScript moveEnemy; void Awake() < spriteRenderer = GetComponent(); animator = GetComponent(); collider = GetComponent(); rigidBody = GetComponent(); changerLayer = GetComponent(); moveEnemy = GetComponent(); rigidBody.isKinematic = true; > >
#6
17:09, 10 авг 2017
moveEnemy = GetComponent();
если ты префаб собираешь всё равно — сделай свойство публичным и ручками накидай
тогда ты сможешь от MovingEnemyScript отнаследоваться в EmenyMoveScript, Emeny2MoveScript и т.д. и этот компонент ручками перетянутый в свойство moveEnemy подцепится по базовому классу MovingEnemyScript
в юнити управление кодом не всегда лучше «визуального программирования» (: хоть там и паблик свойства будут лишние, но тебе я думаю от них хуже не будет, тем более гибче сможешь в ним обращаться из соседних компонентов через основной, если обратные ссылки проставишь (а они думаю понадобятся если не усеешь всё событийно-ориентированным подходом)
UPD: короче, возми просто и отбрось названия классов юнитевские, и воспринимай MonoBehaviour класс как GameObject , а GameObject просто как посредник, просто компонент основного твоего класса «врага»
т.е. представь зависимости GameObject и класса отнаследованного от MonoBehaviour в зеркальном виде, и всё встанет на свои места
#7
17:40, 10 авг 2017
в юнити управление кодом не всегда лучше «визуального программирования»
Вот, в точку! Я из-за этого и путаюсь. На самом деле, я все это дело вижу так:
1. Делаем скрипт, который добавляет автоматически нужные компоненты для врага( чтобы я ручками каждый раз не собирал его )
тогда ты сможешь от MovingEnemyScript отнаследоваться в EmenyMoveScript, Emeny2MoveScript и т.д. и этот компонент ручками перетянутый в свойство moveEnemy подцепится по базовому классу MovingEnemyScript
2. Зачем тогда вообще наследование?:) Просто делаем разные скрипты для разных поведений врагов. Создаем разных врагов из пункта 1, и перетягиваем на каждый конкретный ему скрипт. Все. Зачем наследование и тд и тп?
3. С графикой аналогично. Так как мы добавили в пункте 1 компоненты SpriteRenderer и Animator, то просто опять для каждого врага настраиваем их определенным образом.
4. Запихиваем все это в префабы.
5. Пишем генератор для создания случайно выбранных врагов из определенной точки.
6. Запускаем и радуемся
#8
19:35, 10 авг 2017
bretbas
> 1. Делаем скрипт, который добавляет автоматически нужные компоненты для врага(
> чтобы я ручками каждый раз не собирал его )
ручками компоненты собираются один раз для префаба, а дальше можешь его клонировать и вносить тока мелкие изменения по параметрам например
bretbas
> 2. Зачем тогда вообще наследование?:) Просто делаем разные скрипты для разных
> поведений врагов. Создаем разных врагов из пункта 1, и перетягиваем на каждый
> конкретный ему скрипт. Все. Зачем наследование и тд и тп?
чтобы в том скрипте, в котором у тебя будет коллекция всех игровых объектов, ты не делал для каждого GetComponent да ещё как-то по тегу или названию выбирая какой класс тебе нужен
наследуешься от базового, в котором есть полный набор виртуальных методов, и обращаешься к каждому объекту в коллекции как к базовому классу
а поведение будет уже у них разное, как в переопределенных наследных классах
bretbas
> 3. С графикой аналогично. Так как мы добавили в пункте 1 компоненты
> SpriteRenderer и Animator, то просто опять для каждого врага настраиваем их
> определенным образом.
графику конечно придется визуально мышкой настраивать, кодом коротко и удобно не будет, кодом только для особых нестандартных случаев стоит графикой морочиться
и дальше да, генератор по хорошему должен быть из одной строчки:
public LinkedList objects = new LinkedList(); public CreateEnemy(string prefabName, Vector3 position, Quaternion rotation) < objects.AddLast(Instantiate(Resources.Load("Prefabs/Enemies/" + prefabName), position, rotation).GetComponent()); >
префабы должны при этом лежать в папке Assets/Resources/Prefabs/Enemies/
prefabName — название префаба
position — точка размещения нового врага в мире
rotation — понятно думаю, его вращение, куда он смотрит
objects — коллекция всех врагов с компонентом в наличии — базовым классом EnemyScript, или любым другим от него отнаследованным
из коллекции ты будешь сразу получать класс EnemyScript, а не GameObject, с которым лишние GetComponent нужны
всю настройку самих себя эти твои враги должны сделать сами в своем методе Start()
#9
19:53, 10 авг 2017
чтобы в том скрипте, в котором у тебя будет коллекция всех игровых объектов, ты не делал для каждого GetComponent да ещё как-то по тегу или названию выбирая какой класс тебе нужен
В каком скрипте у меня должна быть коллекция игровых объектов? Я этого не говорил. Я говорил, что просто хочу реализовать разное поведение у врагов и все. Но чтобы они полиморфно где-то лежали. я этого не сказал.
У каждого уникального скрипта поведения врага, есть Update(), так пусть он и выполняется для этого врага. Но в кучу собирать всех врагов, пока что мне не нужно:)
и дальше да, генератор по хорошему должен быть из одной строчки
Я вот такой написал:
void Update () < accumulator += Time.deltaTime; if( accumulator >= interval ) < BoxCollider2D componentCollider = areaEnemyAppear.GetComponent(); float halfHeightArea = componentCollider.size.y / 2; float halfWidthArea = componentCollider.size.x / 2; Vector3 positionArea = areaEnemyAppear.transform.position; foreach( var enemyObject in enemyObjects ) < Vector3 position = new Vector3( Random.Range( positionArea.x - halfWidthArea, positionArea.x + halfWidthArea ), Random.Range( positionArea.y - halfHeightArea, positionArea.y + halfHeightArea ) ); Instantiate( enemyObject, position, Quaternion.identity ); >accumulator = 0; > >
Где enemyObjects — массив врагов, которые передаются через инспектор. Я думаю так удобно, так как я могу быстренько убрать для генерации того или иного врага.
areaEnemyAppear — GameObject, у которого есть BoxCollider2D. Он служит площадкой, внутри которой будут генерироваться враги.
interval — генератор служит как бы таймером для генерации, поэтому это время, когда таймер должен сработать. Задается также через инспектор. Можно в принципе разделить таймер и генератор на два скрипта.
#10
20:46, 10 авг 2017
bretbas
> Но чтобы они полиморфно где-то лежали.
ну пусть они полиморфно нигде не лежат
но полиморфно взаимодействовать они же будут? например какие-то базовые параметры у них общие, элементы поведения, рассчет атаки/ущерба там
это же нужно? или будет отдельный компонент без наследования эти общие для всех штуки содержащий?
#11
21:13, 10 авг 2017
ну пусть они полиморфно нигде не лежат
но полиморфно взаимодействовать они же будут? например какие-то базовые параметры у них общие, элементы поведения, рассчет атаки/ущерба там
это же нужно? или будет отдельный компонент без наследования эти общие для всех штуки содержащий?
Ладно, это уже пошло угадывание:) Враги умирают с одного выстрела, сами они атаки не имеют. Вообщем, если разобраться, а я уже в принципе разобрался. у них общее только набор компонентов, больше ничего в них общего то и нет.
Как сделать движение условного врага к игроку в юнити (RigidBody2d)
Я много где искал ответ и находил то что мне не совсем нужно. У меня есть игра видом Сверху, в ней расположен игрок и враг, враг должен преследовать игрока. Внимание, не через Transform а через RigidBody2D , т.е как видно враг должен преследовать игрока и просто долбиться в него. Мне не нужно ничего лишнего. Я пытался написать ИИ для врага используя уже написанный заранее скрипт передвижения игрока но таки не разобрался с направлением. Вот код движения игрока по которому я хотел сделать ИИ врагу. Цель: Сделать движение врага к игроку без использования Transform .
public class PlayerMove : MonoBehaviour < private Vector2 MoveObject; private Rigidbody2D PhysicalMove; private int PlayerSpeed = 10; void Start() < PhysicalMove = GetComponent(); > void Update() < Vector2 InputData = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")); MoveObject = InputData.normalized * PlayerSpeed; >private void FixedUpdate() < PhysicalMove.MovePosition(PhysicalMove.position + MoveObject * Time.fixedDeltaTime); >>
Отслеживать
задан 9 апр 2022 в 13:45
466 4 4 серебряных знака 16 16 бронзовых знаков
Туториал 3: 2Д спейс шутер (новичок)
В этом туториале рассказывается как сделать небольшую космическую стрелялку в 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 //перезапустить сцену (так как игрок погиб)
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.
скачать готовый проект этого туториала.