Делаем арканоид на движке 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
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.