Как сделать РПГ в Unity – Полный гайд Часть 1

Как сделать РПГ в Unity – Полный гайд Часть 1


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

Чтобы следовать этому руководству, вы должны знать следующие понятия:

  • C# программирование
  • Использование инспектора Unity, например, импорт ассетов, создание префабов и добавление компонентов
  • Создание тайлмап, тайлсетов и т.д.

Прежде чем начать чтение урока, создайте новый проект Unity и импортируйте все спрайты, доступные в исходном коде. Вам нужно будет нарезать spritesheet.

Вы можете скачать исходный код этого урока здесь

Права на ассеты

Спрайты, используемые в этом уроке, относятся к паку Superpowers от Pixel-boy. Все они доступны под лицензией Creative Commons Zero (CC0). Таким образом, вы можете использовать их в своих играх, даже коммерческих.

Меню

Background слой

Прежде всего, мы создадим слой для отображения фонового изображения в Меню. Вы можете сделать это, создав новый слой под названием BackgroundCanvas и установив его режим рендеринга Screen Space - Camera. Для этого нам нужно определить главную камеру на слой. Кроме того, режим масштабирования пользовательского интерфейса (в Canvas Scaler) будет установлен так, чтобы соответствовать размеру экрана с опорным разрешением 1280 × 960.

После этого мы создаем новый объект Image как дочерний элемент этого слоя. Исходным изображением будет фоновая картинка. Мы можем установить его собственный размер, чтобы все правильно отображалось.

HUD слой

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

Начнем с создания другого слоя, проделывая тоже самое, что и с BackgroundCanvas. Однако, чтобы показать этот холст поверх фона, нам нужно правильно установить его "слой сортировки". Мы собираемся создать еще один слой под названием HUD и поместить HUDCanvas на него.

Наконец, нам нужно создать два объекта HUD. Первый - это текст, а второй - кнопка. Для объекта Text нам нужно только установить его текст, а для объекта Button нам нужно установить метод OnClick.

Объект PlayButton будет иметь следующий скрипт ChangeScene для вызова OnClick. Этот скрипт просто определит метод для запуска новой сцены с ее именем. Затем мы определяем вызов OnClick кнопки Play, чтобы вызвать метод loadNextScene с параметром «Town».

public class ChangeScene : MonoBehaviour {

public void loadNextScene(string sceneName) {

SceneManager.LoadScene (sceneName);

}

}

Player party

В нашей игре мы хотим сохранить данные об игроке даже при смене сцен. Для этого мы создадим постоянный объект PlayerParty, который не будет уничтожаться при смене сцен. Вам также нужно будет правильно установить его положение так, чтобы создать его в правильных координатах в Battle Scene.

Чтобы сохранить PlayParty в живых при смене сцен, мы будем использовать следующий скрипт StartBattle, который не позволяет уничтожать объект при смене сцен в методе «Start». Он также добавляет вызов при загрузке новой сцены и устанавливает объект как неактивный, чтобы он не отображался в меню.

Вызов (OnSceneLoaded) проверяет, является ли текущая загруженная сцена Меню. Если это так, то объект PlayerParty должен быть уничтожен, чтобы избежать дублирования. В противном случае он должен быть установлен как активный, если текущая сцена является сценой битвы.

void Start () {
DontDestroyOnLoad (this.gameObject);

SceneManager.sceneLoaded += OnSceneLoaded;

this.gameObject.SetActive (false);

}

private void OnSceneLoaded(Scene scene, LoadSceneMode mode) {

if (scene.name == "Title") {

SceneManager.sceneLoaded -= OnSceneLoaded;

Destroy (this.gameObject);

} else {

this.gameObject.SetActive(scene.name == "Battle");

}

}

}

Кроме того, PlayerParty будет иметь двух наследников, чтобы предоставлять им данные игрока. Итак, сначала давайте создадим префаб PlayerUnit только с несколькими элементами. Позже в этом уроке мы добавим его остальную часть.

На данный момент PlayerUnit будет иметь только Sprite Renderer и скрипт под названием UnitStats, который будет хранить значение каждой харакатеристики, например: здоровье, ману, атаку, магическую атаку, защиту и скорость.

public class UnitStats : MonoBehaviour {

public float health;

public float mana;

public float attack;

public float magic;

public float defense;

public float speed;

}

На следующем рисунке показан пример одного игрока, называемого MageUnit. В этом примере скрипт UnitStats имеет другие атрибуты, которые будут использоваться позже (например, Animator и Damage Text Prefab), но сейчас вы можете игнорировать их.

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

Карта города

Мы начнем с создания сцены карты города. Итак, создайте новую сцену в своем проекте и назовите ее «Town».

Добавление Tiled map в Unity

В сцене карты города будет карта с тайлами, поэтому мы начнем с ее создания. Мы сделаем Tiled map только с tile слоями. Объекты будут добавлены позже в Unity.

Я собираюсь использовать приведенную ниже карту в этом уроке. Поскольку создание карты тайлов не является целью данного гайла, вы можете использовать эту же карту (доступную с исходным кодом) или создать свою собственную.

На нашей карте есть слой, называемый buildings, который должен иметь возможность сталкиваться с игроком. Чтобы создать коллайдер для Тайлов в Unity, нам нужно установить зону столкновения для каждой твердого тайла. Мы можем это сделать, открыв редактор Tiled Collision Editor в Tiled (в меню просмотра) и добавив прямоугольник, представляющий зону столкновения. Это необходимо сделать для каждого твердого тайла.

Чтобы импортировать эту карту в Unity, мы собираемся использовать стороннюю программу под названием Tiled2Unity (http://www.seanba.com/tiled2unity). Эта программа загрузит нашу карту Tiled и создаст Unity GameObjects для нее.

После загрузки Tiled2Unity первое, что вам нужно сделать, это открыть Tiled2Unity.unitypackage, который будет импортировать пакет в ваш проект Unity.

Затем вы можете открыть файл карты Tiled в Tiled2Unity и экспортировать ее в проект Unity.

После этого Tiled2Unity создаст GameObject в TownScene для карты города. Обратите внимание, что он автоматически создает коллайдер для твердых тайлов. На приведенных ниже картинках показан объект города на сцене и его свойства в инспекторе.

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

Префаб игрока

Теперь, когда мы добавили карту города в Unity, мы собираемся создать Prefab Player. Игрок сможет передвигаться по городу и должен сталкиваться с твердыми тайлами.

Итак, давайте начнем с создания GameObject под названием Player, добавив правильный рендерер спрайтов, Box Collider 2D и Rigidbody 2D, как показано ниже. Обратите внимание, что нам нужно установить атрибут Gravity Scale для Rigidbody2D равным 0, так как на него не будет влиять сила тяжести.

Нам также необходимо создать анимацию Игрока. У него будет четыре ходячие анимации и четыре бездействующие анимации, по одному для каждого направления. Итак, во-первых, мы создаем все анимации, называя их IdleLeft, IdleRight, IdleUp, IldeDown, WalkingLeft, WalkingRight, WalkingUp и WalkingDown.

Следующее, что нам нужно, это аниматор игрока. Итак, мы создаем новый аниматор под названием PlayerAnimator и добавляем к нему все созданные анимации. Как только мы добавим аниматор в объект Player в инспекторе, мы можем создать его анимацию, используя соответсвующее окно Unity и spritesheet игрока. На рисунке ниже показан пример анимации WalkingUp.

Теперь нам нужно настроить переходы анимации в аниматоре игрока. Он будет иметь два параметра: DirectionX и DirectionY, который описывает текущее направление движения игрока. Например, если игрок перемещается влево, DirectionX равен -1, а DirectionY равно 0. Эти параметры будут правильно установлены позже в скрипте движения.

Каждая анимация бездействия будет иметь переход к каждой анимации движения. Параметры направления должны иметь значения в соответствии с направлением анимации. Например, анимация Idle Left изменится на Walking Left, если DrectionX равно -1. Кроме того, каждая ходячая анимация будет иметь переход для своей анимации бездействия. Наконец, если игрок меняет направление ходьбы без остановки, нам нужно обновить анимацию. Итак, нам нужно добавить переходы между ходячей анимацией.

В конце концов, аниматор игрока должен выглядеть так, как показано на скриншоте ниже. На следующих картинках показаны примеры переходов между анимациями (IdleLeft -> WalkingLeft and WalkingLeft -> IdleLeft).

Теперь давайте создадим скрипт PlayerMovement. Все движения происходят в методе FixedUpdate. Мы используем входные данные по горизонтальной и вертикальной оси, чтобы проверить, должен игрок двигаться горизонтально или вертикально. Игрок может перейти в заданное направление, если он еще не переместился в противоположную сторону. Например, он может перемещаться влево, если он еще не перемещается вправо. Мы делаем это для обоих направлений. При перемещении в заданное направление нам нужно установить параметры аниматора. В конце мы применяем скорость к игроку в Rigidbody2D.

public class PlayerMovement : MonoBehaviour {

[SerializeField]

private float speed;

[SerializeField]

private Animator animator;

 

void FixedUpdate () {

float moveHorizontal = Input.GetAxis ("Horizontal");

float moveVertical = Input.GetAxis ("Vertical");

 

Vector2 currentVelocity = gameObject.GetComponent<Rigidbody2D> ().velocity;

 

float newVelocityX = 0f;

if (moveHorizontal < 0 && currentVelocity.x <= 0) {

newVelocityX = -speed;

animator.SetInteger ("DirectionX", -1);

} else if (moveHorizontal > 0 && currentVelocity.x >= 0) {

newVelocityX = speed;

animator.SetInteger ("DirectionX", 1);

} else {

animator.SetInteger ("DirectionX", 0);

}

 

float newVelocityY = 0f;

if (moveVertical < 0 && currentVelocity.y <= 0) {

newVelocityY = -speed;

animator.SetInteger ("DirectionY", -1);

} else if (moveVertical > 0 && currentVelocity.y >= 0) {

newVelocityY = speed;

animator.SetInteger ("DirectionY", 1);

} else {

animator.SetInteger ("DirectionY", 0);

}

 

gameObject.GetComponent<Rigidbody2D> ().velocity = new Vector2 (newVelocityX, newVelocityY);

}

}

В конце концов, префаб игрока должен выглядеть так, как показано на картинке ниже.

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

Начало боя

Игрок может начать сражения, взаимодействуя со спаунерами боев. Это будет неподвижный объект, который при касании игрока переключится на другую сцену под названием Battle Scene.

Кроме того, спаунер боев будет ответственным за создание объектов вражеских юнитов в боевой сцене. Мы сделаем это, создав префаб EnemyEncounter, с вражескими юнитами в качестве наследников. Как и игровым юнитам из сюжетной сцены, мы добавляем скрипт UnitStats и Sprite Renderer к вражеским юнитам. На картинке ниже показан пример вражеского юнита. Вы можете создать EnemyEncounter, создав новый префаб и добавив к нему желаемые вражеские юниты. Вам также нужно будет правильно установить его положение, чтобы оно было создано в верной позиции в сцене боя.

Итак, давайте создадим префаб под названием EnemySpawner. Этот префаб будет иметь коллайдер и Rigidbody2D, чтобы проверить наличие столкновений с игроком.

Кроме того, он будет иметь скрипт под названием SpawnEnemy, как показано ниже. Этот скрипт реализует метод OnCollisionEnter2D для проверки на наличие столкновений с игроком. Мы делаем это, проверяя, есть ли у объекта тег «Player» (не забудьте правильно установить тег на игроке). Если есть столкновение, он запустит сцену битвы и установит для атрибута нереста значение true.

Чтобы создать вражеские юниты в боевой сцене, скрипту нужнен префаб EnemyEncounter , и EnemySpawner не должен уничтожаться при смене сцен (выполненно в методе «Start»). При загрузке сцены (в методе OnSceneLoaded), если загружаемая сцена является сценой битвы, EnemySpawner уничтожит себя и создаст экземпляр EnemyEncounter, если spawning атрибут true. Таким образом, мы можем убедиться, что только один спаунер создаст экземпляр EnemyEncounter, но все они будут уничтожены.

public class SpawnEnemy : MonoBehaviour {

 

[SerializeField]

private GameObject enemyEncounterPrefab;

 

private bool spawning = false;

 

void Start() {

DontDestroyOnLoad (this.gameObject);

 

SceneManager.sceneLoaded += OnSceneLoaded;

}

 

private void OnSceneLoaded(Scene scene, LoadSceneMode mode) {

if (scene.name == "Battle") {

if (this.spawning) {

Instantiate (enemyEncounterPrefab);

}

SceneManager.sceneLoaded -= OnSceneLoaded;

Destroy (this.gameObject);

}

}

 

void OnTriggerEnter2D(Collider2D other) {

if (other.gameObject.tag == "Player") {

this.spawning = true;

SceneManager.LoadScene ("Battle");

}

}

}

Сейчас вы можете попробовать запустить свою игру и взаимодействовать со спаунером боев. Попробуйте создать пустую сцену битвы, чтобы попробовать смену сцен.

В следующей части мы продолжим создавать Боевую систему для нашей РПГ !


Источник - GamedevAcademy

Report Page