Best Practices for Capturing Input While Using Fixed Timestep

So I’m pretty new to defold, and am trying to learn basic best practices for capturing player input.

In one of the very basic tutorials, player input is handled in the “on_input” function, by basically setting a variable to say that a key has been pressed (in this case, if the up arrow has been pressed, we set the variable “self.input.y” to 1.) Then, the movement is actually implemented in the “update” function, by checking to see if self.input.y is equal to 1, and if so, we move the player character up, and finally, we set self.input.y to 0 at the end of the function, so that if the up arrow is not pressed during the next game loop, we will not continue moving up.

This approach works well, and seem logical to me, as executing movement inside the “update” function instead of during the “on_input” function allows us to do things like check if multiple keys are pressed at the same time, and change behavior accordingly, etc.

However, I have run into a problem while trying to implement this same approach while using a fixed time step. As far as I can tell, the “on_input” function is triggered during every game loop when using a variable timestep, but when using a fixed timestep, every 50 frames or so (on my pc, anyways) there will randomly be a loop where the “fixed_update” function runs, but the “on_input” function does not, even while holding a key down continuously.

I’m including the full test script at the end of this post; if you build a game with an object running that script, and hold down the up arrow key, it constantly prints “input” in the console, reliably doing so every frame until you release the arrow key. However, if you change the “update” function to “fixed_update” and enable “Use Fixed Timestep” in the game settings, then build the game and test out holding the up arrow continuously, it will have a (seemingly random) loop every second or so where it prints “no input”, meaning it thought the key wasn’t pressed.

So my question is: how should input be handled when using a fixed timestep? If I do like the tutorial indicates and set a variable during the “on_input” function, and then use that variable during the “fixed_update” function to determine if the key is pressed, there will be random frames where it thinks the key isn’t pressed, and motion will be jittery.

Any advice and/or guidance would be greatly appreciated. Thanks!

function init(self)                                 -- [1]
	msg.post(".", "acquire_input_focus")            -- [2]

	self.moving = false                             -- [3]
	self.input = vmath.vector3()                    -- [4]
	self.dir = vmath.vector3(0, 1, 0)               -- [5]
	self.speed = 50                                 -- [6]
end

function final(self)                                -- [7]
	msg.post(".", "release_input_focus")            -- [8]
end

function update(self, dt)                           -- [9]
	if self.moving then
		print('input')
	else
		print('no input')
	end

	self.input.x = 0                                -- [13]
	self.input.y = 0
	self.moving = false
end

function on_input(self, action_id, action)          -- [14]
	if action_id == hash("up") then
		self.input.y = 1                            -- [15]
	elseif action_id == hash("down") then
		self.input.y = -1
	elseif action_id == hash("left") then
		self.input.x = -1
	elseif action_id == hash("right") then
		self.input.x = 1
	end

	if vmath.length(self.input) > 0 then
		self.moving = true                          -- [16]
		self.dir = vmath.normalize(self.input)      -- [17]
	end
end

You need to use action.pressed and action.released
Fixed update can be called multiple times per frame so you not get new input. To keep update fixed:)

1 Like

Also if you reset input in on_message.
It should worked.

function on_message(self, message_id, message, sender)
	if(message_id == hash("post_frame")) then
		self.input.x = 0                                
		self.input.y = 0
		self.moving = false
	end
end


function fixed_update(self, dt)                           -- [9]
	if self.moving then
		print('input')
	else
		print('no input')
	end
	msg.post(".", "post_frame")
end
3 Likes

Ah, that makes sense! I hadn’t even considered the fixed update running multiple times per frame, lol.

So if I use action.released, I assume I need to watch out for if the input gets consumed (for instance, by a pause menu), or if the player loses input focus, that they’ll just keep moving forever because the release input won’t trigger? Any tips for a newbie on how to keep track of this and make sure it doesn’t happen? (Also, if the game window loses focus while a key is pressed, then the key is released before the game window comes back into focus, will action.released still get triggered?)

Or is it just better practice to use the second method you mentioned to avoid the above issues?

Thanks so much for your help! :slight_smile:

1 Like

Yes of course. For example i capture all input in one script and saved it, before input send to other scripts.
So i always know is button pressed or not.
If game lose focus you will get action.released.
And you will get action.pressed when game received focus again.

Use second method, it is more simple:)

2 Likes