Unity 2d скрипт движения. Пишем арканоид на Unity. Механика мяча и платформы

Итак. Всем привет. И сегодня я расскажу, как сделать простое движение персонажа. Сейчас только от третьего лица... Приступим...
Начнём, пожалуй, с создания персоажа. У меня это будет куб. Кто не знает, как создавать кубы или круги, поясняю - "GameObject" => "CreateOther" => "Cube". Создаём таким же образом камеру и привязываем к кубу (то бишь просто в иерархии перетаскиваем камеру на куб).
Так... Теперь создадим поверхность, по которой персонаж будет ходить. Пусть это будет просто "Plane". Ах, да... В конце урока будет ссылка с исходником по туториалу для тех, кто не понял.
Итак. Теперь создадим скрипт "Move". Добавим переменную игрока и переменную скорости.

Public GameObject player;
public int speed = 5;


Теперь укажем в методе старта, что это объект, на котором висит скрипт.

Void Start () {
player = (GameObject)this.gameObject;
}


Теперь сделаем само передвижение игрока вперёд при нажатии на "W" или стрелку вверх. Это делаем в методе void Update()! Для этого мы будем прибавлять позицию. Например вперёд.

If (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
}


Мы прибавили позицию вперёд (forward) и умножили на скорость, а точнее её переменную. И обязательно надо умножить на кадры в секунду (deltaTime).
Таким же образом сделаем движение назад. Только будем отнимать позицию.

If (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
{
player.transform.position -= player.transform.forward * speed * Time.deltaTime;
}


Таким же образом можем сделать и вправо и влево (right, left), но я сделаю просто поворот игрока, при нажатии на "A" или "D".
Я буду использовать "Rotate()". Чтобы поворачивать по оси "Y", я буду использовать "up" и "down". Кстати, для этого ещё надо объявить переменную "public int speedRotation = 3". И пишем в условиях.

If (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
player.transform.Rotate(Vector3.down * speedRotation);
}
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
player.transform.Rotate(Vector3.up * speedRotation);
}


Ну... Сейчас пришло время анимировать. Я записываю анимацию в самой юнити. Это можно открыть в "Window" => "Animation". В этом окне мы можем анимировать куб. Итак... Пропустим момент создания анимации. Давайте теперь создадим переменную анимации.

Public AnimationClip anima;


Теперь в старте добавим клип.

Animation.AddClip(anima, "animCube");


Теперь мы будем его воспроизводить через "CrossFade". Воспроизводить буду в условиях ходьбы вперёд и назад. Чтобы воспроизвести, нужно написать.



Итак... У нас получился хороший код. Сейчас мы сделаем прыжок. Всё так же просто. Опять мы будем прибавлять позицию. Только вверх (up).
И так же с новой переменной анимации "public AnimationClip anima2;"? так же добавим и переменной "public int jumpSpeed = 50;". И мы получаем условие.

If (Input.GetKeyDown(KeyCode.Space))
{
player.transform.position += player.transform.up * jumpSpeed * Time.deltaTime;
}


Всё... Наш код готов.

using UnityEngine;
using System.Collections;
public class Move: MonoBehaviour {
public GameObject player;
public int speedRotation = 3;
public int speed = 5;
public AnimationClip anima;
public int jumpSpeed = 50;

Void Start () {
player = (GameObject)this.gameObject;
animation.AddClip(anima, "animCube");
}
void Update(){
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
player.transform.position += player.transform.forward * speed * Time.deltaTime;
animation.CrossFade("animCube");
}
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
{
player.transform.position -= player.transform.forward * speed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
player.transform.Rotate(Vector3.down * speedRotation);
}
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
player.transform.Rotate(Vector3.up * speedRotation);
}
if (Input.GetKeyDown(KeyCode.Space))
{
player.transform.position += player.transform.up * jumpSpeed * Time.deltaTime;
}

Итак, мы продолжаем цикл статей о написании простой игры на Unity - классического арканоида. Использовать будем только 2D инструменты, предоставляемые нам движком. В каждой из статей мы затронем один из аспектов написания игры, а в этой приведем в движение мячик и платформу, находящуюся под управлением игрока.

Вот список всех статей:

  1. Механика мяча и платформы.

Где мы остановились?

В предыдущем уроке мы настроили проект, перенесли в него ресурсы и создали первую простенькую сцену. Если вы не прочитали , мы настоятельно рекомендуем вам исправить этот недочет.

Превью результата

Движение платформы

Сама платформа у нас уже есть - мы ее создали еще в прошлом уроке. Осталось научить ее двигаться, причем исключительно влево или вправо, т.е. по оси X. Для этого нам потребуется написать сценарий (Script ).

Сценарии представляют собой фрагменты программного кода, которые ответственны за какую-то конкретную задачу. Unity может работать со скриптами, написанными на трех языках программирования: Boo, JavaScript и C#. Мы будем использовать последний, но вы можете попробовать свои силы и с другими языками.

Итак, для создания скрипта перейдем на вкладку Project , найдем там одноименную папку Scripts и кликнем на нее правой кнопкой мыши. Выберем Create -> C# Script . Появится новый файл с названием NewBehaviourScript . Переименуйте его в PlayerScript для удобства. На вкладке Inspector вы можете видеть содержимое скрипта.

Двойным кликом откройте скрипт. Запуститься среда разработки MonoDevelop, которую вы впоследствии можете изменить на любой удобный для вас редактор. Вот то, что вы увидите:

Using UnityEngine; using System.Collections; public class NewBehaviourScript: MonoBehaviour { // используйте этот метод для инициализации void Start () { } // Update вызывается при отрисовке каждого кадра игры void Update () { } }

Все сценарии на Unity имеют по умолчанию два метода:

  • Start() : используется для инициализации переменных или параметров, необходимых нам в коде.
  • Update() : вызывается каждый кадр игры, необходим для обновления состояния игры.

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

Таким образом, необходимо создать две переменные для сохранения этой информации:

public float playerVelocity;
private Vector3 playerPosition;

Обратите внимание, что одна переменная объявлена публично, а вторая - приватно. Для чего это делается? Дело в том, что Unity позволяет редактировать значения публичных переменных не переходя в редактор MonoDevelop, без необходимости изменения кода. Эта возможность очень полезна в тех случаях. когда необходимо «на лету» корректировать какое-то значение. Скорость платформы - одно из таких значений, и именно поэтому мы объявили его публично.

Сохраните сценарий в редакторе MonoDevelop и перейдите в редактор Unity. Теперь у нас есть сценарий и нам нужно присвоить его какому то объекту, в нашем случае - платформе. Выберите нашу платформу во вкладке Hierarchy и в окне Inspector добавьте компонент, кликнув на кнопку Add Component .

Добавление нашего скрипта в компонент можно сделать и по-другому. Перетащите наш сценарий в область кнопки Add Component . Во вкладке Inspector вы должны увидеть что-то подобное:

Обратите внимание, что в компоненте скрипта появилось поле Player Velocity , которое можно тут же изменить. Это получилось возможным благодаря публичному объявлению переменной. Установите параметр в значение 0.3 и перейдите в редактор MonoDevelop.

Теперь нам надо узнать позицию платформы: playerPosition . Для того, чтобы инициализировать переменную, следует обратиться к объекту сценария в методе Start() :

// используйте этот метод для инициализации void Start () { // получим начальную позицию платформы playerPosition = gameObject.transform.position; }

Отлично, мы определили начальную позицию платформы, и теперь можно ее двигать. Так как нам надо, чтобы платформа перемещалась только по оси X, то мы сможем использовать метод GetAxis класса Input . Этой функции мы передадим строку Horizontal , и она вернет нам 1, если была нажата клавиша «вправо», и -1 - «влево». Умножив полученное число на скорость и прибавив эту величину к текущей позиции игрока, мы и получим движение.

Также добавим проверку на выход из приложения по нажатию на клавишу Esc .

Вот то, что у нас должно получиться в итоге:

Using UnityEngine; using System.Collections; public class PlayerScript: MonoBehaviour { public float playerVelocity; private Vector3 playerPosition; // используйте этот метод для инициализации void Start () { // получим начальную позицию платформы playerPosition = gameObject.transform.position; } // Update вызывается при отрисовке каждого кадра игры void Update () { // горизонтальное движение playerPosition.x += Input.GetAxis ("Horizontal") * playerVelocity; // выход из игры if (Input.GetKeyDown(KeyCode.Escape)){ Application.Quit(); } // обновим позицию платформы transform.position = playerPosition; } }

Сохраните скрипт и вернитесь в редактор Unity. Нажмите кнопку Play и попробуйте передвинуть платформу при помощи кнопок «влево» и «вправо».

Определение игровой области

Вы, скорее всего, заметили, что платформа может двигаться и за пределами игрового поля. Дело в том, что у нас нет никаких проверок на выход за пределы каких-то границ.

Давайте добавим в наш существующий скрипт еще одну публичную переменную и назовем его boundary .

Эта переменная будет хранить максимальную координату платформы по оси X. Так как мы собираемся строить уровни в симметричной форме вокруг точки с координатами (0, 0, 0), то абсолютное значение переменной boundary будет одинаковым и для положительной части оси X, и для отрицательной.

А теперь добавим пару условий. Поступим достаточно просто: если вычисленная нами позиция будет больше boundary или меньше -boundary , то мы просто зададим новую позицию по оси X, равную значению переменной boundary . Таким образом, мы гарантируем, что платформа не уедет за пределы наших границ и никогда не покинет игровую зону. Вот код:

Using UnityEngine; using System.Collections; public class PlayerScript: MonoBehaviour { public float playerVelocity; private Vector3 playerPosition; // используйте этот метод для инициализации void Start () { // получим начальную позицию платформы playerPosition = gameObject.transform.position; } // Update вызывается при отрисовке каждого кадра игры void Update () { // горизонтальное движение playerPosition.x += Input.GetAxis ("Horizontal") * playerVelocity; // выход из игры if (Input.GetKeyDown(KeyCode.Escape)){ Application.Quit(); } // обновим позицию платформы transform.position = playerPosition; // проверка выхода за границы if (playerPosition.x < -boundary) { transform.position = new Vector3 (-boundary, playerPosition.y, playerPosition.z); } if (playerPosition.x > boundary) { transform.position = new Vector3(boundary, playerPosition.y, playerPosition.z); } } }

Теперь вернитесь в редактор и, переключаясь в игру, найдите оптимальное значение переменной boundary . В нашем случае подошло число 5.46. Откройте Inspector и сбросьте позицию платформы по оси X на 0, а параметр Boundary выставьте согласно найденному вами значению.

Нажмите кнопку Play и убедитесь в том, что вы все сделали правильно. Платформа должна двигаться только в пределах игрового поля.

Включение физики

Чтобы столкновения были более реалистичные - воспользуемся симуляцией физики. В этой статье мы добавим физические свойства мячику, платформе и границам поля. Так как мы пишем 2D игру, то будем использовать 2D коллайдеры. Коллайдер - это отдельный тип компонентов, который позволяет объекту реагировать на коллайдеры других объектов.

В окне Hierarchy выберем нашу платформу, перейдем в Inspector и нажмем на кнопку Add Component . В появившемся окошке наберем collider . Как вы можете увидеть - вариантов достаточно много. Каждый коллайдер имеет специфические свойства, соответствующие связанным объектам - прямоугольникам, кругам и т.д.

Так как наша платформа имеет прямоугольную форму, мы будем использовать Box Collider 2D . Выберите именно его, и компонент автоматически определит размеры платформы: вам не нужно будет задавать их вручную, Unity сделает это за вас.

Сделайте то же самое и для 3 границ (Hierarchy -> Inspector -> Add Component -> Box Collider 2D ).

С мячиком чуть-чуть по другому: он имеет круглую форму. Выберем мяч и добавим для нее компонент Circle Collider 2D .

На самом деле коллайдер окружности и прямоугольника очень похожи, за исключением того, что вместо параметра Size , определяющим ширину и длину, в окружности используется Radius . Объяснения здесь, думаем, излишни.

Упругое столкновение

Для того, чтобы наш мячик отскакивал от блоков, стен и платформы, нам следует задать поверхность (material) для физического компонента, добавленного ранее. В Unity все уже имеется, нам остается только добавить нужный материал.

Откройте окно Project и внутри папки Asset создайте новую папку под названием Physics . Кликните по только что созданной папке правой кнопкой мыши и выберите Create -> Physics2D Material . Задайте название BallPhysicsMaterial .

Каждая поверхность в Unity имеет два параметра: трение (friction) и упругость (bounciness) . Более подробно вы можете прочитать про физический движок и ряд физических параметров . Если вам требуется абсолютно упругое тело, то следует выставить трение на 0, а упругость на 1.

Сейчас у нас есть готовый материал, но он пока никак не связан с мячом. Выберите объект мяча во вкладке Hierarchy и в окне Inspector вы увидите поле Material компонента Circle Collider 2D . Перетащите сюда недавно созданный материал.

Добавление компонента Rigid Body

Для того, чтобы наш мячик двигался под контролем физики, мы должны добавить ему еще один компонент: Rigid Body 2D . Выберите объект мяча в окне Hierarchy и добавьте вышеупомянутый компонент - хоть он и имеет несколько параметров, нас интересует только один: Gravity Scale . Так как наш шарик будет двигаться только за счет отскоков, то мы зададим этому параметру 0 - таким образом мы гарантируем, что гравитация не будет реагировать на объект. Все остальное можно не менять.

Поведения шарика

Давайте создадим для шарика отдельный скрипт (снова воспользуемся C# в качестве языка программирования) и назовем его BallScript . Свяжите созданный скрипт с объект (Hierarchy -> Inspector -> Add Component ).

Перед тем, как начать писать скрипт, давайте определим поведение шарика:

  1. Шар имеет два состояния: неактивное (когда он в начале игры находится на платформе) и активное (когда находится в движении).
  2. Шар будет становиться активным только один раз.
  3. Когда шар становится активным, мы применяем к нему силу для того, что он начал движение.
  4. Если шар вышел за пределы игрового поля, он переводится в неактивное состояние и помещается на платформу.

Основываясь на этой информации, давайте создадим глобальные переменные ballIsActive , ballPosition и ballInitialForce:

private bool ballIsActive;
private Vector3 ballPosition;
private Vector2 ballInitialForce;

Теперь, когда у нас есть набор переменных, мы должны подготовить объект. В методе Start() мы должны:

  • создать силу, которая будет применена к шару;
  • перевести шар в неактивное состояние;
  • запомнить позицию шара.

Вот, как это можно сделать:

Void Start () { // создаем силу ballInitialForce = new Vector2 (100.0f,300.0f); // переводим в неактивное состояние ballIsActive = false; // запоминаем положение ballPosition = transform.position; }

Как вы могли заметить, сила прилагается не строго вертикальная, а наклоненная вправо - шарик будет двигаться по диагонали.

Void Update () { // проверка нажатия на пробел if (Input.GetButtonDown ("Jump") == true) { } }

Следующим шагом является проверка состояния шара, поскольку задать силу нам надо только в том случае, если шар находится в неактивном состоянии:

Void Update () { // проверка нажатия на пробел if (Input.GetButtonDown ("Jump") == true) { // проверка состояния if (!ballIsActive){ } } }

Если предположить, что мы находимся в начале игры, то мы должны применить силу к шару и установить его в активное состояние:

Void Update () { // проверка нажатия на пробел if (Input.GetButtonDown ("Jump") == true) { // проверка состояния if (!ballIsActive){ // применим силу rigidbody2D.AddForce(ballInitialForce); // зададим активное состояние ballIsActive = !ballIsActive; } } }

Если теперь вы включите игру, то, нажав на пробел, шар действительно начнет движение. Однако вы можете заметить, что мяч в неактивном состоянии ведет себя не совсем правильно: если мы будем двигать платформу, то мяч должен двигаться вместе с ней, но на самом деле остается на прежней позиции. Остановите игру, давайте исправим это.

В методе Update мы должны проверять состояние шарика, и в случае если оно неактивное, нам надо задать позицию мячика по оси X таким же, какое оно у платформы.

Решение достаточно простое, но как нам получить координату совсем другого объекта? Элементарно - мы создадим переменную типа GameObject и сохраним ссылку на объект платформы:

public GameObject playerObject;

Вернемся к методу Update() :

Void Update () { // проверка нажатия на пробел if (Input.GetButtonDown ("Jump") == true) { // проверка состояния if (!ballIsActive){ // применим силу rigidbody2D.AddForce(ballInitialForce); // зададим активное состояние ballIsActive = !ballIsActive; } if (!ballIsActive && playerObject != null){ // задаем новую позицию шарика ballPosition.x = playerObject.transform.position.x; // устанавливаем позицию шара transform.position = ballPosition; } } }

Сохраните скрипт и вернитесь в редактор Unity. Вы наверняка заметили, что переменная playerObject объявлена, используется, но нигде не инициализирована. Да, так и есть. Чтобы ее проинициализировать, перейдите во вкладку Hierarchy , найдите шар и в окне Inspector найдите компонент Ball Script . У данного компонента есть параметр Player Object , в настоящее время пустующий:

Найдите во вкладке Hierarchy нашу платформу и перетащите ее на поле Player Object . Запустите игру, нажав кнопку Play , и убедитесь, что все работает.

Сброс игры

Если на данном этапе запустить игру и проиграть (чтобы шар упал за пределы поля), то ничего не вернется на круги своя. А на самом деле должен произойти сброс состояния игры. Давайте исправим это.

Это состояние отловить очень просто: шар будет активен, а его положение по оси Y отрицательно. Если это так, то мы переводим шар в неактивное состояние и ставим его на платформу:

If (ballIsActive && transform.position.y < -6) { ballIsActive = !ballIsActive; ballPosition.x = playerObject.transform.position.x; ballPosition.y = -4.2f; transform.position = ballPosition; }

Но и это не конец. Сохранив скрипт и запустив игру, вы заметите, что каждый раз при перезапуске уровня шар обретает всю большую и большую силу. Почему так происходит? Дело в том, что мы не очищаем силы, приложенные к шару. Чтобы исправить этот недочет, мы можем воспользоваться параметром IsKinematic , выключая его перед добавлением силы и включая - после падения.

Вот такой вышел у нас итоговый метод Update() :

Public class BallScript: MonoBehaviour { private bool ballIsActive; private Vector3 ballPosition; private Vector2 ballInitialForce; // GameObject public GameObject playerObject; // используйте этот метод для инициализации void Start () { // создаем силу ballInitialForce = new Vector2 (100.0f,300.0f); // переводим в неактивное состояние ballIsActive = false; // запоминаем положение ballPosition = transform.position; } void Update () { // проверка нажатия на пробел if (Input.GetButtonDown ("Jump") == true) { // проверка состояния if (!ballIsActive){ // сброс всех сил rigidbody2D.isKinematic = false; // применим силу rigidbody2D.AddForce(ballInitialForce); // зададим активное состояние ballIsActive = !ballIsActive; } if (!ballIsActive && playerObject != null){ // задаем новую позицию шарика ballPosition.x = playerObject.transform.position.x; // устанавливаем позицию шара transform.position = ballPosition; } // проверка падения шара if (ballIsActive && transform.position.y < -6) { ballIsActive = !ballIsActive; ballPosition.x = playerObject.transform.position.x; ballPosition.y = -4.2f; transform.position = ballPosition; rigidbody2D.isKinematic = true; } } }

А вот теперь точно все. Запустите игру и проверьте, все ли работает как положено.

В следующей части

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

Введение:

В этом уроке мы сделаем простой 2D платформер на движке Unity. В туториале будет использоваться встроенная физика2D. Также я хочу показать основы анимации (мы будем работать с листами спрайтов). Для этого урока вам понадобятся несколько разных файлов, таких как спрайты, звуки. Вы можете скачать архив.zip .

1. Шаг

Запустите Unity и создайте новый 2D-проект.

2. Шаг

Сначала создайте землю для нашей игры. Импортируйте largeGround из загруженного архива ( ссылка снова, если ещё не скачали. Распакуйте архив.zip после его загрузки).

Перетащите largeGround в сцену. Переименуйте созданный игровой объект как Ground . Проверьте, что Z-позиция равна нулю (измените её на 0 , если отличается). Добавьте к нему Box Collider 2D.

3. Шаг

Теперь мы можем сделать игрока. Для этого у меня есть лист спрайтов с крысой. Импортируйте ratIdle (из скаченного архива) в окно Assets . Нам надо разделить спрайты. Выберите ratIdle и переключите Sprite Mode на Multiple . Нажмите Apply .

Нажмите на Sprite Editor и новое окно будет открыто. Нажмите Slice . Измените Type на Grid By Cell Count . Установите Column на 4 и Row на 5 . Нажмите на кнопку Slice . Нажмите на Apply . Закройте это окно.

Раскройте спрайт. В нем должно быть теперь 20 кадров. Перетащите первый кадр в сцену. Будет создан новый игрвой объект. Выделите этот объект и назовите его Rat . Посмотрите на его Inspector . Измените Tag на Player . Убедитесь, что его Z-позиция 0. Добавьте к нему Capsule Collider 2D . Измените Direction на Horizontal . Нажмите на кнопку Edit и настройте размер коллайдера (тяните за зеленые точки). Теперь добавьте Rigidbody 2D . Установите Interpolate на Interpolate . Разверните Constraints и включите Freeze Rotation Z .

4. Шаг

Для крысы нужен скрипт движения. Создайте новый C# скрипт (правый клик в Assets) и назовите его PlayerMove . Для движения будем использовать встроеннную физику 2Д, то-есть будем использовать Rigidbody2D . Мы можем изменять скорость в зависимости о данных, полученных от Input Manager (я надеюсь, вы смотрели предыдущие туториалы и мне не надо будет повторяться). Откройте PlayerMove скрипт.

using System.Collections;

using UnityEngine;


public float speed;

Rigidbody2D rb;

void Start () {

rb = GetComponent ();
}

void FixedUpdate () {








rb.velocity = move;
}
}

Выберите крысу в иерархии и добавьте к ней скрипт PlayerMove . Измените переменную Speed (около 3). Вы можете запустить игру и проверить сцену. Крыса должна двигаться влево и вправо (использовать левую/правую стрелку или A/D).

5. Step

Теперь мы можем добавить прыжок крысе. Мы будем применять силу вдоль оси Y для прыжка. Смотрите Unity Manual для подробностей. Сила не должна добавляться каждый раз, когда нажимается кнопка прыжка, или крыса полетит как ракета. Также нам понадобится логическая переменная для управления прыжком. И нам нужна функция для переключения этой переменной относительно положения крысы к земле. Есть очень много способов достичь этого (триггер, raycast, таймер или что-то еще). Мы будем использовать Physics2D.OverlapPoint , чтобы определить, какой слой находится под контрольной точкой. Смотрите Unity Manual для деталей. Откройте PlayerMove скрипт снова.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMove: MonoBehaviour {






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

void Start () {
//делаем ссылку на Rigidbody2D
rb = GetComponent ();
}


void Update () {




//переключаем переменную, чтобы предотвратить следующий прыжок, или мы могли бы снова прыгнуть (до того, как isGrounded будет переключена в FixedUpdate ())
isGrounded = false;
}
}

void FixedUpdate () {
//изменяем переменную, зависящую от результата Physics2D.OverlapPoint

//декларация переменной с её инициализацией значением полученным с горизонтальной оси (значение лежит в области между -1 и 1)
float x = Input.GetAxis ("Horizontal" );
//декларация локального вектора и инициализация посчитанным значением
//x: значение от InputManager * speed
//y: принять текущее значение, мы не будем его менять, из-за использования силы тяжести
//z: должно быть равно нулю, нам не нужно движение по оси Z
Vector3 move = new Vector3 (x * speed, rb.velocity.y, 0f );
//Изменить скорость игрока на вычисленный вектор
rb.velocity = move;
}
}

Выберите Rat в иерархии, щелкните по нему правой кнопкой мыши и выберите Create Empty . Выберите этот созданный объект и переименуйте его в GroundCheck .

Выберите GroundCheck и включите иконку для лучшей видимости (смотрите скриншот). Включите moving tool и передвиньте GroundCheck под крысу, но не в сам коллайдер крысы. Убедитесь, что его Z-Position равна 0.

Выберите Ground в иерархии и смените Layer на Ground (добавьте новый Layer с этим названием, если в списке нет такого).

Выделите Rat в иерархии. В компоненте PlayerMove есть теперь новые поля. Включите Ground в whatIsGround . Поместите GroundCheck в поле GroundCheck . Измените JumpForce на 5.

Проверьте игру. Крыса должна теперь двигаться налево/направо и прыгать.

6. Step

Крыса должна уметь поворачиваться. Нам нужна функция для этого. В этой функции мы будем проверять значение "Horizontal" оси в Input Manager и поворачивать крысу в зависимости от этого значения. Для поворота будем использовать обратное значение x Scale в Transform компоненте. Откройте PlayerMove скрипт.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMove: MonoBehaviour {

//в инспекторе мы можем выбрать, какие слои будут землёй
public LayerMask whatIsGround;
//позиция для проверки касания земли
public Transform groundCheck;
//переменная, которая будет true, если крыса находится на земле
public bool isGrounded;
//значение величины силы
public float jumpForce;
//переменная для скорости движения
public float speed;
//ссылочная переменная для компонента Rigidbody2D
Rigidbody2D rb;
//переменная контроля направления крысы
public bool isLookingLeft;

void Start () {
//делаем ссылку на Rigidbody2D
rb = GetComponent ();
}

//я буду использовать Update() для более точного определения прыжка
void Update () {
//проверка, нажат-ли прыжок и находится-ли крыса на земле
if (Input .GetButtonDown ("Jump" ) && isGrounded) {
//применяем силу на Rigidbody2D вдоль оси Y для прыжка
rb.AddForce (Vector2 .up * jumpForce, ForceMode2D .Impulse);
//sпереключаем переменную, чтобы предотвратить следующий прыжок, или мы могли бы снова прыгнуть (до того, как isGrounded будет переключена в FixedUpdate ())
isGrounded = false;
}
}

void FixedUpdate () {
//изменяем переменную, зависит от результата Physics2D.OverlapPoint
isGrounded = Physics2D .OverlapPoint (groundCheck.position, whatIsGround);
//декларация переменной с её инициализацией значением полученным с горизонтальной оси (значение лежит в области между -1 и 1)
float x = Input.GetAxis ("Horizontal" );
//декларация локального вектора и инициализация посчитанным значением
//x: значение от InputManager * speed
//: принять текущее значение, мы не будем его менять, из-за использования силы тяжести
//z: должно быть равно нулю, нам не нужно движение по оси Z
Vector3 move = new Vector3 (x * speed, rb.velocity.y, 0f );
//изменить скорость игрока на вычисленный вектор
rb.velocity = move;

if (x < 0 && !isLookingLeft)

TurnTheRat ();
//проверка, совпадает ли направление взгляда с направлением движения
if (x > 0 && isLookingLeft)
//вызов функции поворота крысы, если проверка совпала
TurnTheRat ();
}

//функция поворота крысы
void TurnTheRat ()
{
//смена переменной показывающей направление взгляда на обратное значение
isLookingLeft = !isLookingLeft;
//поворот крысы через инвертацию размера по оси х
transform.localScale = new Vector3 (transform.localScale.x * -1 , transform.localScale.y, transform.localScale.z);
}

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

Крыса может теперь бежать влево и вправо и менять направление взгляда. Вы можете проверить сцену.

7. Step

Теперь можем начать анимировать крысу. Выберите Rat в иерархии и добавьте Animator к ней.

Создайте Animator Controller в Assets . Назовите его RatAnimator .

Выберите Rat и добавьте RatController в поле Controller .

Нам понадобится окно Animation . Включите его в меню Window м поместите его там, где вам удобно (я поместил над Assets ).

Выберите Rat в иерархии. Создайте новую анимацию (нажмите на кнопку Create ). Назовите эту новую анимацию IdleRatAnimation .

Теперь можем добавить спрайтовый лист. Выделите Rat в иерархии. Разверните ratIdle спрайт. Надо выделить все кадры. Выберите первый кадр, зажмитеShift-Key Animation

Включите окошко Animator в меню Window .

Анимация должна быть зацикленна. Убедитесь в этом на всякий случай. Перейдите к окну Animator и сделайте двойной клик на IdleRatAnimation Проверьте Loop Time .

У крысы есть теперь анимация покоя. Запустите сцену и проверьте.

8. Шаг

Теперь создадим анимацию бега. Импортируйте спрайт ratRun в Assets . Смените Sprite Mode на Multiple . Перейдите в Sprite Editor и переключите на Grid By Cell Count и установите Column 4 , Row 5 (смотрите 3. Шаг ). Выделите Rat в иерархии. Перейдите в окно Animations и нажмите на IdleRatAnimation потом нажмите на Create New Clip . Назовите новую анимацию RunRatAnimations .

Раскройте спрайт ratRun и выделите все 20 кадров (нажмите первый кадр, держите кнопку шифт и нажмите последний кадр). Перетащите все кадры в окошко Animations . Установите samples на 20. Сделайте двойной клик на первом кадре, чтобы сделать правильную длину анимации (возможно баг моей версии).

Откройте окно Animator (включите его в меню Window , если не видите его).

IdleRatAnimation стандартная анимация. Нам нужны transition (переходы) к другим анимациям и параметры контроля этих transition. Нажмите на закладку Parameters . Добавьте новый float speed параметр. Сделайте правый клик на IdleRatAnimation и сделайте transition из IdleRatAnimation в RunRatAnimation . Потом правый клик на RunRatAnimation и добавьте transition на IdleRatAnimation .

Выберите transition от IdleRatAnimation к RunRatAnimation . Отключите Has Exit Time speed greater 0.01 .

Выберите transition от RunRatAnimation к IdleRatAnimation . Отключите Has Exit Time . Добавьте новое condition (условие). Измените его на speed less 0.01 .

Анимация бега сейчас слишком медленная. Выберите RunRatAnimation в Animator и измените скорость на 2.5 (можете потом настроить по желанию).

9. Шаг

Нам нужен скрипт для смены условий в Animator . Мы могли бы добавить новые команды в скрипт PlayerMove , но для тренировки сделаем новый скрипт и с него получим нужные доступы. Мы будем проверять двигается ли крыса, и менять анимации в зависимости от движения. Создайте новый С# скрипт и назовите его PlayerAnim .

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


Animator anim;

Rigidbody2D rb;

void Start () {
//делаем ссылку на аниматор
anim = GetComponent ();
//делаем ссылку на Rigidbody2D
rb = GetComponent ();
}

void Update () {
//меняем параметр speed в Animator. Используем значение скорости по оси х

}
}

Добавьте этот скрипт к крысе Rat .

Запустите сцену. У крысы должна появиться анимация бега

10. Шаг

Теперь сделаем анимацию прыжка. Выберите Rat в иерархии. Добавьте новую анимацию (в окошке Animation ) и назовите её JumpRatAnimation .

Я не сделал анимацию прыжка, просто используем замедленную анимацию бега. Выделите Rat в иерархии. Проверьте в окошке Animation , что JumpRatAnimation выбранна. Разверните спрайт ratRun . Надо выделить все кадры. Выберите первый кадр, зажмитеShift-Key и выделите последний кадр. Все 20 кадров должны выбраться. Перетащите все кадры в окошко Animation . Установите samples на 20. Сделайте двойной клик на первом кадре, чтобы сделать правильную длину анимации (возможно баг моей версии).

11. Шаг

Переключитесь на Animator . Нам нужно условие для анимации прыжка. Добавьте новую Bool и назовите её isJumping . Сделайте теперь transition (переход) (правый клик) из Any State к JumpRatAnimation . Добавьте transition от JumpRatAnimation к IdleRatAnimation .

Выделите transition от Any State к JumpRatAnimation . Отключите Has Exit Time . Разверните Settings и отключите Can Transition To (чтобы избежать перезапуска анимации, пока isJumping true). Добавьте condition (условие) isJumping true .

Выберите transition от JumpRatAnimation к IdleRatAnimation . Отключите Has Exit Time . Добавьте condition isJumping false .

Выделите JumpRatAnimation и измените Speed на 0.5. Замедленная анимация бега должна заменить анимацию прыжка.

12. Шаг

Нужен скрипт для контроля анимации прыжка. Откройте скрипт PlayerAnim . Из этого скрипта мы сделаем доступ на скрипт PlayerMove и сможем проверить переменную isGrounded . Если крыса не будет на земле, то сменим анимацию на анимацию прыжка. Также анимация прыжка будет включена при падении крысы.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerAnim: MonoBehaviour {

//ссылочная переменная для аниматора
Animator anim;
//ссылочная переменная для rigidbody2D
Rigidbody2D rb;
//ссылочная переменная для PlayerMove
PlayerMove pm;

void Start () {
//делаем ссылку на Animator
anim = GetComponent ();
//делаем ссылку на Rigidbody2D
rb = GetComponent ();
//делаем ссылку на PlayerMove
pm = GetComponent ();
}

void Update () {
//проверка, находится ли крыса на земле
if (pm.isGrounded) {
//меняем параметр isJumping на false
anim.SetBool ("isJumping" , false );
//меняем параметр speed. Используем абсолютное значение вектора скорости по х
anim.SetFloat ("speed" , Mathf .Abs (rb.velocity.x));
// если крыса не на земле
} else {
//меняем параметр speed на 0
anim.SetFloat ("speed" , 0 );
//меняем параметр isJumping на true
anim.SetBool ("isJumping" , true );
}
}

Можете проверить сцену.

13. Step

Теперь добавим звуки шагов. Нам нужна функция для проигрывания звука. Вызывать функцию будем через аниматор. Создайте новый C# скрипт и назовите его PlayerSound .

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//ссылочная переменная для звукового файла
public AudioClip footsteps;





}
}

Выберите Rat в иерархии и добавьте этот скрипт к крысе. Импортируйте аудио-файл ratStep . И добавьте ratStep в Footsteps поле компонента PlayerSound .

Выделите Rat в иерархии и перейдите в Animation . Смените анимацию на RunRatAnimation . Выберите кадр, когда хотите воспроизвести звук (кликните на полоску времени). Нажмите на Add Event (смотрите скриншот). Выберите добавленный Animations Event и установите Function на FootStepsAudio() .

14. Шаг

Теперь добавим звук после прыжка. Используем OnCollisionEnter2D для этого. Смотрите Unity Manual для деталей. Откройте скрипт PlayerSound .

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerSound: MonoBehaviour {

//ссылочная переменная для аудио-файла
public AudioClip footsteps;

//публичная функция, получим доступ к ней из аниматора
public void FootStepsAudio () {
//воспроизвести заданный звук на позиции крысы
AudioSource .PlayClipAtPoint (footsteps, transform.position);
}

//запустится если было касание другого Collider2D
void OnCollisionEnter2D (Collision2D coll) {
//проверка тэга на тэг "Ground"
if (coll.gameObject.tag == "Ground" ) {
//воспроизвести заданный звук на позиции крысы
AudioSource .PlayClipAtPoint (footsteps, transform.position);
}
}

Выберите Ground в иерархии. Добавьте новый тэг Ground и измените тэг объекта Ground на тэг Ground .

15. Шаг

Выберите Ground в иерархии. Создайте prefab Ground (перетащите объект в Assets ). Поместите prefab Ground в сцену, как следующую платформу.

Выберите Main Camera и сделайте её дочерним объектом Rat (перетащите Main Camera на Rat ). Камера будет следовать за крысой.

Можете изменить X-position камеры на 0, чтобы убрать рывки при повороте.
Я думаю, этого достаточно для первой части. Надеюсь, что вторая часть будет готова в мае-июне (у меня сейчас мало времени).

Скачать готовый проект

Статьи по теме: