Help with toggling a setting with a button press

Ok so I am trying to figure out how to toggle a variables value with a button press. I will post my code below so you can have a look at it and see if you can help. Basically I am trying to press the space bar to change speed of my object. I know that my code is very poor when it comes to efficiency, however I am just messing around and trying to figure things out at this time. Basically what I want to have happen is when the space bar is pressed it changes the speed value from 150 to 300 or from 300 to 150 based on its current setting. The problem that I am having is that it keeps changing back and forth really fast so sometimes you will get a speed change and other times it doesn’t change the speed at all because it is switching back and forth to fast. Need a way to disable the input from registering until the button is released and then repressed. I can do this in c# using monogame as it makes more sense to me but defold is just weird for me because things seem like they need to be all over the place in the script. Anyway here is my code currently in case it will help out.

` local up = hash(“up”)
local down = hash(“down”)
local right = hash(“right”)
local left = hash(“left”)
local speedChange = hash(“speedChange”)
local player = go

–local speed
local minSpeed = 150
local maxSpeed = 300
local speed = minSpeed
local deltaT

local width = tonumber(sys.get_config(“display.width”))
local height = tonumber(sys.get_config(“display.height”))

function init(self)
– Add initialization code here
– Remove this function if not needed
msg.post(".", “acquire_input_focus”)
end

function final(self)
– Add finalization code here
– Remove this function if not needed
end

function update(self, dt)
– Add update code here
– Remove this function if not needed
self.speed = speed * dt

--bounds
local pos = player.get_position()
if pos.x < 30 then 
	pos.x = 30 
end

if pos.x > width - 30  then
	pos.x = width - 30
end

if pos.y < 30 then
	pos.y = 30
end

if pos.y > height - 30 then
	pos.y = height - 30
end

player.set_position(pos)

deltaT = dt

end

function on_message(self, message_id, message, sender)
– Add message-handling code here
– Remove this function if not neededd
if message_id == hash(“changeSpeedMax”) then
speed = maxSpeed
end

if message_id == hash("changeSpeedMin") then
	speed = minSpeed
end

end

function on_input(self, action_id, action)
– Add input-handling code here
– Remove this function if not needed

local pos = player.get_position()

if action_id == up then
	pos.y = pos.y + self.speed
end

if action_id == down then
	pos.y = pos.y - self.speed
end

if action_id == left then
	pos.x = pos.x - self.speed
end

if action_id == right then
	pos.x = pos.x + self.speed
end

if action_id == speedChange then
	if self.speed == 150 * deltaT then
		msg.post("#", "changeSpeedMax")
	else
		msg.post("#", "changeSpeedMin")
	end
end

player.set_position(pos)

end

function on_reload(self)
– Add reload-handling code here
– Remove this function if not needed
end`

You can read action.pressed and action.released. These values will be set once per key press.

2 Likes

Here are some more comments on the problem and your code. I’m trying to be overly clear, don’t read it as being condescending. :slight_smile:

  • For a toggle, you need to store state (on/off, i.e. a boolean or bit), the self variable is a great place for this, possibly self.toggle_speed.
  • Let the state switch on action.pressed/action.released like @britzl suggested. Which you choose is a matter of taste or UX, some things make more sense on key-up, others on key-down.
  • You can opt to mangle the toggle state with the control variable you are affecting (i.e. self.speed), similar to what you did. This is really common for simple cases. The drawback is that you introduce knowledge - of precisely which values that variable can take on - into the input handling function. If you somewhere let that variable take on another value, the toggling will stop working (the control variable is then neither “on” nor “off”).
  • Don’t store deltaT outside the scope of the update function. That value only makes sense within the update scope.
  • Don’t multiply deltaT into the speed, that will turn the speed into a distance covered within the frame and then “speed” becomes a confusing name. Introduce a new local variable which is the speed and dt multiplied. It also caused you to multiply the deltaT into the speed in the on_input function and this is in fact quite error-prone and confusing.
  • Message passing is really only required between different scripts. You don’t need to go through message passing in this case, unless you want to do the same things outside of this script. If you do that, I would suggest you change the message to also pass the value, i.e. msg.post("#", “changeSpeed”, {speed = 20000})

An example:

function update(self, dt)
    local dist = self.speed * dt
    ...
end

function on_input(self, action_id, action)
    ...
    if action_id == speedChange and action.pressed then -- or action.released
        if self.toggle_speed then
            self.speed = maxSpeed
        else
            self.speed = minSpeed
        end
        self.toggle_speed = not self.toggle_speed
    end
end
4 Likes

@Ragnar_Svensson, I really appreciate the notes on things. Like I mentioned the code is not optimized or anything at this point as it is just a testing ground for different things as I am trying to test and learn different things. I am very amazed like normal with the support from the dev team when I have questions about things and this case is no different. Thanks for all of the feedback I really appreciate the time you spent on your answers and help.