Как сделать змейку unity
Как я делал змейку на Unity3D( Часть 1)
Проект змейки на Unity3D
Итак в этой статье я попробую рассказать как сделать игру змейку на Unity3D. Поиграть в это чудо можно сразу на сайте кликнув на картинку под этим абзацем.
Игра будет простой но по завершении когда будет создан основной игровой процесс можно будет дополнять его различными элементами. Стены бонусы и так далее. Но в данный момент нужно хотя бы создать основные элементы игры это будет:
1) поле по которому будет перемещаться наша змейка и в котором будут появляться те элементы игры которые можете добавить и вы сами
2) Наш персонаж змея она будет состоять из 2 частей из головы и хвоста отличающегося лишь длинной.
3) И на пос ледок добавим на поле яблоко за счет которого наша змейка будет расти.
Вот по такому плану и будем делать нашу игру.
1) Создаем карту игры
Карта игры на самом деле это будет двумерный массив квадратных блоков размером по оси X =30 и по Y =20 клеток. Каждая клетка во время игры будет способна сменить свой внешний вид на основе значения номера ее состояния. Таким образом мы будем отображать то что нам нужно во время игрового процесса.
2) Для начала создаем новый проект. Для этого запускаем Unity3D и жмем на кнопку New project.
Указываем название проекта и путь где он будет находиться у вас на компьютере. А так же говорим что проект будет 2D до 3D мой skill еще маловат.
3) После создания проекта нужно взять картинки которые мы подготовили для нашей части карты и перенести в проект. Можно просто открыть проводник в том месте где лежат картинки и мышкой перетащить их в окно Unity3D под названием Assset
По сути это контейнер который может хранить все что угодно в себе поэтому в него мы положим картинку и далее заставим его подменять одну картинку на другую с помощью логики описанной в отдельном файле кода. Созданный Prefab назовем Map
5) Отлично теперь создадим логику поведения данного игрового объекта. Нужно определиться что он должен делать. Он выводит на экран какую то картинку не важно видим мы ее или она полностью прозрачна но он это делает. Итак мы сделаем список в который поместим набор картинок которые нужно будет выводить при определенных ситуациях. И объявим переменную в которой будем хранить номер картинки которую нужно показать в текущий момент времени. Для реализации этого плана создадим C# скрипт в котором опишем данное поведение. Нажимаем пунк меню Assets >> create >> C# Script
1) нужно объявить список наших картинок которые будут меняться по мере надобности.
Unity3D Объявляем список sprite объектов
2) Нужно объявить номер выбранной картинки чтобы блок знал как ему себя вести в конкретный момент игры
Unity3D объявление номера для выбора картинки из списка
public int IndexImg=0;
//выбранная картинка в текущий момент
3) Описать процесс смены картинки на блоке. Опишем данную процедуру отдельным блоком и назовем ее Changeimg()
а) Если картинка уже установлена с таким же номером то нет смысла ее менять
б) Если номер картинки не существует в списке то не меняем значение
Unity3D функция смены изображения спрайта
//если выбранная картинка отличается от той которая уже нарисована то меняем значение
int Listsize = img.Length;
//если номер не выходит за границы списка задаем номер картинки
if (Listsize > IndexImg) <
//получаем компонент который хранит картинку
SpriteRenderer S = this.GetComponent ();
//рисуем выбранную картинку
S.sprite = img [IndexImg];
//запоминаем текущий выбор картинки
Так как нам нужно чтобы это действие срабатывало постоянно добавляем вызов блока Changeimg() в функцию Update.
Unity3D функция Update
//вызываем функцию обновления картинки
Полный текст файла можно увидеть здесь. Либо скопировав его с этой страницы сайта
Unity3D файл MyMap.cs
public class MyMap : MonoBehaviour <
//объявить список наших картинок
//объявить номер выбранной картинки
public int IndexImg=0;
//выбранная картинка в текущий момент
//Описать процесс смены картинки на блоке
//если выбранная картинка отличается от той которая уже нарисована то меняем значение
int Listsize = img.Length;
//если номер не выходит за границы списка задем номер картинки
if (Listsize > IndexImg) <
//получаем компонент который хранит картинку
SpriteRenderer S = this.GetComponent ();
//рисуем выбранную картинку
S.sprite = img [IndexImg];
//запоминаем текущий выбор картинки
// Use this for initialization
// Вызываемый при каждом обновлении экрана
//вызываем функцию обновления картинки
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
Далее выделяем в окошен Hierarchy камеру и добавляем данный скрипт как компонент.
Двойным щелчком на файле открываем его для редактирования. И будем описывать следующие действия.
1) нам нужно создать карту по которой будет двигаться змейка. Конечно игрок не знает, что мы его привязываем в сетке ограничивая его действия но это нас не должно останавливать.
А) Создаем процедуру создания карты
1) Для того чтобы создавать карту из наших блоков объявим переменную которая свяжет нас созданный блок с этим скриптом.
Unity3D публикуем переменную Block
public GameObject Block;
Далее внутри кода MainGameScript создадим двумерный массив для поля игры допустим путь он будет размером 20 на 20 блоков.
Текстовое поле
GameObject[,] MapArea =new GameObject[20,20];
Хотя размер в дальнейшем придется подгонять на месте но пока сойдет. Итак далее с генерируем поле для игры. Для этого напишем процедуру GenMap() она будет выполнять следующие действия.
1) мы возьмем точку отсчета например 0 0 0 и поставим в нее блок далее сдвинем координаты на ширину блока на 1 по оси X и создадим еще блок и так 20 раз потом сдвинем координаты по Y на 1 и вернем координату X снова в точку 0 как печатная машинка будем печатать наши блоки с верху вниз и всего их будет 20 на 20 блоков. Такая импровизированная сетка из блоков.
Опишем данную функцию
Unity3D функция GenMap()
Vector3 PointStart=new Vector3(0,0,0);
//запускаем цикл расстановки по Y
Текстовое поле
//Задаем координаты для камеры
//так как скрипт находиться на том же объекте то задаем позицию так
//перед заданием параметра Size есго нужно получить из камеры
Camera C = this.GetComponent ();
Таким образом при старте уровня наша камера сама встанет в положенную точку.
Как видно мы можем увеличить количество клеток по оси X еще где то на 10 Для этого нужно увеличить значение в массиве карты на данное число и далее изменить цикл рисования точек. теперь он будет расcчитывать точку по оси X не 20 раз а 30.
Модифицируем функцию и массив.
Unity3D изменяем размер карты
GameObject[,] MapArea =new GameObject[30,20];
Vector3 PointStart=new Vector3(0,0,0);
//запускаем цикл расстановки по Y
Unity3D меняем значение положение камеры по оси X
//Задаем координаты для камеры
//так как скрипт находиться на том же объекте то задаем позицию так
//перед заданием параметра Size есго нужно получить из камеры
Camera C = this.GetComponent ();
После запуска программы можно заметить что вся наша карта теперь влезает в окно игры.
Теперь возвращаемся в assets и меняем изображение головы обратно на пустой блок.
Запускаем и проверяем что действительно все блоки стали прозрачными.
теперь можно приступить к созданию змеи которая в дальнейшем будет ползать по уровню.
Весь текст файл можно скачать здесь. или скопировать с текст написанный ниже
Весь код функции генерации карты игры.
public class MainGameScript : MonoBehaviour <
//переменная для связи с блоком карты из которых она будет строиться
public GameObject Block;
//карта игры 20 на 20 точек
GameObject[,] MapArea =new GameObject[30,20];
Как я делал змейку на Unity3D( Часть 2)
3) Движение персонажа
Unity3D Объявляем переменные для указания направления движения
Плюсы редактора игр Unity3D в том что большинство вещей он умеет делать сам итак игрок может по разному управлять движением змеи во первых он может изменять направление с помощью клавиш стрелок на клавиатуре а может с помощью клавиш WASD как привыкли большинство игроков по всему миру. Это учли разработчики и позволили создать группу кнопок которые выполняют одно действие данные группы называются Axis. Чтобы увидеть какие кнопки были заданы для тех или иных комбинаций достаточно открыть в редакторе пункт Edit >> Project Settings >> Input
Нас будут интересовать два значения это управление стрелками и управление клавишами SAWD. Значит получение значений при нажатии кнопок по горизонтали называется Horizontal а по вертикали называется Vertical.
Итак опишем функцию которая будет получать значение от данных параметров и задавать значение направление нашей змейке. Во время нажатия стрелок по одной оси движение по другой мы будем останавливать чтобы наша змейка перемещалась по диагонали хотя ничего ей это не запрещает делать и возможно игра от этого бы даже выиграла. Но в оригинале такого не было поэтому сделаем как в оригинале.
Unity3D обработка нажатия клавишь InputKey()
//получаем что нажал игрок по горизонтали по Х
float X = Input.GetAxis(«Horizontal»);
//если X больше 0 значит задаем DX будет равен 1
//если Y меньше 0 значит задаем DY будет равен 1
Unity3D описываем функцию двигаем персонажа
//если указано направление для движения то двигаемся
//смещаем змею с хвоста в сторону головы
//двигаем голову в новую точку
Unity3d Двигаем персонажа функцией Update
//обработка нажатия клавиш
//Двигаем персонажа если возможно
Итак если в данный момент запустить игру и заставить двигаться персонажа в любом направлении он за считанные миллисекунды долетит до границы наших блоков и далее просто вылетит за пределы, тем самым вызвав ошибку. Потому что рассчитывая индекс следующего блока при движении мы не проверяем его минимум или максимум.
Для того чтобы наш персонаж не вылетал за границы массива. Добавим проверку на вылет за границы во все стороны движения персонажа. Для этого нужно понять что нам нужно проверять лишь координаты головы так как все остальные части двигаются вслед за ней. Итак после расчета координат головы добавим проверку не вышли ли наши координаты за пределы массива и если это так то будем позволять змейке вылезать с другой стороны экрана Для этого будем либо обнулять координаты либо делать их максимальным значением в зависимости от того в каком направлении мы пришли к границе.
Unity3D проверяем выход персонажа за границы карты
//если указано направление для движения то двигаемся
//смещаем змею с хвоста в сторону головы
//двигаем голову в новую точку
//если мы вылезли на граници поля то 2 варианат 1 мы вылезаем с другой стороны либо умераем
//Проверяем координаты по оси X
if (Snake[0].IndexX > 29) Snake[0].IndexX = 0;
if (Snake[0].IndexX 19) Snake[0].IndexY = 0;
if (Snake[0].IndexY Как можно вспомнить когда мы генерировали карту мы ее делали размерами по X=30 а по Y=20 именно проверку на эти границы мы и добавили.
После запуска игры и указании нашей змейки направления с помощью клавиш мы увидим как змейка улетает в один угол экрана но не упирается в стену а выходит с другой стороны и продолжает свой путь.
Змейка летает в нужном нам направлении с невероятной скоростью. И это мы сейчас исправим. Дело в том что перерисовка окна происходит максимально быстро силами видео карты. Но нам нужно чтобы движения были более медленными. Для этого функцию движения персонажа будем запускать с задержкой в определенное количество миллисекунд. Это сделать довольно просто зададим переменную в которую поместим значение времени которое было во время движения змейки. Далее зададим параметр скорость который будет означать то количество времени которое должно пройти до следующего момента движения. И далее будем считывать при каждом кадре текущее время. Вычитая из текущего время тот момент когда была перерисована змейка в один прекрасный момент мы получим что разница во времени чуть больше чем скорость в этот момент мы запустим процесс движения снова запомнив время когда это движение было выполнено и так по кругу. Таким образом наша скорость будет привязана к секундам прошедшим в реальном времени а не к мощности вашего устройства.
Объявим глобальную переменную в начале кода которая будет означать скорость игры и сделаем ее публичной. Возможно захочется во время работы проекта поэкспериментировать с этим параметром что публикация его нам позволит реализовать. И еще две переменные которые будут нужны для расчета времени.
Unity3D Объявляем параметр скорости персонажа
public float speedSnake = 0.6f;
//объявляем 2 переменные для хранения
float RenderTime, //время перерисовки
CurrentTime; //Текущее время
Опишем в функции Update задержку перед движением персонажа.
Update добавляем управления скорости движения персонажа
//обработка нажатия клавиш
//получаем текущее время
//получаем разницу между текущим временем и когда был сделан ход змейки
if (alpha > speedSnake)
//Двигаем персонажа если возможно
//запоминаем время когда двинули персонажа
Теперь персонаж будет перемещаться медленнее и плавнее.
Следующая неприятность которую мы устраним это то что след от хвоста остается в тех местах где прошла змейка. Если бы мы были следопытом и выслеживали ее по пути следования думаю было бы интересно но сейчас это выглядит не очень хорошо. Итак что нужно сделать. В функции которая отвечает за движение змейки мы рисовали голову у первого блока рассчитав его позицию. По аналогии последний элемент нужно просто закрасить. Так как последний элемент смещается на одну клетку а в той клетке где он располагался уже ничего нет. Добавим данное действие в функцию.
Unity3D movesnake()
//если указано направление для движения то двигаемся
//затираем прозрачным цветом последнюю часть хвоста
//если змейка существует
SetColor(MapArea[Snake[L].IndexX, Snake[L].IndexY], 0);
//смещаем змею с хвоста в сторону головы
//двигаем голову в новую точку
//если мы вылезли на граници поля то 2 варианат 1 мы вылезаем с другой стороны либо умераем
//Проверяем координаты по оси X
if (Snake[0].IndexX > 29) Snake[0].IndexX = 0;
if (Snake[0].IndexX 19) Snake[0].IndexY = 0;
if (Snake[0].IndexY Теперь наш игрок обрел скорость и не оставляет следов за собой при движении персонажа по полю.
4) Расстановка бонусов на поле
Unity3D функция создания бонусов
//флаг смогли ли установить яблоко
bool Insert_Apple = true;
CoordX, //координата бонуса по оси Х
CoordY; //координата бонуса по оси У
//выполняем действие пока яблоко не найдет себе место
//получаем случайные 2 координаты
CoordX = Random.Range(0, 29);
CoordY = Random.Range(0, 19);
Debug.Log(«X «+CoordX+» Y «+CoordY);
//если полученные координаты не выходят за границы массива
//смотрим какой цвет у части карты
int cvet = GetColor(MapArea[CoordX, CoordY]);
//если в блоке картинка с номером 0
//задаем цвет картинки
SetColor(MapArea[CoordX, CoordY], 3);
//и останавливаем цикл установки яблока
Данную функцию вызовем например 10 раз при старте игры.
Unity3D функция добавления еды в случайные места
//Задаем координаты для камеры
//так как скрипт находиться на том же объекте то задаем позицию так
//перед заданием параметра Size есго нужно получить из камеры
Camera C = this.GetComponent ();
//Генерация карты игры
//Добавляем 2 блока к игроку
for (int I = 0; I При запуске игры мы увидим как на поле разбросанные яблоки.
Но если мы наедем на данный бонус нашем героем то ничего не произойдет это потому что мы не проверяем при движении героя куда он наступил. Итак давайте добавим реакцию игрока на прикосновение к бонусу. Для этого создадим новую функцию и назовем ее TestStep(); проверка шага.
Unity3D добавление функции проверки куда наступил игрок TestStep()
//получаем место куда должна переместится голова
int HeadX = Snake[0].IndexX,
//смотрим что находилось в данной точке
int Step = GetColor(MapArea[HeadX, HeadY]);
//проверяем куда наступил персонаж
//если наступили на яблоко то запускаем рост змеи
//помещаем яблоко в другом месте
Далее во время движения персонажа в функцию movesnake() добавляем проверку куда наступил игрок.
Unity3D двигаем персонажа
//если указано направление для движения то двигаемся
//затираем прозрачным цветом последнюю часть хвоста
//если змейка существует
SetColor(MapArea[Snake[L].IndexX, Snake[L].IndexY], 0);
//смещаем змею с хвоста в сторону головы
//двигаем голову в новую точку
//если мы вылезли на граници поля то 2 варианат 1 мы вылезаем с другой стороны либо умераем
//Проверяем координаты по оси X
if (Snake[0].IndexX > 29) Snake[0].IndexX = 0;
if (Snake[0].IndexX 19) Snake[0].IndexY = 0;
if (Snake[0].IndexY У вас может возникнуть вопрос а зачем отдельная функция. Ну возможно захотеться добавить еще какие то блоки например стены или другие препятствия легче запомнить что все это проверяется именно в определенной функции чем искать в коде один оператор условия. После запуска игры мы теперь можем перемещаться по уровню и наступая на яблоки заставлять расти нашу змейку.
Весть текст кода нашей текущей игры выглядит следующим образом.
Unity 3D код игрового процесса в файле MainGameScript()
public class MainGameScript : MonoBehaviour <
//Объявляем параметр скорости персонажа
public float speedSnake = 0.6f;
//обьявляем 2 переменные для хранения
float RenderTime, //время перерисовки
CurrentTime; //Текущее время
//переменная для связи с блоком карты из которых она будет строиться
public GameObject Block;
//карта игры 20 на 20 точек
GameObject[,] MapArea =new GameObject[30,20];
//структура описания одной части тела змеи
public int IndexX, IndexY;
//обьявим тело змеи длинной в 600 элементов
Telo[] Snake = new Telo[600];
//Объявляем переменные для указания направления движения змеи
//создаем функцию случайного размещения яблок на поле
//флаг смогли ли установить яблоко
bool Insert_Apple = true;
CoordX, //координата бонуса по оси Х
CoordY; //координата бонуса по оси У
//выполняем действие пока яблоко не найдет себе место
//получаем случайные 2 координаты
CoordX = Random.Range(0, 29);
CoordY = Random.Range(0, 19);
Debug.Log(«X «+CoordX+» Y «+CoordY);
//если полученные координаты не выходят за границы массива
//смотрим какой цвет у части карты
int cvet = GetColor(MapArea[CoordX, CoordY]);
//если в блоке картинка с номером 0
//задаем цвет картинки
SetColor(MapArea[CoordX, CoordY], 3);
//и останавливаем цикл установки яблока
//функция проверки куда наступил персонаж
//получаем место куда должна переместится голова
int HeadX = Snake[0].IndexX,
//смотрим что находилось в данной точке
int Step = GetColor(MapArea[HeadX, HeadY]);
//проверяем куда наступил персонаж
//если наступили на яблоко то запускаем рост змеи
//помещаем яблоко в другом месте
//Функция отображения змейки
//перебираем весь хвост пока не дойдем до его максимальной длинны
//Для 0 блока змейки задаем что это голова
SetColor(MapArea[Snake[0].IndexX, Snake[0].IndexY], 1);
//функция получения цвета у объекта
int GetColor(GameObject O)<
//читаем нужный нам компонент из объекта
//получаем номер используемой картинки
int Imgcolor = ComonentObject.IndexImg;
//возвращаем цвет выбранного блока
void SetColor(GameObject O, int Set_color)
//читаем нужный нам компонент из объекта
//Задаем номер используемой картинки
//функция роста змеиного хвоста
//смотрим какой длинны хвост
//стартовая точка будет 10 10 примерно центр экрана
//если хвост есть то добавляем его к последней части
//Функция обработки нажатия клавиш
//получаем что нажал игрок по горизонтали по Х
float X = Input.GetAxis(«Horizontal»);
//если X больше 0 значит задаем DX будет равен 1
//если Y меньше 0 значит задаем DY будет равен 1
//Генерация карты игры
//Добавляем 2 блока к игроку
SetColor(MapArea[Snake[L].IndexX, Snake[L].IndexY], 0);
//смещаем змею с хвоста в сторону головы
//двигаем голову в новую точку
//если мы вылезли на граници поля то 2 варианат 1 мы вылезаем с другой стороны либо умераем
//Проверяем координаты по оси X
if (Snake[0].IndexX > 29) Snake[0].IndexX = 0;
if (Snake[0].IndexX 19) Snake[0].IndexY = 0;