Solarus-Games English Forum

Solarus => Development => Topic started by: wizard_wizzle (aka ZeldaHistorian) on April 10, 2015, 05:01:22 PM

Title: Collision tests and movement
Post by: wizard_wizzle (aka ZeldaHistorian) on April 10, 2015, 05:01:22 PM
I have the snippet of code below where I'm essentially trying to re-create block pushing in a custom entity (in order to additional functionality for the block). The problem I'm having having is that the block doesn't start moving until after the hero has stopped pushing and has moved away from the block - rather than actually pushing the block. Is this the expected behavior and I'm doing something wrong? Is there a way to have the movement start as the hero is pushing?

  self:add_collision_test("touching", function(self, other)

    if other:get_type() == "hero" then
      local m = sol.movement.create("path")
      m:set_ignore_obstacles(true)
      m:set_snap_to_grid(true)

      if other:get_direction() == 0 then m:set_path({0,0})
      elseif other:get_direction() == 1 then m:set_path({2,2})
      elseif other:get_direction() == 2 then m:set_path({4,4})
      elseif other:get_direction() == 3 then m:set_path({6,6}) end
      m:start(self)
Title: Re: Collision tests and movement
Post by: Christopho on April 10, 2015, 05:51:39 PM
Maybe it works, but the collision test is repeatedly called, starting a new movement every time?
If this is the problem, adding a boolean on the block to remember if it is already being pushed or not should work. Or maybe just checking first that the block has no movement.
Title: Re: Collision tests and movement
Post by: wizard_wizzle (aka ZeldaHistorian) on April 10, 2015, 07:14:33 PM
Adding a boolean worked perfectly - thanks Christopho!

One more question. Is it possible to make my custom entity block act like a block for the purpose of activating switches? It appears that when I push my block onto a switch that requires a block to activate, my block knows it's on a switch, but it does not activate the switch.
Title: Re: Collision tests and movement
Post by: Christopho on April 10, 2015, 08:02:48 PM
No, but you should be able to do it manually with a collision test on switches.
Title: Re: Collision tests and movement
Post by: wizard_wizzle (aka ZeldaHistorian) on April 10, 2015, 09:03:27 PM
I can activate it easily by adding this to the code above:

    elseif other:get_type() == "switch" then
      other:set_activated(true)
    end


But I can't figure out how to inactive it on leaving. How do you detect when something's not colliding? (I'm sure it's obvious and I haven't thought of it!)
Title: Re: Collision tests and movement
Post by: Christopho on April 10, 2015, 10:01:15 PM
It is not that obvious in fact.
A way to detect when something is not colliding is to test if entity:overlaps(other). You can do it periodically from a timer, or only when your custom block moves (from custom_block:on_position_changed())
Title: Re: Collision tests and movement
Post by: wizard_wizzle (aka ZeldaHistorian) on April 11, 2015, 05:24:01 PM
Thanks again! That general approach worked. Once I get some final issues worked out, I'll be done!
Title: Re: Collision tests and movement
Post by: Dragon_noir on June 10, 2015, 05:31:22 PM
I've also created a custom entity to act as a block.

It works pretty well, but the only problem i have is, once the entity is pushed once, it doesn't react anymore (i can't push it a second time and the action icon doesn't appear anymore).

here's the code i have so far:


local boulder_small = ...
local map = boulder_small:get_map()
local game = boulder_small:get_game()
local hero = map:get_hero()
local x, y, layer = boulder_small:get_position()
local sprite = boulder_small:get_sprite()

local hero_facing_boulder_small = false
local action_command_boulder_small = false
local pushed = false

boulder_small:set_traversable_by("hero", false)
boulder_small:set_traversable_by("enemy", false)
boulder_small:set_traversable_by("custom_entity", false)

-- Show an action icon when the player faces the boulder.
boulder_small:add_collision_test("facing", function(boulder_small, other)

  if other:get_type() == "hero" then
  if other:get_animation() == "pushing"
    and pushed == false then
      pushed = true
      hero:freeze()
      boulder_small:go()
  hero:go()     
  end

    hero_facing_boulder_small = true

    if boulder_small:get_movement() == nil
      and game:get_command_effect("action") == nil
      and game:get_custom_command_effect("action") == nil then
      action_command_boulder_small = true
      game:set_custom_command_effect("action", "grab")     
    end
  end
end)

-- Remove the action icon when stopping facing the boulder.

function boulder_small:on_update()

  if action_command_boulder_small and not hero_facing_boulder_small then
    game:set_custom_command_effect("action", nil)
    action_command_boulder_small = false
  end

  hero_facing_boulder_small = false
end

--Makes the boulder move.

function boulder_small:go()

  local movement = sol.movement.create("path")
  local direction = hero:get_direction()
  sprite:set_animation("rolling")
  sprite:set_direction(direction)

    if direction == 0 then  
      sprite:set_animation("rolling")
      sprite:set_direction(0)
      movement:set_path({0, 0})
    end

    if direction == 1 then   
      sprite:set_animation("rolling")
      sprite:set_direction(1)
      movement:set_path({2, 2})
    end

    if direction == 2 then   
      sprite:set_animation("rolling")
      sprite:set_direction(2)
      movement:set_path({4, 4})
    end

    if direction == 3 then   
      sprite:set_animation("rolling")
      sprite:set_direction(3)
      movement:set_path({6, 6})
    end

  movement:start(boulder_small)

  function movement:on_finished()
    boulder_small:stop()
  end
end

--When boulder stops.

function boulder_small:stop()
  sprite = boulder_small:get_sprite()
  sprite:set_animation("stopped")
  hero:unfreeze()
end

-- Makes the hero move.
function hero:go()
 
  local m = sol.movement.create("straight")
  local direction = hero:get_direction()

    if direction == 0 then  
      m:set_angle(0)
  hero:set_animation("pushing")
    end

    if direction == 1 then   
      hero:set_animation("pushing")
  m:set_angle(math.pi / 2)
    end

    if direction == 2 then   
      hero:set_animation("pushing")
  m:set_angle(math.pi)
    end

    if direction == 3 then   
      hero:set_animation("pushing")
  m:set_angle(3 * math.pi / 2)
    end

  m:start(hero)
end



Any help is welcome :)
Title: Re: Collision tests and movement
Post by: Diarandor on June 11, 2015, 01:24:46 AM
Hi Dragon_noir,

the main problem in your code is probably that you set "pushed = true", but you forgot to change it to false later. I would not use the custom command effect "action" for pushing blocks, since that effect is usually used when the space bar is pressed, but this is just my opinion ;D. As an advice, the Lua API recommends to use a timer or other solution instead the on_update event since it is called too many times (if I understand correctly, this kind of methods could slow down the game a little bit, maybe on slow computers). Another advice, you are repeating some code a lot of times in some functions that is not necessary; this would improve some of your functions:


function boulder_small:go()

  local movement = sol.movement.create("path")
  local direction = hero:get_direction()
  sprite:set_animation("rolling")
  sprite:set_direction(direction)
  movement:set_path({2*direction, 2*direction})
  movement:start(boulder_small)

  function movement:on_finished()
    boulder_small:stop()
  end
end

-- Makes the hero move.
function hero:go()
 
  local m = sol.movement.create("straight")
  local direction = hero:get_direction()
  hero:set_animation("pushing")
  m:set_angle(direction * math.pi / 2)
  m:start(hero)
end


I hope this helps you to improve your script a bit (I have not tried it anyway).
Title: Re: Collision tests and movement
Post by: wizard_wizzle (aka ZeldaHistorian) on June 11, 2015, 03:25:36 AM
function entity:on_created()
  self:set_size(16, 16)
  self:snap_to_grid()
  self:set_modified_ground("ice") --is this useful?
  self:set_traversable_by("hero", false)
  self:set_traversable_by("custom_entity", true) --to allow pushing block into pit
  self:create_sprite("entities/ice_block")

  self:add_collision_test("facing", function(self, other)
    if other:get_type() == "hero" and not pushing then
      pushing = true
      local m = sol.movement.create("path")
      m:set_ignore_obstacles(false)
      m:set_snap_to_grid(true)

      if other:get_direction() == 0 then m:set_path({0,0})
      elseif other:get_direction() == 1 then m:set_path({2,2})
      elseif other:get_direction() == 2 then m:set_path({4,4})
      elseif other:get_direction() == 3 then m:set_path({6,6}) end
      m:start(self, function()
pushing = false
      end)
    end
  end)


This is the code that I use, and it works fine.
Title: Re: Collision tests and movement
Post by: Dragon_noir on June 11, 2015, 04:33:37 PM
Thanks guys ;)

The pushing problem is solved. I indeed forgot to set pushed as false again ;)

The reason i had the on_update() function in there was because I took example of Christopho's minecart.lua script and he used it there. I wanted the action button to appear to let players know you can interact with this item ( same as the blocks do)



Title: Re: Collision tests and movement
Post by: Christopho on June 11, 2015, 04:49:13 PM
Yes I am not very proud of this on_update() in the minecart script :(
I should replace it with a proper timer ^^