Update App.tsx to support gamepad camera look bindings
This commit is contained in:
178
src/app/App.tsx
178
src/app/App.tsx
@@ -190,8 +190,8 @@ import {
|
||||
DEFAULT_SOUND_EMITTER_MAX_DISTANCE,
|
||||
DEFAULT_TELEPORT_TARGET_YAW_DEGREES,
|
||||
PLAYER_START_COLLIDER_MODES,
|
||||
PLAYER_START_GAMEPAD_CAMERA_LOOK_BINDINGS,
|
||||
PLAYER_START_GAMEPAD_BINDINGS,
|
||||
PLAYER_START_KEYBOARD_BINDING_CODES,
|
||||
PLAYER_START_MOVEMENT_ACTIONS,
|
||||
PLAYER_START_NAVIGATION_MODES,
|
||||
DEFAULT_SPOT_LIGHT_ANGLE_DEGREES,
|
||||
@@ -218,6 +218,7 @@ import {
|
||||
normalizeEntityName,
|
||||
normalizeYawDegrees,
|
||||
normalizeInteractablePrompt,
|
||||
type PlayerStartGamepadCameraLookBinding,
|
||||
type PlayerStartColliderMode,
|
||||
type PlayerStartGamepadBinding,
|
||||
type PlayerStartInputBindings,
|
||||
@@ -360,15 +361,15 @@ function getPlayerStartMovementActionLabel(
|
||||
function formatPlayerStartKeyboardBindingLabel(
|
||||
code: PlayerStartKeyboardBindingCode
|
||||
): string {
|
||||
if (/^Key[A-Z]$/.test(code)) {
|
||||
return code.slice(3);
|
||||
}
|
||||
|
||||
if (/^Digit[0-9]$/.test(code)) {
|
||||
return code.slice(5);
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case "KeyW":
|
||||
return "W";
|
||||
case "KeyA":
|
||||
return "A";
|
||||
case "KeyS":
|
||||
return "S";
|
||||
case "KeyD":
|
||||
return "D";
|
||||
case "ArrowUp":
|
||||
return "Arrow Up";
|
||||
case "ArrowLeft":
|
||||
@@ -377,14 +378,18 @@ function formatPlayerStartKeyboardBindingLabel(
|
||||
return "Arrow Down";
|
||||
case "ArrowRight":
|
||||
return "Arrow Right";
|
||||
case "KeyI":
|
||||
return "I";
|
||||
case "KeyJ":
|
||||
return "J";
|
||||
case "KeyK":
|
||||
return "K";
|
||||
case "KeyL":
|
||||
return "L";
|
||||
case "Space":
|
||||
return "Space";
|
||||
case "Tab":
|
||||
return "Tab";
|
||||
case "Enter":
|
||||
return "Enter";
|
||||
case "Backspace":
|
||||
return "Backspace";
|
||||
case "Escape":
|
||||
return "Escape";
|
||||
default:
|
||||
return code.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,6 +416,15 @@ function formatPlayerStartGamepadBindingLabel(
|
||||
}
|
||||
}
|
||||
|
||||
function formatPlayerStartGamepadCameraLookBindingLabel(
|
||||
binding: PlayerStartGamepadCameraLookBinding
|
||||
): string {
|
||||
switch (binding) {
|
||||
case "rightStick":
|
||||
return "Right Stick";
|
||||
}
|
||||
}
|
||||
|
||||
const STARTER_MATERIAL_ORDER = new Map(
|
||||
STARTER_MATERIAL_LIBRARY.map((material, index) => [material.id, index])
|
||||
);
|
||||
@@ -1446,6 +1460,8 @@ export function App({ store, initialStatusMessage }: AppProps) {
|
||||
);
|
||||
const [playerStartInputBindingsDraft, setPlayerStartInputBindingsDraft] =
|
||||
useState<PlayerStartInputBindings>(createPlayerStartInputBindings());
|
||||
const [playerStartKeyboardCaptureAction, setPlayerStartKeyboardCaptureAction] =
|
||||
useState<PlayerStartMovementAction | null>(null);
|
||||
const [soundEmitterAudioAssetIdDraft, setSoundEmitterAudioAssetIdDraft] =
|
||||
useState(DEFAULT_SOUND_EMITTER_AUDIO_ASSET_ID ?? "");
|
||||
const [soundEmitterVolumeDraft, setSoundEmitterVolumeDraft] = useState(
|
||||
@@ -1739,6 +1755,52 @@ export function App({ store, initialStatusMessage }: AppProps) {
|
||||
whiteboxSnapStep
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setPlayerStartKeyboardCaptureAction(null);
|
||||
}, [selectedPlayerStart?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (playerStartKeyboardCaptureAction === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleWindowKeyCapture = (event: globalThis.KeyboardEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (event.repeat) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.code === "Escape") {
|
||||
setPlayerStartKeyboardCaptureAction(null);
|
||||
setStatusMessage("Cancelled Player Start key capture.");
|
||||
return;
|
||||
}
|
||||
|
||||
const capturedCode = event.code.trim();
|
||||
|
||||
if (capturedCode.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
handlePlayerStartKeyboardBindingChange(
|
||||
playerStartKeyboardCaptureAction,
|
||||
capturedCode
|
||||
);
|
||||
setPlayerStartKeyboardCaptureAction(null);
|
||||
setStatusMessage(
|
||||
`Bound ${getPlayerStartMovementActionLabel(playerStartKeyboardCaptureAction)} to ${formatPlayerStartKeyboardBindingLabel(capturedCode)}.`
|
||||
);
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleWindowKeyCapture, true);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleWindowKeyCapture, true);
|
||||
};
|
||||
}, [playerStartKeyboardCaptureAction]);
|
||||
|
||||
useEffect(() => {
|
||||
setSceneNameDraft(editorState.document.name);
|
||||
}, [editorState.document.name]);
|
||||
@@ -4017,6 +4079,25 @@ export function App({ store, initialStatusMessage }: AppProps) {
|
||||
);
|
||||
};
|
||||
|
||||
const handlePlayerStartGamepadCameraLookBindingChange = (
|
||||
nextBinding: PlayerStartGamepadCameraLookBinding
|
||||
) => {
|
||||
const nextBindings = createPlayerStartInputBindings({
|
||||
keyboard: playerStartInputBindingsDraft.keyboard,
|
||||
gamepad: {
|
||||
...playerStartInputBindingsDraft.gamepad,
|
||||
cameraLook: nextBinding
|
||||
}
|
||||
});
|
||||
|
||||
setPlayerStartInputBindingsDraft(nextBindings);
|
||||
scheduleDraftCommit(() =>
|
||||
applyPlayerStartChange({
|
||||
inputBindings: nextBindings
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const applySceneEntryChange = () => {
|
||||
if (selectedSceneEntry === null) {
|
||||
setStatusMessage("Select a Scene Entry before editing it.");
|
||||
@@ -10108,24 +10189,25 @@ export function App({ store, initialStatusMessage }: AppProps) {
|
||||
<span className="label">
|
||||
{getPlayerStartMovementActionLabel(action)} Key
|
||||
</span>
|
||||
<select
|
||||
<button
|
||||
type="button"
|
||||
data-testid={`player-start-keyboard-binding-${action}`}
|
||||
className="select-input"
|
||||
value={playerStartInputBindingsDraft.keyboard[action]}
|
||||
onChange={(event) =>
|
||||
handlePlayerStartKeyboardBindingChange(
|
||||
action,
|
||||
event.currentTarget
|
||||
.value as PlayerStartKeyboardBindingCode
|
||||
)
|
||||
}
|
||||
className="toolbar__button"
|
||||
onClick={() => {
|
||||
setPlayerStartKeyboardCaptureAction(action);
|
||||
setStatusMessage(
|
||||
`Press any key for ${getPlayerStartMovementActionLabel(action)}. Press Escape to cancel.`
|
||||
);
|
||||
}}
|
||||
>
|
||||
{PLAYER_START_KEYBOARD_BINDING_CODES.map((code) => (
|
||||
<option key={code} value={code}>
|
||||
{formatPlayerStartKeyboardBindingLabel(code)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{playerStartKeyboardCaptureAction === action
|
||||
? "Press Any Key..."
|
||||
: formatPlayerStartKeyboardBindingLabel(
|
||||
playerStartInputBindingsDraft.keyboard[
|
||||
action
|
||||
]
|
||||
)}
|
||||
</button>
|
||||
</label>
|
||||
<label className="form-field">
|
||||
<span className="label">
|
||||
@@ -10153,9 +10235,35 @@ export function App({ store, initialStatusMessage }: AppProps) {
|
||||
</div>
|
||||
))}
|
||||
|
||||
<label className="form-field">
|
||||
<span className="label">Camera Pad</span>
|
||||
<select
|
||||
data-testid="player-start-gamepad-camera-look-binding"
|
||||
className="select-input"
|
||||
value={playerStartInputBindingsDraft.gamepad.cameraLook}
|
||||
onChange={(event) =>
|
||||
handlePlayerStartGamepadCameraLookBindingChange(
|
||||
event.currentTarget
|
||||
.value as PlayerStartGamepadCameraLookBinding
|
||||
)
|
||||
}
|
||||
>
|
||||
{PLAYER_START_GAMEPAD_CAMERA_LOOK_BINDINGS.map(
|
||||
(binding) => (
|
||||
<option key={binding} value={binding}>
|
||||
{formatPlayerStartGamepadCameraLookBindingLabel(
|
||||
binding
|
||||
)}
|
||||
</option>
|
||||
)
|
||||
)}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<div className="material-summary">
|
||||
These bindings feed the same typed movement actions in
|
||||
First Person and Third Person.
|
||||
These bindings feed the same typed movement and camera
|
||||
actions in First Person and Third Person. Mouse look
|
||||
stays available as before.
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user