Skip to content

Commit

Permalink
Fix: Tsdoc code comment warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
KW-M committed Sep 27, 2024
1 parent 177c127 commit 74b373e
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 8 deletions.
253 changes: 253 additions & 0 deletions examples/game_engine/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import { GamepadEmulator, DEFAULT_GPAD_BUTTON_COUNT, DEFAULT_GPAD_AXIS_COUNT } from "../../src/GamepadEmulator.js";
import { GamepadApiWrapper } from "../../src/GamepadApiWrapper.js";
import { GamepadDisplay } from "../../src/GamepadDisplay.js";
import { gamepadButtonType, gamepadDirection } from "../../src/enums.js";
import { CenterTransformOrigin } from "../../src/utilities.js";
// import "./phaser.js";
// import "./twin-stick-game.js"
// the gamepad emulator MUST be created before creating the GamepadApiWrapper, a game engine or any other library that uses navigator.getGamepads()
const gamepadEmu = new GamepadEmulator(0.1);
const gpadApiWrapper = new GamepadApiWrapper({
updateDelay: 0, // update the gamepad state every frame
axisDeadZone: 0.05, // set the deadzone for all axes to 0.05 [5%] (to avoid extra events when the joystick is near its neutral point).
});
// CONSTS
const GPAD_DISPLAY_CONTAINER = document.body;
const START_BUTTON_INDEX = 8;
const LEFT_X_AXIS_INDEX = 0;
const LEFT_Y_AXIS_INDEX = 1;
const RIGHT_X_AXIS_INDEX = 2;
const RIGHT_Y_AXIS_INDEX = 3;
const BUTTON_ID_NAMES = [
"button_1",
"button_2",
"button_3",
"button_4",
"shoulder_button_front_left",
"shoulder_button_front_right",
"shoulder_trigger_back_left",
"shoulder_trigger_back_right",
"select_button",
"start_button",
"stick_button_left",
"stick_button_right",
"d_pad_up",
"d_pad_down",
"d_pad_left",
"d_pad_right",
/* "vendor" */ // generally not available to browsers because it is used by OS vendors (eg: Xbox Game Bar, Steam HUD).
];
/** Setup the touch targets & input parameters for translating onscreen events into events for the emulated gamepad (part of the emulated gamepad module) */
function setupEmulatedGamepadInput(gpadIndex, display_gpad) {
/* ----- SETUP BUTTON INPUTS ----- */
const emulatorButtonConfigs = BUTTON_ID_NAMES.map((name, i) => {
if (name.includes("trigger")) {
// trigger buttons usually take variable pressure so can be represented by a variable button that is dragged down.
return {
buttonIndex: i,
type: gamepadButtonType.variable,
tapTarget: display_gpad.querySelector("#" + name + "_touch_target"),
dragDistance: 50, // pixels that the user must drag the button down to fully press it.
lockTargetWhilePressed: true,
directions: {
[gamepadDirection.up]: false,
[gamepadDirection.down]: true,
[gamepadDirection.left]: false,
[gamepadDirection.right]: false,
}
};
}
else {
return {
buttonIndex: i,
type: gamepadButtonType.onOff,
lockTargetWhilePressed: name.includes("stick"),
tapTarget: display_gpad.querySelector("#" + name + "_touch_target")
};
}
});
gamepadEmu.AddDisplayButtonEventListeners(gpadIndex, emulatorButtonConfigs);
/* ----- SETUP JOYSTICK INPUTS ----- */
const emulatorStickConfigs = [{
tapTarget: display_gpad.querySelector("#stick_button_left_touch_target"),
dragDistance: 30, // pixels that the user must drag the joystic to represent +/- 1.
xAxisIndex: 0,
yAxisIndex: 1,
lockTargetWhilePressed: true,
directions: {
[gamepadDirection.up]: true,
[gamepadDirection.down]: false, // disable the down direction so that the joystick can only be dragged up (note that manual calls to moveAxis can still override this).
[gamepadDirection.left]: true,
[gamepadDirection.right]: true,
},
},
{
tapTarget: display_gpad.querySelector("#stick_button_right_touch_target"),
dragDistance: 30, // pixels that the user must drag the joystic to represent +/- 1.
xAxisIndex: 2,
yAxisIndex: 3,
lockTargetWhilePressed: true,
directions: {
[gamepadDirection.up]: true,
[gamepadDirection.down]: true,
[gamepadDirection.left]: true,
[gamepadDirection.right]: true,
},
}];
gamepadEmu.AddDisplayJoystickEventListeners(gpadIndex, emulatorStickConfigs);
}
/** Setup the display buttons & axes of the onscreen gamepad to react to the state of the gamepad from the browser gamepad api (uses the gamepadApiWrapper) */
function setupGamepadDisplay(gpadIndex) {
document.querySelectorAll("#stick_right, #stick_left").forEach((element) => {
CenterTransformOrigin(element); // useful if you want to visually transform the joystick with rotation and scaling
// CenterTransformOriginDebug(element as SVGGraphicsElement); // show debug bounding boxes used in this feature.
});
/* ----- SETUP BUTTON DISPLAY ----- */
const buttons = BUTTON_ID_NAMES.map((name, i) => {
console.log(name);
if (name.includes("trigger")) {
// trigger buttons usually take variable pressure so can be represented by a variable button that is dragged down.
return {
type: gamepadButtonType.variable,
highlight: GPAD_DISPLAY_CONTAINER.querySelector("#" + name + "_highlight"),
buttonElement: GPAD_DISPLAY_CONTAINER.querySelector("#" + name),
direction: gamepadDirection.down,
directionHighlight: GPAD_DISPLAY_CONTAINER.querySelector("#" + name + "_direction_highlight"),
movementRange: 10, // pixels that the button can move
extraData: {
myCustomData: "variable btn name is " + name
}
};
}
else {
// all other buttons are simply on (pressed) or off (not pressed).
return {
type: gamepadButtonType.onOff,
highlight: GPAD_DISPLAY_CONTAINER.querySelector("#" + name + "_highlight"),
extraData: {
myCustomData: "onOff btn name is " + name
}
};
}
});
/* ----- SETUP JOYSTICK DISPLAY ----- */
const joysticks = [{
joystickElement: GPAD_DISPLAY_CONTAINER.querySelector("#stick_left"),
xAxisIndex: 0,
yAxisIndex: 1,
movementRange: 10,
highlights: {
[gamepadDirection.up]: GPAD_DISPLAY_CONTAINER.querySelector("#l_stick_up_direction_highlight"),
[gamepadDirection.down]: GPAD_DISPLAY_CONTAINER.querySelector("#l_stick_down_direction_highlight"),
[gamepadDirection.left]: GPAD_DISPLAY_CONTAINER.querySelector("#l_stick_left_direction_highlight"),
[gamepadDirection.right]: GPAD_DISPLAY_CONTAINER.querySelector("#l_stick_right_direction_highlight"),
}
}, {
joystickElement: GPAD_DISPLAY_CONTAINER.querySelector("#stick_right"),
xAxisIndex: 2,
yAxisIndex: 3,
movementRange: 10,
highlights: {
[gamepadDirection.up]: GPAD_DISPLAY_CONTAINER.querySelector("#r_stick_up_direction_highlight"),
[gamepadDirection.down]: GPAD_DISPLAY_CONTAINER.querySelector("#r_stick_down_direction_highlight"),
[gamepadDirection.left]: GPAD_DISPLAY_CONTAINER.querySelector("#r_stick_left_direction_highlight"),
[gamepadDirection.right]: GPAD_DISPLAY_CONTAINER.querySelector("#r_stick_right_direction_highlight"),
}
}];
// create the gamepad display class instance and pass the config
const display = new GamepadDisplay({
gamepadIndex: gpadIndex,
pressedHighlightClass: "pressed",
touchedHighlightClass: "touched",
moveDirectionHighlightClass: "moved",
buttons: buttons,
sticks: joysticks,
}, gpadApiWrapper); // we can pass our existing instance of the gpadApiWrapper to the gamepad display so that it can use it to update the gamepad state efficiently.
}
function setupEmulatedGamepad() {
const EMULATED_GPAD_INDEX = 0; // in this example we will only add one emulated gamepad at position/index 0 in the navigator.getGamepads() array.
gamepadEmu.AddEmulatedGamepad(EMULATED_GPAD_INDEX, true, DEFAULT_GPAD_BUTTON_COUNT, DEFAULT_GPAD_AXIS_COUNT);
setupGamepadDisplay(EMULATED_GPAD_INDEX); // setup the display buttons to react to the events FROM the gamepad api directly
setupEmulatedGamepadInput(EMULATED_GPAD_INDEX, GPAD_DISPLAY_CONTAINER); // setup event listeners on the buttons/joysticks to send button/axis updates TO the emulated gamepad.
// the game engine expects some BUTTON event to detect that our emulated gamepad has connected (and is ready to use):
gamepadEmu.PressButton(EMULATED_GPAD_INDEX, START_BUTTON_INDEX, 1, true); // press the start button
setTimeout(() => { gamepadEmu.PressButton(EMULATED_GPAD_INDEX, START_BUTTON_INDEX, 0, false); }, 2); // release the start button after one "frame" (2ms) (if this is less than the rate at which the game engine checks for button presses, the game engine will not detect it)
// also add keyboard bindings to the gamepad emulator (NOTE that this is through the gamepad emulator. The game engine thinks it is reciving gamepad events)
window.onkeydown = (e) => {
const numberKey = parseInt(e.key);
// wasd to move the left stick
if (e.key === "a")
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, 0, -1);
else if (e.key === "d")
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, 0, 1);
else if (e.key === "w")
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, 1, -1);
else if (e.key === "s")
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, 1, 1);
// arrow keys to move the right stick (prevent default to prevent scrolling)
else if (e.key === "ArrowLeft") {
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, 2, -1);
e.preventDefault();
}
else if (e.key === "ArrowRight") {
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, 2, 1);
e.preventDefault();
}
else if (e.key === "ArrowUp") {
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, 3, -1);
e.preventDefault();
}
else if (e.key === "ArrowDown") {
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, 3, 1);
e.preventDefault();
}
// all other gamepad buttons are mapped to the number keys or keycodes for high button numbers
else if (!isNaN(numberKey))
gamepadEmu.PressButton(EMULATED_GPAD_INDEX, numberKey, 1, true);
else if (e.keyCode)
gamepadEmu.PressButton(EMULATED_GPAD_INDEX, e.keyCode - 66 + 10, 1, true); // 66 is the keycode for "B" (A is already used), 10 is the count of number keys on the keyboard (0-9), so "b" is button #10, "c" is button #11, etc.
};
window.onkeyup = (e) => {
const numberKey = parseInt(e.key);
// wasd to move the left stick
if (e.key === "a")
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, LEFT_X_AXIS_INDEX, 0);
else if (e.key === "d")
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, LEFT_X_AXIS_INDEX, 0);
else if (e.key === "w")
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, LEFT_Y_AXIS_INDEX, 0);
else if (e.key === "s")
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, LEFT_Y_AXIS_INDEX, 0);
// arrow keys to move the right stick (prevent default to prevent scrolling)
else if (e.key === "ArrowLeft") {
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, RIGHT_X_AXIS_INDEX, 0);
e.preventDefault();
}
else if (e.key === "ArrowRight") {
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, RIGHT_X_AXIS_INDEX, 0);
e.preventDefault();
}
else if (e.key === "ArrowUp") {
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, RIGHT_Y_AXIS_INDEX, 0);
e.preventDefault();
}
else if (e.key === "ArrowDown") {
gamepadEmu.MoveAxis(EMULATED_GPAD_INDEX, RIGHT_Y_AXIS_INDEX, 0);
e.preventDefault();
}
// all other gamepad buttons are mapped to the number keys or keycodes for high button numbers
else if (!isNaN(numberKey))
gamepadEmu.PressButton(EMULATED_GPAD_INDEX, numberKey, 0, false);
else if (e.keyCode)
gamepadEmu.PressButton(EMULATED_GPAD_INDEX, e.keyCode - 66 + 10, 0, false); // 66 is the keycode for "B" (A is already used), 10 is the count of number keys on the keyboard (0-9), so "b" is button #10, "c" is button #11, etc.
};
}
// expose for the game engine / page to let us know when the game is ready to start
globalThis.onGameReady = () => {
document.querySelector("#loading-msg")?.remove();
document.querySelectorAll(".gpad-display").forEach(elem => {
elem.classList.remove("hidden");
});
// setup a new emulated gamepad with the existing gamepad emulator:
setupEmulatedGamepad();
};
2 changes: 1 addition & 1 deletion src/GamepadApiWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface wrapperConfig {
/** A range in which axis values closer to zero than this are simply treated as zero
* Used to prevent noise from analog sticks from registering as changes when they are not being used */
axisDeadZone?: number;
/** An array of {@link wrapperButtonConfig} that tell the wrapper how to respond to button changes. Should be in the same order as buttons are listed in a native browser {@link Gamepad.buttons} list. */
/** An array of {@link wrapperButtonConfig} that tell the wrapper how to respond to button changes. Array index corresponds the the index of the button the a native browser gamepad.buttons array as returned from eg: `navigator.getGamepads()[0].buttons` */
buttonConfigs?: wrapperButtonConfig[];
}

Expand Down
6 changes: 3 additions & 3 deletions src/GamepadDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,11 @@ export class GamepadDisplay {
}

/**
* This function is registered as the callback for {@link GamepadApiWrapper.onGamepadAxisChange}
* This function is registered as the callback for {@link GamepadApiWrapper.onGamepadAxisChange()}
* it calls the {@link DisplayGamepadConfig.joystickDisplayFunction} (if specified) or the {@link GamepadDisplay.DefaultJoystickDisplayFunction} otherwise
* for each configured joystick with axies that have changed
* @param gpadIndex The index of the gamepad that has changed
* @param gpadState The new state of the gamepad as reported by the browser / {@link GamepadApiWrapper.onGamepadAxisChange}
* @param gpadState The new state of the gamepad as reported by the browser
* @param axisChangesMask An array of booleans, where each true indicates that the corresponding axis has changed since the last update
*/
protected displayJoystickChanges: AxisChangeCallback = (gpadIndex, gpadState, axisChangesMask) => {
Expand Down Expand Up @@ -203,7 +203,7 @@ export class GamepadDisplay {


/**
* This function is registered as the callback for {@link GamepadApiWrapper.onGamepadButtonChange}
* This function is registered as the callback for {@link GamepadApiWrapper.onGamepadButtonChange()}
* it calls the {@link DisplayGamepadConfig.buttonDisplayFunction} (if specified) or the {@link GamepadDisplay.DefaultButtonDisplayFunction} otherwise
* for every button that has changed since the last update
* @param gpadIndex The index of the gamepad that has changed
Expand Down
8 changes: 4 additions & 4 deletions src/GamepadEmulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface ButtonConfig {
type: gamepadButtonType.onOff,
/** Taps/clicks/hovers on this element will trigger events for this button on the emulated gamepad. */
tapTarget: (HTMLElement | SVGElement)
/** The index of the button this emulated button should controll in the {@link Gamepad.buttons} array */
/** The index of the button this emulated button should control in the {@link EGamepad.buttons} array */
buttonIndex: number,
/** Should this button lock the cursor once it is preseed (mouse or touch), such that NO pointer/mouse/touch events are fired with that pointer on any other elements on the page unil the finger leaves the screen or mouse lets go.
* This option also prevents this button from reacting when a press starts on another button or page element and then the pointer/touch moves over the tap target of this button while being held down. */
Expand All @@ -40,7 +40,7 @@ export interface VariableButtonConfig {
/** The element where a tap or mouse click must start to control this variable button.
* The pointer does not need to remain within this element while dragging to continue controlling the variable button as long as the mouse / touch / pointer is held down */
tapTarget: (HTMLElement | SVGElement)
/** The index of the button this emulated button should controll in the {@link Gamepad.buttons} array */
/** The index of the button this emulated button should controll in the {@link EGamepad.buttons} array */
buttonIndex: number,
/** The distance drag gesture must go in pixels to appear as a fully pressed button: value = 1 */
dragDistance: number,
Expand All @@ -63,9 +63,9 @@ export interface JoystickConfig {
tapTarget: HTMLElement | SVGElement;
/** The distance a drag gesture must go in pixels to register as a full 1 or -1 on the x or y axis (Alternatively, the distance from the touch start posisiton that the joystick can be dragged) */
dragDistance: number;
/** What emulated gamepad axis (the index in {@link Gamepad.axes}) to drive When the virtual joystick is dragged left (-) and right (+) */
/** What emulated gamepad axis (the index in {@link EGamepad.axes}) to drive When the virtual joystick is dragged left (-) and right (+) */
xAxisIndex?: number;
/** What emulated gamepad axis (the index in {@link Gamepad.axes}) to drive When the virtual joystick is dragged up (-) and down (+) */
/** What emulated gamepad axis (the index in {@link EGamepad.axes}) to drive When the virtual joystick is dragged up (-) and down (+) */
yAxisIndex?: number;
/** Should the joystick lock the cursor once a drag gesture has started, such that NO pointer/mouse/touch events are fired with that pointer on any other elements on the page unil the gesture is finished (finger leaves the screen or mouse lets go) */
lockTargetWhilePressed?: boolean;
Expand Down

0 comments on commit 74b373e

Please sign in to comment.