Log In  

I have an if statement with two lines in the code block. One changes the value of the sprite property on my player object. The second changes the movement speed property for the player object. The former always works when the condition is triggered. The latter works unreliably. This is pretty strange to me.

Essentially, I am detecting terrain via grass sprites on the map. When I collide, I want the player to slow down. This only works when I hit what I assume to be the first grass sprite in the array. I added the sprite swap line for debug purposes. The sprite will always change when I collide, but the movement speed change only works on one grass sprite out of all of them.

I don't get how my condition can be met, and one line always executes while the other may or may not. I've also printed the player speed to verify this. It doesn't seem performance related, even though it is inside of a pretty hefty series of loops.

Any ideas? Is this a weird bug?

Edit: this has been resolved, thanks samhocevar!

P#58954 2018-11-12 07:15 ( Edited 2018-11-13 23:24)

Could you show the code?

P#58958 2018-11-12 09:17 ( Edited 2018-11-12 14:17)

This function is called within _update() after player and enemy movement occurs.

So there's a player object, two enemy objects in a table, and x amount of obstacle objects in a table. I want to look at each obstacle in the table and see if a player or enemy collides. It works on all obstacles if I remove the else statements but then the speed is locked. With the else statements, the speed only changes when 1 specific obstacle is hit out of all of them, no matter the amount of obstacles. I have created everything from 1 to 40 obstacles and get no performance dips.

function obstacle_collision()
  for obstacle in all(obstacles) do
    if player.x > obstacle.x - 7 and player.x < obstacle.x + 7
    and player.y > obstacle.y - 7 and player.y < obstacle.y + 7 then
      player.speed = 0.2
      player.sprite = 5
    else
      player.speed = 3
    end
    for enemy in all(enemies) do
      if enemy.x > obstacle.x - 7 and enemy.x < obstacle.x + 7
      and enemy.y > obstacle.y - 7 and enemy.y < obstacle.y + 7 then
        enemy.speed = 0.2
      else
        enemy.speed = 0.5
      end
    end
  end
end

EDIT: So this is interesting: if I don't initialize and let player.speed be set by this function, triggered by _update, occasionally I get a runtime error where player.speed is nil and the movement function is trying to run (which runs after this function, so player.speed should be set). This indicates to me that for some reason, this object property is not always updated reliably, while player.sprite is always updated. That makes no sense because they are completely arbitrary object properties and should exhibit the same behavior.

Additionally, if I get rid of the else statements that change the speed back when there is no collision, and then assign a value to player.speed at the start of the _update loop, I can see that my player.speed is changing correctly through a print statement in _draw, yet the movement function seems to still be using the default value during collision. So I can see player.speed at 0.2 as it is printed, yet the movement function seems to be using the initialized value of 3.

All of this seems quite bizarre. I also threw my game over condition into the collision code and I can trigger a game over whenever a collision is made, not just on one specific obstacle like the speed property change.

I am moving my player with this pretty standard function. You can ignore the player.moving stuff as that is for a sprite animation function.

function player_move()
  player.moving = false
  if btn(0) and player.x > 0 then
    player.x -= player.speed
    player_animate()
  end
  if btn(1) and player.x < 120 then
    player.x += player.speed
    player_animate()
  end
  if btn(2) and player. y > 0 then
    player.y -= player.speed
    player_animate()
  end
  if btn(3) and player.y < 120 then
    player.y += player.speed
    player_animate()
  end
  if not player.moving then
    player.sprite = 0
  end
end

EDIT2: I just added a boolean called "hit" that is true on collision and false on the else statement. It has the same behavior as the player.speed property. It only works on one obstacle (what I am assuming is the first one in the table) while the sprite change works on every obstacle. It's as if the condition is working properly, but only certain lines of code are triggering when all of them should.

EDIT3: Okay, so when I get rid of the else statements and just move the object property assignments to the top of the loop (essentially the same logic) then everything works:

function obstacle_collision()
  --reset values
  player.speed = 2
  for enemy in all(enemies) do
    enemy.speed = enemy.default_speed
  end
  --detection
  for obstacle in all(obstacles) do
    --player
    if player.x >= obstacle.x - 7 and player.x <= obstacle.x + 7
    and player.y >= obstacle.y - 7 and player.y <= obstacle.y + 7 then
      player.speed = 0.25
    end
    --enemies
    for enemy in all(enemies) do
      if enemy.x > obstacle.x - 7 and enemy.x < obstacle.x + 7
      and enemy.y > obstacle.y - 7 and enemy.y < obstacle.y + 7 then
        enemy.speed = 0.25
      end
    end
  end
end

So it works, which is great, but I still would like to know why my else statements behaved so erratically if anyone has any ideas.

P#58976 2018-11-13 00:47 ( Edited 2018-11-13 08:53)

All your if statements behaved correctly. The problem is that only the last statement was effective, since you would always update player.speed for each obstacle.

Your fix is correct. Another way to correct your code could be this:

function obstacle_collision()
  local function collide(entity)
    for obstacle in all(obstacles) do
      if abs(entity.x - obstacle.x) < 7 and abs(entity.y - obstacle.y) < 7 then
        return true
      end
    end
    return false
  end

  if collide(player) then
    player.speed = 0.2
    player.sprite = 5
  else
    player.speed = 3
  end

  for enemy in all(enemies) do
    if collide(enemy) then
      enemy.speed = 0.2
    else
      enemy.speed = 0.5
    end
  end
end
P#58987 2018-11-13 11:07 ( Edited 2018-11-13 16:07)

I don't know why I couldn't wrap my head around that. It all makes sense now; the else would be triggering for every other obstacle so it will always overwrite the one case that hits the if. The sprite change issue muddied the waters for me, I think. Rather than toggling that back in the else as well I was letting my animation function fix it. If I had added a sprite property change to the else I would have gotten the same behavior as my speed property. Also, the speed change only working on one specific obstacle was confusing me; I'm assuming that had to do with being the first obstacle in the table.

I did want to clean up the code a bit after I figured it out so thanks for the example!

If you have a chance, could you describe how you reworked the collision condition vs my long and sloppy method?

Thanks again for your help.

P#58989 2018-11-13 17:43 ( Edited 2018-11-13 22:45)

The following condition:

if player.x > obstacle.x - 7 and player.x < obstacle.x + 7

is equivalent to this:

if player.x - obstacle.x > -7 and player.x - obstacle.x < 7

this is of the form "if A > -B and A < B", which is equivalent to the absolute value inequality (you can Google for that term) "if abs(A) < B":

if abs(player.x - obstacle.x) < 7
P#58990 2018-11-13 18:24 ( Edited 2018-11-13 23:24)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-29 00:38:41 | 0.007s | Q:17