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!
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.
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
|
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.
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 |
[Please log in to post a comment]



