Как я делал змейку на Unity3D( Часть 1)

Проект змейки на Unity3D

    Итак в этой статье я попробую рассказать как сделать игру змейку на Unity3D. Поиграть в это чудо можно сразу на сайте кликнув на картинку под этим абзацем.

http://cibergod.narod.ru/snake/

  Игра будет простой но по завершении когда будет создан основной игровой процесс можно будет дополнять его различными элементами. Стены бонусы и так далее. Но в данный момент нужно хотя бы создать основные элементы игры это будет:

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

   2) Наш персонаж змея она будет состоять из 2 частей из головы и хвоста отличающегося лишь длинной.

   3) И на пос ледок добавим на поле яблоко за счет которого наша змейка будет расти.

Вот по такому плану и будем делать нашу игру.

1) Создаем карту игры

   Карта игры на самом деле это будет двумерный массив квадратных блоков  размером по оси X =30 и по Y =20 клеток. Каждая клетка во время игры будет способна сменить свой внешний вид на основе значения номера ее состояния. Таким образом мы будем отображать то что нам нужно во время игрового процесса.

    1) Для изменения изображений на экране их нужно создать в MSPaint или скачать из таблицы ниже. Конечно ни кто не мешает нарисовать и свои варианты так как я не особо художник и рисовал картинки больше схематично чем как художник.
 Пусто (прозрачная картинка голова змеи хвост змеи яблоко
 
 

 

 

    2) Для начала создаем новый проект. Для этого запускаем Unity3D и жмем на кнопку New project.

    Указываем название проекта и путь где он будет находиться у вас на компьютере. А так же говорим что проект будет 2D до 3D мой skill еще маловат.

    3) После создания проекта нужно взять картинки которые мы подготовили для нашей части карты и перенести в проект. Можно просто открыть проводник в том месте где лежат картинки и мышкой перетащить их в окно Unity3D под названием Assset
    4) Теперь создадим блок карты который будет менять эти 4 картинки по мере надобности. Итак жмем в меню пункт Asset >> Create >> Prefab.

    По сути это контейнер который может хранить все что угодно в себе поэтому в него мы положим картинку и далее заставим его подменять одну картинку на другую с помощью логики описанной в отдельном файле кода. Созданный Prefab назовем Map

    Логика такая на уровне мы собираем какой ни будь игровой объект и далее можно сохранить его в prefab контейнер а далее этим одним контейнером пользоваться чтобы не собирать каждый раз новый такой же объект.  Поэтому мы возьмем одну из картинок и перенесем ее в окно сцены. а далее из окошка Hierarchy перенесем его в объект prefab таким образов сохранив его как игровой объект который можно будет в дальнейшем изменять получая доступ к его различным параметрам.

   5) Отлично теперь создадим логику поведения данного игрового объекта. Нужно определиться что он должен делать. Он выводит на экран какую то картинку не важно видим мы ее или она полностью прозрачна но он это делает. Итак мы сделаем список в который поместим набор картинок которые нужно будет выводить при определенных ситуациях. И объявим переменную в которой будем хранить номер картинки которую нужно показать в текущий момент времени. Для реализации этого плана создадим C# скрипт в котором опишем данное поведение. Нажимаем пунк меню Assets >> create >> C# Script
    Созданный объект назовем MyMap. Важно запомнить данное имя так как во множестве случаев через это имя мы будем получать доступ к параметрам скрипта. Единственный нюанс имя нужно назначать сразу после создания. Если его переименовать потом то Unity3D может не понять что в файле написано. Так как она создает шаблоную заготовку файла в котором указывает имя класса который называеться точно так же как и сам файл.
    6) Двойным щелчком открываем код MyMap и в нем нужно записать следующие действия.

1) нужно объявить список наших картинок которые будут меняться по мере надобности.

Unity3D Объявляем список sprite объектов

//объявить список наших картинок

public Sprite[] img;

2) Нужно объявить номер выбранной картинки чтобы блок знал как ему себя вести в конкретный момент игры

Unity3D объявление номера для выбора картинки из списка

//объявить номер выбранной картинки

public int IndexImg=0;

//выбранная картинка в текущий момент

int CurrentIndex=0;

3) Описать процесс смены картинки на блоке. Опишем данную процедуру отдельным блоком и назовем ее Changeimg()

    а) Если картинка уже установлена с таким же номером то нет смысла ее менять

    б) Если номер картинки не существует в списке то не меняем значение

Unity3D функция смены изображения спрайта

//Описать процесс смены картинки на блоке

void Changeimg()

{

//если выбранная картинка отличается от той которая уже нарисована то меняем значение

if(CurrentIndex !=IndexImg){

//размер списка

int Listsize = img.Length;

//если номер не выходит за границы списка задаем номер картинки

if (Listsize > IndexImg) {

//получаем компонент который хранит картинку

SpriteRenderer S = this.GetComponent<SpriteRenderer> ();

//рисуем выбранную картинку

S.sprite = img [IndexImg];

//запоминаем текущий выбор картинки

CurrentIndex=IndexImg

}

}

}

    Так как нам нужно чтобы это действие срабатывало постоянно добавляем вызов блока Changeimg() в функцию Update.

Unity3D функция Update

// Вызываемый при каждом обновлении экрана

void Update () {

//вызываем функцию обновления картинки

Changeimg ();

}

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

Unity3D файл MyMap.cs

using UnityEngine;

using System.Collections;

public class MyMap : MonoBehaviour {

//объявить список наших картинок

public Sprite[] img;

//объявить номер выбранной картинки

public int IndexImg=0;

//выбранная картинка в текущий момент

int CurrentIndex=0;

//Описать процесс смены картинки на блоке

void Changeimg()

{

//если выбранная картинка отличается от той которая уже нарисована то меняем значение

if(CurrentIndex !=IndexImg){

//размер списка

int Listsize = img.Length;

//если номер не выходит за границы списка задем номер картинки

if (Listsize > IndexImg) {

//получаем компонент который хранит картинку

SpriteRenderer S = this.GetComponent ();

//рисуем выбранную картинку

S.sprite = img [IndexImg];

//запоминаем текущий выбор картинки

CurrentIndex=IndexImg;

}

}

}

// Use this for initialization

void Start () {

}

// Вызываемый при каждом обновлении экрана

void Update () {

//вызываем функцию обновления картинки

Changeimg ();

}

}

    7) Для того чтобы скрипт понимал кому менять картинку его нужно добавить в компоненты объекта Map для этого выбираем объект map и в его свойствах добавим скрипт нажав кнопку в меню Component >> Scripts >> My Map

    Этим действием в нашем окне Inspector у объекта map появиться новая вкладка с названием нашего скрипта. Ее нужно будет нам настроить, а именно назначить связь картинок с картинками в Assets.

    Для этого раскрываем компонент и задаем размер нашему списку картинок их у нас 4 значит так и задаем размер равный этому значению.

   

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

 номерназвание описание 
 0 1Это полностью прозрачная картинка будет означать пустые клетки нашего уровня 
 1 HeadЭто голова змеи она будет рисоваться в том месте где будет расположена голова 
 2 chankХвост змеи будет рисоваться в том месте где расположен хвост 
 3 4Яблоко которое будет заставлять нашу змею расти. 

    8) Создадим новый файл кода который будет основным описанием всей остальной логики нашей игры. Для этого нажимаем в меню пункт Asset >> Create >> C# Script

    Далее называем файл MainGameScript -  главный игровой скрипт. По сути в него мы напишем описание создание карты и всех элементов игры которые нам понадобятся. Конечно Вам ни кто не запрещает наделять сотню таких файлов и в каждом что то описать но я думаю в таком простом игровом проекте это лишнее действие.

    Далее выделяем в окошен Hierarchy камеру и добавляем данный скрипт как компонент.

    Двойным щелчком на файле открываем его для редактирования. И будем описывать следующие действия.

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

    А) Создаем процедуру создания карты

      1) Для того чтобы создавать карту из наших блоков объявим переменную которая свяжет нас созданный блок с этим скриптом.

Unity3D публикуем переменную Block

//переменная для связи с блоком карты из которых она будет строиться

public GameObject Block;

      Сохранив код нужно вернуться в Unity и связать блок map с данной переменной в компоненте. Для этого на против названия переменной можно нажать на точку. Откроется диалоговое окно со списком всех подходящих объектов. Выбираем наш элемент map двойным щелчком. И он свяжется с блоком из assets

    Далее внутри кода MainGameScript создадим двумерный массив для поля игры допустим путь он будет размером 20 на 20 блоков.

Текстовое поле

//карта игры 20 на 20 точек

GameObject[,] MapArea =new GameObject[20,20];

    Хотя размер в дальнейшем придется подгонять на месте но пока сойдет. Итак далее с генерируем поле для игры. Для этого напишем процедуру GenMap() она будет выполнять следующие действия.

   1) мы возьмем точку отсчета например 0 0 0 и поставим в нее блок далее сдвинем координаты на ширину блока на 1 по оси X и создадим еще блок и так 20 раз потом сдвинем координаты по Y на 1 и вернем координату X снова в точку 0 как печатная машинка будем печатать наши блоки с верху вниз и всего их будет 20 на 20 блоков. Такая импровизированная сетка из блоков.

    Опишем данную функцию

Unity3D функция GenMap()

void GenMap(){

//точка отсчета

Vector3 PointStart=new Vector3(0,0,0);

//запускаем цикл расстановки по Y

for (int Y=0; Y<20; Y++) {

//запускаем цикл расстановки по X

for (int X=0; X<20; X++) {

                 //сдвигаем точку 

PointStart=new Vector3(X,Y,0);

//создаем блок в указанном месте

MapArea [X, Y] = (GameObject)Instantiate (Block, PointStart, Quaternion.identity);

}

}

}

   

    Итак процедура должна срабатывать при старте нашего уровня поэтому мы добавляем ее вызов в процедуру Start

Unity3D вызов функции создания карты игры

// Срабатывает при начале игры 1 раз

void Start () {

//создаем карту для игры

GenMap ();

}

    Далее можно запустить проект и увидить что прозрачный фон сыграл с нами злую шутку так как все блоки прозрачные мы их не увидим на экране а лишь перейдя в редактор и нажав на конкретный блок в окне Hierachy сможем удивить расположение того или иного блока. 

Но нам нужно настроить камеру таким образом чтобы она видела все наше поле поэтому мы всем создаваемым блокам назначим временно видимую текстуру например головы. Для этих целей выберем нашу часть карты map в assetы и в его параметрах выставим IndexImg = 1. Таким образом каждый новый блок будет изначально иметь текстуру головы. И мы увидим границы поля и сможем подогнать координаты камеры под поле.

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

    Координаты камеры должны быть в позиции (9.5f, 9.5f, -10) . Добавим в скрипт запуска игры назначение данных параметров камере.Дальность изображения я выставил в 10.  Таким образом я могу сделать поле шире по горизонтали еще на несколько клеток чтобы змейка бегала по всему экрану.

Текстовое поле

// Настройка параметров при старте игры

void Start () {

//Задаем координаты для камеры

Vector3 CameraPose = new Vector3 (9.5f, 9.5f, -10);

//так как скрипт находиться на том же объекте то задаем позицию так

this.transform.position = CameraPose;

//перед заданием параметра Size есго нужно получить из камеры

Camera C = this.GetComponent ();

//задаем параметр

C.orthographicSize = 10;

GenMap ();

}

    Таким образом при старте уровня наша камера сама встанет в положенную точку.

   

    Как видно мы можем увеличить количество клеток по оси X еще где то на 10 Для этого нужно увеличить значение в массиве карты на данное число и далее изменить цикл рисования точек. теперь он будет расcчитывать точку по оси X не 20 раз а 30.

    Модифицируем функцию и массив.

Unity3D изменяем размер карты

//карта игры 20 на 20 точек

GameObject[,] MapArea =new GameObject[30,20];

void GenMap(){

//точка отсчета

Vector3 PointStart=new Vector3(0,0,0);

//запускаем цикл расстановки по Y

for (int Y=0; Y<20; Y++) {

//запускаем цикл расстановки по X

for (int X=0; X<30; X++) {

//сдвигаем точку 

PointStart=new Vector3(X,Y,0);

//создаем блок в указанном месте

MapArea [X, Y] = (GameObject)Instantiate (Block, PointStart, Quaternion.identity);

}

}

}

После запуска проекта станет ясно что камеру нужно подвинуть немного правее на то количество клеток которые мы создали. 

Меняем значение по X у камеры вместо 9,5 на 14.5f

Unity3D меняем значение положение камеры по оси X

// Настройка параметров при старте игры

void Start () {

//Задаем координаты для камеры

Vector3 CameraPose = new Vector3 (14.5f, 9.5f, -10);

//так как скрипт находиться на том же объекте то задаем позицию так

this.transform.position = CameraPose;

//перед заданием параметра Size есго нужно получить из камеры

Camera C = this.GetComponent ();

//задаем параметр

C.orthographicSize = 10;

GenMap ();

}

   После запуска программы можно заметить что вся наша карта теперь влезает в окно игры.

    Теперь возвращаемся в assets и меняем изображение головы обратно на пустой блок.

    Запускаем и проверяем что действительно все блоки стали прозрачными. 

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

Весь текст файл можно скачать здесь. или скопировать с текст написанный ниже

Весь код функции генерации карты игры.

using UnityEngine;

using System.Collections;

public class MainGameScript : MonoBehaviour {

//переменная для связи с блоком карты из которых она будет строиться

public GameObject Block;

//карта игры 20 на 20 точек

GameObject[,] MapArea =new GameObject[30,20];

void GenMap(){

//точка отсчета

Vector3 PointStart=new Vector3(0,0,0);

//запускаем цикл расстановки по Y

for (int Y=0; Y<20; Y++) {

//запускаем цикл расстановки по X

for (int X=0; X<30; X++) {

//сдвигаем точку 

PointStart=new Vector3(X,Y,0);

//создаем блок в указанном месте

MapArea [X, Y] = (GameObject)Instantiate (Block, PointStart, Quaternion.identity);

}

}

}

// Настройка параметров при старте игры

void Start () {

//Задаем координаты для камеры

Vector3 CameraPose = new Vector3 (14.5f, 9.5f, -10);

//так как скрипт находиться на том же объекте то задаем позицию так

this.transform.position = CameraPose;

//перед заданием параметра Size есго нужно получить из камеры

Camera C = this.GetComponent ();

//задаем параметр

C.orthographicSize = 10;

GenMap ();

}

// Update is called once per frame

void Update () {

}

}

2) Создаем персонажа

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

    Итак в файле MainGameScript объявим структуру которая будет хранить 2 параметра целого типа.

Unity3D структура описания одной части тела змеи

//структура описания одной части тела змеи

struct Telo 

{

public int IndexX, IndexY;

}

    Так как данная структура хранит описание всего об одном элементе змеи а у нас их может быть 20*30=600 частей это когда змея занимает всю карту. Для этого мы объявим массив структур размером в 600 элементов если будет мало добавим еще.

Unity3D объявим тело змеи длинной в 600 элементов

//объявим тело змеи длинной в 600 элементов

Telo[] Snake = new Telo[600];

    Но как понятно мы не будет сразу заполнять все 600 значений для того чтобы знать текущий размер змеи мы объявим переменную для хранения длинны. Чтобы можно было в любой момент узнать текущую длину.

Unity3D Длинна змеи

//Длинна змеи

int SizeSnake=0;

    Далее напишем две функции которые дальше очень облегчат нам жизнь. 

Итак первая функция будет получать текущее значение картинки в блоке на карте.

Unity3D функция получения цвета у объекта

//функция получения цвета у объекта

int GetColor(GameObject O){

//читаем нужный нам компонент из объекта

MyMap ComonentObject=O.GetComponent();

//получаем номер используемой картинки

int Imgcolor = ComonentObject.IndexImg;

//возвращаем цвет выбранного блока

return Imgcolor;

}

   Вторя будет будет задавать значение картинки на указанном блоке карты.

Unity3D задание картинки блоку

//задание картинки блоку

void SetColor(GameObject O, int Set_color)

{

//читаем нужный нам компонент из объекта

MyMap ComonentObject=O.GetComponent();

//Задаем номер используемой картинки

ComonentObject.IndexImg = Set_color;

}

    Отлично теперь можно написать функцию которая будет добавлять к змейке части тем самым заставляя ее расти. Назовем ее GrowSnake();

Логика ее работы будет следующая. Первым делом мы узнаем размер если функция выполняется впервые то у нас еще нет ничего ни головы ни может поэтому мы должны создать первую точку голову а далее при следующим добавлении частей будут.

Unity3D создание функции добавления частей змейки GrowSnake()

    //функция роста змеиного хвоста

    void GrowSnake()

    {

        //смотрим какой длинны хвост

        if (SizeSnake == 0)

        {

            //стартовая точка будет 10 10 примерно центр экрана

            Snake[SizeSnake].IndexX = 10; 

            Snake[SizeSnake].IndexY = 10;

        }

        else 

        { 

            //если хвост есть то добавляем его к последней части

            Snake[SizeSnake].IndexX = Snake[SizeSnake-1].IndexX;

            Snake[SizeSnake].IndexY = Snake[SizeSnake-1].IndexY;

        }

     //увеличиваем длину

         SizeSnake++;

    }

    Наша функция GrowSnake() просто добавлении координаты в массив описывающий расположение тела змеи но у нас нет функции которая прочтет эти координаты и закрасит именно те блоки в которых стоит наша змейка. Координаты в данном смысле это номера тех блоков в двумерном массиве который мы создали при создании карты. Теперь опишем функцию которая будет пробегать по массиву тела змейки и закрашивать блоки. Итак нулевой блок у нас будет голова поэтому после закраски всех блоков картинкой хвоста мы будем брать нулевой блок и закрашивать его картинкой головы.

Unity3D Функция отображения змейки

 //Функция отображения змейки 

    void DrawSnake()

    {

        //перебираем весь хвост пока не дойдем до его максимальной длинны

        for (int I = 1; I < SizeSnake; I++) 

            //задаем картинку блоку вида 0 картинки

            SetColor(MapArea[Snake[I].IndexX, Snake[I].IndexY], 2);

        //если змейка существует и ее размер >0

        if (SizeSnake > 0) 

            //Для 0 блока змейки задаем что это голова

            SetColor(MapArea[Snake[0].IndexX, Snake[0].IndexY], 1);

    }

    При первом старте игры нужно будет создать нашего героя вызвав функцию определенное количество. Функция сработает и заполнит массив тела змейки. Добавим данный вызов в процедуру Start в которой мы уже вызвали рисование карты в прошлом.

Unity3D вызываем функцию GrowSnake при старте игры

// Настройка параметров при старте игры

void Start () {

//Задаем координаты для камеры

Vector3 CameraPose = new Vector3 (14.5f, 9.5f, -10);

//так как скрипт находиться на том же объекте то задаем позицию так

this.transform.position = CameraPose;

//перед заданием параметра Size есго нужно получить из камеры

Camera C = this.GetComponent ();

//задаем параметр

C.orthographicSize = 10;

        //Генерация карты игры

GenMap ();

        //Добавляем 2 блока к игроку

        for (int I = 0; I < 2; I++) 

            //вызываем функцию добавления к змейке части

            GrowSnake();

}

    Данная функция должна будет отображать нашего персонажа при каждой перерисовке окна поэтому добавим ее в блок Updtae который отвечает за этот процесс. Она уже есть в нашем коде ее создал за нас редактор Unity3d при создании кода.

Unity3D перерисовка персонажа

// Функция обновления окна игры

void Update () {

   //перерисовка персонажа

        DrawSnake(); 

}

    При старте игры мы увидим только голову нашего персонажа. Это связанно с тем что координаты остальных частей рассчитались по предыдущим частям и так как змейка стоит у нас на месте то все координаты совпали. Поэтому мы видим голову.

   



Как я делал змейку на Unity3D (часть 2)
379 0 850 2
0
RENDER.RU