I'm looking to make a custom entity which behaves like quicksand (think Link to the Past's desert temple) - when the hero approaches it, he's slowly dragged into the center, then when he reached the center, he sinks (behavior like a hole, probably). I can't seem to figure it out. Any thoughts?
This is what I have so far, which isn't even close to working:
local entity = ...
local map = entity:get_map()
local hero = map:get_entity("hero")
-- Quicksand: entity which slows the hero until
-- he finally falls in
function entity:on_created()
self:create_sprite("entities/quicksand")
self:set_size(32, 32)
self:set_origin(16, 16)
ex, ey, el = self:get_position()
self:get_sprite():set_animation("quicksand")
end
function entity:on_update()
self:add_collision_test("overlapping", function()
if hero:get_walking_speed() >= 8 then hero:set_walking_speed(hero:get_walking_speed()-1) end
m = sol.movement.create("target")
m:set_target(ex, ey)
m:start(hero)
end)
end
- The variables ex, ey and el should be local to the file, otherwise they are global and all quicksand instances share them.
- The collision test should only react to the hero, not to other entities. Your current code tries to move the hero every time any entity overlaps the quicksand.
- Your code is creating a new collision test at each frame (on_update). You should create the collision test only once. Creating a new movement at each frame is also wrong.
- Starting a movement on the hero is probably a bad idea because an entity cannot have several movements at the same time. If you start a movement, then the player's movement from the keyboard/joypad is lost. And as soon as the player changes the state (like by using the sword), your movement is lost and the keyboard/joypad movement is back. A better approach should be to directly change the hero coordinates towards the quicksand.
local entity = ...
local map = entity:get_map()
local hero = map:get_entity("hero")
local ex, ey, el
local timer
-- Quicksand: entity which slows the hero until
-- he finally falls in
function entity:on_created()
self:create_sprite("entities/quicksand")
self:set_size(32, 32)
self:set_origin(16, 16)
ex, ey, el = self:get_position()
self:get_sprite():set_animation("quicksand")
self:add_collision_test("overlapping", function(quicksand, other)
-- This callback will be repeatedly called while other is overlapping the quicksand
if other:get_type() ~= "hero"
return
end
local hero = other
-- Only do this in some specific states (in particular, don't do it while jumping, flying with the hookshot, etc.)
if hero:get_state() ~= "free" and hero:get_state() ~= "sword loading" ........(and maybe others)......... then
return
end
-- Move the hero towards the quicksand's center every 200 ms while he is overlapping it.
if timer == nil then
timer = sol.timer.start(self, 200, function()
hero:set_xy(........) -- Some coordinates one pixel towards the center of the quicksand
timer = nil -- This variable "timer" ensures that only one timer is running.
end)
end
end)
end
I did not test it, but this is an idea. I hope it can help!
(By the way, I still need to play your game! Sorry for the long time!)
Works great, Christopho! Thanks!
One question - if I wanted to slow the hero's walking speed when he overlaps the quicksand (making it harder to get out), how would I go about returning his speed when he's no longer in the quicksand? Also, any advice on how to make the hero sink/fall when he gets to the center?
Updated (working) code:
local entity = ...
local map = entity:get_map()
local hero = map:get_entity("hero")
local ex, ey, el
local timer
-- Quicksand: entity which slows the hero until
-- he finally falls in
function entity:on_created()
self:create_sprite("entities/quicksand")
self:set_size(32, 32)
self:set_origin(16, 16)
ex, ey, el = self:get_center_position()
self:get_sprite():set_animation("quicksand")
self:add_collision_test("overlapping", function(quicksand, other)
-- This callback will be repeatedly called while other is overlapping the quicksand
if other:get_type() ~= "hero" then
return
end
local hero = other
-- Only do this in some specific states (in particular, don't do it while jumping, flying with the hookshot, etc.)
if hero:get_state() ~= "free" and hero:get_state() ~= "sword loading" then
return
end
--hero:set_walking_speed(44)
-- Move the hero toward the quicksand's center every 20 ms while he is overlapping it.
if timer == nil then
timer = sol.timer.start(self, 20, function()
hx, hy, hl = hero:get_position()
if ex > hx then hx = hx + 1 else hx = hx - 1 end
if ey > hy then hy = hy + 1 else hy = hy - 1 end
hero:set_position(hx, hy)
timer = nil -- This variable "timer" ensures that only one timer is running.
end)
end
end)
end
Speaking of my game, I just (quietly) released a new version today! :)
Quote from: wrightmat on January 20, 2015, 12:46:21 AM
One question - if I wanted to slow the hero's walking speed when he overlaps the quicksand (making it harder to get out), how would I go about returning his speed when he's no longer in the quicksand?
A way to do this is to create a second timer that checks the collision again (with if quicksand:overlaps(hero)) and if not, restore the speed. Warning: if the quicksand can die, the timer should belong to the hero and not to the quicksand, in order to be sure that it will be called.
Quote from: wrightmat on January 20, 2015, 12:46:21 AM
Also, any advice on how to make the hero sink/fall when he gets to the center?
Call hero:freeze() to remove control from the player and then do what you want with the sprites of the hero. Like starting the animation "falling" (usually used with holes) or a custom one.