Суббота, 22 Сентября 2018, 22:05

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

Меню сайта
Категории каталога
Создание игр [312]
Статьи об общих понятиях связанных с созданием игр.
Программирование [69]
Гайды по программированию на разных ЯП.
Движки и Гейммейкеры [123]
Статьи о программах для создания игр, уроки и описания.
Софт [27]
Различные программы, в том числе в помощь игроделам.
2D-графика [11]
Уроки по рисованию, растр, пиксель-арт, создание спрайтов и пр.
3D-графика [10]
Уроки по моделированию, ландшафт, модели, текстурирование и пр.
Моддинг игр [5]
Модификация компьютерных игр, создание дополнений, перевод, хакинг.
Игры [91]
Статьи об играх, в том числе и сделанных на гейммейкерах.
Разное [68]
Статьи, которые не вошли в определённые разделы.
Наш опрос
Как вы относитесь к созданию игр без программирования?
Всего ответов: 10193
Главная » Статьи » Создание игр

GameMaker.Match-3 с нуля

“ТРИ В РЯД” (Game Maker)
пример(gmk) + подсказки по ходам, небольшие модификации

Игра типа “три в ряд(match-3)” увлекательная аркада для убиения своего времени. Довольно много представителей данного жанра заполонили интернет и мобильные платформы.
Как же сделать подобную игру самому? Все намного проще чем может показаться с первого взгляда.
Для начала определимся с основными задачами:
-построение поля из случайных фишек.
-проверка поля на ряды из 3-х и более фишек.
-удаление найденных рядов.
-замещение убранных фишек новыми.
-возможность перемещения фишек игроком.
-проверка на отсутствие ходов на поле.


Начнем с игрового поля:
В теории это матрица с номерами фишек в ней.
создадим объект o_control и в событии Create инициализируем нашу матрицу

Код
w=8;//количество фишек по ширине
h=6;//количество фишек по высоте
for(i=0; i<w;i+=1){
     for(j=0; j<h; j+=1){
     _map[i,j]=0;
     _piece[i,j]=0;//массив объектов фишек
}
}

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

Я решил взять 6 типов фишек, при большем количестве повышается шанс получения поля без ходов.
Для фишки создадим объект o_piece и назначаем ему наш подготовленный спрайт. В Create:

Код
type = 0; //тип фишки
xx = 0;//позиция х в матрице
yy = 0;//позиция у в матрице

в Draw:

Код
draw_sprite(sprite_index, type-1, x,y);//”type-1” - индекс кадра с фишкой, т.к 0 - пустая фишка


Т.к нам создание новой фишки может еще потребоваться вынесем все это в отдельный скрипт scr_piece_new:

Код
//позиция в матрице поля
_x=argument0;
_y=argument1;

_map[_x,_y]=irandom_range(1,6);// рандомно назначим тип фишек

// сразу расставим фишки по позициям, координатам
//и передадим им необходимые данные
_piece[_x,_y] = instance_create(30+48*_x,+30+48*_y,o_piece)
_piece[_x,_y].xx=_x;
_piece[_x,_y].yy=_y;
_piece[_x,_y].type=_map[_x,_y];


Теперь можно заменить в o_control(Create) строчки

Код
_map[i,j]=0;
_piece[i,j]=0;//массив объектов фишек


на

Код
scr_piece_new(i,j);

Теперь можно создать новую комнату, поставить на нее понравившийся фон, разместить экземпляр o_control, запустить и понаблюдать за нашим полем заполненным разноцветными(в моем случае) фишками…
Как по мне скучновато - так что продолжим!
При рассмотрении получающихся полей можно заметить, что некоторые фишки сразу же складываются в ряды из 3-х и более одинаковых фишек, а значит нужно написать метод поиска этих рядов.

В теории нужно просто перебрать все фишки по вертикали и горизонтали и запомнить фишки которые сложились в ряд, но не удалять их сразу, а именно запомнить - ведь можно удалить фишки по горизонтали, а подходящие к ним по вертикали уже не сработают…
Так что для начала в o_control(Create) объявим список:

Код
match_list = ds_list_create();


В этот список и запишем наших “смертничков”.
Для этого создадим скрипт scr_line_test:

Код
prew_piece=0;//для записи типа начала “ряда”
num_piece=1;//количество повторов фишки

sc = false;//данная переменная нужна что бы посмотреть нужно удалять что-то или нет
// так же можно в нее записывать количество набранных очков…

//тут просто прогоняем по циклу все фишки горизонтально
for(i=0;i<w;i+=1){

//обнуляем значения что бы не переносить их на новую строку
prew_piece=0;
num_piece=1;
     for(j=0;j<h;j+=1){
         if(_map[i,j]!=prew_piece ){//проверяем текущую фишку на соответствие подбераемому ряду и если не сходиться...
             prew_piece=_map[i,j] передаем новый тип фишек для подбора,
             if(num_piece>=3){ проверяем сколько было подобных фишек(если больше 3-х)...
             sc=true;//ставим флаг, что проверка нашла хоть один ряд,
                 while(num_piece>0)//просматриваем количество фишек в ряду
                 {
                     ds_list_add(match_list,_piece[i,j-num_piece]);//добавляем фишки из найденного ряда в “смертники”
                     num_piece-=1;//уменьшаем количество до 0
                 }
             }
             num_piece=1;
         }
         else// если фишка соответствует ряду...
         {
         num_piece+=1;просто добавляем количество
         }
         if (j==h-1 && num_piece>=3){//Дополнительная проверка “крайних” фишек
             sc=true;
                 while(num_piece>0)
                 {
                     ds_list_add(match_list,_piece[i,j-(num_piece-1)]);
                     num_piece-=1;
                 }
         }     
    }
    
}

//Повторяем то же самое но вертикально(главное не запутаться и правильно все заменить)
for(j=0;j<h;j+=1){
prew_piece=0;
num_piece=1;
     for(i=0;i<w;i+=1){
         if(_map[i,j]!=prew_piece){
             prew_piece=_map[i,j]
             if(num_piece>=3){
             sc=true;
                 while(num_piece>0)
                 {
                     ds_list_add(match_list,_piece[i-num_piece,j]);
                     num_piece-=1;
                 }
             }
             num_piece=1;
         }
         else
         {
         num_piece+=1;
         }
         if (i==w-1 && num_piece>=3){
             sc=true;
                 while(num_piece>0)
                 {
                     ds_list_add(match_list,_piece[i-(num_piece-1),j]);
                     num_piece-=1;
                 }
         }
    }
     
}

return sc;//возвращаем результат проверки(true - есть “ряды”, false - нету)

Теперь мы знаем какие фишки нужно удалить и это можно сделать с этой же ф-ции, но может потребоваться удаление фишек и при других обстоятельствах(бонусные “взрывы” и т.п) поэтому создадим еще скрипт scr_line_clear:

Код
while(ds_list_size(match_list)>0)//проверяем наш лист на наличие в нем фишек
{
with(ds_list_find_value(match_list,ds_list_size(match_list)-1))//выбераем последнюю из списка
{
     instance_destroy();//удаляем
     score+=50;//добавляем очки
}
ds_list_delete(match_list,ds_list_size(match_list)-1);//убераем из списка
}

так же в o_piece(Destroy):

Код
o_control._map[xx,yy]=0//делаем ячейку поля пустой


Теперь добавим пару строчек в o_control:
Create(после создания поля):

Код
alarm[0] = .5*room_speed;//задержим на пол секунды


Alarm 0:

Код
if(scr_line_test())//если есть что удалять...
alarm[1]=5;


Alarm 1:

Код
scr_line_clear()//очищаем поле от собранных рядов


Мы можем теперь удалять собранные ряды, но у нас новая проблема - пустые дыры в поле…
Нужно это дело поправить и научить игру “залатывать” подобное. Есть много вариантов движения фишек - сверху вниз, снизу вверх, с боков, диагонально, да хоть бубликом могут закручиваться. Т.к я ленивый то взял самый простой способ - сверху вниз. Но для начала научим наши фишки двигаться, ведь так намного приятнее будет наблюдать за происходящим.
в o_piece(Step) напишем:
Код
if (y<30+yy*48)//смотрим как изначально расположили фишки
y+=16//двигаем фишку
if (y>30+yy*48)
y-=16
if (x<30+xx*48)
x+=16
if (x>30+xx*48)
x-=16

Теперь можно залатать наши “дыры”. Для этого мы будем опускать все наши фишки если под ними пусто. В теории пройдемся горизонталями поля снизу вверх, заменяя пустые слоты на те которые над ними или если это верхний ряд, то создадим новую фишку.
Скрипт scr_line_drop:

Код
next=true

while(next)//повторям, пока не заполним все
{
next=false;
for(i=0;i<w;i+=1){
     for(j=h-1;j>=0;j-=1){прогоняем цикл задом наперед
         if (_map[i,j]=0){//если фишка отсутствует...
         next=true;
             if (j=0){//если это верхний ряд...
                 scr_piece_new(i,j)//создаем новую фишку в этом ряду
             }
             else{//если нет то подмениваем значения
                 _map[i,j] = _map[i,j-1]
                 _piece[i,j]=_piece[i,j-1]
                 if(instance_exists(_piece[i,j])){
                     _piece[i,j].xx=i;
                     _piece[i,j].yy=j;
                     }
                 _map[i,j-1]=0
                  
             }
         }
     }
}    
}
if(scr_line_test())//когда все пустоты заполнены снова проверяем нужно ли убрать чего...
     alarm[1]=5;//если нужно, возвращаемся к удалению

Этот скрипт можно запускать в конце скрипта scr_line_clear().
И при колдовстве с настройками начального положения созданных фишек и задерками можно получить неплохой эффект… Если запустить игру уже будет похоже на то что ожидалось, но пока от игрока нету толку - это сейчас и поправим.
Сделаем выбор и перемещение фишек игроком.
Для начала добавим пару переменных в o_control(create)

Код
g1=-1;//ссылка на первую выбранную фишку
g2=-1;//на вторую


Подготовим спрайт для выделения фишки, хотя можно и программно эффект нарисовать(например увеличение\уменьшение), я же просто
нарисовал рамку.
Теперь сделаем выбор фишки. в o_piece(Mouse Left Released):

Код
if (o_control.g1=-1)//если “первая” фишка еще не выбрана...
{
     o_control.g1=id передаем указателю свой айди
}
else // в противном случае
{
     if(abs(o_control.g1.xx-xx)=1 && (o_control.g1.yy=yy))//проверяем соседство по горизонтали данной фишки с “первой”...
     {
         o_control.g2=id если все впорядке то присваиваем указателю что это 2-ая фишка
     }
     else if(abs(o_control.g1.yy-yy)=1 && (o_control.g1.xx=xx))//тоже самое по вертикали
     {
         o_control.g2=id
     }
     else //если не сосед то делаем ее “первой”
     {
         o_control.g1=id
     }
}


теперь нужно сделать обмен фишек когда они выбраны, а также их обратный возврат если был неправильный ход.
заведем в o_control(create) проверочную переменную:

Код
gg=false;


Она понадобится нам в следующем скрипте
scr_piece_remove:

Код
var ds;//0 - прямая замена, 1 обратная
     ds=argument2;
var p1,p2;//это фишки для замены
     p1 = argument0
     p2 = argument1
var t1,t2;// тип фишек
     t1=o_control._map[p1.xx,p1.yy]
     t2=o_control._map[p2.xx,p2.yy]
var px1,py1,px2,py2;//координаты фишек
     px1=p1.xx;
     py1=p1.yy;
     px2=p2.xx;
     py2=p2.yy;
//тут идет простая подмена информации
o_control._map[px1,py1] = t2
o_control._piece[px1,py1] = p2
o_control._piece[px1,py1].xx =px1
o_control._piece[px1,py1].yy =py1
o_control._map[px2,py2] = t1
o_control._piece[px2,py2] = p1
o_control._piece[px2,py2].xx =px2
o_control._piece[px2,py2].yy =py2
//за анимацию не беспокоимся уже все сделано
if(scr_line_test())проверяем на наличие рядов
{
     alarm[1]=5
//обнуляем ссылки
     o_control.g1=-1
     o_control.g2=-1
}
else if ds=0//если не сходиться и выбрана прямая замена то шаманим в o_control(alarm 2)
{
     alarm[2]=5
     gg=true//нужно , что бы не зациклить замену
}
else
{
     o_control.g1=-1
     o_control.g2=-1
     gg=false//заканчивает обратную замену
}


Сейчас немного подправим o_control:
step:
добавим

Код
if (g1!=-1 && g2!=-1 && gg=false)//проверяем выбранные и делаем замену
     scr_piece_remove(g1,g2,0)

alarm 2:

Код
scr_piece_remove(g1,g2,1)//это обратная замена, если ход был ошибочным


так же, что бы не выскакивали странные ошибки, добавим в o_piece(destroy):

Код
if (o_control.g1=id)
o_control.g1=-1;
else if (o_control.g2=id)
o_control.g2=-1;


Запустим наше детище! Уже почти готовая игра(только графический марафет навести осталось)... Но что делать если вдруг на поле больше не будет ходов? Такая проверка немного запутанная, но если вдуматься все намного проще чем кажется!
В теории нам нужно разобраться, а что же мы ищем? перебирать все возможные перемещения фишек глупо и ресурсозатратно(хотя на современных машинах этого даже не заметишь). Посмотрим на поиски поближе если мы найдем рядом 2 фишки или через 1 где может быть третья? рассмотрим на примере:

То есть судя по картинке нам нужно найти аналогию “красным” фишкам и проверить “оранжевые” на сходство и если нету ничего подходящего то на поле закончились ходы…
Напишем проверку в скрипте scr_line_end_test:
Код
var piece_type;// тип проверяемой фишки

for(i=0;i<w;i+=1){ прогоняем все поле горизонтально
     for(j=0;j<h;j+=1){
     piece_type=_map[i,j];
          if (j+1<h){//проверяем на выход за границы
          if (board_map[i,j+1]=piece_type){//проверяем следующую фишку
             if (j+3<h)//проверяем на выход за границы
                 if (board_map[i,j+3]=piece_type){
                 return true;
                 }
             if (j+2<h && i+1<w)//проверяем на выход за границы
                 if (_map[i+1,j+2]=piece_type){проверка “оранжевой” фишки
                 return true;//выходим из проверки, все впорядке
                 }
//дальше по аналогии проверяем остальное
             if (j+2<h && i-1>0)
                 if (_map[i-1,j+2]=piece_type){
                 return true;
                 }
             if (j-2>0)
                 if (_map[i,j-2]=piece_type){
                 return true;
                 }
             if (j-1>0 && i-1>0)
                 if (_map[i-1,j-1]=piece_type){
                 return true;
                 }
             if (j-1>0 && i+1<w)
                 if (_map[i+1,j-1]=piece_type){
                 return true;
                 }
          }}
          if (j+2<h){
          if (_map[i,j+2]=piece_type){
             if (i-1>0)
                 if (_map[i-1,j+1]=piece_type){
                 return true;
                 }
             if (i+1<w)
                 if (_map[i+1,j+1]=piece_type){
                 return true;
                 }
             if (j+3<h)
                 if (_map[i,j+3]=piece_type){
                 return true;
                 }
          }}
     }
}

//проверяем вертикально
for(j=0;j<h;j+=1){
     for(i=0;i<w;i+=1){
     piece_type=_map[i,j];
          if (i+1<w){
          if (_map[i+1,j]=piece_type){
             if (i+3<w)
                 if (_map[i+3,j]=piece_type){
                 return true;
                 }
             if (i+2<w && j+1<h)
                 if (_map[i+2,j+1]=piece_type){
                 return true;
                 }
             if (i+2<w && j-1>0)
                 if (_map[i+2,j-1]=piece_type){
                 return true;
                 }
             if (i-2>0)
                 if (_map[i-2,j]=piece_type){
                 return true;
                 }
             if (i-1>0 && j-1>0)
                 if (_map[i-1,j-1]=piece_type){
                 return true;
                 }
             if (i-1>0 && j+1<h)
                 if (_map[i-1,j+1]=piece_type){
                 return true;
                 }
          }}
          if (i+2<w){
          if (_map[i+2,j]=piece_type){
             if (j-1>0)
                 if (_map[i+1,j-1]=piece_type){
                 return true;
                 }
             if (j+1<h)
                 if (_map[i+1,j+1]=piece_type){
                 return true;
                 }
             if (i+3<h)
                 if (_map[i+3,j]=piece_type){
                 return true;
                 }
          }}
     }
}

return false;//если все проверки не дали результата то ходов больше нету

скрипт нужно использовать в связке:

Код
if(scr_line_test())
     alarm[1]=5;
else if !scr_line_end_test()//если ходов больше нет...
     game_end();//тут может быть перебор поля или еще что...

вместо :

Код
if(scr_line_test())
     alarm[1]=5;


Например в конце скрипта scr_line_drop();

И вот наконец, если все сделано правильно, можно поиграть в свой “три в ряд”!
И конечно он еще далек до идеала - нет уровней, бонусов, красивых эффектов, подсказок, меню и еще каких нибудь наворотов, но это дело прихоти и личной фантазии поэтому в данный урок входить не будет.
Если вам понравилось оставляем репутацию и ждем новых уроков от BSpot - a, спасибо за ваше время!
Категория: Создание игр | Добавил: BrightSpot (03 Февраля 2016) | Автор: Поляков владислав
Просмотров: 4336 | Комментарии: 1 | Рейтинг: 5.0/4 |
Теги: Статья для новичков, BSpot уроки, math 3, пример Math 3, Game Maker, Урок по GM, GM уроки, Пример игры три в ряд, три в ряд, уроки на game maker
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:

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

Всего комментариев: 1
+2-
1 Fargost   (05 Февраля 2016 20:51)
FargostВсе бы хорошо, да вот начальная расстановка фишек не делает проверку на возможные совпадения, т.е. при начальной расстановке могут попасть 3 в ряд фишки

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • Oddity Engine
  • Quest
  • Ogre3D
  • Coffee
  • Skulltag
  • NeoAxis
  • Nuclear Basic
  • Genesis 2D
  • Quest 3D
  • 3D Rad
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2018 Рейтинг