Page 1 of 1

How to time keypresses

Posted: Thu Mar 23, 2017 1:26 am
by PunchyBreloom
I am a newbie at coding in love and am trying to make a semi-basic platformer as my first project. I wanted to code the ability to use a 'short hop', pushing the jump button down for a shorter time in order to do a shorter jump. Is this possible, and if so could you explain to me how to do it? I have defined both the short and long jump's velocities already. This in an excerpt from my current main.lua file and I am using love 0.8.0 currently, though plan to upgrade if necessary.

Current code:
if love.keyboard.isDown("up") then
player:jump()
end

What I have unsuccesfully tried:
if love.keyboard.isDown("up") > love.timer.getMicroTime(0.5) then
player:jump()
end

Thank you in advance for your replies!

Re: How to time keypresses

Posted: Thu Mar 23, 2017 4:44 am
by zorg
Hi and welcome to the forums!

First, love.timer.getMicroTime doesn't accept parameters, so that's wrong.

Second, it's strongly recommended to use the latest löve version, unless your computer's specs can't run it (mostly due to the gpu being old).

In any case, there are a few ways to accomplish what you want; here's how i'd do it (I tried to use functions as they worked in 0.8):

Code: Select all

-- This will hold the states for each key.
-- state can be 0,1,2,3 with meanings: depressed, pressed, held and released
-- held is the time the button was held down
local keys = {}
keys.up    = {state = 0, held = 0}
keys.down  = {state = 0, held = 0}
keys.left  = {state = 0, held = 0}
keys.right = {state = 0, held = 0}

function love.load()
	-- Turn off key repeat; in 0.8 it was Löve's own doing, but after, it delegated it to the OS;
	-- i always code my own, so i disable this anyways.
	love.keyboard.setKeyRepeat(0)
end

-- This gets called by Löve whenever a key is pressed, but only at that moment.
-- (unless key repeat is enabled, i usually disable that, see above.)
love.keypressed = function(key)
	if keys[key] then
		keys[key].state = 1 -- Pressed.
		keys[key].held  = 0 -- Clear the time it was held for previously.
	end
end

-- This gets called by Löve whenever a key is released, but only at that moment.
-- (same disclaimer as above.)
love.keyreleased = function(key)
	if keys[key] then
		keys[key].state = 3 -- Released.
	end
end

-- Do most of the stuff in love.update
function love.update(dt)

	-- Iterate over our keys table and update state.
	for key, tbl in pairs(keys) do
		if love.keyboard.isDown(key) then
			if tbl.state == 1 then
				-- It was just pressed.
				doStuffPressed() -- Whatever function you need to do.
				-- Modify state to held and update held time.
				tbl.state = 2
				tbl.held = tbl.held + dt
			elseif tbl.state == 2 then
				-- It was already held for more than one update loop.
				doStuffHeld(tbl.held) -- Do whatever you need to do, the passed parameter holds how long it was pressed down for.
				-- Update held time.
				tbl.held = tbl.held + dt
			end
		else
			-- If state is released, then set state to depressed.
			if tbl.state == 3 then
				-- Just released.
				doStuffReleased(tbl.held) -- If you want to, you can detect a release here.
				tbl.state = 0
			end
		end
	end
end
This may be overcomplicating things a bit, with all keys having 4 "virtual" states, but it does keep track of how long the keys were held down, and you can use that information for whatever you want; also, detecting release may also be important since that should be what stops the ascent of your player.

Edit: Fixed a mistake in my code...

Re: How to time keypresses

Posted: Thu Mar 23, 2017 11:07 am
by PunchyBreloom
Ok, I'll try this! Thank you!

Re: How to time keypresses

Posted: Thu Mar 23, 2017 11:23 am
by PunchyBreloom
I've had an issue with my code, this line returns an error- "attempt to index local 'tbl' (a number value)" I updated to the newest version of Love.
if tbl.state == 3 then


Also, did I do this right? juump is just what i called my shorter jump.


local keys = {state = 0, held = 0}
keys.up = {state = 0, held = 0}
keys.down = {state = 0, held = 0}
keys.left = {state = 0, held = 0}
keys.right = {state = 0, held = 0}

function love.load()
love.keyboard.setKeyRepeat(0)
end

love.keypressed = function(key)
if keys[key] then
keys[key].state = 1
keys[key].held = 0
end
end


love.keyreleased = function(key)
if keys[key] then
keys[key].state = 3
end
end


function love.update(dt)


for key, tbl in pairs(keys) do
if love.keyboard.isDown(key) then
if tbl.state == 1 then
player:jump()
tbl.state = 2
tbl.held = tbl.held + dt
elseif tbl.state == 2 then
doStuffHeld(tbl.held) player:juump()
tbl.held = tbl.held + dt
end
else

if tbl.state == 3 then
doStuffReleased(tbl.held)
tbl.state = 0
end
end
end
end

Re: How to time keypresses

Posted: Thu Mar 23, 2017 3:02 pm
by zorg
PunchyBreloom wrote: Thu Mar 23, 2017 11:23 am I've had an issue with my code, this line returns an error- "attempt to index local 'tbl' (a number value)" I updated to the newest version of Love.
You're probably getting that error because i had an error in my code; that's what i get for not testing it beforehand. :P

I went through it again, didn't see any other issues, so yes, hopefully it'll work. (Unless you'll have issues with setKeyRepeat not being happy with a 0 being passed to it; if you did update your Löve... switching out 0 for false would do, i think.)

Also, please do use [ code ] [ /code ] tags, it makes source code easier to read.

Re: How to time keypresses

Posted: Thu Mar 23, 2017 5:48 pm
by airstruck
That seems unnecessarily... hairy.

What I've found to work pretty well is setting the y-velocity to the "max jump velocity" when the key is pressed, and then if it's released while the player is still moving upward, cut the y-velocity in half (or so).

I've attached a small demo (use WASD).

Re: How to time keypresses

Posted: Thu Mar 23, 2017 8:52 pm
by MrFariator
If you want to reproduce jumping controls from something like Mega Man, you can just set the vertical speed to 0 if player releases the jump button, and gravity hasn't caught up yet (ie. vertical speed is still such that player is ascending).

For funsies, you can pull the exact movement values for Mega Man titles from here and here. Alternatively for something like Sonic, here is a page for that. Should give you plenty of ideas on how plenty of classic platformers handle their jump arcs.

Re: How to time keypresses

Posted: Thu Mar 23, 2017 9:21 pm
by airstruck
Worth noting, none of those solutions need to track the amount of time the button was pressed.

That sort of thing seems like the way to go, although setting the velocity to an absolute value (especially 0) feels too abrupt for my liking; I generally prefer something like cutting it in half instead, but of course it depends on the game. Setting it to 0 makes more sense if there are spikes right above your head.

Re: How to time keypresses

Posted: Thu Mar 23, 2017 10:48 pm
by PunchyBreloom
Well thanks guys, I'll give these suggestions a try!

EDIT: I used airtruck's method and it worked fine, thanks!

Re: How to time keypresses

Posted: Fri Mar 24, 2017 12:21 am
by airstruck
Glad it worked for you. Just want to note a couple of things about that code.

Because the velocity is cut in half whenever the key is released while the character is ascending, it's technically possible to cut the jump short(er) if you're able to hit the key multiple times while the player is still ascending. I couldn't hit the key fast enough to notice a difference, but if it's a concern you can introduce some kind of "isJumping" flag (maybe your character has really strong legs or lives on the moon). Of course this also wouldn't be an issue if you set velocity to a fixed amount on key release, as in the examples MrFariator gave.

Also, the method used for acceleration/velocity/position integration is heavier than usual because it's designed to be accurate under a variable-length timestep. If you use a fixed-length timestep, you can get away with a cheaper approximation, like vel=vel+acc*dt; pos=pos+vel*dt. Of course you can still use the integration method in the demo with a fixed-length timestep if you just want that "extra" accuracy.