- From: Glenn Maynard <glenn@zewt.org>
- Date: Sat, 4 Aug 2012 12:03:15 -0500
- To: Scott Graham <scottmg@chromium.org>
- Cc: Rick Waldron <waldron.rick@gmail.com>, olli@pettay.fi, Webapps WG <public-webapps@w3.org>, Ted Mielczarek <ted@mielczarek.org>
- Message-ID: <CABirCh8BAKrQz+4d6YbtV5V8=u_x=3MotwmUTzX8r+OuSD98Eg@mail.gmail.com>
Here's a rough sketch of an API that provides clean forwards-compatibility for devices. I think this also avoids all of the issues I talked about earlier: it gives a clean, easy to use event-based API that preserves ordering and timestamps; it can be used in both an event-based and polling way; it can retrieve both events and the current state; and most importantly it doesn't push things into a library, with all of the serious (and hopefully obvious to everyone on this list) problems that would cause. // Retrieve a representation of the device, with no actual access to inputs: var gamepad = getSomeInputDevice(); // Get a list of supported layouts: console.log(gamepad.layouts); ["X360", "NES", "SNES", "raw"] // Get an interface, specifying which layout to use. var gamepadInput = gamepad.getLayout("X360"); // Read the current state. var state = gamepadInput.getCurrentState(); /* state == { // These values are defined by the X360 layout. leftThumbStickX: 0, leftThumbStickY: 0, rightThumbStickX: 0.4, rightThumbStickY: 0.5, up: true, right: false, down: false, left: false, buttonX: false, buttonY: false, buttonA: false, buttonB: false, // Inputs which have no representation in this layout are returned in raw form. rawCompassAngle: 45, } */ // Read the buffer of changes: var stateChanges = gamepadInput.read(); /* stateChanges == [{ input: "rightThumbstickX", // These labels are equal to the object keys in "state" above. value: 0.5, lastValue: 0.4, timestamp: 1336102719319, }, { input: "rawCompassAngle", value: 55, lastValue: 45, timestamp: 1336102721319, }] */ function processInput(stateChanges) { // Process inputs: for(var i = 0; i < stateChanges.length; ++i) { var change = stateChanges[i]; var delta = change.lastValue - change.value; switch(change.input) { case "left": console.log("Left button was " + change.value? "pushed":"released"); break; case "leftThumbStickX": console.log("Left stick X axis:" + change.value + " with a relative change of " + delta); break; } } // Update the game state. This API allows us to process all inputs that happen simultaneously together, // such as axis changes, and then only update the game state once for the whole batch. processInputs(); } // Receiving an event on change: gamepadInput.onchange = function(e) { processInput(gamepadInput.read()); }; Some points: - A browser may support many different layouts for the same device. For example, an Xbox 360 controller can easily be used as an NES or SNES controller, so it can expose those profiles. - Raw inputs for any unmapped inputs are included for all profiles. This allows games to make use of additional controls, without being forced to drop back to "raw" to access them at all. - If a profile is exposed at all, the inputs of the profile must be completely covered. - Layout may not always map 1:1 to raw inputs. For example, the Playstation controller's D-pad is four buttons in a cross; layouts may map these four buttons down to two axes. - All data in getCurrentState is absolute, since it doesn't mutate the object. Therefore, purely relative inputs, such as trackballs and (probably) steering wheels, are returned in an absolute, accumulated form. To get relative motion, subtract the value from the previous value. By the way, while I only included "NES" as an example, having a "baseline" layout like that (with a more generic name) is useful: tons of simple games use only a D-pad and a couple buttons. This would give them get the widest input device support possible, by not having to request a profile with far more features than they need. I haven't tried to incorporate Florian's suggestion of using something like ArrayBuffer. That could be supported later, eg. by providing a readIntoBuffer(buffer) next to read(). That's too complex to try to tackle all at once. -- Glenn Maynard
Received on Saturday, 4 August 2012 17:03:44 UTC