I’ve started trying out Defold, and made the tutorial that’s on the dashboard. It runs just fine, but when the game calls “reset” in controller.script after the game has been running for ~30 seconds. There are also minor errors that needed some code tweaks, although that is most likely just me being very bad at LUA. Those things are the animation script never thinking that hero.go is touching the ground, resulting in the hero being in the falling animation while grounded (this was fixed by adding a check that was looking for the velocity being exactly 0), as well as having the call for removing a specific platform instead of go.delete() make the game run into a sprite buffer overflow after a small amount of time.
controller.script, where the “reset” and “remove specific platform” calls exist:
go.property("speed", 6)
local grid = 460
local platform_heights = { 100, 200, 350 }
local coins = 3
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"
local coins = coins
if math.random() > 0.5 then
f = "#platform_long_factory"
coins = coins * 2 -- Twice the number of coins on long platforms
end
local p = factory.create(f, vmath.vector3(1600, h, 0), nil, {}, 0.6)
msg.post(p, "set_speed", { speed = self.speed })
msg.post(p, "create_coins", { coins = coins })
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.script, calling “delete specific platform” (call commented out and replaced with go.delete()):
function init(self)
self.speed = 9 -- Default speed
self.coins = {}
end
function final(self)
for i,p in ipairs(self.coins) do
go.delete(p)
end
end
function update(self, dt)
local pos = go.get_position()
if pos.x < -200 then
go.delete()
--msg.post("/level/controller#script", "delete_spawn", { id = go.get_id() })
end
pos.x = pos.x - self.speed
go.set_position(pos)
end
function create_coins(self, params)
local spacing = 56
local pos = go.get_position()
local x = pos.x - params.coins * (spacing*0.5) - 24
for i = 1, params.coins do
local coin = factory.create("#coin_factory", vmath.vector3(x + i * spacing , pos.y + 64, 1))
msg.post(coin, "set_parent", { parent_id = go.get_id() })
msg.post(coin, "start_animation", { delay = i/10 })
table.insert(self.coins, coin)
end
end
function on_message(self, message_id, message, sender)
if message_id == hash("set_speed") then
self.speed = message.speed
elseif message_id == hash("create_coins") then
create_coins(self, message)
end
end
And finally, hero.script, calling “reset”:
-- gravity pulling the player down in pixel units/sˆ2
local gravity = -20
-- take-off speed when jumping in pixel units/s
local jump_takeoff_speed = 900
function init(self)
-- this tells the engine to send input to on_input() in this script
msg.post(".", "acquire_input_focus")
-- save the starting position
self.position = go.get_position()
msg.post("#", "reset")
-- keep track of movement vector and if there is ground contact
self.velocity = vmath.vector3(0, 0, 0)
self.ground_contact = false
end
function final(self)
-- Return input focus when the object is deleted
msg.post(".", "release_input_focus")
end
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"))
elseif self.velocity.y == 0 then
play_animation(self, hash("run_right"))
else
play_animation(self, hash("fall_right"))
end
end
end
function update(self, dt)
local gravity = vmath.vector3(0, gravity, 0)
if not self.ground_contact then
-- Apply gravity if there's no ground contact
self.velocity = self.velocity + gravity
end
-- apply velocity to the player character
go.set_position(go.get_position() + self.velocity * dt)
-- reset volatile state
self.correction = vmath.vector3()
self.ground_contact = false
-- apply it to the player character
go.set_position(go.get_position() + self.velocity * dt)
update_animation(self)
end
local function handle_geometry_contact(self, normal, distance)
-- project the correction vector onto the contact normal
-- (the correction vector is the 0-vector for the first contact point)
local proj = vmath.dot(self.correction, normal)
-- calculate the compensation we need to make for this contact point
local comp = (distance - proj) * normal
-- add it to the correction vector
self.correction = self.correction + comp
-- apply the compensation to the player character
go.set_position(go.get_position() + comp)
-- check if the normal points enough up to consider the player standing on the ground
-- (0.7 is roughly equal to 45 degrees deviation from pure vertical direction)
if normal.y > 0.7 then
self.ground_contact = true
end
-- project the velocity onto the normal
proj = vmath.dot(self.velocity, normal)
-- if the projection is negative, it means that some of the velocity points towards the contact point
if proj < 0 then
-- remove that component in that case
self.velocity = self.velocity - proj * normal
end
end
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("/level/controller#script", "reset")
end)
elseif message.group == hash("geometry") then
handle_geometry_contact(self, message.normal, message.distance)
end
end
end
local function jump(self)
-- only allow jump from ground
if self.ground_contact then
-- set take-off speed
self.velocity.y = jump_takeoff_speed
end
end
local function abort_jump(self)
-- cut the jump short if we are still going up
if self.velocity.y > 0 then
-- scale down the upwards speed
self.velocity.y = self.velocity.y * 0.5
end
end
function on_input(self, action_id, action)
if action_id == hash("jump") or action_id == hash("touch") then
if action.pressed then
jump(self)
elseif action.released then
abort_jump(self)
end
end
end
Thanks for reading, and really hope you can help me see the problem.