Туториал 4: 2Д платформер (для новичков). Часть 1.

Введение:

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

1. Шаг

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

2. Шаг

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

3. Шаг

Теперь мы можем сделать игрока. Для этого у меня есть лист спрайтов с крысой. Импортируйте ratIdle (из скаченного архива) в окно Assets. Нам надо разделить спрайты. Выберите ratIdle и переключите Sprite Mode на Multiple. Нажмите Apply .
import ratIdle
Нажмите на Sprite Editor и новое окно будет открыто. Нажмите Slice. Измените Type на Grid By Cell Count. Установите Column на 4 и Row на 5. Нажмите на кнопку Slice. Нажмите на Apply. Закройте это окно.
slice ratIdle
Раскройте спрайт. В нем должно быть теперь 20 кадров. Перетащите первый кадр в сцену. Будет создан новый игрвой объект. Выделите этот объект и назовите его Rat. Посмотрите на его Inspector. Измените Tag на Player. Убедитесь, что его Z-позиция 0. Добавьте к нему Capsule Collider 2D. Измените Direction на Horizontal. Нажмите на кнопку Edit и настройте размер коллайдера (тяните за зеленые точки). Теперь добавьте Rigidbody 2D. Установите Interpolate на Interpolate. Разверните Constraints и включите Freeze Rotation Z.
players gameobject

4. Шаг

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

public class PlayerMove : MonoBehaviour {

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

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

void FixedUpdate () {
//декларация переменной с её инициализацией значением полученным с горизонтальной оси (значение лежит в области между -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;
}
}
Выберите крысу в иерархии и добавьте к ней скрипт PlayerMove . Измените переменную Speed (около 3). Вы можете запустить игру и проверить сцену. Крыса должна двигаться влево и вправо (использовать левую/правую стрелку или A/D).
add PlayerMove to the rat

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 LayerMask whatIsGround;
//позиция для проверки касания земли
public Transform groundCheck;
//переменная, которая будет true, если крыса находится на земле
public bool isGrounded;
//значение величины силы
public float jumpForce;
//переменная для скорости движения
public float speed;
//ссылочная переменная для компонента Rigidbody2D
Rigidbody2D rb;

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

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

void FixedUpdate () {
//изменяем переменную, зависящую от результата Physics2D.OverlapPoint
isGrounded = Physics2D.OverlapPoint (groundCheck.position, whatIsGround);
//декларация переменной с её инициализацией значением полученным с горизонтальной оси (значение лежит в области между -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 .
create GroundCheck
Выберите GroundCheck и включите иконку для лучшей видимости (смотрите скриншот). Включите moving tool и передвиньте GroundCheck под крысу, но не в сам коллайдер крысы. Убедитесь, что его Z-Position равна 0.
move GroundCheck
Выберите Ground в иерархии и смените Layer на Ground (добавьте новый Layer с этим названием, если в списке нет такого).
change Layer to Ground
Выделите Rat в иерархии. В компоненте PlayerMove есть теперь новые поля. Включите Ground в whatIsGround. Поместите GroundCheck в поле GroundCheck. Измените JumpForce на 5.
set up PlayerMove
Проверьте игру. Крыса должна теперь двигаться налево/направо и прыгать.

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 <Rigidbody2D> ();
}

//я буду использовать 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 появилась новая переменная для обозначения направления взгляда. Включите её, так как крыса смотрит налево.
enable isLookingLeft
Крыса может теперь бежать влево и вправо и менять направление взгляда. Вы можете проверить сцену.

7. Step

Теперь можем начать анимировать крысу. Выберите Rat в иерархии и добавьте Animator к ней.
add Animator to the rat
Создайте Animator Controller в Assets. Назовите его RatAnimator.
create AnimatorController
Выберите Rat и добавьте RatController в поле Controller.
create AnimatorController
Нам понадобится окно Animation. Включите его в меню Window м поместите его там, где вам удобно (я поместил над Assets).
enable Animation window
Выберите Rat в иерархии. Создайте новую анимацию (нажмите на кнопку Create). Назовите эту новую анимацию IdleRatAnimation.
create new animation
Теперь можем добавить спрайтовый лист. Выделите Rat в иерархии. Разверните ratIdle спрайт. Надо выделить все кадры. Выберите первый кадр, зажмитеShift-Key и выделите последний кадр. Все 20 кадров должны выбраться. Перетащите все кадры в окошко Animation. Установите samples на 20. Сделайте двойной клик на первом кадре, чтобы сделать правильную длину анимации (возможно баг моей версии).
add frames to animation
Включите окошко Animator в меню Window.
enable Animator window
Анимация должна быть зацикленна. Убедитесь в этом на всякий случай. Перейдите к окну Animator и сделайте двойной клик на IdleRatAnimation Проверьте Loop Time.
animations loop
У крысы есть теперь анимация покоя. Запустите сцену и проверьте.

8. Шаг

Теперь создадим анимацию бега. Импортируйте спрайт ratRun в Assets. Смените Sprite Mode на Multiple. Перейдите в Sprite Editor и переключите на Grid By Cell Count и установите Column 4, Row 5 (смотрите 3. Шаг). Выделите Rat в иерархии. Перейдите в окно Animations и нажмите на IdleRatAnimation потом нажмите на Create New Clip. Назовите новую анимацию RunRatAnimations.
add new animation
Раскройте спрайт ratRun и выделите все 20 кадров (нажмите первый кадр, держите кнопку шифт и нажмите последний кадр). Перетащите все кадры в окошко Animations. Установите samples на 20. Сделайте двойной клик на первом кадре, чтобы сделать правильную длину анимации (возможно баг моей версии).
add frames to animation
Откройте окно Animator (включите его в меню Window, если не видите его).
animator window
IdleRatAnimation стандартная анимация. Нам нужны transition (переходы) к другим анимациям и параметры контроля этих transition. Нажмите на закладку Parameters. Добавьте новый float speed параметр. Сделайте правый клик на IdleRatAnimation и сделайте transition из IdleRatAnimation в RunRatAnimation. Потом правый клик на RunRatAnimation и добавьте transition на IdleRatAnimation.
make transition
Выберите transition от IdleRatAnimation к RunRatAnimation. Отключите Has Exit Time. Добавьте новое condition (условие). Измените его на speed greater 0.01.
make transition
Выберите transition от RunRatAnimation к IdleRatAnimation. Отключите Has Exit Time. Добавьте новое condition (условие). Измените его на speed less 0.01.
make transition
Анимация бега сейчас слишком медленная. Выберите RunRatAnimation в Animator и измените скорость на 2.5 (можете потом настроить по желанию).
change animations speed

9. Шаг

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

public class PlayerAnim : MonoBehaviour {

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

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

void Update () {
//меняем параметр speed в Animator. Используем значение скорости по оси х
anim.SetFloat ("speed", Mathf.Abs (rb.velocity.x));
}
}
Добавьте этот скрипт к крысе Rat.
add PlayerAnim to Rat
Запустите сцену. У крысы должна появиться анимация бега

10. Шаг

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

11. Шаг

Переключитесь на Animator. Нам нужно условие для анимации прыжка. Добавьте новую Bool и назовите её isJumping. Сделайте теперь transition (переход) (правый клик) из Any State к JumpRatAnimation. Добавьте transition от JumpRatAnimation к IdleRatAnimation.
add Bool and make transitions
Выделите transition от Any State к JumpRatAnimation. Отключите Has Exit Time. Разверните Settings и отключите Can Transition To (чтобы избежать перезапуска анимации, пока isJumping true). Добавьте condition (условие) isJumping true.
set up condition
Выберите transition от JumpRatAnimation к IdleRatAnimation. Отключите Has Exit Time. Добавьте condition isJumping false.
set up condition
Выделите JumpRatAnimation и измените Speed на 0.5. Замедленная анимация бега должна заменить анимацию прыжка.
change animations speed

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 <Animator> ();
//делаем ссылку на Rigidbody2D
rb = GetComponent <Rigidbody2D> ();
//делаем ссылку на PlayerMove
pm = GetComponent <PlayerMove> ();
}

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 class PlayerSound : MonoBehaviour {

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

//публичная функция, получим доступ к ней из аниматора
public void FootStepsAudio () {
//воспроизвести заданный звук на позиции крысы
AudioSource.PlayClipAtPoint (footsteps, transform.position);
}
}
Выберите Rat в иерархии и добавьте этот скрипт к крысе. Импортируйте аудио-файл ratStep. И добавьте ratStep в Footsteps поле компонента PlayerSound.
add PlayerSound to the rat
Выделите Rat в иерархии и перейдите в Animation. Смените анимацию на RunRatAnimation. Выберите кадр, когда хотите воспроизвести звук (кликните на полоску времени). Нажмите на Add Event (смотрите скриншот). Выберите добавленный Animations Event и установите Function на FootStepsAudio().
add animations event

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.
change tag to Ground

15. Шаг

Выберите Ground в иерархии. Создайте prefab Ground (перетащите объект в Assets). Поместите prefab Ground в сцену, как следующую платформу.
create prefab from ground
Выберите Main Camera и сделайте её дочерним объектом Rat (перетащите Main Camera на Rat). Камера будет следовать за крысой.
create prefab from ground
Можете изменить X-position камеры на 0, чтобы убрать рывки при повороте.
Я думаю, этого достаточно для первой части. Надеюсь, что вторая часть будет готова в мае-июне (у меня сейчас мало времени).


download UnityPackage скачать готовый проект
К сожалению на некоторых версиях Юнити бывает ошибка при импорте проекта. Tag у GameObject может сброситься на undefinied.