FlushKeys?

Monkey Forums/Monkey Programming/FlushKeys?

marksibly(Posted 2011) [#1]
Hi,

Just noticed some hacks to implement FlushKeys - is this really necessary?

FlushKeys is already executed at the end of OnUpdate automatically.

The only other reason I can think of for needing FlushKeys is if you are testing the same key multiple times.

In bmx, only the first call to KeyHit would return 'true' - in monkey, all KeyHits with the same key will return 'true'. This seemed more useful at the time, since keys are flushed after OnUpdate anyway.

Would it be best to revert to bmx style behaviour - ie: keys are flushed individually each KeyHit AND all keys are flushed at the end of OnUpdate?


Wagenheimer(Posted 2011) [#2]
At least in the XNA Build it seems that the flushkeys is really necessary! In my tests, it seen to be not being flushed after OnUpdate. But I need to test it further!


Leo Santos(Posted 2011) [#3]
I ran into a couple of situations where I needed to clear the input right in the middle of a frame update, like when a key shortcut 'activates' an object which will in turn wait for the same key stroke to toggle it back off. I don't have a single keyboard input manager, each object that can take shortcuts does its own checking, so the object ends up enabled and disabled within the same frame.

Without Flush keys I have to do all kinds of juggling to get it to work, like delaying said object's update for a frame... it would be just simpler and easier to clear the stack on demand!

So I vote or adding a straightforward FlushKeys function. Flushing a key individually every time it is hit could be ugly if you want two objects performing an action triggered by the same key, in the same update loop.

Thanks!
Leo.


therevills(Posted 2011) [#4]
I needed Flushkeys for Monkey for fixed rate logic.

	Method OnUpdate:Int()
		local now% = Millisecs()
		
		If now < lastTime
			numTicks = lastNumTicks
		else
			tmpMs = now - lastTime
			if tmpMs > maxMs tmpMs = maxMs
			numTicks = tmpMs / ms
		Endif
	
		lastTime = now
		lastNumTicks = numTicks
	
		For local i% = 1 to Floor(numTicks)
			screen.update()
		Next
		
		return 0
	End Method


In the loop where the screen is updated if you hit a key, it would repeat that action a few times due to the key hasn't been flushed.

So now with my "hack" I do this:

		For local i% = 1 to Floor(numTicks)
			screen.update()
			FlushKeys()
		Next



marksibly(Posted 2011) [#5]
Hi,

Ok, so both these would be solved if KeyHit 'flushed' the key that was hit?


Leo Santos(Posted 2011) [#6]
That would work for me.
(But I would still prefer an actual 'manual' FlushKeys function! ;-) )

Thanks!
Leo.


therevills(Posted 2011) [#7]
That would work for me.
(But I would still prefer an actual 'manual' FlushKeys function! ;-) )


Same here :)


DGuy(Posted 2011) [#8]
(I assume we're taking about "KeyDown()" which returns true/false and not "KeyHit" which returns a number ...)

An emphatic "NO!" to BMX style input!

I like the way Monkey does things ATM: A "snapshot" is taken of the input state at the start of an Update, and that snapshot stays constant for the entire update.

If I have several elements on screen with which the user can interact, and each element "managed" by it's own class/module/whatever, I don't want the first element-manager that checks for input to "consume" that input.

For example, say there's button-A and button-B. The user taps button-B, but button-A gets updated first. Button-A calls "KeyDown()" (eating the input) which returns "true" but ultimately finds button-A was not tapped. Button-B then gets updated and calls "KeyDown()" which return "false" and chaos ensues.

If updating is done via one mega update routine which updates all game elements, then maybe BMX style input works, but if updating of game elements is divided up amongst unrelated pieces of code, than Monkeys' current way of handling input make much more sense.


skid(Posted 2011) [#9]
The problem at the moment is there is no way to

- invoke external processes from a keydown/mousedown and then flush that state so when the process finishes the down flag is cleared

- call game functions intermittently that use keyhit/mousehit so all systems in a game must be called every frame

- call some game functions repeatedly in a single update that depend on keyhit/mousehit as the hit counts are not currently decremented after each call


slenkar(Posted 2011) [#10]
yes bmx style flushkeys please


DGuy(Posted 2011) [#11]
The problem at the moment is there is no way to


Thinking about these scenarios, to me, they all seem easily (and more properly) addressed by caching the pertinent parts of the current input state in local/private variables and working with/modifying those local/private variables, not by using the global input state as a collection of scratch/work variables.

I view the input state as a globally, shared, non-mutable (for the current update) resource: Not something that can be or should be changed by the program code.

BTW, I have no issues with adding a FlushKeys() type function: If people WANT to modify the input state during the middle of an Update, let them ... :) ... but rather I have an issue with having the calling of input-checking related functions modifying the input state.


skid(Posted 2011) [#12]
There is no way around the touch stuck down problem in scenario 1, buttons for iOS games that spawn GameCenter windows or input requesters must wait for mouse up state which could be argued is no bad thing. Mojo could flush down state when it's window loses focus if there is such an event, which I suspect would be more proper.

In regards to JoyHit/MouseHit/KeyHit being flushed at the end of every Update and hence not available from OnRender, this might be proper but I would argue it is undocumented and confusing for typical Blitz user. If this behavior remains I think these commands should be blocked from being called in OnRender similar to the way drawing commands are blocked from being called in OnUpdate.


Corum(Posted 2011) [#13]
Yes to a manual FlushKeys() call.
What about a "global" command such as AutoFlushKeys(true/false), to let monkey manage the things just as it does at the moment?
"The more the merrier"? :)


Tibit(Posted 2011) [#14]
Is checking keydown and comparing against the last frame different from using KeyHit?


DGuy(Posted 2011) [#15]
KeyDown => Returns TRUE if a key was down at the start of the current update cycle.
KeyHit => Returns the number of times a key has been pressed since the start of the last update cycle.

KeyHit is useful if the user presses & releases a key during a single update: KeyDown will miss such a key-press (as its only concerned with the key-state at the start of a update, not key-state changes during an update), while KeyHit will catch it and return some value > 0 during the following update.


marksibly(Posted 2011) [#16]
Hi,

I am currently leaning towards reverting to the old b3d/bmx system, complete with FlushKeys.

Having keys autoflush at the end of OnUpdate was a nice idea, but with people doing multiple updates per OnUpdate, updates during renders etc, it just seems to be getting in the way.

And the quick solution to this is to just have individual keys 'auto-flush' themselves when KeyHit is used, ala b3d/bmx.

I also think this provides a 'lower level' wrapper around what's (usually) going on internally, which often involves dealing with KeyDown events etc - if such events just inc a counter when they arrive, and reading the key just decs the counter, there's a nice simple symmetry there which is hard to screw up!

You still need to remember to FlushKeys before any 'hit any key' stuff, but I think that's worth the extra flexibility.

Also, since FlushKeys effectively flushes mouse/joystick too, can we rename it FlushInput?!?


Perturbatio(Posted 2011) [#17]
FlushInput makes sense to me.


impixi(Posted 2011) [#18]
Yes, FlushInput.


DGuy(Posted 2011) [#19]
Mark, do you foresee any of the possible changes affecting the current behavior of the KeyDown, MouseDown or TouchDown functions (short of a call to FlushInput)?

More explicitly, are you thinking of changing them such that they'll only possibly return TRUE the first time they're called during an update cycle, after which they will always return FALSE?


marksibly(Posted 2011) [#20]
Hi,

> More explicitly, are you thinking of changing them such that they'll only
> possibly return TRUE the first time they're called during an update cycle, after which they will always return FALSE?

Yes, I am thinking of doing this - see my post above.


DGuy(Posted 2011) [#21]
Honestly, this seems like a step backwards, but hey, ... I'll survive ... :)

If the changes come to pass, I'll just have to cache the input state at the very start of the update cycle ...


Leo Santos(Posted 2011) [#22]
Why not simply keep the current, simple way that Monkey handles it, but then implement an additional FlushInput() function for whoever needs it? Seems like that would make everybody happy!

Leo.


therevills(Posted 2011) [#23]
Because of fixed rate logic, I really need Monkey to behave like BlitzMax...

Adding FlushKeys at the end of the loop helped for KeyHit, but its no good for KeyDown - because as soon as the the FlushKeys command is called it flushes the KeyDown event, so it looks like a KeyHit.


Jesse(Posted 2011) [#24]
why not FlushKeyHit(), FlushMouseHit()...?


DGuy(Posted 2011) [#25]
@therevills:
Adding FlushKeys at the end of the loop helped for KeyHit, but its no good for KeyDown - because as soon as the the FlushKeys command is called it flushes the KeyDown event, so it looks like a KeyHit.

(forgive me if my assumptions are wrong but ...)

It sounds like your running a loop and each time through the loop your checking/testing KeyHit() and/or KeyDown(). If this is what your doing, why don't you store the KeyHit()/KeyDown() states in local variables before you enter the loop and update those local variables?

I don't understand ... :(

Maybe its the way I code things, but I've (as far as I can remember) always setup my update loop like so:
Update-Loop-Start:
  Module-A Update (possibly checking input state)
  Module-B Update (possibly checking input state)
  etc...
Update-Loop-Ends

The way Monkey currently implements input, and the way other SDKs/Tools such as Unity/Playground SDK/Corona/Airplay/GameMaker (yes I've tried a bunch of them :) ), makes this setup possible, and event enforces it, by treating the input state as a shared resource that is constant and unalterable for the duration of the current update.

I have no qualms with FlushWhatever() type functions allowing the programmer to discard the input if they choose, but to have the checking of the input state alter the input state (as Mark is thinking), just seems so ... odd.

Anyway, back to Monkey coding ... ;)


therevills(Posted 2011) [#26]
This is how I am doing fixed rate logic:

	Method OnUpdate:Int()
		For local i% = 1 to Floor(numTicks)
			screen.Update()
		Next
		return 0
	End Method


So while in the screen.update users can press keys, but it will do the action more than once, as the keystate is stored and never flushed.

For example:
Class TitleScreen Extends Screen
	Method Update:Void()
		If KeyHit(KEY_SPACE)
			Print "Hello"
		End
	End
End


This will print "Hello" out more than once, even though the user has only hit the spacebar once...

just seems so ... odd.

Ever used BlitzBasic/BlitzMax/Blitz3D? Monkey is the one which is odd! ;)


marksibly(Posted 2011) [#27]
Hi,

> This is how I am doing fixed rate logic:

Ok...I promised myself I was gonna leave this alone...but I can't resist!

Why are you doing this at all?!?

The built-in update timer is supposed to provide a 'solid' way to do fixed rate timing, including accounting for 'lost frames'.

It's been through several updates by now, and if it's not working I consider it a bug!

And this is the thing: if there's no need for 'multiple updates' per OnUpdate, then is there anything really wrong with the current system?

I'll probably end up 'fixing' it regardless, but I get the distinct reason I'll be fixing it for the sake of a 'phantom' issue.


therevills(Posted 2011) [#28]
Fixed rate logic(FRL) works really well for collisions... I changed my platformer code from delta timing to FRL to fix the collisions.

Heres a quote from Jake Birket (Grey Alien):
The high resolution logic works great for collisions and receiving input snappily and gives very smooth animations with VSync on or off, and it allows me to do slow motion by tweaking the params


Of course he's talking about FRL in BlitzMax...


marksibly(Posted 2011) [#29]
Hi,

But...but...why have multiple updates per OnUpdate...Mojo does this automatically...


DGuy(Posted 2011) [#30]
Did a lot of Blitz3D coding a couple years back … :)

… but even then my update-loop called out to many update-routines, so I cached the input-state and it was shared amongst the update routines.

I honestly believe that the current way Monkey handles input enables more complex, more modern program design, where the functionality of the program is divided up between several interrelated but independent modules/classes/managers working together to get things done. Y'know, … divide and conquer.

If you have one monolithic routine which updates everything or a very simple update routine, where input is only being checked for in that one routine then you can play fast and lose with the input state, but I believe Monkey has taken a proper step away from the one-update-routine-to-update-them-all approach to coding.


And this is the thing: if there's no need for 'multiple updates' per OnUpdate, then is there anything really wrong with the current system?

No, Mark, there is nothing wrong with it: Nothing to fix! :) You've done a great job with Monkey: getting the input from all these very different, cutting edge platforms to appear to the programmer is such a unified way … brilliant! Why do you want "fix" it and move backwards towards the type of input system used during the dark days of DOS … ?


But…but…why have multiple updates per OnUpdate...Mojo does this automatically...

There where/are techniques that had/have to be used with other blitz products (Boy, do I remember the hoops I had to jump through with Blitz3D) that Monkey renders unnecessary. As the saying goes, when all you have is a hammer, everything looks like a nail. ;)


therevills(Posted 2011) [#31]
Maybe Im too old skool ;)

Have a look at my platform code in the Monkey Code section: http://www.monkeycoder.co.nz/Community/posts.php?topic=449

At first I was using Jame's delta timing, but the player was getting stuck in the ground a lot, so I changed to fixed rate logic and it fixed it...


skid(Posted 2011) [#32]
Mojo does this automatically...


Only since a quite recent update :)

Ummm, I think the point is some people will want to run their physics by passing it a timestamp so should probably just call physics.Step(time) from OnRender and steer clear of using hit based hotkeys.

Others may want to call UpdateWorld() at 20hz and use tweening to get smooth renders. Again, Update from OnRender and steer clear of KeyHit.

Others still will want to call game.Update at 300hz so relatively fast moving objects collide with no complex interpolation required. They are then free to set Update/Refresh rate per target platform without worrying about changing behavior of the game.

As DGuy says, current system is fine, it just requires user to collate input hits. I just fail to see how removing autoflush of hitcounts with decrement per hittest will impact his approach in anyway.