Как я делал CandyCrash на Unity3D( Часть 2)

Продолжение блога  Как я делал CandyCrash на Unity3D( Часть 1)


04) Группировка блоков

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

    У большинства объектов Unity3D есть такой параметр как tag именно он позволит нам понимать на какой именно тип кубика пользователь нажал мышкой. Итак для того чтобы присвоить теги каждому элементу нужно сначала создать их. Их будет 8 штук и они будут называться точно так же как номера элементов массива. В дальнейшем Вы поймете зачем так упрощать.

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

    Видим в окне свойств небольшое выпадающее меню именно в него мы и добавим список наших тегов. К сожалению как это сделать из кода я не знаю. Поэтому щелкаем по данному списку и нажимаем на пункт  Add tag (добавить тэг).

    В Unity3d параметр в tag есть встроенный набор а так же можно добавлять свои уникальные значения для этого в открывшемся редакторе жмем на клавишу + и добавляем в поля цифры от 0 до 8



    Данные значения имеют текстовый тип данных поэтому в них можно написать что угодно но нас интересуют эти цифры.  После того как все добавили щелкаем снова в Assert store ( хранилище ресурсов ) на  наш prefab и проверяем что в выпадающем меню тегов они появились. 

    Если ничего нет то возвращаемся обратно и повторяем попытку с кнопкой плюс. Если все хорошо то продолжаем. Теперь когда теги существуют мы можем их присваивать нашим игровым объектам. Для этого снова заходим в наш скрипт Change созданный в предыдущем примере. И дополняем функцию Update присвоением значения tag.

Дополняем функцию Update

// Функция срабатывает  каждый раз при обновлении экрана

void Update () {

/*при обновлении экрана задаем картинку по номеру из списка*/

Sprite.sprite = ImgList [IndexImg];

/*задаем уникальный tag для данного типа картинки*/

tag = IndexImg.ToString ();

}

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

    Запускаем и смотрим.

 Для всех белых будет tag 1Для всех синих будет tag 6

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

05) Создание карты

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


 Выделяем и удаляем 

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

    Создаем и называем CreateMap

  Настроем обзор камеры для этого в параметре Size установим значение 6.5 это число я узнал экспериментальным методом.
    Добавляем скрипт как компонент к камере.

 

    Далее двойным щелчком откроем код CreateMap. Он откроется в том редакторе который настроен в Unity3D. У меня MonoDevelop не работают  горячие клавиши поэтому я перенастроил Unity на Visual Studio в параметрах.

   Объявим 2 переменные которые будут обозначать ширину и высоту поля в квадратах.

   

Переменные определяющие размеры карт

using UnityEngine;

using System.Collections;

public class CreateMap : MonoBehaviour {

   

   /*Объявляем переменные*/

    public float StartX, StartY;

// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

}

}

    Чтобы создать объект нужно передать в функцию оригинал объекта и задать его координаты и  скорость движения.  Итак решаем задачи по порядку.

   1) создаем переменную типа GameObject для ссылки на оригинал нашего Prefab который мы создавали все это время. У нашей камеры после сохранения проекта появиться ряд параметров.

   2) Свяжем переменную Box с нашем Prefab перетащив из Assert Store объект с названием Box.

   3) Зададим размеры поля пусть это будет 2х2 для пробы а далее может вынесем их в отдельные настройки но пока так.

   Теперь у нас есть все элементы. Для задания логики работы создания карты. Итак как будем создавать. 

1) В центре экрана у нас точка 0 0 как в OpenGL а квадратики у нас размером 1 х 1 поэтому будем от точки 0 х 0 зеркально двигаться в обе стороны таким образом вместо 2 блоков будут создаваться 4 по диагонали и 4 по вертикали. С помощью двух циклов.

   Но сначала попробуем программно добавить 1 блок чтобы был понятен принцип. Для этого используется команда Instantiate(Box, new Vector3(0, 0, 0), Quaternion.identity); Ее мы и запишем в функцию Start нашего скрипта CreateMap

Модифицируем Start функцию

void Start () {

        Instantiate(Box, new Vector3(0, 0, 0), Quaternion.identity);

}

   Запускаем игру и видим что Unity3d самостоятельно добавим на нашу сцену блок в точку 0 0 0. 

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

Добавляем цикл к функции Start ()

void Start () {

        for (float X = -StartX; X < StartX; X++ ) Instantiate(Box, new Vector3(X, 0, 0), Quaternion.identity);

}

    При запуска цикл сработает 4 раза от -StartX до +StartX тем самым нарисует в данном случае 4 квадратика.

    Добавляем 2 строку цикла которая будет создавать элементы в высоту по Y

Добавляем цикл рисования координате Y

void Start () {

        for (float Y = -StartY; Y < StartY; Y++) 

        for (float X = -StartX; X < StartX; X++ ) 

            Instantiate(Box, new Vector3(X, Y, 0), Quaternion.identity);

}

    В итоге наша функция из 3 строк рисует любую карту из кубиков зависимую от двух значений StartX и StartY

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

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

06) Обработка нажатия на кубик

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

   Шаг 1. Обработка клика мышкой по объекту сцены. Для данной ситуации в Unity3D существует специальная функция которую нужно дописать в скрипт Change созданный в статье 3) Смена цвета блока. выглядит она следующим образом.

Unity3d Обработка нажатий мышкой

void OnMouseDown() 

{

     //если нажата любая клавиша мыши что то делаем

     }

    Шаг 2. При нажатии мышкой на данный объект попробуем спрятать его изменив параметр IndexImg на 0; Для этого открываем наш скрипт Change  и добавляем в него следующий код.

Unity3D скрываем блок

/*добавляем обработчик нажатия мышкой*/

void OnMouseDown() 

{

/*задаем прозрачный цвет для данного объекта*/

IndexImg = 0;

}

В итоге весь код будет выглядеть следующим образом.

Unity3D код скрипта Chage

using UnityEngine;

using System.Collections;

public class Change : MonoBehaviour {

//Объявим  список картинок

public Sprite[] ImgList;

//Объявляем  переменную для доступа к картинке

SpriteRenderer Sprite;

//номер выбранного элемента массива

public int IndexImg=0;

// Функция срабатывает  1 раз при создании объекта

void Start () {

/*получаем случайное число от 0 до 8*/

IndexImg = Random.Range (1, 8);

//получаем доступ к компоненту

Sprite = GetComponent ();

}

// Функция срабатывает  каждый раз при обновлении экрана

void Update () {

/*при обновлении экрана задаем картинку по номеру из списка*/

Sprite.sprite = ImgList [IndexImg];

/*задаем уникальный tag для данного типа картинки*/

tag = IndexImg.ToString ();

}

/*добавляем обработчик нажатия мышкой*/

void OnMouseDown() 

{

/*задаем прозрачный цвет для данного объекта*/

IndexImg = 0;

}

}

Запускаем проект 

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

    Ура мы создали игру если вам этого хватит то можно закончить. Но мы продолжаем. Задачка в том что нам необходимо нажав на один кубик посмотреть что вокруг и если он стоит один то не уничтожать его а если вокруг есть кубики того же цвета то уничтожить их все. 

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

    Итак Unity3D умеет искать на сцене объекты по одному из типов либо о имени но все кубики у нас называются box либо по типу элемента но все элементы у нас одного типа prefab либо по параметру tag вот то что нужно нам. Так же поиск может возвращать первый найденный элемент а может все в виде массива. Мы будем получать весь массив элементов.

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

Unity3D поиск объектов по tag

/*добавляем обработчик нажатия мышкой*/

void OnMouseDown() 

{

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

//Для сохранения результатов поиска

GameObject[] O = GameObject.FindGameObjectsWithTag (tag);

/*задаем прозрачный цвет для данного объекта*/

IndexImg = 0;

}

    А далее чтобы проверить что функция нашла элементы. Мы возьмем найденные объекты по очереди и зададим для каждого параметр IndexImg = 0; Таким образом спрятав все квадратики одного цвета целиком. Итак после щелчка мышкой по компоненту в функцию поиска FindGameObjectsWithTag  мы передаем значение параметра tag текущего кубика на сцене, как мы помним для разных цветов данное значение отлично. Далее мы получаем динамический список элементов в котором лежат все объекты с таким же параметром tag что и наш. С помощью цикла foreach очень просто перебирать значения из массива. Выбрав из массива игровой объект GameObject мы понимаем что параметр IndexImg лежит в нестандартных компонентах объекта в опубликован нами в скрипте Chage.

    По этому мы воспользуемся знакомой командой GetComponent<имя секции в свойствах объекта> ( ); для получения доступа к компоненту change. А далее просто зададим параметр IndexImg=0 в компоненте change для найденного квадратика. И последнее если мы нашли все объекты с нужным нам tag то перебрав массив мы зададим значение для всей группы элементов. Поэтому строку IndexImg=0 для этого элемента можно закомментировать либо просто удалить. Я не удалил вдруг пригодиться.

Unity3D поиск объектов по tag и изменение значения поля

/*добавляем обработчик нажатия мышкой*/

void OnMouseDown() 

{

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

//Для сохранения результатов поиска

GameObject[] O = GameObject.FindGameObjectsWithTag (tag);

/*Перебираем полученный результат поиска в цикле*/

foreach (GameObject M in O) {

//компонент в котором расположен параметр IndexImg называется Change

Change Flag =M.GetComponent ();

//получаем доступ к параметру IndexImg через переменную Flag

Flag.IndexImg=0;

}

/*задаем прозрачный цвет для данного объекта*/

//IndexImg = 0;

}

    Теперь запустим проект и выберем один из кубиков.

     

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

07) Алгоритм проверки объектов

    Теперь мы знаем как получить отдельно информацию о выделенном кубике на сцене и как получить значения всех кубиков с тем же цветом на этом уровне.

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

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

    Координаты в Unity3D хранятся в типе данных Vector3() Для того чтобы их получить нам нужно запросить у объекта сцены значение по адресу: Имя объекта . Transform . Position. Модифицируем нашу функцию OnKeyDown и внесем в нее следующий код. Получим координаты куба на который щелкнули мышкой и выведем их в консоль отладки Unity3d.

Unity3d OnMouseDown() получение координат объекта при нажатии по нему мыкой

/*добавляем обработчик нажатия мышкой*/

void OnMouseDown() 

{

/*обьявим переменную для хранения координат*/

Vector3 Pozition = transform.position;

/*выведем данную позицию в консоль*/

Debug.Log (Pozition);

}

Запускаем проект и нажимаем на любой кубик и видим как в окне Console отображается текущая позиция элемента.

    Ура теперь когда мы знаем позицию элемента мы можем рассчитать значения для соседних координат где нужно искать подходящих соседей. Так как мы будем пользоваться этим расчетом много раз в дальнейшем то опишем ее как отдельную функцию в нашем коде. Она будет принимать на входе координаты которые нужно рассчитать и в результате возвращать нам массив значений типа Vector3 с координатами место которые нам нужно будет проверить.

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

/*функция создания массива координат*/ 
 Vector3[] GenCoordTest(Vector3 T)

 { 

 /*Объявим массив из 4 точек*/ 

 Vector3[] S = new Vector3[4]; 

 /*задаем координаты для точек*/ 

 S [0] = T; S [0].x++; //правая точка 

 S [1] = T; S [1].x--; //левая точка 

 S [2] = T; S [2].y++; //верхняя точка

 S [3] = T; S [3].y--; //нижняя точка 

 /*возвращаем позиции для проверки*/ 

 return S; 

 }

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

Unity3D текст скрипта Chage

using UnityEngine;

using System.Collections;

public class Change : MonoBehaviour {

//Объявим  список картинок

public Sprite[] ImgList;

//Объявляем  переменную для доступа к картинке

SpriteRenderer Sprite;

//номер выбранного элемента массива

public int IndexImg=0;

// Функция срабатывает  1 раз при создании объекта

void Start () {

/*получаем случайное число от 0 до 8*/

IndexImg = Random.Range (1, 8);

//получаем доступ к компоненту

Sprite = GetComponent ();

}

// Функция срабатывает  каждый раз при обновлении экрана

void Update () {

/*при обновлении экрана задаем картинку по номеру из списка*/

Sprite.sprite = ImgList [IndexImg];

/*задаем уникальный tag для данного типа картинки*/

tag = IndexImg.ToString ();

}

/*функция создания массива координат*/

Vector3[] GenCoordTest(Vector3 T)

{

/*Объявим массив из 4 точек*/

Vector3[] S = new Vector3[4];

/*задаем координаты для точек*/

S [0] = T; S [0].x++; //правая точка

S [1] = T; S [1].x--; //левая точка

S [2] = T; S [2].y++; //верхняя точка

S [3] = T; S [3].y--; //нижняя точка

/*возвращаем позиции для проверки*/

return S;

}

/*добавляем обработчик нажатия мышкой*/

void OnMouseDown() 

{

/*объявим переменную для хранения координат*/

Vector3 Pozition = transform.position;

/*выведем данную позицию в консоль*/

Debug.Log ("выбранная точка " + Pozition);

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

Vector3[] TestCoord = GenCoordTest (Pozition);

Debug.Log ("координаты точек для проверки  ");

/*выводим данный список на консоль*/

foreach(Vector3 T in TestCoord)Debug.Log (T);

}

}

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

    Теперь можно получить массив объектов в которых будем искать подходящих соседей как в статье 6) Обработка нажатия на кубик для этого вызовем команду GameObject.FindGameObjectsWithTag (tag); которая вернет только необходимые для проверки объекты сцены.  Добавим данный код в функцию OnMouseDown().

Unity3D OnMouseDown() поиск объектов по параметру tag

/*добавляем обработчик нажатия мышкой*/

void OnMouseDown() 

{

/*обьявим переменную для хранения координат*/

Vector3 Pozition = transform.position;

/*выведем данную позицию в консоль*/

Debug.Log ("выбранная точка " + Pozition);

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

Vector3[] TestCoord = GenCoordTest (Pozition);

Debug.Log ("координаты точек для проверки  ");

/*выводим данный список на консоль*/

foreach(Vector3 T in TestCoord)Debug.Log (T);

/*получаем список кубиков с одним цветом*/

GameObject[] O = GameObject.FindGameObjectsWithTag (tag);

}

    Далее выбираем каждый объект из найденных и сверяем с координатами, если координаты совпадут то выведем сообщение в console. Добавим данный код в функцию OnMouseDown().

Unity3D OnMouseDown() поиск соседних объектов на основе координат

/*добавляем обработчик нажатия мышкой*/

void OnMouseDown() 

{

/*объявим переменную для хранения координат*/

Vector3 Pozition = transform.position;

/*выведем данную позицию в консоль*/

Debug.Log ("выбранная точка " + Pozition);

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

Vector3[] TestCoord = GenCoordTest (Pozition);

Debug.Log ("координаты точек для проверки  ");

/*выводим данный список на консоль*/

foreach(Vector3 T in TestCoord)Debug.Log (T);

/*получаем список кубиков с одним цветом*/

GameObject[] O = GameObject.FindGameObjectsWithTag (tag);

/*перебираем объекты из массива найденных*/

foreach (GameObject S in O) {

//получаем и выбранного объекта координаты

Vector3 Coord_S = S.transform.position;

/*Перебираем наш список координат*/

foreach(Vector3 T in TestCoord)

{

/*сравниваем с нашим списком координат*/

if(T==Coord_S)

{

/*значит сосед найден выводим данные в console*/

Debug.Log(" Нашли соседа в координатах " + T);

/*прекращаем проверку для объекта*/

break;

}

}

}

}

   После запуска программы можно наблюдать что при нажатии на Объект с соседями система находит их.

    Если же выбрать одиноко стоящий объект у которого визуально нет соседей то наша функция не найдет их что логично.

    Теперь когда мы будем находить соседей мы будем их прятать изменяя в них параметр Indeximg на 0. Чтобы проверить как правильно ли наша функция ищет их. Итак для того чтобы понять есть ли у нас соседи мы введем переменную она будет увеличиваться каждый раз на 1 при нахождении соседа. Далее в функции где мы выводили на console (консоль лога) что сосед найден мы получим его свойство Indeximg и изменим его на 0 тем самым стерев его с экрана. В конце работы функции мы проверим нашла ли функция соседей если да то изменим и в выделенном объекте значение IndexImg на 0 если же нет то все оставим как есть.

Код

/*добавляем обработчик нажатия мышкой*/

void OnMouseDown() 

{

          /*количество найденных соседей*/

int CountFindBox= 0;

/*объявим переменную для хранения координат*/

Vector3 Pozition = transform.position;

/*выведем данную позицию в консоль*/

Debug.Log ("выбранная точка " + Pozition);

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

Vector3[] TestCoord = GenCoordTest (Pozition);

Debug.Log ("координаты точек для проверки  ");

/*выводим данный список на консоль*/

foreach(Vector3 T in TestCoord)Debug.Log (T);

/*получаем список кубиков с одним цветом*/

GameObject[] O = GameObject.FindGameObjectsWithTag (tag);

/*перебираем объекты из массива найденных*/

foreach (GameObject S in O) {

//получаем и выбранного объекта координаты

Vector3 Coord_S = S.transform.position;

/*Перебираем наш список координат*/

foreach(Vector3 T in TestCoord)

{

/*сравниваем с нашим списком координат*/

if(T==Coord_S)

{

/*значит сосед найден выводим данные в console*/

Debug.Log(" Нашли соседа в координатах " + T);

//подсчитываем количество

CountFindBox++;

//компонент в котором расположен параметр IndexImg называется Change

Change Flag = S.GetComponent ();

//получаем доступ к параметру IndexImg через переменную Flag

Flag.IndexImg=0;

/*прекращаем проверку для объекта*/

break;

}

}

}

/*если выбранный блок имеет соседей то и его уничтожаем*/

if (CountFindBox > 0) {

IndexImg=0;

}

}

    Запускаем проверяем.

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

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

    Отлично теперь жмем на желтую изогнутою линию внизу и тут становиться что то интересное. Желтая загогулина не удалилась до конца оставив нам маленький подарок из одной точки.

    В общем все работает мы находим соседей выделенной точки и удаляем их. Дальше я расскажу как искать соседей наших соседей чтобы проверять их.

Следующая часть
252 0 850 1
0
RENDER.RU