Создание пиксельной RPG с помощью Unity 3D
С помощью этого пошагового руководства вы сможете самостоятельно создать игру в жанре RPG в Unity.
План работы:
- Создание игрового мира с помощью тайловых карт.
- Базовая анимация для игрового персонажа.
- Простое взаимодействие объектов.
- Создание меню.
Посмотреть демо готовой игры можно здесь.

Начало работы
Создайте проект 2D Unity.
Добавьте образцы пользовательского интерфейса Unity из Asset Store. Этот актив будет использоваться для создания сцены меню.
Создайте в окне проекта следующее дерево папок (на русском или английском языке, в зависимости от вашей версии программы):
-
-
- Animations
►Animals
►OtherCharacters
►Plants
►Players-
-
- Fonts
- Prefabs
- Scenes
- Scripts
- Sprites
-
►Animals
►HouseIndoor
►Houses
►Items
►Players
►Roads
►Rocks
►Tilesets -
- ►Vegetation
- TilePalettes
Вы можете скачать любые бесплатные шрифты, которые вам нравятся и использовать их при работе с проектом (добавьте их в папку Шрифты/Fonts).
Что касается спрайтов, то вы можете воспользоваться этим сервисом для их создания, если у вас нет готового набора.
Объекты не должны превышать размер 64 на 64 пикселя
Персонаж и его экипировка могут быть созданы с помощью еще одного простого онлайн создателя спрайтов.
Большинство спрайтов размещено на листах спрайтов, которые вам необходимо предварительно нарезать в Unity 3D.
Выберите лист спрайтов в окне проекта, как показано на изображении ниже:

В инспекторе измените параметр Sprite Mode на Multiple и нажмите кнопку Sprite Editor:

Теперь в меню Slice выберите значение Type.
Лучший способ здесь — метод проб и ошибок, особенно, если вы не знаете точный размер ваших спрайтов (в противном случае просто вбейте известные значения).
Нажмите кнопку Slice и проверьте результат. Вы также можете переименовать каждый спрайт, щелкнув по нему мышкой.

После закрытия окна Slice, справа от листа спрайтов в окне проекта появляется небольшая кнопка; это позволяет видеть и получать доступ ко всем нарезанным вами спрайтам:

Создание карты
Для быстрой прорисовки базовых карт пиксельных RPG используется специальная палитра.
Откройте меню Window ⇒ 2D ⇒ Tile Palette.
Теперь создайте новую палитру и добавьте спрайты.
При необходимости можно добавить несколько палитр.

Теперь вам нужно выбрать нужные плитки:

И установите тип коллайдера в инспекторе.
Выбранные плитки должны иметь значение None, деревья и стены — Sprite или Grid.


Теги и слои
Создайте недостающие теги:
- MapBackground
- AICharacter
Создайте несколько слоев для хранения объектов.
Определите также порядок сортировки слоев в разделе Sorting Layers:

Создание анимации
Добавьте все типы имеющихся анимаций в один и тот же GameObject:
1.выбирайте все спрайты движения в одну сторону (на север)
2.перетаскивайте выбранные спраты в меню их в Hierarchy, чтобы добавить GameObject и сохранить анимационный клип
3. выбирайте оставшиеся спрайты движения и перетащите их на созданный GameObject
4. сделайте то же самое со всеми другими анимациями
5. Проверьте анимационные клипы в окне Animation (меню Window ⇒ Animation ⇒ Animation).

Добавьте камеру как дочерний элемент персонажа.
Это позволит перемещать камеру во время движения персонажа.

В инспекторе в разделе Sprite Renderer установите для параметра Sorting Layer значение Player.

Добавьте 2D-компонент Rigidbody.
Установите Gravity Scale = 0 и в разделе Constraints заморозьте вращение по Z.

Добавьте 2D-компонент Collider для взаимодействия с другими GameObject, такими как предметы или животные.

Прямо сейчас у нашего персонажа есть анимационные клипы, но он не воспроизводит никакой анимации или движений. Для этого нам нужно использовать Animator и написать скрипт.
Откройте меню Window ⇒ Animation ⇒ Animator.
Добавьте два параметра: DirectionX и DirectionY.

Для каждого перехода определите в Инспекторе следующее:
►снимите галочку с Has Exit Time
►в разделе Settings снимите флажок Fixed Duration
►в разделе Settings установите Transition Duration на 0.
►определить условия (например, EastIdle ⇒ WalkNorth ≡ DirectionY ⇒ 0, WalkNorth ⇒ IdleNorth ≡ DirectionX = 0 & DirectionY = 0)

Создайте новый скрипт PlayerMovement и прикрепите его к GameObject.
Добавьте этот код:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
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);
}
} Вернувшись в Инспектор, установите значения:

После чего создайте префаб персонажа.
В окне Project удалите Animation Controller, созданный при добавлении клипов анимации.
Создайте новый, используя контекстное меню Create ⇒ Animation Controller, и назовите его.
В окне Animator создайте два новых состояния из Blend Trees.

Переименуйте объекты в окне Inspector, IdleTree и MoveTree, как показано на изображени ниже.

В этом случае вам нужно будет обработать только два перехода: между режимом ожидания и движением. Чтобы определить их, нужен новый логический параметр:

Создайте два перехода и установите настройки и условия, как показано на изображениях ниже.

Создайте 4 новых параметра float:
►DirectionX
►НаправлениеY
►LastDirX
►LastDirY
Параметр LastDir будут использоваться, чтобы узнать, какой неактивный клип вы должны использовать.
Теперь дважды щелкните IdleTree:
установите Blend Type на 2D Simple Directional
установите Parameters в LastDirX и LastDirY
в разделе Motion добавьте 4 элемента.
для каждого движения добавьте параметр idle clip (вверх, вниз, вправо, влево) и установите значения PosX и PosY

Вернитесь к Base Layer и сделайте то же самое с MoveTree.

Вернувшись в окно Scene, проверьте, есть ли у GameObject игрока компонент Animator.
Если он существует, удалите его.
Перетащите вновь созданный контроллер из окна проекта в инспектор.
Создайте новый скрипт PlayerBlendMovement и добавьте его в плеер.
Добавьте этот код:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerBlendMovement : MonoBehaviour
{
//Movement variables
public float MoveSpeed = 4f;
//Animation variables
Animator thisAnim;
float lastX, lastY;
// Start is called before the first frame update
void Start()
{
thisAnim = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
Move();
}
void Move()
{
Vector3 rightMovement = Vector3.right * MoveSpeed * Time.deltaTime * Input.GetAxis("Horizontal");
Vector3 upMovement = Vector3.up * MoveSpeed * Time.deltaTime * Input.GetAxis("Vertical");
Vector3 heading = Vector3.Normalize(rightMovement + upMovement);
transform.position += rightMovement;
transform.position += upMovement;
UpdateAnimation(heading);
}
void UpdateAnimation(Vector3 dir)
{
if((dir.x == 0f) && (dir.y == 0f))
{
//idle animation if no key pressed
thisAnim.SetFloat("LastDirX", lastX);
thisAnim.SetFloat("LastDirY", lastY);
thisAnim.SetBool("Movement", false);
}
else
{
lastX = dir.x;
lastY = dir.y;
thisAnim.SetBool("Movement", true);
}
thisAnim.SetFloat("DirectionX", dir.x);
thisAnim.SetFloat("DirectionY", dir.y);
}
} Создание карты
Для начала рекомендуется создать несколько тайловых карт:
►карта мира
►тайловая карта помещений
►карта растений
►Карта дорог и скал
Таким образом вы сможете быстрее работать с фоновыми изображениями на ваших локациях.
Карта тайлов мира содержит информацию о мире, такую как вода, песок, трава, и т.п.
Прежде всего, создайте новую карту в меню Hierarchy с помощью контекстного меню 2D Object ⇒ Tilemap.
Это добавляет сетку и карту тайтлов, которую нужно переименовать в Tilemap_Map.
Установите значение Grid’s Layer на MapBackground.
Далее в Grid под Grid установите размер ячейки равным размеру вашего спрайта, как показано на изображении ниже.

В Tilemap Map в разделе Tilemap Renderer установите Sorting Layer на Map Background.

Откройте окно Tile Palette, выберите Active Tilemap и палитру.
Попробуйте нарисовать свой игровой мир, на рисунке ниже наш вариант, в качестве примера.

Вы могли заметить, что вокруг острова много воды и в данный момент персонаж может ходить по воде.
Чтобы это исправито вам нужно выполнить несколько действий:
убедитесь, что у плитки с водой есть коллайдер
добавить новый Tilemap Collider 2D в тайловую карту
добавить Composite Collider 2D на карту
указать Tilemap Collider 2D использовать Composite Collider 2D
добавьте Edge Radius в Composite Collider 2D, чтобы сгладить границы

Теперь персонаж может перемещаться только по мелководью.

В Иерархии выберите существующую сетку и создайте новую карту листов с именем Tilemap_Indoor.

На этот раз вы будете рисовать Tilemap_Indoor, используя несколько палитр (например, стены, полы).

Добавим несколько деревьев на нашу локацию.
В меню Hierarchy выберите существующую сетку и создайте новую карту листов с именем Tilemap_Plants.
Создайте новый слой, как дочерний элемент сетки.

Таким же образом вы можете добавить дороги или другие объекты на карте.

Создание интерактивных объектов
Для начала поместите префаб персонажа в исходное местоположение игрока.

Теперь вы сможете бегать по миру с помощью клавиш со стрелками.
Согласитесь, пока выглдит пустовато.
Давайте поставим несколько зданий.
Поместите спрайт дома в нужное место, затем установите Sorting Layer и добавьте Polygon Collider 2D.

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

так как это RPG, персонажу нужен инвентарь и древо умений.
Создайте новый UI Text и назовите его MessageBox, пока не заполняйте эту область.

Создайте еще один UI Text и назовите его CounterBox, также оставьте это поле пустым.

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

Используйте для этого следующий скрипт:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LoadGameScene : MonoBehaviour
{
public string SceneToLoad = "Island000";
private int p1Toggle;
private int p2Toggle;
private int p3Toggle;
public void LoadSceneByName()
{
p1Toggle = PlayerPrefs.GetInt("p1Toggle");
p2Toggle = PlayerPrefs.GetInt("p2Toggle");
p3Toggle = PlayerPrefs.GetInt("p3Toggle");
if ((p1Toggle == 1) || (p2Toggle == 1) || (p3Toggle == 1))
{
SceneManager.LoadScene(SceneToLoad);
}
}
} Далее нужно настроить кнопку Player Setup, используя следующий код:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AppQuit : MonoBehaviour
{
public void QuitApp()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#elif UNITY_WEBGL
if(Screen.fullScreen == true)
{
Screen.fullScreen = !Screen.fullScreen;
}
Application.OpenURL("http://mountainpath.ch/cmsimplexh/index.php?Unity-3D");
#else
Application.Quit();
#endif
}
} Далее приступим к созданию меню для основных действий:
►Инвентарь: когда игрок собирает/обменивает вещи.
►Загрузка: когда игра запускается (запуск или изменение уровня).
►Сохранение игрового процеса.
Для этого вам понадобится следующий скрипт:
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GridScript : MonoBehaviour
{
public Text CounterBox;
private string CounterText;
public string PlayerName;
private int PlayerType;
private string PlayerCharName;
public Transform male;
public Transform female;
public Transform skeleton;
private int i;
public int nbCrab;
public int nbFish;
public int nbApple;
public int nbBanana;
public int nbOrange;
public int nbMushroom;
public int nbKeySilver;
public int nbKeyYellow;
public int nbFishingRod;
public int nbBackpack;
public int nbCoins;
public int nbOrbs;
public int nbBread;
public int HouseBrownDoorLock;
public int HouseGreenDoorLock;
public int Backpack1n;
public int fishingrod1n;
public int KeyGold1n;
public int KeySilver1n;
public int TentCoinsn;
public int WellCoinsn;
public int OrangeTree1n;
public int AppleTree1n;
public int BananaTree1n;
public int BananaTree2n;
public int BlueMushroom1n;
public int CrabRedE1n;
public int CrabRedE2n;
public int CrabRedE3n;
public int CrabRedN1n;
public int CrabRedS1n;
public int CrabRedS2n;
public int CrabRedW1n;
public int CrabRedW2n;
public int CrabRedW3n;
public int FishRedWn;
public int FishBlueEn;
public int FishWhiteNn;
public int FishBlackSn;
public int ThisSceneID;
{
if (PlayerPrefs.GetInt("p1Toggle") == 1)
{
i = 1;
PlayerName = PlayerPrefs.GetString("p1Name");
PlayerType = PlayerPrefs.GetInt("p1CharType");
MyPlayerChar(PlayerType);
}
if (PlayerPrefs.GetInt("p2Toggle") == 1)
{
i = 2;
PlayerName = PlayerPrefs.GetString("p2Name");
PlayerType = PlayerPrefs.GetInt("p2CharType");
MyPlayerChar(PlayerType);
}
if (PlayerPrefs.GetInt("p3Toggle") == 1)
{
i = 3;
PlayerName = PlayerPrefs.GetString("p3Name");
PlayerType = PlayerPrefs.GetInt("p3CharType");
MyPlayerChar(PlayerType);
}
ThisSceneID = PlayerPrefs.GetInt("ThisSceneID" + i);
nbCrab = PlayerPrefs.GetInt("nbCrab" + i);
nbFish = PlayerPrefs.GetInt("nbFish" + i);
nbApple = PlayerPrefs.GetInt("nbApple" + i);
nbBanana = PlayerPrefs.GetInt("nbBanana" + i);
nbOrange = PlayerPrefs.GetInt("nbOrange" + i);
nbMushroom = PlayerPrefs.GetInt("nbMushroom" + i);
nbKeySilver = PlayerPrefs.GetInt("nbKeySilver" + i);
nbKeyYellow = PlayerPrefs.GetInt("nbKeyYellow" + i);
nbBackpack = PlayerPrefs.GetInt("nbBackpack" + i);
nbFishingRod = PlayerPrefs.GetInt("nbFishingRod" + i);
nbCoins = PlayerPrefs.GetInt("nbCoins" + i);
nbOrbs = PlayerPrefs.GetInt("nbOrbs" + i);
nbBread = PlayerPrefs.GetInt("nbBread" + i);
HouseBrownDoorLock = PlayerPrefs.GetInt("HouseBrownDoorLock" + i);
HouseGreenDoorLock = PlayerPrefs.GetInt("HouseGreenDoorLock" + i);
Backpack1n = PlayerPrefs.GetInt("Backpack1n" + i);
fishingrod1n = PlayerPrefs.GetInt("fishingrod1n" + i);
KeyGold1n = PlayerPrefs.GetInt("KeyGold1n" + i);
KeySilver1n = PlayerPrefs.GetInt("KeySilver1n" + i);
TentCoinsn = PlayerPrefs.GetInt("TentCoinsn" + i);
WellCoinsn = PlayerPrefs.GetInt("WellCoinsn" + i);
AppleTree1n = PlayerPrefs.GetInt("AppleTree1n" + i);
BananaTree1n = PlayerPrefs.GetInt("BananaTree1n" + i);
BananaTree2n = PlayerPrefs.GetInt("BananaTree2n" + i);
OrangeTree1n = PlayerPrefs.GetInt("OrangeTree1n" + i);
BlueMushroom1n = PlayerPrefs.GetInt("BlueMushroom1n" + i);
CrabRedE1n = PlayerPrefs.GetInt("CrabRedE1n" + i);
CrabRedE2n = PlayerPrefs.GetInt("CrabRedE2n" + i);
CrabRedE3n = PlayerPrefs.GetInt("CrabRedE3n" + i);
CrabRedN1n = PlayerPrefs.GetInt("CrabRedN1n" + i);
CrabRedS1n = PlayerPrefs.GetInt("CrabRedS1n" + i);
CrabRedS2n = PlayerPrefs.GetInt("CrabRedS2n" + i);
CrabRedW1n = PlayerPrefs.GetInt("CrabRedW1n" + i);
CrabRedW2n = PlayerPrefs.GetInt("CrabRedW2n" + i);
CrabRedW3n = PlayerPrefs.GetInt("CrabRedW3n" + i);
FishRedWn = PlayerPrefs.GetInt("FishRedWn" + i);
FishBlueEn = PlayerPrefs.GetInt("FishBlueEn" + i);
FishWhiteNn = PlayerPrefs.GetInt("FishWhiteNn" + i);
FishBlackSn = PlayerPrefs.GetInt("FishBlackSn" + i);
if (Backpack1n == 0)
{
GameObject.Find("Backpack1").SetActive(false);
}
if (fishingrod1n == 0)
{
GameObject.Find("fishingrod1").SetActive(false);
}
if (KeyGold1n == 0)
{
GameObject.Find("KeyGold1").SetActive(false);
}
if (KeySilver1n == 0)
{
GameObject.Find("KeySilver1").SetActive(false);
}
if (BlueMushroom1n == 0)
{
GameObject.Find("BlueMushroom1").SetActive(false);
}
if (CrabRedE1n == 0)
{
GameObject.Find("CrabRedE1").SetActive(false);
}
if (CrabRedE2n == 0)
{
GameObject.Find("CrabRedE2").SetActive(false);
}
if (CrabRedE3n == 0)
{
GameObject.Find("CrabRedE3").SetActive(false);
}
if (CrabRedN1n == 0)
{
GameObject.Find("CrabRedN1").SetActive(false);
}
//disable animal (crab) object if already picked up
if (CrabRedS1n == 0)
{
GameObject.Find("CrabRedS1").SetActive(false);
}
if (CrabRedS2n == 0)
{
GameObject.Find("CrabRedS2").SetActive(false);
}
if (CrabRedW1n == 0)
{
GameObject.Find("CrabRedW1").SetActive(false);
}
if (CrabRedW2n == 0)
{
GameObject.Find("CrabRedW2").SetActive(false);
}
if (CrabRedW3n == 0)
{
GameObject.Find("CrabRedW3").SetActive(false);
}
SetCounterText();
}
void Update()
{
SetCounterText();
if (Input.GetKeyDown(KeyCode.Q))
{
QuitGame(0,0);
}
}
void SetCounterText()
{
CounterText = "";
CounterText = CounterText + "Crab: " + nbCrab;
CounterText = CounterText + " Fish: " + nbFish;
CounterText = CounterText + " Apple: " + nbApple;
CounterText = CounterText + " Banana: " + nbBanana;
CounterText = CounterText + " Orange: " + nbOrange;
CounterText = CounterText + " Mushroom: " + nbMushroom;
CounterText = CounterText + " Bread: " + nbBread;
CounterText = CounterText + "\r\n Backpack: " + nbBackpack;
CounterText = CounterText + " Fishing rod: " + nbFishingRod;
CounterText = CounterText + " Silver key: " + nbKeySilver;
CounterText = CounterText + " Gold key: " + nbKeyYellow;
CounterText = CounterText + " Coins: " + nbCoins;
CounterText = CounterText + " Orbs: " + nbOrbs;
CounterBox.text = CounterText;
} /
public void QuitGame(int SceneNb, int flag)
{
float posX, posY;
Scene ThisScene = SceneManager.GetActiveScene();
ThisSceneID = ThisScene.buildIndex;
PlayerPrefs.SetInt("ThisSceneID" + i, ThisSceneID);
PlayerPrefs.SetInt("nbCrab" + i, nbCrab);
PlayerPrefs.SetInt("nbFish" + i, nbFish);
PlayerPrefs.SetInt("nbApple" + i, nbApple);
PlayerPrefs.SetInt("nbBanana" + i, nbBanana);
PlayerPrefs.SetInt("nbOrange" + i, nbOrange);
PlayerPrefs.SetInt("nbMushroom" + i, nbMushroom);
PlayerPrefs.SetInt("nbKeySilver" + i, nbKeySilver);
PlayerPrefs.SetInt("nbKeyYellow" + i, nbKeyYellow);
PlayerPrefs.SetInt("nbBackpack" + i, nbBackpack);
PlayerPrefs.SetInt("nbFishingRod" + i, nbFishingRod);
PlayerPrefs.SetInt("nbCoins" + i, nbCoins);
PlayerPrefs.SetInt("nbBread" + i, nbBread);
PlayerPrefs.SetInt("nbOrbs" + i, nbOrbs);
PlayerPrefs.SetInt("HouseBrownDoorLock" + i, HouseBrownDoorLock);
PlayerPrefs.SetInt("HouseGreenDoorLock" + i, HouseGreenDoorLock);
PlayerPrefs.SetInt("Backpack1n" + i, Backpack1n);
PlayerPrefs.SetInt("fishingrod1n" + i, fishingrod1n);
PlayerPrefs.SetInt("KeyGold1n" + i, KeyGold1n);
PlayerPrefs.SetInt("KeySilver1n" + i, KeySilver1n);
PlayerPrefs.SetInt("TentCoinsn" + i, TentCoinsn);
PlayerPrefs.SetInt("WellCoinsn" + i, WellCoinsn);
PlayerPrefs.SetInt("AppleTree1n" + i, AppleTree1n);
PlayerPrefs.SetInt("BananaTree1n" + i, BananaTree1n);
PlayerPrefs.SetInt("BananaTree2n" + i, BananaTree2n);
PlayerPrefs.SetInt("OrangeTree1n" + i, OrangeTree1n);
PlayerPrefs.SetInt("BlueMushroom1n" + i, BlueMushroom1n);
PlayerPrefs.SetInt("CrabRedE1n" + i, CrabRedE1n);
PlayerPrefs.SetInt("CrabRedE2n" + i, CrabRedE2n);
PlayerPrefs.SetInt("CrabRedE3n" + i, CrabRedE3n);
PlayerPrefs.SetInt("CrabRedN1n" + i, CrabRedN1n);
PlayerPrefs.SetInt("CrabRedS1n" + i, CrabRedS1n);
PlayerPrefs.SetInt("CrabRedS2n" + i, CrabRedS2n);
PlayerPrefs.SetInt("CrabRedW1n" + i, CrabRedW1n);
PlayerPrefs.SetInt("CrabRedW2n" + i, CrabRedW2n);
PlayerPrefs.SetInt("CrabRedW3n" + i, CrabRedW3n);
PlayerPrefs.SetInt("FishRedWn" + i, FishRedWn);
PlayerPrefs.SetInt("FishBlueEn" + i, FishBlueEn);
PlayerPrefs.SetInt("FishWhiteNn" + i, FishWhiteNn);
PlayerPrefs.SetInt("FishBlackSn" + i, FishBlackSn);
if (flag == 1)
{
posX = -18.0f; //origin X scene2
posY = -4.0f; //origin Y scene2
} else
{
GameObject playerObject = GameObject.FindGameObjectsWithTag("Player")[0];
posX = playerObject.transform.position.x;
posY = playerObject.transform.position.y;
}
PlayerPrefs.SetFloat("PosX" + i, posX); //origin X scene1 = 1.63
PlayerPrefs.SetFloat("PosY" + i, posY); //origin Y scene1 = -4.28
SceneManager.LoadScene(SceneNb);
}
void MyPlayerChar(int CharType)
{
float posX = PlayerPrefs.GetFloat("PosX" + i);
float posY = PlayerPrefs.GetFloat("PosY" + i);
if (CharType == 0)
{
Instantiate(male, new Vector3(posX, posY, 0f), male.rotation);
}
else if (CharType == 1)
{
Instantiate(female, new Vector3(posX, posY, 0f), female.rotation);
}
else if (CharType == 2)
{
Instantiate(skeleton, new Vector3(posX, posY, 0f), skeleton.rotation);
}
}
} В Unity назначьте префабы трех игроков и текстовое поле Counter, как показано ниже.

Создание дорожных знаков
Знак — это статический объект, который дает игроку некоторую информацию, когда он сталкивается с ним.
Создайте новый SignScript и добавьте его к знаку.
Используйте код:
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
public class SignScript : MonoBehaviour
{
public string TextToWriteLine1;
public string TextToWriteLine2;
public Text MessageBox;
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Player")
{
SetMessageText();
}
}
void SetMessageText()
{
if (gameObject.name == "SignRed")
{
MessageBox.text = TextToWriteLine1 + "\n\r" + TextToWriteLine2 + ", " + GameObject.Find("Grid").GetComponent<GridScript>().PlayerName + "!";
}
else
{
MessageBox.text = TextToWriteLine1 + "\n\r" + TextToWriteLine2;
}
StartCoroutine(WaitSomeSec(3));
}
IEnumerator WaitSomeSec(int waitTime)
{
yield return new WaitForSecondsRealtime(waitTime);
MessageBox.text = "";
}
} Вернувшись в Unity3D, свяжите MessageBox и добавьте текст для отображения.

Добавление звуковых эффектов
Фоновая музыка добавляется к объекту Grid.

Чтобы иметь возможность включать и выключать музыку, добавьте этот код в функцию Grid’s Update:
if (Input.GetKeyUp(KeyCode.M))
{
gameObject.GetComponent<AudioSource>().mute = !gameObject.GetComponent<AudioSource>().mute;
} Меню выбора языка
Создайте обычный шаблон с фоновым изображением и флажком и кнопкой для каждого языка, как показано ниже.

Добавьте новый GameObject и назовите его LocalizationManager.
Добавьте сценарий LocalizationManager.cs.
Добавьте еще один GameObject и назовите его StartupManager.
Добавьте сценарий StartupManager.cs.
Перетащите LocalizationManager GO в область OnClick каждой языковой кнопки и установите локализованный файл JSON.

Сцена меню
Содержание пользовательского интерфейса (включая названия кнопок и области прокрутки) переводятся путем добавления LocalizedText.cs к объекту Text и ввода ключа перевода.

Для DropDown нужен сценарий DropDownOverwrite, который воссоздает параметры перехода с нуля.
Текст вызывается с помощью этой команды:
LocalizationManager.instance.GetLocalizedValue("Player_Dropdown_Boy") Далее ипользуйте следующий код:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DropDownOverwrite : MonoBehaviour
{
Dropdown.OptionData m_NewData, m_NewData2, m_NewData3;
List<Dropdown.OptionData> m_Messages = new List<Dropdown.OptionData>();
public Sprite Image1, Image2, Image3;
Dropdown m_Dropdown;
string m_MyString;
int m_Index;
void Start()
{
m_Dropdown = GetComponent<Dropdown>();
m_Dropdown.ClearOptions();
and add to messages List
m_NewData = new Dropdown.OptionData();
m_NewData.text = LocalizationManager.instance.GetLocalizedValue("Player_Dropdown_Boy");
m_NewData.image = Image1;
m_Messages.Add(m_NewData);
and add to messages List
m_NewData2 = new Dropdown.OptionData();
m_NewData2.text = LocalizationManager.instance.GetLocalizedValue("Player_Dropdown_Girl");
m_NewData2.image = Image2;
m_Messages.Add(m_NewData2);
and add to messages List
m_NewData3 = new Dropdown.OptionData();
m_NewData3.text = LocalizationManager.instance.GetLocalizedValue("Player_Dropdown_Skeleton");
m_NewData3.image = Image3;
m_Messages.Add(m_NewData3);
foreach (Dropdown.OptionData message in m_Messages)
{
m_Dropdown.options.Add(message);
m_Index = m_Messages.Count - 1;
}
}
} 
Реализация WebGL
WebGL не принимает папку StreamingAssets, и ее файлы не входят в состав приложения.
Чтобы иметь возможность скомпилировать сборку WebGL, вам нужно загрузить папку StreamingAssets и ее файлы.
Необходимый код уже реализован в сценарии LocalizationManager, но перед компиляцией его необходимо раскомментировать.

- Если у вас возникнут затруднения или вопросы по работе с Unity, обратитесь к нашему подробному руководству для начинающих.







Создание пиксельной RPG с помощью Unity и костылей. #facepalm
Unity — это и есть набор костылей
Да, стопицот строк по каждому крабу, рыбе и банану впечатляют.
Чувак, ты не слышал про Enum, абстрактные классы и наследование?
Словари и коллекции?
Половина простыней умещается в одну строчку массива.
Причем там лежит и банан, и краб.
Не теряя уникальных свойств.
Для учета их места и количества, контроля рекомендуется создавать отдельный класс EnemyManager.
Спавн, триггеры и уничтожение этих предметов ТОЛЬКО через него..
Контроллер анимации не должен быть похож на несколько слипшихся паутин.
Львиную часть логики оттуда надо переносить в скрипты.
А вот сохранение и загрузка игр на Web под Unity это да , костыли.
Вы правы, код не идеален. Тут показан принцип.