Author Topic: Freeze when using movement:set_path()  (Read 1056 times)

MetalZelda

  • Hero Member
  • *****
  • Posts: 508
    • View Profile
Freeze when using movement:set_path()
« on: January 20, 2017, 02:28:23 pm »
Hello

I was modifying a platform script (from Diarandor I guess), which was a simple platform script, and I decided to modify it so it allow custom movement, path movement, and in the future circular movement

Code: Lua
  1. local entity = ...
  2. local hero = entity:get_map():get_entity("hero")
  3.  
  4. -- Platform
  5. -- changes:
  6.  
  7. -- size              -> Entity size
  8. -- time_stopped      -> self.time_stopped
  9. -- speed             -> self.speed
  10. -- directionnal path -> self.path
  11.  
  12. --todo
  13. --entity.movement_type (path / circular / straight / target)
  14.  
  15. -- And anything related to these movement
  16.  
  17. function entity:on_created()
  18.   local size_x, size_y = self:get_size()
  19.   self:set_size(size_x, size_y)
  20.   self:set_origin(size_x / 2, size_y / 2)
  21.   self:set_can_traverse("jumper", true)
  22.   self:set_can_traverse_ground("hole", true)
  23.   self:set_can_traverse_ground("deep_water", true)
  24.   self:set_can_traverse_ground("lava", true)
  25.   self:set_can_traverse_ground("traversable", true)
  26.   self:set_can_traverse_ground("shallow_water", false)
  27.   self:set_can_traverse_ground("wall", false)
  28.   self:set_modified_ground("traversable")
  29.   self:set_layer_independent_collisions(false)
  30.  
  31.   self:start_movement()
  32. end
  33.  
  34. function entity:start_movement()
  35.   local m = sol.movement.create("path")
  36.   m:set_path(self.path)
  37.   m:set_speed(self.speed)
  38.   m:set_loop(true)
  39.   m:start(self)
  40.  
  41.   self:add_collision_test("touching", function(_, other)
  42.     if other:get_type() == "wall" or (other:get_type() == "custom_entity" and other:get_model() == "object/platform/limit") then
  43.       self:on_obstacle_reached(m)
  44.     end
  45.   end)
  46. end
  47.  
  48. function entity:on_obstacle_reached(movement)
  49.   -- Reverse the movement, reverse the table
  50.   for i = 1, math.floor(#self.path / 2) do
  51.     local tmp = self.path[i]
  52.     self.path[i] = self.path[#tbl - i + 1]
  53.     self.path[#self.path - i + 1] = tmp
  54.   end
  55.  
  56.   --Make the platform turn back.
  57.   movement:stop()
  58.   movement = sol.movement.create("path")    
  59.   movement:set_path(self.path)
  60.   movement:set_speed(self.speed)
  61.   movement:set_loop(true)
  62.  
  63.   sol.timer.start(self, self.time_stopped, function()
  64.     movement:start(self)
  65.   end)
  66. end
  67.  
  68. function entity:on_position_changed()
  69.   -- Moves the hero if located over the platform.
  70.   if not self:is_on_platform(hero) then return end
  71.     local hx, hy, hl = hero:get_position()
  72.     local direction4 = self:get_direction()
  73.     local dx, dy = 0, 0 --Variables for the translation.
  74.     if direction4 == 0 then dx = 1
  75.     elseif direction4 == 1 then dy = -1
  76.     elseif direction4 == 2 then dx = -1
  77.     elseif direction4 == 3 then dy = 1
  78.     end
  79.     if not hero:test_obstacles(dx, dy, hl) then hero:set_position(hx + dx, hy + dy, hl) end
  80. end
  81.  
  82. function entity:on_movement_changed(movement)
  83.   --Change direction of the sprite when the movement changes.
  84.   local direction4 = movement:get_direction4()
  85.   self:set_direction(direction4)
  86. end
  87.  
  88. function entity:is_on_platform(other_entity)
  89.   --Returns true if other_entity is on the platform.
  90.   local ox, oy, ol = other_entity:get_position()
  91.   local ex, ey, el = self:get_position()
  92.   if ol ~= el then return false end
  93.   local sx, sy = self:get_size()
  94.   if math.abs(ox - ex) < sx/2 -1 and math.abs(oy - ey) < sy/2 -1 then return true end
  95.   return false
  96. end
  97.  

Everything works fine, the entity's movement, speed and delay is now customizable from the map script

However

This is the script call in the map script, the platform is respectively named "platform0" and tried to test a simple squarishpath

Code: Lua
  1. local platform0 = map:get_entity("platform0")
  2. platform0.path = {0, 0, 0, 0, 6, 6, 6, 6, 4, 4, 4, 4, 2, 2, 2, 2}
  3. platform0.speed = 60
  4. platform0.time_stopped = 1000

Running the game, no errors whatsoever, but as soon as the map is loaded, the game freeze, the only way to quit the game is by force closing it.

What's wrong ?

Diarandor

  • Hero Member
  • *****
  • Posts: 787
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #1 on: January 20, 2017, 10:55:03 pm »
Hi! I still don't know where is the problem (I haven't tested the script directly), but I will try to help a bit.

When the engine freezes, a possible cause is an infinite loop in the code, but I am not sure yet if that is the case here.

Since there is an obvious syntax error in line 52 ("tbl" variable is not defined, and should be "#self.path"), that means that the problem happens before the event "on_obstacle_reached(movement)" is called, which is probably not important. I suspected that there is a loop that starts with your collision test, which is probably being called many times, but if the event "on_obstacle_reached(movement)" is actually not being called, then the bug has to be a different thing happening before, probably.

Another possible problem is that you are initializing the path list after the entity is created, so maybe there is a problem in line 36: "m:set_path(self.path)". Maybe the path is nil when it is used, I don't know. (And the same for the speed.) If that is the case then this could be an engine bug that should display an error and it does not.

Try to use the "print" function, for debugging, to detect the possible line or function where the problem happens, or at least to get more info. Is the collision test being called?
« Last Edit: January 20, 2017, 10:57:18 pm by Diarandor »

MetalZelda

  • Hero Member
  • *****
  • Posts: 508
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #2 on: January 20, 2017, 11:59:32 pm »
I will try something different that could work: create the platform dynamically through something like

map:create_platform(x, y, size_x, size_y, speed, sprite, direction, pause_delay, {path})

it is possible that when the map script register entity.path the entity script might have already been created, and thus, the movement might be started a frame before the entity.path got registered ?

Another thing, I did tried a circular motion styled platform, it works great, but link's position is not synced with the platform, too bad, it looked awesome and it seems that circle_movement:get_angle() doesn't exist so we can't get the current angle and make this type of platform impossible...
the solution might be place an invisible entity at the center for the circle movement and constantly getting the angle (http://www.solarus-games.org/doc/latest/lua_api_entity.html#lua_api_entity_get_angle), what do you think ? Mad scientist experiment or possible ?
« Last Edit: January 21, 2017, 12:05:11 am by MetalZelda »

Diarandor

  • Hero Member
  • *****
  • Posts: 787
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #3 on: January 21, 2017, 12:03:36 am »
...
it is possible that when the map script register entity.path the entity script might have already been created, and thus, the movement might be started a frame before the entity.path got registered ?

Yes, that is what I suspect, but I am not sure. I would use a function to initialize the parameters. That would be cleaner and will notify your platform script when it needs to create/initialize the movements.

EDIT: by the way, my script that you used was very old. You should get the "ground position" of the hero instead of the normal position, to move him. And the same for enemies. For other entities that is not so important since they do not fall in bad grounds by default.
« Last Edit: January 21, 2017, 12:09:08 am by Diarandor »

Diarandor

  • Hero Member
  • *****
  • Posts: 787
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #4 on: January 21, 2017, 12:12:38 am »
For a circular movement I would not use angles, that is a bad approach in the pixel case. I would store the positions of the platform for each position change; in that way you can know in which direction you have to shift the position of the entities above the platform.

EDIT: note that with the trigonometric/polynomial functions (and others) you can make almost any possible custom movement you can imagine, so there should not be many limitations for movements, unless you don't know how to parametrize the trajectories. So if you have some problem due to limitations of the built-in movements, you can always use custom ones.
« Last Edit: January 21, 2017, 12:16:57 am by Diarandor »

MetalZelda

  • Hero Member
  • *****
  • Posts: 508
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #5 on: January 21, 2017, 12:17:36 am »
For a circular movement I would not use angles, that is a bad approach in the pixel case. I would store the positions of the platform for each position change; in that way you can know in which direction you have to shift the position of the entities above the platfom.

I was thinking of using sin and cos at each position check, kinda like how I did the Majora's Mask Hud clock, but well, I'm stuck at this point, there might be some tutorial on the net about 2d circular motion

...
it is possible that when the map script register entity.path the entity script might have already been created, and thus, the movement might be started a frame before the entity.path got registered ?

Yes, that is what I suspect, but I am not sure. I would use a function to initialize the parameters. That would be cleaner and will notify your platform script when it needs to create/initialize the movements.

EDIT: by the way, my script that you used was very old. You should get the "ground position" of the hero instead of the normal position, to move him. And the same for enemies. For other entities that is not so important since they do not fall in bad grounds by default.

Getting the ground position ? You mean this ? http://www.solarus-games.org/doc/latest/lua_api_hero.html#lua_api_hero_get_solid_ground_position

Diarandor

  • Hero Member
  • *****
  • Posts: 787
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #6 on: January 21, 2017, 12:28:07 am »
Exactly, I was refering to "hero:get_solid_ground_position()", that is the one the engine uses to make the hero fall in bad grounds. Since the usual position is different, it could happen (if you use the normal position in your script) that the hero is moved by the platform and suddenly he falls into some bad ground. (The hero will need to be in the down border of the platform to make this "bug" happen.)

If you need help just ask. I made this video some time ago with ellipses and rotations:
https://www.youtube.com/watch?v=7dw59QQrzL8&list=PLysNP7i5PKBCgT898-xreWyPcaByEB6Rg&index=17

If you can do what you want to do with the built-in circular movement, then you should avoid creating your custom one (that is a lot of work). So make sure that you really need a custom one.

MetalZelda

  • Hero Member
  • *****
  • Posts: 508
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #7 on: January 21, 2017, 12:42:24 am »
Exactly, I was refering to "hero:get_solid_ground_position()", that is the one the engine uses to make the hero fall in bad grounds. Since the usual position is different, it could happen (if you use the normal position in your script) that the hero is moved by the platform and suddenly he falls into some bad ground. (The hero will need to be in the down border of the platform to make this "bug" happen.)

If you need help just ask. I made this video some time ago with ellipses and rotations:
https://www.youtube.com/watch?v=7dw59QQrzL8&list=PLysNP7i5PKBCgT898-xreWyPcaByEB6Rg&index=17

If you can do what you want to do with the built-in circular movement, then you should avoid creating your custom one (that is a lot of work). So make sure that you really need a custom one.

Ooooh I like this, this is awesome, yet, all I want is a simple circle platform, is it possible with pixel movement type ? (with ease)

Diarandor

  • Hero Member
  • *****
  • Posts: 787
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #8 on: January 21, 2017, 12:47:07 am »
Yes, it should be easy (but not short to do) with the built-in circle movement, but I haven't tested it for platforms. Recall to use the event "on_position_changed" to save each new position of the platform, so that you can compare the new and old position of the platform, which will allow you to know in which direction you have to move the movable entities above the platform.

MetalZelda

  • Hero Member
  • *****
  • Posts: 508
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #9 on: January 21, 2017, 01:00:22 am »
Ok now the path issue is fixed, I tried to fall, even if the hero ground is constantly saved through on_position_changed, the hero keeps respawning in the hole

Diarandor

  • Hero Member
  • *****
  • Posts: 787
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #10 on: January 21, 2017, 01:11:26 am »
That was the hardest problem I found when I coded my rafts, but I solved it somehow. It is annoying. You need to use a callback parameter for the function "hero:save_solid_ground(callback)", so that you can make the hero respawn above the raft. (Thanks to Christopho we can use a callback as parameter in Solarus 1.5!!!) You can do that if some collision test with the platform is called, and then start a timer to check if the hero is still over the platform. When the hero leaves the platform and is over solid ground, then stop the timer loop and remove the callback for saving solid ground position. If you allow the hero to jump from one platform to another, this is slightly more delicate, but possible.

MetalZelda

  • Hero Member
  • *****
  • Posts: 508
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #11 on: January 21, 2017, 01:54:44 am »
This seems complicated, the best workaround is to save the hero's ground before he gets in the platform, this looks more efficient.

Also spin attacking on the platform makes somethig weird, the hero is no longer affected by the platform movement and will fall if over holes, this might not cause this on your game because you are using a custom sword
« Last Edit: January 21, 2017, 02:15:11 am by MetalZelda »

Diarandor

  • Hero Member
  • *****
  • Posts: 787
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #12 on: January 21, 2017, 11:07:53 am »
Your workaround is easier (but different too), so yes, better to use it instead. But be careful when restoring ground position if you use several platforms, one after another. Note that your workaround is not good for other aims, like making a raft that changes map with the hero and is not controlled by him.

Yes, the custom sword gives no problem. But maybe the built-in one does not allow to move the hero, which would be bad.

MetalZelda

  • Hero Member
  • *****
  • Posts: 508
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #13 on: January 21, 2017, 02:39:17 pm »
Is it possible to set a sprite direction higher than 3 ? movement:get_direction4() only returns the direction from 0 to 3, while path movement can go up to 7 and movement:get_angle() or movement:get_direction() doesn't exist

Which is problematic to do diagonal translation

Edit: I found a workaround and it kinda work

Code: [Select]
local time_changed = -1
function entity:on_movement_changed(movement)
  time_changed = (time_changed + 1) % #self.path

  --Change direction of the sprite when the movement changes.
  local direction = self.path[time_changed]
  self:get_sprite():set_direction(direction)
end

For the circle movement, why not recreating such function in pure lua since get_initial_angle exist and constatly calculate the sin and cos to position the hero on the platform

https://github.com/solarus-games/solarus/blob/dev/src/movements/CircleMovement.cpp#L354
« Last Edit: January 21, 2017, 04:07:19 pm by MetalZelda »

Diarandor

  • Hero Member
  • *****
  • Posts: 787
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Freeze when using movement:set_path()
« Reply #14 on: January 21, 2017, 04:13:04 pm »
Actually there is a function movement:get_angle() for some particular types of movements, like "target" and "circle" movements.

In any case, I still don't understand why you need to use the angle for the platform script. I guess you are programming it in a different way. But in my opinion it would be much better to use the event "on_position_changed" of the platform, as I did in my script. For instance, if the position of the platform before the movement position changed was x0, y0, and after the position changed it becomes x1, y1, then you can use the shifts "dx = x1 - x0" and "dy = y1 - y0" to shift the position of all entities above the platform. This is much more precise than getting an angle, which is approximate and introduce errors.
« Last Edit: January 21, 2017, 04:29:07 pm by Diarandor »