Deceleration error (SOLVED)

I’ve run into an error where the on_input function is reading that I released either of the right/left keys when I’m still holding one. This makes it so that the player decelerates before they even started moving.

 function on_input(self, action_id, action)
    	if action_id == hash("left") and action.repeated then
    		move = 0
    		accelerate(self, -maxSpeed)
    		print("left")
    	elseif action_id == hash("right") and action.repeated then
    		move = 0
    		accelerate(self, maxSpeed)
    		print("right")
    	elseif action_id == hash("left") or hash("right") and action.released then
    		move = 0
    		accelerate(self, 0)
    		print("decelerating")
    	end
    end

What’s up? If I’m holding the left/right key, then the input should read that I’m still holding it, not that I released it…

You need to watch out for operator precedence.

A or B and C is equivalent to A or (B and C) because and has higher precedence than or.

The fix:

elseif action_id == (hash("left") or hash("right")) and action.released then

(The order for all operators is described here: https://www.lua.org/pil/3.5.html)

EDIT: my “fix” actually introduces a very subtle bug. See @britzl’s answer below and a proper way to solve this input problem further down.

1 Like

Ah, so basically like the order of operations in Algebra? I see. Thank you very much sir!

No no, are you sure that will work? (You are correct in saying that and has higher precedence than or). The and operator definition states that and will give its first argument if it is false, otherwise the second (Programming in Lua : 3.3).

1st argument: action_id == (hash("left") or hash("right"))
and
2nd argument: action.released

So, let’s examine the first argument and what that evaluates to:

action_id == (hash("left") or hash("right"))

We need to look at that or first. The or in Lua is defined to return its first argument if it is true, otherwise the second.

1st argument: hash("left")
or
2nd argument: hash("right")

Both of these are true. Lua will return hash("left") which leaves us with:

action_id == hash("left") and action.released

Which isn’t what was intended. This would be the correct conditional:

elseif (action_id == (hash("left") or action_id == hash("right")) and action.released then
3 Likes

Actually, this is not right either. :slight_smile:

The on_input() function is called twice, once when action_id == hash(“left”) and one where it’s hash(“right”). You could accumulate the input and simply move the reaction code to update():

function update(self, dt)
    accelerate(self, maxSpeed * self.move)
    self.move = 0
end

function on_input(self, action_id, action)
    if action_id == hash("left") then
        self.move = -1
    elseif action_id == hash("right") then
    	self.move = 1
    end
 end

This is a bit unpredictable if the player presses left and right simultaneously since on_input will be called once for each input, but in what order? That’s easily fixed though. Say that you want left to take precedence:

function on_input(self, action_id, action)
    if action_id == hash("left") then
        self.move = -1
    elseif action_id == hash("right") and self.move == 0 then
    	self.move = 1
    end
end

What I meant was that hash(“left”) and hash(“right”) are values that are considered true in Lua (nil and false are considered false). I’m fully aware that you’ll get multiple calls to on_input() once per input “event”. :slight_smile:

:slight_smile: Yes, you are correct.

But for what @Gmanicus wants to achieve, neither conditional will get the expected result.

True! I got caught up in the details of the language. Storing key states (pressed/released) in a table and updating in update() is better in this case.

1 Like

@britzl @sicher

Haha, well I’m glad I got you both thinking. I’ve already got a plan in mind from what you concluded. Thanks for the tips!

3 Likes