Делаем арканоид на движке Dreemchest

Всем привет! В этой статье я опишу процесс создания простейшего арканоида :) Начнем с создания проекта, потом добавим необходимые ресурсы, напишем пару классов и арканоид готов! :)

Скачать готовый проект можно отсюда, а необходимые изображения отсюда

Шаг 1 — Подготовка
Итак приступим, качаем редактор отсюда, запускаем и создаем проект:


Импортируем необходимые изображения (ball.png, box.png, pad.png и background.png):


Изображению мячика присваиваем класс bmpBall, для того, чтоб получить доступ к этому изображению из скрипта. Для этого, выделяем необходимый ресурс на панели Assets и задаем свойство класс как bmpBall на панели Properties:


Перетащим изображение background.png на рабочую область, выделим его щелчком мыши и выставим свойства Scale X и Scale Y на панели Properties как 1.5. Перемещаем изображение таким образом, чтоб оно закрыло весь экран. Сохраняем сцену, запускаем тестовый режим клавишей F5. Увидим наш фон и больше ничего.

Шаг 2 — Шарик
Теперь приступим к игровой логике. Начнем написание скриптов с самой «сложной» части — с шарика. Запаситесь терпением и кофе, потому как скрипт достаточно большой — целых 25 строк кода, тут нужна выдержка и стальной зад :)) Столкновения с игровыми объектами будет расчитывать физический движок Box2D, нам лишь остается применить небольшой хак, для того, чтоб скорость перемещения шарика не падала со временем.

Щелкаем правой кнопкой мыши на панели Assets, и выбираем пункт меню Create -> Script. Созданному ресурсу задаем имя и двойным щелчком открываем скрипт. Пишем код:

-- Объявляем класс Ball как наследник класса Graphics
-- Это нужно для того, чтоб мы могли поместить этот объект на сцену
-- А так же установить ему изображение
class "Ball"( Graphics )

-- Конструктор класса
function Ball:Ball()
       
-- Устанавливаем текстуру
       
-- Идентификатор bmpBall был задан на панели Assets
       
self.texture = 'bmpBall'
       
       
-- Наделяем объект физическим телом, передав таблицу с полем radius
       
self:physicalize( { radius = self.width * 0.5 } )

       
-- Задаем начальную скорость тела
       
self.physicalBody:setLinearVelocity( math.random() * 10 - 10,
                                             math
.random() * 10 - 10 )

       
-- Добавляем слушателя события onUpdate, в данном случае будем
       
-- слушать сами себя
       
self:attachListener( Event.Update, self )
end

-- Обработчик события onUpdate
function Ball:onUpdate( e )
       
-- Получаем параметры шарика
       
local body  = self.physicalBody
       
local vel       = body.linearVelocity
       
local speed = math.abs( vel.x ) + math.abs( vel.y )

       
if speed <= 0.5 then
                speed
= 0.5
       
end

       
-- Если скорость движения шарика меньше определенного порога
       
-- увеличиваем её
       
if speed < 5 then
               
local rescale   = 5 / speed
                body
:setLinearVelocity( vel.x * rescale, vel.y * rescale )
       
end
end

Мячик готов. Теперь его можно положить на сцену перетащив скрипт Ball из панели Assets на рабочую область. Сохранив сцену и запустив приложение увидим, как шарик улетает за пределы экрана.

Шаг 3 — Стенки
Теперь нам необходимо ограничить игровую область. Можно создать еще один тип объекта Wall и расставить его по бокам и сверху/снизу, но я покажу другой способ — создание стенок из скрипта.

Добавим стартовый класс, который будет создаваться первым при старте приложения и у которого будет вызван метод main, в котором мы и добавим на сцену стенки.

Создаем еще один скрипт и пишем туда вот такой код:

-- Стартовый класс должен быть контейнером
class "App"( StageObjectContainer )

-- Объявляем метод main
function App:main()
       
-- Получаем размеры экрана
       
local w = Stage.getWidth()
       
local h = Stage.getHeight()
       
       
-- Выключаем гравитацию
       
Physics.setGravity( 0, 0 )
       
       
-- Горизонтальные стенки
       
self:createWall( w * 0.5, 0, w, 5 )
       
self:createWall( w * 0.5, h, w, 5 )
       
       
-- Вертикальные стенки
       
self:createWall( 0, h * 0.5, 5, h )
       
self:createWall( w, h * 0.5, 5, h )
end

-- Метод создания стены
function App:createWall( x, y, w, h )
       
-- Создаем новый объект класса Graphics
       
local wall       = Graphics.new()
        wall
.x           = x
        wall
.y           = y
        wall
.visible = false

       
-- Делаем его прямоугольником
        wall
:setAsRectangle( w, h )

       
-- Добавляем статическое физическое тело
        wall
:physicalize( { static = true } )
       
       
-- Цепляем объект на сцену (физика будет работать и без этого)
       
-- Но это полезно для дебага, т.к. будет видно где находится
       
-- стена, если выставить флаг visible
       
self:attach( wall )
end

После того, как скрипт готов, необходимо выбрать класс App как стартовый:


Теперь мячик прыгает по сцене и не вылетает за её пределы.

Шаг 4 — Ракетка
Добавляем ракетку — объект с картинкой и физическим телом, который двигается вслед за мышью.

Ракетка должна иметь выпуклую форму, поэтому сначала нужно эту самую форму задать в редакторе форм. Для этого нажимаем правой кнопкой мыши на панели Assets и выбираем пункт меню Create -> Shape. Двойным щелчком по созданному ресурсу открываем редактирование формы. Чтоб не промахнуться с размерами выберем эталонный объект:

В обозревателе ресурсов выбираем изображение pad.png

Добавляем к форме полигон:


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


Сохраняем форму и привязываем к ней идентификатор shPad (таким же образом, как это было сделано с изображением мячика). Изображение pad.png связываем с идентификатором bmpPad.

Теперь создадим скрипт для ракетки и напишем туда код:

class "Pad"( bmpPad )

function Pad:Pad()
       
-- Для ракетки я сделал специальную форму физического тела
       
-- А для того, чтоб использовать именно её необходимо передать
       
-- идентификатор формы в метод physicalize
       
self:physicalize( { shape = 'shPad', kinematic = true } )

       
-- Слушаем события движения мыши
       
Stage.attachListener( TapEvent.Move, self )
end

-- Обработчик движения
function Pad:onTapMove( e )
       
-- Просто изменяем координаты объекта
       
self.x = e.x
end

Ракетка готова, теперь её можно так же переместить на сцену к шарику и после запуска можно будет гонять шарик по сцене.

Шаг 5 — Блоки
Осталось добавить блоки, которые будут исчезать при столкновении с шариком.
Свяжем изображение box.png с идентификатором bmpBox
Напишем класс блока:

-- По идентификатору был автоматически сгенерирован класс от которого
-- можно наследоваться в скрипте, задавать изображение в данном случае уже не нужно
class "Block"( bmpBox )

function Block:Block()
       
-- Цепляем физическое тело
       
self:physicalize( { static = true } )

       
-- Добавляем слушателя на событие onCollisionBegin
       
self:attachListener( CollisionEvent.Begin, self )
end

-- Обработчик столкновения
function Block:onCollisionBegin( e )
       
-- Т.к. у нас на данном этапе только шарик летает по сцене
       
-- столкнуться мы можем лишь с ним, поэтому не делаем никаких
       
-- проверок, а просто удаляем блок со сцены
       
self:release()
end


Сохраняем скрипт и бросаем блоки на сцену. Сохраняем и запускаем проект — прототип арканоида готов!:)

Результат на скриншоте ниже:

  • +10

Комментарии (18)

0
неплохо было бы сделать в примерах, еще какую-нибудь казуальную механику :)
0
буду добавлять по мере возможности :)
+3
Интересный движок. Респект.
А можно сделать ещё туториал не обязательно с механикой игровой — а базовые элементы:
— переключение между экранами (интро, уровень 1, уровень 2)
— событие звука (что-т я это нигде не увидел)
— событие прикосновение пальца(тапа), работа с акселерометром и т.п.

на ту же простенькую механику матч2 в 2 уровня, просто чтобы увидеть, как этот базис выглядит.
0
похорошел двиг:)
0
а то! фирма веников не вяжет =))
0
фирма делает движки
0
www.emanueleferonato.com/2013/04/22/breakout-prototype-made-with-dreemchest/ — здорово, что о тебе даже Великий пишет :-)
  • SeeD
  • SeeD
0
ну надо ж как-то продвигать :)
0
А вот интересно. Бесплатно Феронато пишет/размещает статьи или за денежку? :)
0
Он даже рекламой не брезгует:)
0
Я думаю у него уже давно проблема чего бы такого написать, чтобы блог не мхом не зарос, так что рад любой теме. Ну мне так кажется.
0
Куда-то исчез мой каммент… напишу еще раз :)
Чтоб разместить квадратный баннер, нужно задонатить, объем доната зависит от тебя. На статьи тоже можно задонатить, но Эмануэль может и бесплатно разместить, если тема покажется ему интересной :)
0
фигасе O_o
0
О движке написал еще один блоггер! :)
  • _dm_
  • _dm_
0
Если у кого-то есть аккаунты на Reddit, поддержите пожалуйста голосом. Ну оочень надо :)
+1
Done:).
0
Спасибо :)
0
Интересный движок. Спасибо.
  • Tim
  • Tim
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.