Input priority? (or just the wrong logics) (SOLVED)

Hi all,

I’m currently going through the, hmm… very first tutorial :baby: (side scroller, with the spaceship, etc.) and in order to learn in a relevant manner, I want to tweak it as much as possible (by improving controls, adding inertia, replacing some assets, tweaking animations, bonuses, maybe by adding enemies etc.) and understand Defold’s editor & logics in the process.

But I’m struggling at the very beginning with something probably stupid and… I don’t know why. By “experience” I’d say I just did something wrong (it’s never a bug :sweat_smile:)… I could move on but I refuse to go to the next step before understanding what is wrong here, because it’s something it’ll eventually face again in the future and/or that may hide something more important.

What I want to do:
To prevent my ship from moving up (“up” arrow) when already moving down (and vice versa). Same for right/left.

So I added move_x / move_y (boolean) variables and use them as conditions… For some reason, when the ship is moving to the right (“right” key still pressed) and I press the “left” arrow, the ship stops moving to the right and goes to the left (while the “right” key never stopped being pressed)… But it won’t behave the same way for the other direction! (this is where I find it weird, actually :grimacing:)

Same issue for the up/down movement.

I put the (super simple and well-known but slightly modified) source code here so maybe someone can take a look:

local max_speed_x = 300
local min_x = 60
local max_x = 1800

local max_speed_y = 300
local min_y = 60
local max_y = 600

function init(self)
	-- Let the script receive input from the player
	msg.post(".", "acquire_input_focus")
	-- the current speed of the space ship
	-- JC 23/12/2020 00:05 # replace "speed" by speed_x and speed_y
	self.speed_x = 0
	self.speed_y = 0
	self.move_x = 0
	self.move_y = 0
	
	-- Animate the ship so it's swaying back and forth
	go.set(".", "euler.z", -5)
	go.animate(".", "euler.z", go.PLAYBACK_LOOP_PINGPONG, 5, go.EASING_INOUTSINE, 2)
end

function update(self, dt)
	local p = go.get_position()
	p.x = p.x + self.speed_x * dt
	p.y = p.y + self.speed_y * dt

	if p.x < min_x then
		p.x = min_x
	elseif p.x > max_x then
		p.x = max_x
	end
	if p.y < min_y then
		p.y = min_y
	elseif p.y > max_y then
		p.y = max_y
	end
	
	-- JC 23/12/2020 00:18 # Debug tests
	local color_white = vmath.vector4(1, 1, 1, 1)
	local debug_txt_x = 20
	local debug_txt_y = 600
	msg.post("@render:", "draw_debug_text", { text = "self.speed_x: " .. self.speed_x, position = vmath.vector3(debug_txt_x, debug_txt_y, 1), color = color_white })
	debug_txt_y = debug_txt_y - 15
	msg.post("@render:", "draw_debug_text", { text = "self.move_x (bool): " .. self.move_x, position = vmath.vector3(debug_txt_x, debug_txt_y, 1), color = color_white })
	debug_txt_y = debug_txt_y - 15
	msg.post("@render:", "draw_debug_text", { text = "self.speed_y: " .. self.speed_y, position = vmath.vector3(debug_txt_x, debug_txt_y, 1), color = color_white })
	debug_txt_y = debug_txt_y - 15
	msg.post("@render:", "draw_debug_text", { text = "self.move_y (bool): " .. self.move_y, position = vmath.vector3(debug_txt_x, debug_txt_y, 1), color = color_white })
	
	go.set_position(p)
	self.speed_x = 0
	self.speed_y = 0
	self.move_x = 0
	self.move_y = 0
	
end

function on_input(self, action_id, action)
	-- JC 23/12/2020 00:05 # horizontal movement
	if action_id == hash("right") and self.move_x == 0 then
		self.speed_x = max_speed_x
		self.move_x = 1
		print("RIGHT!")
	elseif action_id == hash("left") and self.move_x == 0 then
		self.speed_x = -max_speed_x
		self.move_x = 1
		print("LEFT!")	
	end
	-- JC 23/12/2020 00:05 # vertical movement	
	if action_id == hash("down") and self.move_y == 0 then
		self.speed_y = -max_speed_y
		self.move_y = 1
		print("DOWN!")
	elseif action_id == hash("up") and self.move_y == 0 then
		self.speed_y = max_speed_y
		self.move_y = 1
		print("UP!")
	end
end

I switched the input condition blocks (“action id” etc.) to see if it came from the logics itself) but it doesn’t change anything.

1/ Is there some kind of hidden “priority” here? Or just something I did wrong?

2/ Question: how would you detect if a key is NOT pressed? (maybe I could use it to set move_x/y to 0 instead of doing it at the end of the update loop)

Thanks

You will get input events from the engine basically in the order they are received from the system, with some additional information.

First of all there is action.pressed which is true on the frame when a key is pressed. Then you have action.released on the frame it is released. In between these you will get action.repeated at an interval configured in game.project.

I usually only track the pressed and released events and use those to set and clear input state, instead of clearing input state every frame like in the example you have.

I’m guessing that the repeated events you get while the keys are pressed always comes in the same order. You get the Right key event first in each frame and then the Left key (or the other way around). I guess this is what results in the behavior you are seeing?

Put a print(action_id) in on_input() and also a print(“update”) in the update() function to confirm this.

1 Like

The action.pressed/released thing was exactly what I needed to synchronize the input and direction/movement “states”. I just implemented exactly what I had in mind :slight_smile: (in terms of UX/gameplay at least)

Thank you!

2 Likes