Пример создания простой 2D игры для Android с использованием игрового движка Unity

Пример создания простой 2D игры для Android с использованием игрового движка Unity


Прежде всего, хочу сразу отметить, что я не являюсь профессиональным разработчиком. В этой статье я постараюсь изложить свой опыт создания игры «Feel Speed Racing». Данный материал, скорее всего не будет интересен тем, кто уже имеет большой опыт в разработке игр, но начинающим разработчикам, которые хоть немного работали с Unity думаю, будет интересно.

Дизайн

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

Разработка

Игра состоит из 2-х сцен: главное меню и сама игровая сцена:

Где «menu» это главное меню а «1» это игровая сцена.

Главное меню

Для создания столь незамысловатого меню нам потребуется элемент управления GUI, который является стандартным в Unity.

В качестве фона я использовал спрайт с именем «background» заполненный серым цветом. Вы же можете выбрать что угодно.

Далее создаем скрипт «menu.cs»(Щелкаем правой кнопкой->выбираем Create-> C# Script) и вешаем его на background.

Содержимое скрипта:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

public class menu : MonoBehaviour {
 
 public GUIStyle mystyle; //объявляется для того чтобы изменять начертание GUI компонентов(шрифт, размер и.т.п.)
 string score; //переменная для хранения пройденной дистанции
 void Start () 
 {
  StreamReader scoredata = new StreamReader (Application.persistentDataPath + "/score.gd"); //создание файловой переменной
  score = scoredata.ReadLine (); //чтение строки
  scoredata.Close (); //закрытие файловой переменной
 }
 
 
 void Update () {
 
 }
 void OnGUI(){
  GUI.Box (new Rect (Screen.width*0.15f, Screen.height*0.8f, Screen.width*0.7f, Screen.height*0.1f), "MAX DISTANCE:"+score,mystyle); //создаем небольшое окошко для показа пройденного расстояния
  if (GUI.Button (new Rect (Screen.width*0.15f, Screen.height*0.25f, Screen.width*0.7f, Screen.height*0.1f), "Start game",mystyle)) //создаем кнопку для запуска игровой сцены
  {
   Application.LoadLevel(1);//Загрузка игровой сцены
  }
  if (GUI.Button (new Rect (Screen.width*0.15f, Screen.height*0.4f, Screen.width*0.7f, Screen.height*0.1f), "Exit",mystyle)) //создаем кнопку для выхода из игры
  {
   Application.Quit();//Выход из игры
  }
 }
}

В результате должно получиться примерно вот так:

Шрифт, цвет и размер GUI элементов вы можете изменить с помощью MyStyle.

Создание игровой сцены

Основными на этой сцене элементами является дорога, автомобиль и шкала топлива.

1. Дорога:

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

Кидаем спрайт с дорогой на игровую сцену и подгоняем по размерам камеры.

Затем добавляем как дочерние объекты внутрь дороги 4 блока с препятствиями, топливный бак и не забываем добавить к ним Box Collider 2D.Еще надо отметить Is Triger для пересечения с автомобилем.

Теперь создаем скрипт moveroad.cs и вешаем его на нашу дорогу.

Добавляем в него следующий код:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class moveroad : MonoBehaviour {

 public GUIStyle mystyle;//создание стиля
 int f,fuelst;
 float score=0,speed=-0.2f,data,fuelpos;// переменные для хранения расстояния, скорости и рекорда
 public GameObject block;// игровой объект для размещения блока
 public GameObject block1;
 public GameObject block2;
 public GameObject block3;
 public GameObject fuel;
 bool turbotriger=false;

 void Start () 
 {
  StreamReader scoredata = new StreamReader (Application.persistentDataPath + "/score.gd");
  data = float.Parse(scoredata.ReadLine ());//чтение с файла информации о рекорде
  scoredata.Close ();
 }
 

 void Update () 
 {
  transform.Translate (new Vector3 (0f,speed,0f));//движение дороги с заданной выше скоростью
  score = score + (speed*-10);// подсчет расстояния
  if (transform.position.y < -19f) // если дорога уходит за пределы камеры то она "теле портируется" вверх
  {
   transform.position=new Vector3(0f,33.4f,0f);//новая позиция дороги
   block.transform.position=new Vector3(10.15f,block.transform.position.y,block.transform.position.z); 
   block1.transform.position=new Vector3(8.42f,block1.transform.position.y,block1.transform.position.z); 
   block2.transform.position=new Vector3(6.62f,block2.transform.position.y,block2.transform.position.z); 
   block3.transform.position=new Vector3(4.95f,block3.transform.position.y,block3.transform.position.z); 
   fuel.transform.position=new Vector3(11.86f,fuel.transform.position.y,fuel.transform.position.z);
                        //скрытие за пределы камеры всех препятствий(блоков)
   f = Random.Range (0, 5);//случайное появление на дороге 1-го из 4-х блоков или канистры с бензином
   switch (f) 
   {
   case 0:block.transform.position=new Vector3(2.40f,block.transform.position.y,block.transform.position.z); break;
   case 1:block1.transform.position=new Vector3(0.90f,block1.transform.position.y,block1.transform.position.z); break;
   case 2:block2.transform.position=new Vector3(-0.80f,block2.transform.position.y,block2.transform.position.z); break;
   case 3:block3.transform.position=new Vector3(-2.35f,block3.transform.position.y,block3.transform.position.z); break;
   case 4:
    fuelst=Random.Range(0,4);
    if(fuelst==0){fuelpos=2.40f;}
    if(fuelst==1){fuelpos=0.90f;}
    if(fuelst==2){fuelpos=-0.80f;}
    if(fuelst==3){fuelpos=-2.35f;}
    fuel.transform.position=new Vector3(fuelpos,fuel.transform.position.y,fuel.transform.position.z);
    break;
   }
   if (score>data)// если текущее пройденное расстояние превышает то что записано в файле рекорда то идет обновление данных
   {
    StreamWriter scoredata=new StreamWriter(Application.persistentDataPath + "/score.gd");//создаем файловую переменную для хранения пройденного расстояния
    scoredata.WriteLine(score);//записываем новое значение в файл
    scoredata.Close();//закрываем файловую переменную
   }
  }
  
 }
 void OnGUI(){
  GUI.Box (new Rect (0, 0, Screen.width, Screen.height*0.05f), "Distance(m): " + score,mystyle);//создаем окно для подсчета расстояния
 }
}

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

Что бы решить эту проблему надо создать дубликат уже готовой дороги и немного изменить скрипт.

Должно получиться вот так.

2. Автомобиль:

Кидаем спрайт автомобиля на сцену и устанавливаем его в любое место на дороге. Затем создаем скрипт carcontroller.cs и вешаем его на автомобиль.

Содержимое carcontroller.cs:

using UnityEngine;
using System.Collections;
using UnityStandardAssets.CrossPlatformInput;

public class carconroller : MonoBehaviour 
{

 void Start () 
 {

 }
 
 
 public void Update () 
 {
  if (transform.rotation.z !=0) //проверка столкновения коллайдера автомобиля и препятствия, при столкновении происходит загрузка меню
  {
   Application.LoadLevel (0);
  }
  
 }
 }
 public void OnGUI()
 {
  if (GUI.RepeatButton (new Rect (Screen.width*0.1f, Screen.height*0.9f, Screen.width*0.2f, Screen.height*0.08f), "L")) //создаем кнопку для движения влево
  {
   if (transform.position.x > -2.4f)
   {
    transform.Translate (new Vector3 (-0.05f, 0f, 0f));
   }
  }

  if (GUI.RepeatButton (new Rect (Screen.width*0.7f, Screen.height*0.9f, Screen.width*0.2f, Screen.height*0.08f), "R")) //создаем кнопку для движения вправо
  {
   if (transform.position.x < 2.4f)
   {
    transform.Translate (new Vector3 (0.05f, 0f, 0f)); 
   }
  }
 }

}


Теперь автомобиль может перемещаться.

3.Шкала топлива:

Для создания шкалы потребовалось 2 спрайта одинаковых размеров, но разных цветов (красный, зеленый). И сделать один из них дочерним (зеленый).

Далее создаем скрипт fuelscript.cs, вешаем его на fuel и добавляем в него код:

using UnityEngine;
using System.Collections;

public class fuelscript : MonoBehaviour {

 public GameObject fuelall;
 float mytimer=100f;// задание плавающего числа
 // Use this for initialization
 void Start () 
 {

 }
 void Update () 
 {
  mytimer = 100f;
  mytimer -= Time.deltaTime;//изменения числа с течением времени
  if (mytimer/mytimer==1f) //проверка на период времени в 1 секунду
  {
   fuelall.transform.position=new Vector3(fuelall.transform.position.x-0.0011f,fuelall.transform.position.y,fuelall.transform.position.z);
   fuelall.transform.localScale = new Vector3(fuelall.transform.localScale.x-0.001f, 1, 1);
                        //выше идет сдвижение влево и уменьшение по ширине зеленой полосы для имитации шкалы
  }
  if (fuelall.transform.localScale.x < 0) //если шкала исчезла то загрузка идет загрузка главного меню 
  {
   Application.LoadLevel(0);
  }
 }
}


Дорога у меня это road183 и ее дубликат road183(1). В ее дочерний объект fueltrack нужно добавить скрипт для обнаружения пересечения с автомобилем и восполнения топлива. 

Создаем скрипт triger.cs и вешаем его на fueltrack в обеих дорогах и отмечаем как Is Triger. Код:

using UnityEngine;
using System.Collections;

public class triger : MonoBehaviour {


 public GameObject fuel;//добавляем сюда greenfuel
 // Use this for initialization
 void Start () {
 
 }
 
 // Update is called once per frame
 void Update () {

 }
 void  OnTriggerEnter2D(Collider2D col)
 {
  if (col.gameObject.name == "playercar") //проверка пересечения автомобиля и объекта fuel
  {
   fuel.transform.position=new Vector3(0,fuel.transform.position.y,fuel.transform.position.z);
   fuel.transform.localScale = new Vector3(1, 1, 1);
                        //восстановление у объекта fuel стандартных значений
  }
 }


}


Итог

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

В отсутствие профессионального художника, с иконкой пришлось работать самостоятельно:


Источник - Habrahabr

Report Page