Key input problem

BlitzMax Forums/BlitzMax Beginners Area/Key input problem

Arska(Posted 2011) [#1]
I created some kind of item system to my game and some problems show up.

Function UpdateItems()
	For Local ii:ITEMS = EachIn itemList
		
		If EntityDistance#(player, ii.model)<10 Then 
			EntityColor ii.model, 255,155,255
			If EntityDistance#(scube,ii.model)<2 Then
				
				EntityColor ii.model, 255,55,255
				youSee = ii.count+" "+ii.itemName+"  (Owner: "+ii.faction+")"
				
				
				If KeyHit(KEY_E)Then 
					
					If ii.itemName="Rock" Then rock=rock+1
					HideEntity ii.model
					
				EndIf
			Else
			youSee = ""
			EndIf
		Else
			EntityColor ii.model, 255,255,255
		EndIf
		
		
		TranslateEntity ii.model,0,gravity,0
		UpdateCollision(ii.model, targets, , 1)
	Next
End Function


It should pickup item when player goes close to item, moves mouse on item and press E key. But problem is. If i press E far from item and then go close to item and move mouse on it. It picks up that item.


GfK(Posted 2011) [#2]
Try adding FlushKeys() at the end of your function.


Arska(Posted 2011) [#3]
This looks like working:
If KeyHit(KEY_E) Then FlushKeys()


Better solution? Only problem with that is, player controls get flushed too.


GfK(Posted 2011) [#4]
Yeah you really only want one flushkeys() per loop so if it causes problems then a good place would be the end of your main loop, after Flip.


H&K(Posted 2011) [#5]
Im a bit confused by your code.

You are looping throu a list of all items, and in the middle of this loop you are looking for a keypress, then doing stuff etc

Well how long does this loop take?

Check for the Keypress Before the loop, store that state, and act on that stored state.

(Oh and flushkeys)


Arska(Posted 2011) [#6]
H&K thanks, that worked.


ima747(Posted 2011) [#7]
Of note: When you call KeyHit(KEY_WHATEVER) the hit state for that key is reset, meaning that realistically you can only test each key once per cycle.
e.g.

Graphics(640, 480)
while(Not KeyHit(KEY_Escape))

if(KeyHit(KEY_SPACE)) print "Space hit test 1 true!"
if(KeyHit(KEY_SPACE)) print "Space hit test 2 true!"

wend


you will never get both outputs in 1 cycle. You will get 1, or 2 (depending on if you hit space between the 2 tests or not...) but never both. If the first test is true, that resets the hit status and therefore the second can't be true.

As a result if this if you will check a hit status for a given key in more than one place (possibly in a loop for instance where it could be checked multiple times) you should cache the value before hand and then check that. Further the hit status will REMAIN true until it is checked... so if you check for say, the space bar ONLY in a menu, and someone presses the space bar before entering the menu, the first check will return true. FlushKeys() resets all the hit tests, so it should be used either after a "cycle" of testing, to keep everything in sync (such as keys you may not have tested but want to have them cleared in preparation for future testing like the menu example above) or at the start of a context change, such as when entering a menu, flush the keys to prevent any previously pressed keys from interfering with the menu's input.

Also of note KeyDown() is not affected in the same way, as it simply reports the status of the current key, and as such doesn't need to track it's history like KeyHit() does.

Last edited 2011


Kryzon(Posted 2011) [#8]
I don't know what's the ongoing programming formality for this kind of thing, but to avoid the problem ima747 pointed above you could do the following:
• Have a single function right at the top of your game-loop that's used to check every keypress and store these states into variables (like global variables or a global array). Test first KeyDown() then KeyHit() so the keydown allows keyhit to be tested as well (while the opposite isn't true).
• On subsequent game-engine functions, check the state of the variables instead of the input functions again (i.e: don't use KeyDown nor Keyhit, but rather the value in the variables).

So the input testing part of your function would be:
If kHit_SPACE Then 'Use the state of the variable for this game cycle.
	
	If ii.itemName="Rock" Then rock=rock+1
	HideEntity ii.model
		
EndIf

And the order of game engine functions in your game loop:
While [condition]
	checkKeyStates() 'Store the state of the keys for this cycle.

	updateItems() 'Update the game itself based on the cached state of the keys ima747 mentioned.
	[...]
Wend


Last edited 2011


ima747(Posted 2011) [#9]
There's really no standard. I've used the methods like kryzon laid out before, as well as interleaved checks and others. Checking early allows you to flushkeys() if needed sooner which gives more time for the user to hit a key for the next frame, which is good for responsiveness, but if your calls are interleaved with things that execute fast, or everything is tested every frame then it's generally not a problem... really depends on your program flow, which is why it's not in a more formalized layout beyond a per key hit state/reset combo function, which lets you code your own layout to whatever is appropriate.

I generally use KeyDown() myself since more often than not that's a sufficient, or better trigger for things (like say movement, where holding the key should keep the character moving). Sometimes I even write my own hit manager instead of using keyhit() due to other conditions...

Another side note: They joystick button checks work the same way as keys, there's a hit and a down function, the hit resets state, etc.

Last edited 2011