Refactor gamepad polling to use Strudel's signal system
- Replace requestAnimationFrame polling with signal - individual signals for buttons and axes from baseSignal - Remove window message-based gamepad state handling for toggleButtons - Add module-level state storage to keep button state across strudel code update
This commit is contained in:
parent
0be9f439bc
commit
bb56ed479f
1 changed files with 71 additions and 62 deletions
|
|
@ -76,6 +76,8 @@ class ButtonSequenceDetector {
|
||||||
? targetSequence.toLowerCase().split('')
|
? targetSequence.toLowerCase().split('')
|
||||||
: targetSequence.map((s) => s.toString().toLowerCase());
|
: targetSequence.map((s) => s.toString().toLowerCase());
|
||||||
|
|
||||||
|
//console.log(this.sequence);
|
||||||
|
|
||||||
// Get the last n inputs where n is the target sequence length
|
// Get the last n inputs where n is the target sequence length
|
||||||
const lastInputs = this.sequence.slice(-targetSequence.length).map((entry) => entry.input);
|
const lastInputs = this.sequence.slice(-targetSequence.length).map((entry) => entry.input);
|
||||||
|
|
||||||
|
|
@ -103,7 +105,6 @@ class GamepadHandler {
|
||||||
this._axes = [0, 0, 0, 0];
|
this._axes = [0, 0, 0, 0];
|
||||||
this._buttons = Array(16).fill(0);
|
this._buttons = Array(16).fill(0);
|
||||||
this.setupEventListeners();
|
this.setupEventListeners();
|
||||||
this.startPolling();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupEventListeners() {
|
setupEventListeners() {
|
||||||
|
|
@ -122,20 +123,16 @@ class GamepadHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
startPolling() {
|
poll() {
|
||||||
const poll = () => {
|
if (this._activeGamepad !== null) {
|
||||||
if (this._activeGamepad !== null) {
|
const gamepad = navigator.getGamepads()[this._activeGamepad];
|
||||||
const gamepad = navigator.getGamepads()[this._activeGamepad];
|
if (gamepad) {
|
||||||
if (gamepad) {
|
// Update axes (normalized to 0-1 range)
|
||||||
// Update axes (normalized to 0-1 range)
|
this._axes = gamepad.axes.map((axis) => (axis + 1) / 2);
|
||||||
this._axes = gamepad.axes.map((axis) => (axis + 1) / 2);
|
// Update buttons
|
||||||
// Update buttons
|
this._buttons = gamepad.buttons.map((button) => button.value);
|
||||||
this._buttons = gamepad.buttons.map((button) => button.value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
requestAnimationFrame(poll);
|
}
|
||||||
};
|
|
||||||
poll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAxes() {
|
getAxes() {
|
||||||
|
|
@ -146,25 +143,33 @@ class GamepadHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store gamepadValues globally
|
// Module-level state store for toggle states
|
||||||
export const gamepadValues = {};
|
const gamepadStates = new Map();
|
||||||
|
|
||||||
// Replace singleton with factory function
|
|
||||||
export const gamepad = (index = 0) => {
|
export const gamepad = (index = 0) => {
|
||||||
const handler = new GamepadHandler(index);
|
const handler = new GamepadHandler(index);
|
||||||
const sequenceDetector = new ButtonSequenceDetector(2000);
|
const sequenceDetector = new ButtonSequenceDetector(2000);
|
||||||
|
|
||||||
// Initialize state for this gamepad if it doesn't exist
|
// Base signal that polls gamepad state and handles sequence detection
|
||||||
if (!gamepadValues[index]) {
|
const baseSignal = signal((t) => {
|
||||||
gamepadValues[index] = Array(16).fill(0);
|
handler.poll();
|
||||||
}
|
const axes = handler.getAxes();
|
||||||
|
const buttons = handler.getButtons();
|
||||||
|
|
||||||
// Create signals for this specific gamepad instance
|
// Add all button inputs to sequence detector
|
||||||
|
buttons.forEach((value, i) => {
|
||||||
|
sequenceDetector.addInput(i, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { axes, buttons, t };
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create axes patterns
|
||||||
const axes = {
|
const axes = {
|
||||||
x1: signal(() => handler.getAxes()[0]),
|
x1: baseSignal.fmap((state) => state.axes[0]),
|
||||||
y1: signal(() => handler.getAxes()[1]),
|
y1: baseSignal.fmap((state) => state.axes[1]),
|
||||||
x2: signal(() => handler.getAxes()[2]),
|
x2: baseSignal.fmap((state) => state.axes[2]),
|
||||||
y2: signal(() => handler.getAxes()[3]),
|
y2: baseSignal.fmap((state) => state.axes[3]),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add bipolar versions
|
// Add bipolar versions
|
||||||
|
|
@ -173,39 +178,48 @@ export const gamepad = (index = 0) => {
|
||||||
axes.x2_2 = axes.x2.toBipolar();
|
axes.x2_2 = axes.x2.toBipolar();
|
||||||
axes.y2_2 = axes.y2.toBipolar();
|
axes.y2_2 = axes.y2.toBipolar();
|
||||||
|
|
||||||
// Create button signals
|
// Create button patterns
|
||||||
const buttons = Array(16)
|
const buttons = Array(16)
|
||||||
.fill(null)
|
.fill(null)
|
||||||
.map((_, i) => {
|
.map((_, i) => {
|
||||||
const btn = signal(() => {
|
// Create unique key for this gamepad+button combination
|
||||||
const value = handler.getButtons()[i];
|
const stateKey = `gamepad${index}_btn${i}`;
|
||||||
sequenceDetector.addInput(i, value);
|
|
||||||
return value;
|
// Initialize toggle state if it doesn't exist
|
||||||
});
|
if (!gamepadStates.has(stateKey)) {
|
||||||
let lastButtonState = 0;
|
gamepadStates.set(stateKey, {
|
||||||
const toggle = signal(() => {
|
lastButtonState: 0,
|
||||||
const currentState = handler.getButtons()[i];
|
toggleState: 0,
|
||||||
if (currentState === 1 && lastButtonState === 0) {
|
});
|
||||||
// Toggle the state
|
}
|
||||||
const newValue = gamepadValues[index][i] === 0 ? 1 : 0;
|
|
||||||
gamepadValues[index][i] = newValue;
|
// Direct button value pattern (no longer needs to call addInput)
|
||||||
// Broadcast the change
|
const btn = baseSignal.fmap((state) => state.buttons[i]);
|
||||||
window.postMessage({
|
|
||||||
type: 'gamepad-toggle',
|
// Button toggle pattern with persistent state
|
||||||
gamepadIndex: index,
|
const toggle = baseSignal.fmap((state) => {
|
||||||
buttonIndex: i,
|
const currentState = state.buttons[i];
|
||||||
value: newValue,
|
const buttonState = gamepadStates.get(stateKey);
|
||||||
});
|
|
||||||
|
if (currentState === 1 && buttonState.lastButtonState === 0) {
|
||||||
|
// Toggle the state on rising edge
|
||||||
|
buttonState.toggleState = buttonState.toggleState === 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
lastButtonState = currentState;
|
|
||||||
return gamepadValues[index][i];
|
buttonState.lastButtonState = currentState;
|
||||||
|
return buttonState.toggleState;
|
||||||
});
|
});
|
||||||
|
|
||||||
return { value: btn, toggle };
|
return { value: btn, toggle };
|
||||||
});
|
});
|
||||||
|
|
||||||
const checkSequence = (sequence) => {
|
// Create sequence checker pattern
|
||||||
return signal(() => sequenceDetector.checkSequence(sequence));
|
const btnSequence = (sequence) => {
|
||||||
|
return baseSignal.fmap(() => sequenceDetector.checkSequence(sequence));
|
||||||
};
|
};
|
||||||
|
const checkSequence = btnSequence;
|
||||||
|
const btnSeq = btnSequence;
|
||||||
|
const btnseq = btnSeq;
|
||||||
|
|
||||||
// Return an object with all controls
|
// Return an object with all controls
|
||||||
return {
|
return {
|
||||||
|
|
@ -220,18 +234,13 @@ export const gamepad = (index = 0) => {
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
checkSequence,
|
checkSequence,
|
||||||
|
btnSequence,
|
||||||
|
btnSeq,
|
||||||
|
btnseq,
|
||||||
|
raw: baseSignal,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Message-based state updates (add this at the bottom of the file)
|
// Optional: Export for debugging or state management
|
||||||
if (typeof window !== 'undefined') {
|
export const getGamepadStates = () => Object.fromEntries(gamepadStates);
|
||||||
window.addEventListener('message', (e) => {
|
export const clearGamepadStates = () => gamepadStates.clear();
|
||||||
if (e.data.type === 'gamepad-toggle') {
|
|
||||||
const { gamepadIndex, buttonIndex, value } = e.data;
|
|
||||||
if (!gamepadValues[gamepadIndex]) {
|
|
||||||
gamepadValues[gamepadIndex] = Array(16).fill(0);
|
|
||||||
}
|
|
||||||
gamepadValues[gamepadIndex][buttonIndex] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue