max-mapper / voxel-engine

3D HTML5 voxel game engine
http://maxogden.github.com/voxel-engine
BSD 3-Clause "New" or "Revised" License
1.29k stars 220 forks source link

Change game.buttons to use kb-bindings module #96

Closed deathcap closed 10 years ago

deathcap commented 10 years ago

Changes game.buttons to use kb-bindings - which is based on and compatible with @chrisdickinson's kb-controls, but takes it a step further than providing a polling interface, also adding events and reconfigurable bindings.

This functionality is very useful to add here since it allows the game to centrally control the keyboard handling, consistently enabling/disabling capturing as pointer lock is attained/released and providing a unified interface for managing bindings.

Previously, non-polling kb handling code would register its own event handlers on the DOM, for example from @maxogden's voxel-hello-world:

// toggle between first and third person modes
window.addEventListener('keydown', function (ev) {
  if (ev.keyCode === 'R'.charCodeAt(0)) avatar.toggle()
})

which works, but if you're typing 'R' in a text control (etc.), the event handler is still executed, so it needs to hook into attain/release interact itself.. which gets messy quickly, plus, the above handler still hardcoded the specific keybinding. With kb-bindings in voxel-engine, you can register a descriptive binding name (e.g. 'pov' — to optionally let the user rebind it with kb-bindings-ui), then listen for the appropriate events (which are only sent if enabled, so they don't interfere with modal widgets), and it it all works well.

There may be other solutions to these problems than changing game.buttons outright. I also experimented with adding a kb_module option to voxel-engine, allowing users to pass in their own kb module, but this was problematic since it cannot be JSON-serialized (similar problem to GH-95). If kb-controls itself https://github.com/chrisdickinson/kb-controls/issues/4 grows these features, that would be an option too, but I think events is probably out of the scope of kb-controls (hence a more expansive module, kb-bindings). Or if voxel- could be rearchitected somehow so that voxel-engine wouldn't depend on kb-, instead vice versa (or not at all?), allowing modules to be easily swapped in or out (similar to gl- vs three.js as @shama describes in https://github.com/voxel/issues/issues/4) with fewer interdependencies, but I'm not sure exactly how that would work (open to suggestions); though at least the change in this PR was sufficient for my purposes =) (currently using in ~8 voxel modules)

chrisdickinson commented 10 years ago

Event bindings for kb-controls has been oft-requested, but when it comes up I am iffy on implementing it:

kb-controls is designed to be a polling interface primarily so that inputs are recordable. A central point in voxel-engine or other game engine could collect user input state along with the time-of-sample and use that to build replays / update frames for network games. It's a module designed for the collection of input events that affect the simulation.

Event listening is ideal for events that do not affect the simulation -- such as switching POV -- and so can be handled immediately. I've been avoiding adding event listening to kb-controls for the following reasons:

  1. The DOM already provides a event-based API. In the provided example, the problem with accidentally intercepting R would be solved by scoping the keydown listener to the canvas itself instead of the window.
  2. I want to discourage routing non-simulation-mutating events through kb-controls, primarily to avoid inflicting a decision on the end user of the API -- "Should I poll for this action or listen for it?" Keeping kb-controls to a polling interface mandates that any action that mutates the simulation is received in a polling fashion.
  3. Simulation mutating events are easier to make deterministic if polled for (vs. listened for). The more can be made deterministic in the simulation, the easier the simulation state will be to debug, reason about, & transmit.

The confounding bit in this, I think, is that kb-controls does two things: presents a polling interface for input, and also allows key to action mapping, and so there's a desire for an event listening api that allows for key remapping.

(Sorry for the rambling reply. Great work on kb-bindings + kb-bindings-ui!)

shama commented 10 years ago

FWIW, I wanted to event based controls as well until @chrisdickinson so eloquently explained why a polling interface is superior at last nodeconf. My vote is to keep kb-controls and thus keeping it based on polling.

But I am +1 to pulling anything out of voxel-engine and into the user land, if possible.

deathcap commented 10 years ago

Alright I'll close this for now, will see about another solution. I think a customizable keybindings object (exposable to a UI plugin) would go a long way, however. game-shell has its own key bindings mechanism, I'll need to see how to integrate with it (ref https://github.com/voxel/issues/issues/4)