Введение
Прежде всего, хочу сразу отметить, что я не являюсь профессиональным разработчиком. В этой статье я постараюсь изложить свой опыт создания игры «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 я особо не занимался ее продвижением ну и само собой закачек не набралось.
В отсутствие профессионального художника, с иконкой пришлось работать самостоятельно:
Автор: WRXM4STER