В прошлом уроке мы добавили персонажа и научили его двигаться по земле и прыгать. В этом уроке мы добавим несколько игровых объектов (препятствий) и добавим анимацию смерти.
Создание объектов.
Откроем наш проект и немного приберемся, добавим папку Level и перенесем туда наши файлы ground.collection, ground.script, level.collection, controller.script. Добавим в эту папку еще одну папку images, для хранения изображений игровых объектов.
В папку images добавим изображение rock_planks.PNG, все из того же архива из первого урока.
Добавим в папку level новый объект level.atlas и в окне Outline добавим это изображение Add image
В папку Level добавим Game Object file platform.go
В сцене platform.go в окне Outline добавим sprite Add component
В окне Properties, в строке Images выберем level.atlas и в строке Defauit Animation выберем rock_planks
Далее добавим компонент коллизии Collision Object и Add Shape — Box и изменим Type в окне Properties на kinematic. В строках Group и Mask пишем geometry и hero соответственно, для того, чтобы наш персонаж мог взаимодействовать с этим объектом.
Можете продублировать компонент sprite и настроить размеры Box Shape в окне Properties, подстроив под изображение платформ.
Добавим в папку Level новый скрипт platform.script и напишем в нем такой код:
function init(self) self.speed = 9 -- Default speed end function update(self, dt) local pos = go.get_position() if pos.x < -500 then go.delete() end pos.x = pos.x - self.speed go.set_position(pos) end function on_message(self, message_id, message, sender) if message_id == hash("set_speed") then self.speed = message.speed end end
В этом скрипте указывается скорость движения платформы и удаление платформы при достижении определенной позиции.
В platform.go к Game object в окне Outline добавим этот скрипт Add Component From File
В папке Level продублируйте platform.go и назовите platform_long.go и измените его (например, добавив или удалив платформы, изменяя при этом размеры box share) и не забывая все сохранять.
Появление платформ на сцене.
Для рандомного появления платформ на сцене откройте level.collection и в окне Outline к компоненту Controller добавьте Factory Add component
В строке Protorype в окне Properties добавьте нашу платформу
Продублируйте компонент factory и измените Prototype на другую платформу.
В обоих Factory измените Id на platform_factory и platform_long_factory.
Откроем скрипт controller и изменим его, для того чтобы наши платформы появлялись
go.property("speed", 6) local grid = 460 local platform_heights = { 100, 200, 350 } function init(self) msg.post("ground/controller#script", "set_speed", { speed = self.speed }) self.gridw = 0 end function update(self, dt) self.gridw = self.gridw + self.speed if self.gridw >= grid then self.gridw = 0 -- Maybe spawn a platform at random height if math.random() > 0.2 then local h = platform_heights[math.random(#platform_heights)] local f = "#platform_factory" if math.random() > 0.5 then f = "#platform_long_factory" end local p = factory.create(f, vmath.vector3(1600, h, 0), nil, {}, 0.6) msg.post(p, "set_speed", { speed = self.speed }) end end end
Сохраняем все и проверяем игру
Анимация смерти.
Теперь нам необходимо создать анимацию смерти при всех возможных факторах, например: столкновение с препятствием (с шипами на платформе).
Откроем скрипт hero и изменим его, перед функцией Update добавим
local function play_animation(self, anim) -- only play animations which are not already playing if self.anim ~= anim then -- tell the spine model to play the animation spine.play("#spinemodel", anim, go.PLAYBACK_LOOP_FORWARD, 0.15) -- remember which animation is playing self.anim = anim end end local function update_animation(self) -- make sure the right animation is playing if self.ground_contact then play_animation(self, hash("run_right")) else if self.velocity.y > 0 then play_animation(self, hash("jump_right")) else play_animation(self, hash("fall_right")) end end end
и в самой функции Update после
go.set_position(go.get_position() + self.velocity * dt)
добавим
update_animation(self)
В папку level/image добавим изображение spikes.png.
Откроем Level.atlas и добавим туда это изображение
Откроем platform.go и добавим еще один спрайт. В строке image окна Properties выберете level.atlas и в Defauit Animation выбрать наши шипы
Добавим еще один компонент collision для шипов и разместим его в нижней части платформы, Box shape самой платформы разместите в верхней части платформы и разместите шипы по бокам и по низу, дублируя sprite и изменяя их позицию в окне Properties.
В компоненте коллизинон (danger_spike) измените Type на kinematic, Group на danger и Mask hero
Откройте hero.go и добавьте в окне Outline к Colisionobject еще mask danger
Откройте hero скрипт и измените функцию on_message()
function on_message(self, message_id, message, sender) if message_id == hash("reset") then self.velocity = vmath.vector3(0, 0, 0) self.correction = vmath.vector3() self.ground_contact = false self.anim = nil go.set(".", "euler.z", 0) go.set_position(self.position) msg.post("#collisionobject", "enable") elseif message_id == hash("contact_point_response") then -- check if we received a contact point message if message.group == hash("danger") then -- Die and restart play_animation(self, hash("die_right")) msg.post("#collisionobject", "disable") go.animate(".", "euler.z", go.PLAYBACK_ONCE_FORWARD, 160, go.EASING_LINEAR, 0.7) go.animate(".", "position.y", go.PLAYBACK_ONCE_FORWARD, go.get_position().y - 200, go.EASING_INSINE, 0.5, 0.2, function() msg.post("#", "reset") end) elseif message.group == hash("geometry") then handle_geometry_contact(self, message.normal, message.distance) end end end
и функцию init()
function init(self) -- this lets us handle input in this script msg.post(".", "acquire_input_focus") -- save position self.position = go.get_position() msg.post("#", "reset") end
Можете запустить игру и проверить, наш персонаж при столкновении с шипами будет умирать, и появляться в том же месте.
Теперь давайте сделаем сброс уровня при смерти персонажа.
Откроем скрипт controller и изменим его
go.property("speed", 6) local grid = 460 local platform_heights = { 100, 200, 350 } function init(self) msg.post("ground/controller#script", "set_speed", { speed = self.speed }) self.gridw = 0 self.spawns = {} end function update(self, dt) self.gridw = self.gridw + self.speed if self.gridw >= grid then self.gridw = 0 -- Maybe spawn a platform at random height if math.random() > 0.2 then local h = platform_heights[math.random(#platform_heights)] local f = "#platform_factory" if math.random() > 0.5 then f = "#platform_long_factory" end local p = factory.create(f, vmath.vector3(1600, h, 0), nil, {}, 0.6) msg.post(p, "set_speed", { speed = self.speed }) table.insert(self.spawns, p) end end end function on_message(self, message_id, message, sender) if message_id == hash("reset") then -- Tell the hero to reset. msg.post("hero#script", "reset") -- Delete all platforms for i,p in ipairs(self.spawns) do go.delete(p) end self.spawns = {} elseif message_id == hash("delete_spawn") then for i,p in ipairs(self.spawns) do if p == message.id then table.remove(self.spawns, i) go.delete(p) end end end end
Теперь, при смерти будут удалятся все платформы со сцены и игра будет начинаться заново.
Откроем срипт platform и изменим строку
go.delete()
на
msg.post("/level/controller#script", "delete_spawn", { id = go.get_id() })
И в скрипте hero изменим
go.animate(".", "position.y", go.PLAYBACK_ONCE_FORWARD, go.get_position().y - 200, go.EASING_INSINE, 0.5, 0.2, function() msg.post("#", "reset") end)
на
go.animate(".", "position.y", go.PLAYBACK_ONCE_FORWARD, go.get_position().y - 200, go.EASING_INSINE, 0.5, 0.2, function() msg.post("controller#script", "reset") end)
На этом данный урок завершен, можете запустить игру на данной стадии и протестировать. В следующем мы добавим монеты и научим нашего персонажа их собирать. Удачных проектов!