Quicksand custom entity

Started by wizard_wizzle (aka ZeldaHistorian), January 19, 2015, 04:38:08 PM

Previous topic - Next topic
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

January 19, 2015, 05:33:29 PM #1 Last Edit: January 19, 2015, 05:37:35 PM by Christopho
- 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.