Game Maker - создание игр | HellRoom Games
Июль 12, 2025, 12:27:21 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.

Войти
Новости:
 
   Начало   Game Maker Помощь Правила форума Поиск Календарь Войти Регистрация  
Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Как написать Lode Runner на Game Maker  (Прочитано 42319 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Dmi7ry
Гл. Администратор
*

Репутация: 1379
Offline Offline

Пол: Мужской
Награды:
5000 сообщений!За постоянность! [200 дней на форуме]За лояльность! [+1000 репутации]За помощь в развитии форума!Знаток Game Maker!За помощь новичкам!
API: GameMaker Studio Master
Деятельность: Code, design
Сообщений: 6626



WWW
« : Июль 23, 2011, 17:56:42 »

Делаем классический Lode Runner на Game Maker.


Этот урок касается следующих вопросов:
  • Загрузка уровня из внешнего файла;
  • Отображение уровня через тайлы и создание объектов (золото, герой, бандиты);
  • Взаимодействие героя с уровнем без использования объектов (лестницы, кирпичики - это не объекты);
  • Перемещение героя;
  • Позиционирование положения на лестнице с использованием погрешности;
  • Сбор золота;
  • Появление выхода с уровня, когда всё золото собрано.

Если вы хотите просто взять исходный код, изменить его и использовать где-то, то этот урок не для вас. Мне хочется, чтобы вы поняли именно принцип.


Итак, приступим.
Наш уровень имеет размер 32 клетки по горизонтали и 22 клетки по вертикали. Для начала рассмотрим, как эти данные будут храниться в памяти.
Это проще всего сделать созданием массива по размеру уровня:
Код:
level[32,22]=0

И будем заносить в соответствующую ячейку массива следующие значения:
1 - кирпичик
2 - твёрдый пол (который нельзя копать)
3 - "фальшивый" кирпичик
4 - лестница
5 - верёвка
6 - золото
7 - бандит
8 - герой
9 - выход с уровня


в итоге у нас получится что-то вроде:
Код:
00000000000000000000000900000000
00706000000000000000000900000000
12212212221411111120000900000000
00000000000455555555555900000000
00000000000400000000000900000000
00000000000400001140000906000000
00000000000400701140001111141111
00000000000400001140000000040000
00000000000400001140000000040000
00000000000400001140000000640000
11141111111100001111111141111111
00040000000000000000000040000000
00040000000000000000000040000000
00040000000000000000000040000000
11111111111111400000000040000000
00000000000000400000000040000000
00000000000000400000000040000000
00000000007060455555555540060700
00000041111111110000000011111114
00000040000000000000000000000004
00000040000000000080600000000004
11111111111111111111111111111111

Все уровни у нас будут закодированы таким образом, и мы будем загружать их из внешнего файла.
Первый уровень у меня готов и он хранится в файле level1.lvl размером 704 байта.

Загрузка файла.
Будем загружать его в коде создания комнаты
Код:
if (file_exists(file_name))     // существует ли файл
{
    file1=file_bin_open(file_name,0)    //открыть на чтение
    siz=file_bin_size(file1)            //считываем размер файла
    a=0;b=0
    while (siz>0)        //пока не прочитаем все байты, по порядку записываем данные в массив
    {
        level[a,b]=file_bin_read_byte(file1)
        if (a=31) {a=0;b+=1}
        else {a+=1}
        siz-=1
    }
    file_bin_close(file1)
}
else
{
    show_message('файл <'+file_name+'> не найден')
    game_end()
    exit
}
Всё,  уровень находится в массиве. Я называю это "картой уровня".


Отображение уровня через тайлы и создание объектов.
Для данных о герое и бандитах создадим массивы

Код:
man[4,3]=0     //массив, данные для четырёх бандитов
player[1,3]=0  //массив, данные героя
В этом массиве описывается, что происходит с человечком
первые два байта содержат координаты человечка, а третий - описывает его состояние:
1-влево; 2-вправо; 3-вверх; 4-вниз; 5-падение; 6-закопан; 0-отсутствует

Итак, начинаем строить уровень...
Код:
a=0;b=0
while (b<22)
{
//отрисовка тайлов
    readtile=level[a,b]
    zz=10;              //всё рисуем в слое 10, кроме выхода с уровня, который в слое 9
    switch (readtile)   //заносим в массив данные о золоте и человечках, а также создаём
                    //соответствующие им объекты
    {
        case 9:      zz=9;break
        case 8:      player[0]=a;player[1]=b;player[2]=6;readtile=0;instance_create(a*tile_size,b*tile_size,obj_player);
                     obj_player.image_speed=0;obj_player.depth=-1;break
        case 7:      man[mans,0]=a;man[mans,1]=b;man[mans,2]=5;readtile=0;
                     instance_create(a*tile_size,b*tile_size,obj_band);obj_band.image_speed=0;break
        case 6:      gold+=1;readtile=0;instance_create(a*tile_size,b*tile_size,obj_gold);break
    }
    tile_add(background0,readtile*tile_size,0,tile_size,tile_size,a*tile_size,b*tile_size,zz)
        if (a<31) {a+=1}
        else {a=0;b+=1}
}
tile_layer_hide(9)
Здесь мы создаём объекты: obj_player (герой), obj_gold (золото), obj_band (бандит).
В конце делаем слой, в котором находится выход с уровня, невидимым.


Взаимодействие героя с уровнем.
Для игры введём несколько глобальных переменных
Код:
mans=0        //количество бандитов на уровне
gold=0        //количество золота на уровне
tile_size=16  //размер тайла у нас будет 16 пикселей

На героя должна действовать гравитация. Нам не подойдут стандартные средства GM, потому что пол, лестницы и верёвки не являются объектами, это всего лишь тайлы.
Поэтому мы будем работать с картой уровня, которая у нас находится в массиве level[]
Код:
//падаем вниз?
step=2
if ((y+step)<(21*tile_size))           //чтобы не вылезти за пределы экрана
{
    x1=floor(obj_player.x/tile_size)   //вычисляем координаты героя внутри карты уровня
    y1=floor((obj_player.y)/tile_size)
    aa=level[x1,y1]                    //берём значение ячейки из карты
    if (y1<21)                         //если не долетел до дна, то пробуем его сделать ниже
    {
        x1=floor(x/tile_size);x2=floor((x+tile_size-1)/tile_size) //смотрим на карте левый (x1) и правый (x2) край героя
        y1=floor((y+step+tile_size-1)/tile_size);y2=floor(y/tile_size) // то же самое, но низ (y1) и верх (y2)
        zz=frac(y/tile_size)
      
        aa=level[x1,y1];ab=level[x2,y1] //берём из карты левый и правый угол снизу
        ba=level[x1,y2];bb=level[x2,y2] //и левая и правая сторона сверху
//проверяем, попадаем ли на лестницу или верёвку
        if ((ba=4||bb=4||aa=4||ab=4)||((ba=5||bb=5)&&(zz=0)))
//то есть: если любой из краёв героя попадает на лестницу
//либо верхний край спрайта попадает на верёвку и при этом он строго наверху, тогда
        {
            if (player[2]=5) {player[2]=6}  // если герой падает (5), то меняем его состояние на "закопан"
        }
        else
//нет, тогда проверяем наличие пола или лестницы под ногами
        {
            if !((aa=1||aa=2||aa=4)||(ab=1||ab=2||ab=4))
                {
                    y=y+2;player[2]=5;changeanim=true  // падаем на 2 пикселя и говорим, что анимацию нужно поменять на "падает"
                }
                else {if (player[2]=5) {player[2]=6}} // иначе меняем состояние героя "падает" на "закопан"
        }                      
    }
zz нужен для того, чтобы определить, висит ли герой на верёвке. Если он хоть на один пиксел ниже, то мы падаем.

Сразу стоит пояснить, что большую часть кода можно оптимизировать. Например, конструкции типа if (aa||aa||aa)||(ab||ab||ab) можно разложить на кучу if. Это немного поднимет скорость выполнения, но при этом понизит читабельность кода (а точнее, его понимаемость). Поэтому здесь нет никакой оптимизации.


Перемещение героя. Позиционирование положения на лестнице с использованием погрешности.
Несколько пунктов, на которых можно остановиться.

  • Если мы лезем по лестнице и нам нужно резко залезть на верёвку, это может быть довольно сложно сделать, потому что скорость... Поэтому мы упрощаем игроку задачу: если верёвка оказывается чуть-чуть выше, герой как бы "подпрыгнет" к ней.
  • То же самое с залезанием на лестницы: если мы чуть-чуть левее или чуть-чуть правее, герой также сам выравнивается на средину лестницы. Если этого не сделать, то играть становится гораздо менее удобно.

Весь этот код делаем в Step.
Итак, идём влево.
Код:
if player[2]=5 {exit} // если падаем, то на нажатие кнопки не реагируем, выходим

step=4   // задаёт скорость перемещения
if (x-step)<0 {exit} // если в результате вылезем за левый край комнаты, то не реагируем, выходим

x1=floor((x-step)/tile_size);x0=floor((x+tile_size-1)/tile_size)
y1=floor(y/tile_size);y2=floor((y+tile_size-1)/tile_size)
aa=level[x1,y1];ab=level[x1,y2]
a0=level[x0,y1]
zz=frac(y/tile_size)
if ((aa=1)||(aa=2))||((ab=1)||(ab=2)) { step=0 }   //стоим, если стена
if (aa=5&&(zz<0.5))                    //автоприлипание к верёвке
{
    y=(floor(y/tile_size))*tile_size
}
else
{
 if (a0=4&&goingup) {step=0} //если в этот момент лезем вверх по лестнице, то не реагируем на нажатие
}
x-=step
if (step)  //смотрим, нужно ли менять анимацию
{
    changeanim=true;
    if !(player[2]=1) {image_index=4}
    player[2]=1 //устанавливаем статус "двигаемся влево"

Для чего нужна эта строка:
Код:
if (a0=4&&goingup) {step=0} //если в этот момент лезем вверх по лестнице, то не реагируем на нажатие
Представим ситуацию, что мы бежим влево и нам нужно забежать на лестницу. для этого мы начинаем одновременно давить клавиши влево и вверх. Но если мы не отпустим клавишу "влево" когда мы попали на лестницу (то есть отпустим её немного позже, чем надо было), тогда герой остановится, потому что сместится от центра лестницы.
Чтобы этого не произошло, мы как раз и делаем данную проверку.

Теперь сделаем обработчик нажатия клавиши вверх.
Код:
goingup=false
if player[2]=5 {exit}   //если падаем, то выход

step=2
if (y-step)<0 {exit}    //если вылазим за экран, то выход

x1=floor(x/tile_size);x2=floor((x+tile_size-1)/tile_size) //x1-левый край, x2 - правый край героя
y1=floor((y-step)/tile_size);y2=floor((y+tile_size-1)/tile_size)  //y1-верхний край, y2 - нижний край
aa=level[x1,y1];ab=level[x2,y1] // aa-левый верхний угол, ab - правый верхний угол
ba=level[x1,y2];bb=level[x2,y2] // ba-левый нижний угол, bb - правый нижний угол
zz=frac(x/tile_size) // позиция внутри клетки  по координате x

if !((ba=4)||(bb=4)) {exit} // если не лестница, то выходим
if (aa=1||aa=2||ab=1||ab=2) {exit} // или если упираемся в кирпич
//автоприлипание к лестнице
if ((zz=0||zz=0.25)&&(aa=4||ba=4)) { x=(floor(x/tile_size))*tile_size }  //если левый край на лестнице, либо чуть правее, тогда ставим ровно
else {
  if ((zz=0.75)&&(ab=4||bb=4)) { x=(floor(x/tile_size))*tile_size+tile_size } //если правый край чуть-чуть левее, тогда ставим ровно
  else {
    step=0 }} //иначе не двигаемся вверх

y-=step
goingup=true
if (step)
{
    changeanim=true;
    if !(player[2]=3) {image_index=7}  //меняем изображение героя
    player[2]=3 //устанавливаем статус "двигаемся вверх"
}

Так как в конце обработки у нас устанавливается переменная goingup в true, что говорит о том, что герой двигается по лестнице, то в событие отпускания клавиши "вверх" нам нужно сбрасывать эту переменную, иначе мы не сможем слезть с лестницы :)

Подобным же образом делается обработка нажатий вправо и вниз.


Сбор золота. Появление выхода с уровня, когда всё золото собрано.
Прописываем в столкновение с золотом:
Код:
instance_destroy()  // золото подобрали. оно исчезает
score+=250 // прибавляем очки
sound_play(snd_gold) // проигрываем звук
gold=instance_number(obj_gold)
if !(gold) // если золото осталось, то выходим
   {
        sound_play(snd_exit) // иначе проигрываем звук
        tile_layer_show(9) // и делаем видимым слой с выходом
// теперь в карте уровня нужно заменить все значения 9 на 4, чтобы герой мог ходить по вновь появившейся лестнице
        for (b=0;b<22;b+=1) {
            for (a=0;a<32;a+=1) {
               if (level[a,b]=9) {level[a,b]=4}
            }
        }
   }

Теперь нам нужно сделать проверку, чтобы игрок мог перейти на следующий уровень. Сделаем это в Begin Step.
Код:
if ((y=0)&&(instance_number(obj_gold)=0))
{
    sound_play(snd_win)
    transition_kind = 3
    room_goto_next()
}

Ещё не забыть сделать смену анимации главного героя. Я разместил этот код в Step End.
Код:
//нужно ли менять анимацию
if (changeanim)
{
    //меняем спрайт в зависимости от направления движения
    switch (player[2])
    {
        case 1:  if (image_index=6) {image_index=4} else {image_index+=1};break
        case 2:  if (image_index=3) {image_index=1} else {image_index+=1};break
        case 3:
        case 4:  if (image_index=8) {image_index=7} else {image_index+=1};break
        case 5:
        case 6:  image_index=0;break
    }
    changeanim=false
}
Здесь отсутствует отдельный вид анимации, когда человечек лезет по верёвке. Попробуйте сделать это сами :)
Кадры анимации - 9-11 - это перемещение по верёвке вправо (и добавить состояние 7), а 12-14 - это перемещение по верёвке влево (состояние 8).

В заключение.
Вы можете спросить, почему всё так заморочено, почему просто не сделать всё это через объекты? Потому что на каждый объект нужно довольно много ресурсов, которые GM довольно медленно обрабатывает. Так получается гораздо быстрее. В качестве оптимизации это можно делать не через массивы, а через сетки, с которыми GM работает гораздо быстрее, чем с массивами.

Как ещё одна оптимизация (но уже по объёму, а не по скорости), можно убрать избыточность массива, который загружается из файла "level1.lvl". Там на одну клетку тратится один байт. Но у нас используется всего 10 значений, что укладывается в 4 бита. Итого, мы можем хранить в одном байте два значения - одно в младших 4х битах. а второе - в старших 4х битах. Тогда размер сразу уменьшится в два раза.
Для более подробного изучения смотрите пример. Если его запустить, также снизу будут выводиться технические данные.
Белые девять символов - это пространство вокруг героя, взятое из карты уровня. Символ "М" там означает границу уровня.
Две цифры под "Gold" - это смещение героя от края тайла.
img_ind - это номер текущего спрайта.
motion - отражает состояние героя.
chanim - разрешено или не разрешено менять анимацию.

Не стал их убирать, потому что могут более наглядно продемонстрировать внутреннюю работу игры.

На этом закончу. Желаю успехов в разработке собственных игр. И до встречи!
Dmi7ry. 23 июля 2011

* lod08.rar (1242.95 Кб - загружено 1135 раз.)
« Последнее редактирование: Август 17, 2011, 08:33:09 от Dmi7ry » Записан

- А какой, собственно, командой процессора колобок ест черта?
- Командой EAT...
Справка и FAQ в правом верхнем углу...
OverBoy
GM Pro user
*

Репутация: 130
Offline Offline

Пол: Мужской
Награды:
За постоянность! [100 дней на форуме]За победу в новогоднем конкурсе [месяц тематических игр - 2012] (1 место)
API: Game Maker 8.0 Pro
Сообщений: 448



WWW
« Ответ #1 : Июль 23, 2011, 18:08:28 »

Ого, как здесь всё сложно....
Но всё равно, крутой урок ! Уважуха
Записан

Alcatraz
Flight Dream Studio
«Старожил форума»
******

Репутация: 392
Offline Offline

Пол: Мужской
Награды:
3000 сообщений!За постоянность! [100 дней на форуме]За лояльность! [+300 репутации]Знаток Unity 3D3 место в конкурсе: Адекватные игры #2 [Стимпанк]2 место за игру: Dead Night (Конкурс Золотые Руки)...
API: Unity 3D
Деятельность: C#, C++, UNITY3D
Сообщений: 3392


http://vk.com/alcatraz_rus


WWW
« Ответ #2 : Июль 23, 2011, 18:47:12 »

Молодец, вот это понимаю статья, а не 2 строчки! ;) лови + Уважуха
Записан

Vendet
Гл. Администратор
*

Репутация: 772
Offline Offline

Пол: Мужской
API: Unity 3D
Сообщений: 3949


HellRoom Games


WWW
« Ответ #3 : Июль 23, 2011, 19:11:31 »

Отлично сделано   Уважуха
Записан
Dgon
GM Pro user
*

Репутация: 40
Offline Offline

Пол: Мужской
Награды:
За постоянность! [50 дней на форуме]
API: Game Maker 8.0 Pro
Сообщений: 352

Boss


« Ответ #4 : Июль 23, 2011, 21:25:31 »

могу сказать только 1. ты красава) благодарен, всегда хотел создать кодировку уровня, но не доходили руки, и вот недавной я решил этим заняться а тут ты)  Уважуха
Записан
Dva_Kota
Гл. Администратор
*

Репутация: 636
Offline Offline

Пол: Мужской
Награды:
3000 сообщений!За постоянность! [500 дней на форуме]За лояльность! [+500 репутации]Настоящий игродел!Тру Админ :D1 место за игру: Energy Wars (Конкурс Золотые Руки)...
API: GameMaker Studio Standard
Сообщений: 3245



« Ответ #5 : Июль 23, 2011, 21:42:07 »

Отличная статья.  Уважуха
Записан

Dankov
«Старожил форума»
******

Репутация: 58
Offline Offline

Пол: Мужской
Награды:
500 сообщений!За постоянность! [10 дней на форуме]
API: 8.1 Standard
Сообщений: 569


« Ответ #6 : Июль 23, 2011, 21:50:47 »

Офигенная статья! Боольшущий  Уважуха!

P.S. На ActionScript 3.0 можно так же уровни кодировать(Это один из способов создания основы
игры(Будь то платформер или что то другое), давно хотел по экспериментировать с этим делом.
Спасибо!
Записан
Nightmare
\Hey!\
Активный участник
*****

Репутация: 99
Offline Offline

Пол: Мужской
Награды:
За постоянность! [100 дней на форуме]За помощь в развитии форума!
API: Multimedia Fusion 2
Деятельность: \Pixel-Art\
Сообщений: 379

\Type your text here\


« Ответ #7 : Август 04, 2011, 16:58:29 »

Сложно , но круто ! 
Записан

\Multimedia Fusion 2 Developer\
Dgon
GM Pro user
*

Репутация: 40
Offline Offline

Пол: Мужской
Награды:
За постоянность! [50 дней на форуме]
API: Game Maker 8.0 Pro
Сообщений: 352

Boss


« Ответ #8 : Октябрь 22, 2011, 20:38:28 »

Поднимаю тему но хочу спросить, по такой системе можно кодировать, переходы между комнатами?
ну типо:
000100
001100
000110
011100
010000
если 0, комната отсутсвует, если, что-то другое то это номер комнаты, которая там.
Записан
Dmi7ry
Гл. Администратор
*

Репутация: 1379
Offline Offline

Пол: Мужской
Награды:
5000 сообщений!За постоянность! [200 дней на форуме]За лояльность! [+1000 репутации]За помощь в развитии форума!Знаток Game Maker!За помощь новичкам!
API: GameMaker Studio Master
Деятельность: Code, design
Сообщений: 6626



WWW
« Ответ #9 : Октябрь 22, 2011, 20:41:07 »

Да, вполне можно.
Записан

- А какой, собственно, командой процессора колобок ест черта?
- Командой EAT...
Справка и FAQ в правом верхнем углу...
Dgon
GM Pro user
*

Репутация: 40
Offline Offline

Пол: Мужской
Награды:
За постоянность! [50 дней на форуме]
API: Game Maker 8.0 Pro
Сообщений: 352

Boss


« Ответ #10 : Октябрь 22, 2011, 20:47:29 »

Это радует, но я уже несколько часов пытаюсь на бумаге хотябы придумать систему, но не могу.. Вот какова её суть.

изначально есть какой-то узор, пусть будет тот который был раньше.
000100
001100
000110
011100
010000

А потом рандомно все еденицы заменяются на какието цифры 1-9.    Получается допустим это:
000200
003100
000390
015400
080000

так как сделать двери, которые бы переводили нас туда сюда... Рабочий способ я придумал, но он тупой, тяжелый, а главное долгий.. Это создавать каждую дверь которая ведет из 1двери во 2, из 1 двери в 9 и так далее... Но это не выход, как можно по короче сделать?
Записан
Dmi7ry
Гл. Администратор
*

Репутация: 1379
Offline Offline

Пол: Мужской
Награды:
5000 сообщений!За постоянность! [200 дней на форуме]За лояльность! [+1000 репутации]За помощь в развитии форума!Знаток Game Maker!За помощь новичкам!
API: GameMaker Studio Master
Деятельность: Code, design
Сообщений: 6626



WWW
« Ответ #11 : Октябрь 22, 2011, 20:53:45 »

Сам переход из комнаты в комнату зависит от того, как сделаны двери.
Например, если в комнате максимум 4 двери (по одной в каждую сторону), то можно в столкновении ГГ с объектом двери проверять, где находится дверь - слева/справа/вверху/внизу.
Вычислив, где находится дверь, смотрим на карте, какая комната в том направлении и делаем переход в неё.
Записан

- А какой, собственно, командой процессора колобок ест черта?
- Командой EAT...
Справка и FAQ в правом верхнем углу...
Dgon
GM Pro user
*

Репутация: 40
Offline Offline

Пол: Мужской
Награды:
За постоянность! [50 дней на форуме]
API: Game Maker 8.0 Pro
Сообщений: 352

Boss


« Ответ #12 : Октябрь 22, 2011, 21:02:16 »

Ну я так и думал, и даже реализовал вычисления с какой стороны дверь, но вот я понятия не имею, каким кодом заставить при в ходе в эту дверь, перейти в комнату, стоящию именно там...

Добавлено: Октябрь 22, 2011, 21:07:29
Я был бы рад, если бы ты мог выкроить время и написал бы мне пример, ибо я в масивах вообще не селен, и уверен, что не смогу создать перехож... Буду очень признателен
Записан
Dmi7ry
Гл. Администратор
*

Репутация: 1379
Offline Offline

Пол: Мужской
Награды:
5000 сообщений!За постоянность! [200 дней на форуме]За лояльность! [+1000 репутации]За помощь в развитии форума!Знаток Game Maker!За помощь новичкам!
API: GameMaker Studio Master
Деятельность: Code, design
Сообщений: 6626



WWW
« Ответ #13 : Октябрь 22, 2011, 21:09:00 »

Раньше понедельника вряд ли найдётся время.
Записан

- А какой, собственно, командой процессора колобок ест черта?
- Командой EAT...
Справка и FAQ в правом верхнем углу...
Dgon
GM Pro user
*

Репутация: 40
Offline Offline

Пол: Мужской
Награды:
За постоянность! [50 дней на форуме]
API: Game Maker 8.0 Pro
Сообщений: 352

Boss


« Ответ #14 : Октябрь 22, 2011, 21:12:28 »

Я всеравно щас лежу болею. Могу подождать, а то хочется создать, но не знаю как.

Добавлено: Октябрь 22, 2011, 22:07:55
Походу пример не нужен, я придумал, как заставить это работать) вроде бы.. ну если, что в личку напешу в понедельник)
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  

HellRoom Games © 2006-2012 All Rights Reserved
Powered by SMF 1.1.21 | SMF © 2013, Simple Machines
Страница сгенерирована за 0.182 секунд. Запросов: 33.