Implement HTML5 gamepad support (thanks @bmfs, resolves #624)

This commit is contained in:
Joshua Granick
2015-12-07 16:17:51 -08:00
parent 33a737395c
commit 533c64ec5a
7 changed files with 340 additions and 37 deletions

View File

@@ -7,19 +7,27 @@ import lime.app.Application;
import lime.app.Config;
import lime.audio.AudioManager;
import lime.graphics.Renderer;
import lime.ui.GamepadAxis;
import lime.ui.KeyCode;
import lime.ui.KeyModifier;
import lime.ui.Gamepad;
import lime.ui.GamepadButton;
import lime.ui.Joystick;
import lime.ui.Window;
@:access(lime._backend.html5.HTML5Window)
@:access(lime.app.Application)
@:access(lime.graphics.Renderer)
@:access(lime.ui.Gamepad)
@:access(lime.ui.Joystick)
@:access(lime.ui.Window)
class HTML5Application {
private var gameDeviceCache = new Map<Int, GameDeviceData> ();
private var currentUpdate:Float;
private var deltaTime:Float;
private var framePeriod:Float;
@@ -190,6 +198,8 @@ class HTML5Application {
private function handleApplicationEvent (?__):Void {
updateGameDevices ();
currentUpdate = Date.now ().getTime ();
if (currentUpdate >= nextUpdate) {
@@ -331,4 +341,187 @@ class HTML5Application {
}
private function updateGameDevices ():Void {
var devices = Joystick.__getDeviceData ();
if (devices == null) return;
var id, gamepad, joystick, data:Dynamic, cache;
for (i in 0...devices.length) {
id = i;
data = devices[id];
if (data == null) continue;
if (!gameDeviceCache.exists (id)) {
cache = new GameDeviceData ();
cache.id = id;
cache.connected = data.connected;
for (i in 0...data.buttons.length) {
cache.buttons.push (data.buttons[i].value);
}
for (i in 0...data.axes.length) {
cache.axes.push (data.axes[i]);
}
if (data.mapping == "standard") {
cache.isGamepad = true;
}
gameDeviceCache.set (id, cache);
if (data.connected) {
Joystick.__connect (id);
if (cache.isGamepad) {
Gamepad.__connect (id);
}
}
}
cache = gameDeviceCache.get (id);
joystick = Joystick.devices.get (id);
gamepad = Gamepad.devices.get (id);
if (data.connected) {
var button:GamepadButton;
var value:Float;
for (i in 0...data.buttons.length) {
value = data.buttons[i].value;
if (value != cache.buttons[i]) {
if (i == 6) {
joystick.onAxisMove.dispatch (data.axes.length, value);
if (gamepad != null) gamepad.onAxisMove.dispatch (GamepadAxis.TRIGGER_LEFT, value);
} else if (i == 7) {
joystick.onAxisMove.dispatch (data.axes.length + 1, value);
if (gamepad != null) gamepad.onAxisMove.dispatch (GamepadAxis.TRIGGER_RIGHT, value);
} else {
if (value > 0) {
joystick.onButtonDown.dispatch (i);
} else {
joystick.onButtonUp.dispatch (i);
}
if (gamepad != null) {
button = switch (i) {
case 0: GamepadButton.A;
case 1: GamepadButton.B;
case 2: GamepadButton.X;
case 3: GamepadButton.Y;
case 4: GamepadButton.LEFT_SHOULDER;
case 5: GamepadButton.RIGHT_SHOULDER;
case 8: GamepadButton.BACK;
case 9: GamepadButton.START;
case 10: GamepadButton.LEFT_STICK;
case 11: GamepadButton.RIGHT_STICK;
case 12: GamepadButton.DPAD_UP;
case 13: GamepadButton.DPAD_DOWN;
case 14: GamepadButton.DPAD_LEFT;
case 15: GamepadButton.DPAD_RIGHT;
case 16: GamepadButton.GUIDE;
default: continue;
}
if (value > 0) {
gamepad.onButtonDown.dispatch (button);
} else {
gamepad.onButtonUp.dispatch (button);
}
}
}
cache.buttons[i] = value;
}
}
for (i in 0...data.axes.length) {
if (data.axes[i] != cache.axes[i]) {
joystick.onAxisMove.dispatch (i, data.axes[i]);
if (gamepad != null) gamepad.onAxisMove.dispatch (i, data.axes[i]);
cache.axes[i] = data.axes[i];
}
}
} else if (cache.connected) {
cache.connected = false;
Joystick.__disconnect (id);
Gamepad.__disconnect (id);
}
}
}
}
class GameDeviceData {
public var connected:Bool;
public var id:Int;
public var isGamepad:Bool;
public var buttons:Array<Float>;
public var axes:Array<Float>;
public function new () {
connected = true;
buttons = [];
axes = [];
}
}

View File

@@ -19,6 +19,8 @@ import lime.app.Application;
import lime.graphics.Image;
import lime.system.Display;
import lime.system.System;
import lime.ui.Gamepad;
import lime.ui.Joystick;
import lime.ui.Touch;
import lime.ui.Window;
@@ -29,6 +31,8 @@ typedef InputEvent = js.html.Event;
#end
@:access(lime.app.Application)
@:access(lime.ui.Gamepad)
@:access(lime.ui.Joystick)
@:access(lime.ui.Window)
@@ -201,6 +205,9 @@ class HTML5Window {
element.addEventListener ("touchmove", handleTouchEvent, true);
element.addEventListener ("touchend", handleTouchEvent, true);
element.addEventListener ("gamepadconnected", handleGamepadEvent, true);
element.addEventListener ("gamepaddisconnected", handleGamepadEvent, true);
}
}
@@ -238,6 +245,34 @@ class HTML5Window {
}
private function handleGamepadEvent (event:Dynamic):Void {
switch (event.type) {
case "gamepadconnected":
trace ("GAMEPAD CONNECTED");
Joystick.__connect (event.gamepad.index);
if (event.gamepad.mapping == "standard") {
Gamepad.__connect (event.gamepad.index);
}
case "gamepaddisconnected":
Joystick.__disconnect (event.gamepad.index);
Gamepad.__disconnect (event.gamepad.index);
default:
}
}
private function handleInputEvent (event:InputEvent):Void {
if (textInput.value != "") {

View File

@@ -190,20 +190,11 @@ class NativeApplication {
case CONNECT:
if (!Gamepad.devices.exists (gamepadEventInfo.id)) {
var gamepad = new Gamepad (gamepadEventInfo.id);
Gamepad.devices.set (gamepadEventInfo.id, gamepad);
Gamepad.onConnect.dispatch (gamepad);
}
Gamepad.__connect (gamepadEventInfo.id);
case DISCONNECT:
var gamepad = Gamepad.devices.get (gamepadEventInfo.id);
if (gamepad != null) gamepad.connected = false;
Gamepad.devices.remove (gamepadEventInfo.id);
if (gamepad != null) gamepad.onDisconnect.dispatch ();
Gamepad.__disconnect (gamepadEventInfo.id);
}
@@ -241,20 +232,11 @@ class NativeApplication {
case CONNECT:
if (!Joystick.devices.exists (joystickEventInfo.id)) {
var joystick = new Joystick (joystickEventInfo.id);
Joystick.devices.set (joystickEventInfo.id, joystick);
Joystick.onConnect.dispatch (joystick);
}
Joystick.__connect (joystickEventInfo.id);
case DISCONNECT:
var joystick = Joystick.devices.get (joystickEventInfo.id);
if (joystick != null) joystick.connected = false;
Joystick.devices.remove (joystickEventInfo.id);
if (joystick != null) joystick.onDisconnect.dispatch ();
Joystick.__disconnect (joystickEventInfo.id);
}

View File

@@ -64,8 +64,8 @@ class Application extends Module {
onExit.add (onModuleExit);
onUpdate.add (update);
Gamepad.onConnect.add (onGamepadConnect);
Joystick.onConnect.add (onJoystickConnect);
Gamepad.onConnect.add (__onGamepadConnect);
Joystick.onConnect.add (__onJoystickConnect);
Touch.onStart.add (onTouchStart);
Touch.onMove.add (onTouchMove);
Touch.onEnd.add (onTouchEnd);
@@ -264,11 +264,6 @@ class Application extends Module {
}
gamepad.onAxisMove.add (onGamepadAxisMove.bind (gamepad));
gamepad.onButtonDown.add (onGamepadButtonDown.bind (gamepad));
gamepad.onButtonUp.add (onGamepadButtonUp.bind (gamepad));
gamepad.onDisconnect.add (onGamepadDisconnect.bind (gamepad));
}
@@ -324,13 +319,6 @@ class Application extends Module {
}
joystick.onAxisMove.add (onJoystickAxisMove.bind (joystick));
joystick.onButtonDown.add (onJoystickButtonDown.bind (joystick));
joystick.onButtonUp.add (onJoystickButtonUp.bind (joystick));
joystick.onDisconnect.add (onJoystickDisconnect.bind (joystick));
joystick.onHatMove.add (onJoystickHatMove.bind (joystick));
joystick.onTrackballMove.add (onJoystickTrackballMove.bind (joystick));
}
@@ -802,6 +790,32 @@ class Application extends Module {
}
@:noCompletion private function __onGamepadConnect (gamepad:Gamepad):Void {
onGamepadConnect (gamepad);
gamepad.onAxisMove.add (onGamepadAxisMove.bind (gamepad));
gamepad.onButtonDown.add (onGamepadButtonDown.bind (gamepad));
gamepad.onButtonUp.add (onGamepadButtonUp.bind (gamepad));
gamepad.onDisconnect.add (onGamepadDisconnect.bind (gamepad));
}
@:noCompletion private function __onJoystickConnect (joystick:Joystick):Void {
onJoystickConnect (joystick);
joystick.onAxisMove.add (onJoystickAxisMove.bind (joystick));
joystick.onButtonDown.add (onJoystickButtonDown.bind (joystick));
joystick.onButtonUp.add (onJoystickButtonUp.bind (joystick));
joystick.onDisconnect.add (onJoystickDisconnect.bind (joystick));
joystick.onHatMove.add (onJoystickHatMove.bind (joystick));
joystick.onTrackballMove.add (onJoystickTrackballMove.bind (joystick));
}
// Get & Set Methods

View File

@@ -59,7 +59,11 @@ class Module implements IModule {
* Called when a gamepad is connected
* @param gamepad The gamepad that was connected
*/
public function onGamepadConnect (gamepad:Gamepad):Void { }
public function onGamepadConnect (gamepad:Gamepad):Void {
trace ("onGamepadConnect (module)");
}
/**

View File

@@ -7,6 +7,8 @@ import lime.app.Event;
@:build(lime.system.CFFI.build())
#end
@:access(lime.ui.Joystick)
class Gamepad {
@@ -41,6 +43,29 @@ class Gamepad {
}
@:noCompletion private static function __connect (id:Int):Void {
if (!devices.exists (id)) {
var gamepad = new Gamepad (id);
devices.set (id, gamepad);
onConnect.dispatch (gamepad);
}
}
@:noCompletion private static function __disconnect (id:Int):Void {
var gamepad = devices.get (id);
if (gamepad != null) gamepad.connected = false;
devices.remove (id);
if (gamepad != null) gamepad.onDisconnect.dispatch ();
}
// Get & Set Methods
@@ -52,6 +77,9 @@ class Gamepad {
#if ((cpp || neko || nodejs) && !macro)
return lime_gamepad_get_device_guid (this.id);
#elseif (js && html5)
var devices = Joystick.__getDeviceData ();
return devices[this.id].id;
#else
return null;
#end
@@ -63,6 +91,9 @@ class Gamepad {
#if ((cpp || neko || nodejs) && !macro)
return lime_gamepad_get_device_name (this.id);
#elseif (js && html5)
var devices = Joystick.__getDeviceData ();
return devices[this.id].id;
#else
return null;
#end

View File

@@ -38,6 +38,38 @@ class Joystick {
}
@:noCompletion private static function __connect (id:Int):Void {
if (!devices.exists (id)) {
var joystick = new Joystick (id);
devices.set (id, joystick);
onConnect.dispatch (joystick);
}
}
@:noCompletion private static function __disconnect (id:Int):Void {
var joystick = devices.get (id);
if (joystick != null) joystick.connected = false;
devices.remove (id);
if (joystick != null) joystick.onDisconnect.dispatch ();
}
#if (js && html5)
@:noCompletion private static function __getDeviceData ():Array<Dynamic> {
return (untyped navigator.getGamepads) ? untyped navigator.getGamepads () : (untyped navigator.webkitGetGamepads) ? untyped navigator.webkitGetGamepads () : null;
}
#end
// Get & Set Methods
@@ -49,6 +81,9 @@ class Joystick {
#if ((cpp || neko || nodejs) && !macro)
return lime_joystick_get_device_guid (this.id);
#elseif (js && html5)
var devices = __getDeviceData ();
return devices[this.id].id;
#else
return null;
#end
@@ -60,6 +95,9 @@ class Joystick {
#if ((cpp || neko || nodejs) && !macro)
return lime_joystick_get_device_name (this.id);
#elseif (js && html5)
var devices = __getDeviceData ();
return devices[this.id].id;
#else
return null;
#end
@@ -71,6 +109,9 @@ class Joystick {
#if ((cpp || neko || nodejs) && !macro)
return lime_joystick_get_num_axes (this.id);
#elseif (js && html5)
var devices = __getDeviceData ();
return devices[this.id].axes.length;
#else
return 0;
#end
@@ -82,6 +123,9 @@ class Joystick {
#if ((cpp || neko || nodejs) && !macro)
return lime_joystick_get_num_buttons (this.id);
#elseif (js && html5)
var devices = __getDeviceData ();
return devices[this.id].buttons.length;
#else
return 0;
#end