Пятница, 19 Апреля 2024, 04:53

Приветствую Вас Гость

Меню сайта
Категории каталога
Создание игр [355]
Статьи об общих понятиях связанных с созданием игр.
Программирование [82]
Гайды по программированию на разных ЯП.
Движки и Гейммейкеры [145]
Статьи о программах для создания игр, уроки и описания.
Софт [43]
Различные программы, в том числе в помощь игроделам.
2D-графика [14]
Уроки по рисованию, растр, пиксель-арт, создание спрайтов и пр.
3D-графика [16]
Уроки по моделированию, ландшафт, модели, текстурирование и пр.
Моддинг игр [5]
Модификация компьютерных игр, создание дополнений, перевод, хакинг.
Игры [161]
Статьи об играх, в том числе и сделанных на гейммейкерах.
Разное [129]
Статьи, которые не вошли в определённые разделы.
Наш опрос
Как часто вы играете?
Всего ответов: 1064
Главная » Статьи » Программирование

Программирование match3 игры на Unity
Прежде чем начнем, можете посмотреть финальный результат

Сначала создадим блоки. Для этого используем простой куб с разными цветами (я взял 4 цвета) и установим скейл кубов в (0.8, 0.8, 0.8).
Затем создадим C# скрипт, назовем его Match3

Что нам нужно сделать:
1) Генерация игрового поля (доски).
2) Выбор двух блоков.
3) Проверка рядом ли они.
4) Обмен позициями и данными.
5) Сравнение блоков.
6) Разрушение и спаун объектов.

Генерация поля(доски).
Код

//Доска
public int[, ] board;
   
//блоки
public Transform[] block;
   
void Start(){
  blocks = new int[10,10];
  //генерация доски
  GenBoard();
}
   
   
void GenBoard(){
  //10x10 = 100blocks
  for(int x=0; x<board.GetLength(0); x++){
  for(int y=0; y<board.GetLength(1); y++){
  int randomNumber = Random.Range(0,block.Length); //ID
  //спаун блоков
  Transform obj = (Transform)Instantiate(block[randomNumber].transform, new Vector3(x,y,0), Quaternion.identity) as Transform;
  //назначаем parent
  obj.parent = transform;
  //назначаем имя
  obj.name = "Block[X:"+x+"Y:"+y+"]ID:"+randomNumber;
  // установим ID на доске в этой позиции
  board[x,y] = randomNumber;
  }
  }
}


Проходим по каждому блоку на доске и получаем для него рандомный номер (это будет цвет). Спауним блок.
Не забудьте каждому блоку назначить префабы в массив block.
Теперь создадим новый скрипт. Назовем его Block. Назначим этот скрипт каждому новому созданному блоку. Добавим в него несколько переменных для позиции и ID, они понадобятся нам в дальнейшем.

Код

//позиции блока
public int ID;
public int x;
public int y;
   
private Vector3 myScale;
private float startTime;
   
public static Transform select;
public static Transform moveTo;
   
void Start(){
  myScale = transform.localScale;
  startTime = Time.time;
}
   
void Update(){
//эффект роста
  if(Time.time-startTime<3){//после спауна блока, расти он будет не более 3 секунд
  transform.localScale = Vector3.Lerp(new Vector3(0,0,0),myScale, (Time.time-startTime)/2);
  }
}


Теперь нужно добавить этот скрипт к каждому блоку, назначить позицию и ID. В скрипте Match3 в функцию GenBoard добавим

Код

//Прикрепим скрипт к каждому блоку
Block b = obj.gameObject.AddComponent<Block>();
//назначим переменные
b.x = x;
b.y = y;
b.ID = randomNumber;


Выбор двух блоков.

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

Код
  
public static Transform select;
public static Transform moveTo;
   
void OnMouseOver(){
  // курсор мыши над блоком
  transform.localScale = new Vector3(myScale.x+0.2f,myScale.y+0.2f,myScale.z+0.2f);
  //выделение объектов
  if(Input.GetMouseButtonDown(0)){
  if(!select){//если ни один не выделен, выделяем данный блок
  select = transform;
  }
  else if(select != transform && !moveTo ){ //если один блок уже выделен, проверим не выделен ли он снова. Выделяем второй объект
  moveTo = transform;
  }
  }
}
   
void OnMouseExit(){
//сбрасываем скейл блока
  transform.localScale = myScale;
}


Проверка рядом ли блоки
Для проверки, находятся ли блоки рядом друг с другом, нужно узнать их позиции.
Используем block.select для доступа к статичной переменной. Мы получим transform выделенного блока.
В скрипте Match3, в функции Update нам нужно проверить, выбраны ли два блока. Если это так, нужно вызвать функцию CheckIfNear, которая вернет true или false.

Код

void Update(){
  if(Block.select && Block.moveTo){//два выбранных блока есть
  //Проверка рядом ли они  
  if(CheckIfNear()==true){
   
   
  else{
  //Deselect
  Block.select = null;
  Block.moveTo = null;
  }
  }
  }


Код

bool CheckIfNear(){
  Block sel = Block.select.gameObject.GetComponent<Block>();
  Block mov = Block.moveTo.gameObject.GetComponent<Block>();
   
  //Проверка рядом ли
  if(sel.x-1 == mov.x && sel.y == mov.y){
  //Left
  return true;
  }
  if(sel.x+1 == mov.x && sel.y == mov.y){
  //Right
  return true;
  }
  if(sel.x == mov.x && sel.y+1 == mov.y){
  //Up
  return true;
  }
  if(sel.x == mov.x && sel.y-1 == mov.y){
  //Down
  return true;
  }
  Debug.Log("What are you trying to select?");
  return false;
}


Обмен данными блока и смена позиций

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

Код

void Swap(){
  Block sel = Block.select.gameObject.GetComponent<Block>();
  Block mov = Block.moveTo.gameObject.GetComponent<Block>();
   
  Vector3 tempPos = sel.transform.position;
  int tempX = sel.x;
  int tempY = sel.y;
   
  //Обмен позиций
  sel.transform.position = mov.transform.position;
  mov.transform.position = tempPos;
   
   
  //Обмен данных
  sel.x = mov.x;
  sel.y = mov.y;
   
  mov.x = tempX;
  mov.y = tempY;
   
  //Изменение ID на доске
  board[sel.x, sel.y] = sel.ID;
  board[mov.x, mov.y] = mov.ID;
   
}


Сравнение

Проверка на сравнение заключается в следующем.
Мы знаем выделенный блок. Который, например, черный. Мы знаем позицию блока (имеем доступ к статичной переменной). Но мы не знаем, так это - есть ли 3 таких же блока рядом с выделенным.
Чтобы узнать это, мы:
1) От позиции выделенного блока пойдем вправо.
2) Если этот блок того же цвета, что и выделенный, снова пойдем вправо и отметим что мы нашли +1 блок.
3) Если цвет блока отличается от выделено, останавливаемся, вправо больше не идем.
Почти то же самое с другими направлениями, за исключение того что в этом случае мы начинаем проверку не с позиции выделенного блока. Отсчет мы начнем с позиции, стоящей перед выделенным блоком. Мы же не хотим проверять один и тот же блок дважды.

Тот же принцип для верхнего и нижнего направлений.

После этого нужно проверить, получился ли у нас ряд одинаковых блоков. Если countL + countR => 3, блоки разрушим и отметим их как пустые.

Код

bool CheckMatch(){
  //получаем все доступ ко всем блокам в сцене
  Block[] allb = FindObjectsOfType(typeof(Block)) as Block[];
  Block sel = Block.select.gameObject.GetComponent<Block>();
   
  int countU = 0; //счетчик вверх
  int countD = 0; //счетчик вниз
  int countL = 0; //счетчик влево
  int countR = 0; //счетчик вправо
   
  //Влево
  for(int l = sel.x-1; l>=0; l--){
  if(board[l,sel.y]==sel.ID){//если у блока такое же ID
  countL++;
  }
  if(board[l,sel.y]!=sel.ID){//если у блока другое ID
  //останавливаемся
  break;
  }
  }
  //Вправо
  for(int r = sel.x; r<board.GetLength(0); r++){
  if(board[r,sel.y]==sel.ID){
  countR++;
  }
  if(board[r,sel.y]!=sel.ID){
  break;
  }
  }
  //Вниз
  for(int d = sel.y-1; d>=0; d--){
  if(board[sel.x,d]==sel.ID){
  countD++;
  }
  if(board[sel.x,d]!=sel.ID){
  break;
  }
  }
   
  //Вверх
  for(int u = sel.y; u<board.GetLength(1); u++){
  if(board[sel.x,u]==sel.ID){
  countU++;
  }
   
  if(board[sel.x,u]!=sel.ID){
  break;
  }
  }
   
   
  //Проверяем, есть ли ряд из трех блоков
  if(countL+countR>=3 || countD+countU>=3){
  if(countL+countR>=3){
  //разрушаем и отмечаем пустые блоки
  for(int cl = 0; cl<=countL; cl++){
  foreach(Block b in allb){
  if(b.x == sel.x-cl && b.y == sel.y){
  Destroy(b.gameObject);
  board[b.x,b.y] = 500; //отмечаем пустой блок
  }
  }
  }
  for(int cr = 0; cr<countR; cr++){
  foreach(Block b in allb){
  if(b.x == sel.x+cr && b.y == sel.y){
  Destroy(b.gameObject);
  board[b.x,b.y] = 500;  
  }
  }
  }
  }
  if(countD+countU>=3){
  for(int cd = 0; cd<=countD; cd++){
  foreach(Block blc in allb){
  if(blc.x == sel.x && blc.y == sel.y - cd){
  board[blc.x, blc.y] = 500;
  Destroy(blc.gameObject);
  }
  }
  }
  for(int cu = 0; cu<countU; cu++){
  foreach(Block blc in allb){
  if(blc.x == sel.x && blc.y == sel.y+cu){
  board[blc.x, blc.y] = 500;
  Destroy(blc.gameObject);
  }
  }
  }
  }
  Respawn();
  return true;
   
  }
   
   
  return false;
}


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

Код

void Respawn(){
  for(int x=0; x<board.GetLength(0); x++){
  for(int y=0; y<board.GetLength(1); y++){
  if(board[x,y]==500){ //спауним только на разрушенной клетке
  int randomNumber = Random.Range(0,blocks.Length); //ID
  Transform obj = (Transform)Instantiate(blocks[randomNumber].transform, new Vector3(x,y,0), Quaternion.identity);
  obj.parent = transform;
  Block b = obj.gameObject.AddComponent<Block>();
  //назначим переменные
  b.ID = randomNumber;
  b.x = x;
  b.y = y;
  //назначим ID на доске новое значение
  board[x,y] = randomNumber;
  }
  }
  }
}


И, уже в функции Update

Код

void Update(){
  if(Block.select && Block.moveTo){ //If 2 blocks are selected, what we need more?
  //Check if they are near  
  if(CheckIfNear()==true){
  //Near
  Swap();//Swap their position and data
   
  //Check for match3
  if(CheckMatch()==true){
  //There is match
  Block.select = null;
  Block.moveTo = null;
  }
  else{
  //There is no match, return them in their default position
  Swap();
  Block.select = null;
  Block.moveTo = null;
  }
  }
  else{
  //Not near
  Block.select = null;
  Block.moveTo = null;
  //We can again select new blocks
  }
  }
}


На этом все.

Здесь скрипт Match3 целиком

Источник: КЛИК»
Категория: Программирование | Добавил: NEBR (07 Июля 2014) | Автор: Дмитрий NEBR Кичеев
Просмотров: 21337 | Комментарии: 13 | Рейтинг: 4.2/5 |
Теги: U3D, C#, match3, юнити, матч3, игра, программирование, game, Coding, Unity
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:

Игровые объявления и предложения:
Если вас заинтересовал материал «Программирование match3 игры на Unity», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела. Предлагаются такие схожие материалы: Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.

Всего комментариев: 13
+0-
12 Бозингар   (19 Ноября 2016 11:09) [Материал]
БозингарКак сделать проверку на совпадения при респавне? Повторить CheckMatch для всех созданных блоков?

+0-
13 NEBR   (19 Ноября 2016 12:42) [Материал]
NEBRНаверное да

+0-
10 DiHand   (07 Марта 2016 15:30) [Материал]
на дропбоксе пусто(((

+0-
11 NEBR   (13 Марта 2016 08:38) [Материал]
NEBRЕстественно, эту заметку я запилил 2,5 года назад...

+0-
9 Just1ucky   (02 Августа 2014 13:22) [Материал]
хороший урок. Кто бы для GMS написал:))

+2-
2 JohnJ   (09 Июля 2014 13:20) [Материал]
JohnJНе правильно работает демонстрация: если в результате смены кубиков образуется не одна линия, а сразу две или больше, то исчезает всё-равно одна. Например:
К З
К З
З К
меняем нижние буквы местами, должно исчезнуть 6 букв (все К и все З), а исчезают только одни.

+0-
3 NEBR   (09 Июля 2014 22:27) [Материал]
NEBRда, в функции сравнения маловато проверок, есть такое дело smile

+0-
1 leo271083   (08 Июля 2014 22:25) [Материал]
leo271083Спасибо, интересненько smile Кстати, есть продолжение этого урока Тыц Там изменен вид появления квадратиков до типичных на сегодня match3 игр smile Очень интересные глюки притом начинают появляться yes

+0-
4 NEBR   (09 Июля 2014 22:28) [Материал]
NEBRда, видел его. Надо бы тоже разобрать.А какие глюки? )

+2-
5 leo271083   (10 Июля 2014 14:43) [Материал]
leo271083Привет) Да, разные. Например, вместо заполнения пустого места с только что удаленным квадратом ничего не приходит, или приходит, но цвет, выглядя, например, красным, на самом деле является зеленым и т.п. Минут пять достаточно поиграть, чтоб эти глюки вылезли smile

+0-
7 NEBR   (11 Июля 2014 08:59) [Материал]
NEBRда, оказалось сыровато

+2-
6 Laush   (10 Июля 2014 23:45) [Материал]
Laushбудет ли доработка данного туториала? Например что бы когда создаются новые квадраты, если сразу же совпадают цвета, сразу же удалялось.

+2-
8 NEBR   (11 Июля 2014 09:00) [Материал]
NEBRСначала хотел доработать, добавить много своего. Но теперь отпала в этом необходимость, да и времени поубавилось ) Посмотрим, может и дойдут руки

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • Castle Game Engine
  • RPGWizard
  • Multimedia Builder
  • Adventure Maker
  • Ethanon Engine
  • Blitz3D
  • 3DSTATE Engine
  • ADRIFT
  • Zephyr3d
  • Axiom Engine
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2024 Рейтинг