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]