feat: 加了 4 个可调项:Forward、Strafe、Turn、Turbo
This commit is contained in:
@@ -13,6 +13,8 @@ const {
|
|||||||
activeSource,
|
activeSource,
|
||||||
activeSourceLabel,
|
activeSourceLabel,
|
||||||
commandLabel,
|
commandLabel,
|
||||||
|
controlLimits,
|
||||||
|
controlTuning,
|
||||||
commandValues,
|
commandValues,
|
||||||
gamepadActive,
|
gamepadActive,
|
||||||
gamepadButtons,
|
gamepadButtons,
|
||||||
@@ -48,17 +50,17 @@ const commandBars = computed(() => [
|
|||||||
{
|
{
|
||||||
label: 'Forward',
|
label: 'Forward',
|
||||||
value: commandValues.value.lx,
|
value: commandValues.value.lx,
|
||||||
max: 1.2,
|
max: controlLimits.value.forward,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Strafe',
|
label: 'Strafe',
|
||||||
value: commandValues.value.ly,
|
value: commandValues.value.ly,
|
||||||
max: 0.4,
|
max: controlLimits.value.strafe,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Turn',
|
label: 'Turn',
|
||||||
value: commandValues.value.az,
|
value: commandValues.value.az,
|
||||||
max: 0.8,
|
max: controlLimits.value.turn,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -101,6 +103,11 @@ function stickOffset(value: number) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p class="summary">
|
||||||
|
Tuning: fwd {{ controlTuning.forward.toFixed(2) }} m/s, strafe {{ controlTuning.strafe.toFixed(2) }} m/s,
|
||||||
|
turn {{ controlTuning.turn.toFixed(2) }} rad/s, turbo x{{ controlTuning.turbo.toFixed(2) }}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div class="feedback-grid" :class="{ compact }">
|
<div class="feedback-grid" :class="{ compact }">
|
||||||
<section class="feedback-card">
|
<section class="feedback-card">
|
||||||
<div class="card-head">
|
<div class="card-head">
|
||||||
@@ -183,7 +190,7 @@ function stickOffset(value: number) {
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="!compact" class="summary">
|
<p v-if="!compact" class="summary accent">
|
||||||
Outgoing command: {{ commandLabel }}
|
Outgoing command: {{ commandLabel }}
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
@@ -350,6 +357,10 @@ function stickOffset(value: number) {
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.summary.accent {
|
||||||
|
color: #aeb9d2;
|
||||||
|
}
|
||||||
|
|
||||||
.mode-chip {
|
.mode-chip {
|
||||||
background: rgba(133, 147, 169, 0.14);
|
background: rgba(133, 147, 169, 0.14);
|
||||||
color: #cad3e8;
|
color: #cad3e8;
|
||||||
|
|||||||
@@ -1,5 +1,30 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import ControlFeedback from '@/components/ControlFeedback.vue'
|
import ControlFeedback from '@/components/ControlFeedback.vue'
|
||||||
|
import { useControlInterface } from '@/composables/useControlInterface'
|
||||||
|
|
||||||
|
const { controlTuning, resetControlTuning, setControlTuning } = useControlInterface()
|
||||||
|
|
||||||
|
const forwardSpeed = computed({
|
||||||
|
get: () => controlTuning.value.forward,
|
||||||
|
set: (value: number) => setControlTuning({ forward: value }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const strafeSpeed = computed({
|
||||||
|
get: () => controlTuning.value.strafe,
|
||||||
|
set: (value: number) => setControlTuning({ strafe: value }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const turnSpeed = computed({
|
||||||
|
get: () => controlTuning.value.turn,
|
||||||
|
set: (value: number) => setControlTuning({ turn: value }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const turboMultiplier = computed({
|
||||||
|
get: () => controlTuning.value.turbo,
|
||||||
|
set: (value: number) => setControlTuning({ turbo: value }),
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -9,6 +34,35 @@ import ControlFeedback from '@/components/ControlFeedback.vue'
|
|||||||
<p class="eyebrow">Control</p>
|
<p class="eyebrow">Control</p>
|
||||||
<h2>Control Feedback</h2>
|
<h2>Control Feedback</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<button type="button" class="reset-button" @click="resetControlTuning">
|
||||||
|
Reset Defaults
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tuning-grid">
|
||||||
|
<label class="tuning-field">
|
||||||
|
<span>Forward</span>
|
||||||
|
<input v-model.number="forwardSpeed" type="number" min="0.05" max="3" step="0.05" />
|
||||||
|
<small>m/s</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="tuning-field">
|
||||||
|
<span>Strafe</span>
|
||||||
|
<input v-model.number="strafeSpeed" type="number" min="0.05" max="3" step="0.05" />
|
||||||
|
<small>m/s</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="tuning-field">
|
||||||
|
<span>Turn</span>
|
||||||
|
<input v-model.number="turnSpeed" type="number" min="0.05" max="3" step="0.05" />
|
||||||
|
<small>rad/s</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="tuning-field">
|
||||||
|
<span>Turbo</span>
|
||||||
|
<input v-model.number="turboMultiplier" type="number" min="1" max="3" step="0.1" />
|
||||||
|
<small>x</small>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ControlFeedback />
|
<ControlFeedback />
|
||||||
@@ -17,6 +71,9 @@ import ControlFeedback from '@/components/ControlFeedback.vue'
|
|||||||
Keyboard mapping: <code>W/S</code> forward-back, <code>A/D</code> strafe, <code>Q/E</code> turn,
|
Keyboard mapping: <code>W/S</code> forward-back, <code>A/D</code> strafe, <code>Q/E</code> turn,
|
||||||
<code>Shift</code> turbo, <code>Space</code> stop.
|
<code>Shift</code> turbo, <code>Space</code> stop.
|
||||||
</p>
|
</p>
|
||||||
|
<p class="hint subtle">
|
||||||
|
The values above update keyboard and browser gamepad output immediately and are saved in this browser.
|
||||||
|
</p>
|
||||||
<p class="hint subtle">
|
<p class="hint subtle">
|
||||||
Browser gamepad support is live here too: left stick drives, right stick turns,
|
Browser gamepad support is live here too: left stick drives, right stick turns,
|
||||||
<code>RB</code> boosts, <code>A</code> sends stop.
|
<code>RB</code> boosts, <code>A</code> sends stop.
|
||||||
@@ -37,6 +94,25 @@ import ControlFeedback from '@/components/ControlFeedback.vue'
|
|||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reset-button {
|
||||||
|
border: 1px solid rgba(133, 147, 169, 0.28);
|
||||||
|
background: rgba(10, 20, 37, 0.88);
|
||||||
|
color: #dfe7fb;
|
||||||
|
border-radius: 999px;
|
||||||
|
min-height: 36px;
|
||||||
|
padding: 0 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-button:hover {
|
||||||
|
border-color: rgba(123, 196, 255, 0.48);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
.eyebrow {
|
.eyebrow {
|
||||||
margin: 0 0 4px;
|
margin: 0 0 4px;
|
||||||
color: #ffb057;
|
color: #ffb057;
|
||||||
@@ -51,6 +127,47 @@ h2 {
|
|||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tuning-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tuning-field {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: rgba(7, 14, 26, 0.86);
|
||||||
|
border: 1px solid rgba(133, 147, 169, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tuning-field span,
|
||||||
|
.tuning-field small {
|
||||||
|
color: #aeb9d2;
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tuning-field input {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 42px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid rgba(133, 147, 169, 0.24);
|
||||||
|
background: rgba(10, 20, 37, 0.96);
|
||||||
|
color: #f6f8fc;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tuning-field input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: rgba(123, 196, 255, 0.62);
|
||||||
|
box-shadow: 0 0 0 3px rgba(91, 122, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
.hint {
|
.hint {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #d5dbee;
|
color: #d5dbee;
|
||||||
@@ -60,4 +177,21 @@ h2 {
|
|||||||
.hint.subtle {
|
.hint.subtle {
|
||||||
color: #96a5c3;
|
color: #96a5c3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.panel-head {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tuning-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.tuning-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ type ButtonFeedback = {
|
|||||||
pressed: boolean
|
pressed: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ControlTuning = {
|
||||||
|
forward: number
|
||||||
|
strafe: number
|
||||||
|
turn: number
|
||||||
|
turbo: number
|
||||||
|
}
|
||||||
|
|
||||||
const TRACKED_KEYS = ['KeyW', 'KeyS', 'KeyA', 'KeyD', 'KeyQ', 'KeyE', 'ShiftLeft', 'ShiftRight', 'Space']
|
const TRACKED_KEYS = ['KeyW', 'KeyS', 'KeyA', 'KeyD', 'KeyQ', 'KeyE', 'ShiftLeft', 'ShiftRight', 'Space']
|
||||||
const KEY_LABELS: Record<string, string> = {
|
const KEY_LABELS: Record<string, string> = {
|
||||||
KeyW: 'W',
|
KeyW: 'W',
|
||||||
@@ -33,11 +40,18 @@ const GAMEPAD_BUTTON_LABELS = ['A', 'B', 'X', 'Y', 'LB', 'RB', 'LT', 'RT', 'Back
|
|||||||
|
|
||||||
const ZERO_COMMAND: CommandTuple = [0, 0, 0, 0, 0, 0]
|
const ZERO_COMMAND: CommandTuple = [0, 0, 0, 0, 0, 0]
|
||||||
const GAMEPAD_DEADZONE = 0.14
|
const GAMEPAD_DEADZONE = 0.14
|
||||||
const TURBO_MULTIPLIER = 1.5
|
|
||||||
const COMMAND_SEND_INTERVAL_MS = 50
|
const COMMAND_SEND_INTERVAL_MS = 50
|
||||||
const KEYBOARD_FORWARD_SPEED = 0.8
|
const DEFAULT_CONTROL_TUNING: ControlTuning = {
|
||||||
const KEYBOARD_STRAFE_SPEED = 0.15
|
forward: 0.8,
|
||||||
const KEYBOARD_TURN_SPEED = 0.4
|
strafe: 0.15,
|
||||||
|
turn: 0.4,
|
||||||
|
turbo: 1.5,
|
||||||
|
}
|
||||||
|
const CONTROL_TUNING_STORAGE_KEY = 'robot-command-center.control-tuning'
|
||||||
|
const MIN_AXIS_SPEED = 0.05
|
||||||
|
const MAX_AXIS_SPEED = 3
|
||||||
|
const MIN_TURBO_MULTIPLIER = 1
|
||||||
|
const MAX_TURBO_MULTIPLIER = 3
|
||||||
|
|
||||||
const pressedKeys = ref<Set<string>>(new Set())
|
const pressedKeys = ref<Set<string>>(new Set())
|
||||||
const socketState = ref<SocketState>('connecting')
|
const socketState = ref<SocketState>('connecting')
|
||||||
@@ -51,6 +65,65 @@ const gamepadAxes = ref<number[]>([0, 0, 0, 0])
|
|||||||
const gamepadButtonPressed = ref<boolean[]>(Array.from({ length: GAMEPAD_BUTTON_LABELS.length }, () => false))
|
const gamepadButtonPressed = ref<boolean[]>(Array.from({ length: GAMEPAD_BUTTON_LABELS.length }, () => false))
|
||||||
const activeSource = ref<ControlSource>('idle')
|
const activeSource = ref<ControlSource>('idle')
|
||||||
|
|
||||||
|
function clampValue(value: number, min: number, max: number) {
|
||||||
|
return Math.min(max, Math.max(min, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeAxisSpeed(value: unknown, fallback: number) {
|
||||||
|
const numericValue = typeof value === 'number' ? value : Number(value)
|
||||||
|
if (!Number.isFinite(numericValue)) {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return roundValue(clampValue(numericValue, MIN_AXIS_SPEED, MAX_AXIS_SPEED))
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeTurboMultiplier(value: unknown, fallback: number) {
|
||||||
|
const numericValue = typeof value === 'number' ? value : Number(value)
|
||||||
|
if (!Number.isFinite(numericValue)) {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return roundValue(clampValue(numericValue, MIN_TURBO_MULTIPLIER, MAX_TURBO_MULTIPLIER))
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeControlTuning(raw?: Partial<ControlTuning>): ControlTuning {
|
||||||
|
return {
|
||||||
|
forward: sanitizeAxisSpeed(raw?.forward, DEFAULT_CONTROL_TUNING.forward),
|
||||||
|
strafe: sanitizeAxisSpeed(raw?.strafe, DEFAULT_CONTROL_TUNING.strafe),
|
||||||
|
turn: sanitizeAxisSpeed(raw?.turn, DEFAULT_CONTROL_TUNING.turn),
|
||||||
|
turbo: sanitizeTurboMultiplier(raw?.turbo, DEFAULT_CONTROL_TUNING.turbo),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPersistedControlTuning() {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return DEFAULT_CONTROL_TUNING
|
||||||
|
}
|
||||||
|
|
||||||
|
let raw: string | null = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
raw = window.localStorage.getItem(CONTROL_TUNING_STORAGE_KEY)
|
||||||
|
} catch {
|
||||||
|
return DEFAULT_CONTROL_TUNING
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw == null) {
|
||||||
|
return DEFAULT_CONTROL_TUNING
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return normalizeControlTuning(JSON.parse(raw) as Partial<ControlTuning>)
|
||||||
|
} catch {
|
||||||
|
return DEFAULT_CONTROL_TUNING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialControlTuning = loadPersistedControlTuning()
|
||||||
|
const forwardSpeed = ref(initialControlTuning.forward)
|
||||||
|
const strafeSpeed = ref(initialControlTuning.strafe)
|
||||||
|
const turnSpeed = ref(initialControlTuning.turn)
|
||||||
|
const turboMultiplier = ref(initialControlTuning.turbo)
|
||||||
|
|
||||||
let socket: WebSocket | null = null
|
let socket: WebSocket | null = null
|
||||||
let sendTimer: number | null = null
|
let sendTimer: number | null = null
|
||||||
let reconnectTimer: number | null = null
|
let reconnectTimer: number | null = null
|
||||||
@@ -72,6 +145,54 @@ function roundValue(value: number) {
|
|||||||
return Math.round(value * 1000) / 1000
|
return Math.round(value * 1000) / 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function persistControlTuning() {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
window.localStorage.setItem(
|
||||||
|
CONTROL_TUNING_STORAGE_KEY,
|
||||||
|
JSON.stringify({
|
||||||
|
forward: forwardSpeed.value,
|
||||||
|
strafe: strafeSpeed.value,
|
||||||
|
turn: turnSpeed.value,
|
||||||
|
turbo: turboMultiplier.value,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
// Ignore storage failures so tuning still works for the current session.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setControlTuning(next: Partial<ControlTuning>) {
|
||||||
|
const resolved = normalizeControlTuning({
|
||||||
|
forward: next.forward ?? forwardSpeed.value,
|
||||||
|
strafe: next.strafe ?? strafeSpeed.value,
|
||||||
|
turn: next.turn ?? turnSpeed.value,
|
||||||
|
turbo: next.turbo ?? turboMultiplier.value,
|
||||||
|
})
|
||||||
|
const changed =
|
||||||
|
resolved.forward !== forwardSpeed.value ||
|
||||||
|
resolved.strafe !== strafeSpeed.value ||
|
||||||
|
resolved.turn !== turnSpeed.value ||
|
||||||
|
resolved.turbo !== turboMultiplier.value
|
||||||
|
|
||||||
|
forwardSpeed.value = resolved.forward
|
||||||
|
strafeSpeed.value = resolved.strafe
|
||||||
|
turnSpeed.value = resolved.turn
|
||||||
|
turboMultiplier.value = resolved.turbo
|
||||||
|
persistControlTuning()
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
refreshSendLoop(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetControlTuning() {
|
||||||
|
setControlTuning(DEFAULT_CONTROL_TUNING)
|
||||||
|
}
|
||||||
|
|
||||||
function packCommand(values: CommandTuple) {
|
function packCommand(values: CommandTuple) {
|
||||||
const buffer = new ArrayBuffer(24)
|
const buffer = new ArrayBuffer(24)
|
||||||
const view = new DataView(buffer)
|
const view = new DataView(buffer)
|
||||||
@@ -95,18 +216,18 @@ function activeTurnAxis() {
|
|||||||
|
|
||||||
function keyboardCommandValues(): CommandTuple {
|
function keyboardCommandValues(): CommandTuple {
|
||||||
const keys = pressedKeys.value
|
const keys = pressedKeys.value
|
||||||
const turbo = keys.has('ShiftLeft') || keys.has('ShiftRight') ? TURBO_MULTIPLIER : 1
|
const turbo = keys.has('ShiftLeft') || keys.has('ShiftRight') ? turboMultiplier.value : 1
|
||||||
|
|
||||||
let lx = 0
|
let lx = 0
|
||||||
let ly = 0
|
let ly = 0
|
||||||
let az = 0
|
let az = 0
|
||||||
|
|
||||||
if (keys.has('KeyW')) lx += KEYBOARD_FORWARD_SPEED
|
if (keys.has('KeyW')) lx += forwardSpeed.value
|
||||||
if (keys.has('KeyS')) lx -= KEYBOARD_FORWARD_SPEED
|
if (keys.has('KeyS')) lx -= forwardSpeed.value
|
||||||
if (keys.has('KeyA')) ly += KEYBOARD_STRAFE_SPEED
|
if (keys.has('KeyA')) ly += strafeSpeed.value
|
||||||
if (keys.has('KeyD')) ly -= KEYBOARD_STRAFE_SPEED
|
if (keys.has('KeyD')) ly -= strafeSpeed.value
|
||||||
if (keys.has('KeyQ')) az += KEYBOARD_TURN_SPEED
|
if (keys.has('KeyQ')) az += turnSpeed.value
|
||||||
if (keys.has('KeyE')) az -= KEYBOARD_TURN_SPEED
|
if (keys.has('KeyE')) az -= turnSpeed.value
|
||||||
|
|
||||||
if (keys.has('Space')) {
|
if (keys.has('Space')) {
|
||||||
return ZERO_COMMAND
|
return ZERO_COMMAND
|
||||||
@@ -128,15 +249,15 @@ function gamepadCommandValues(): CommandTuple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const buttons = gamepadButtonPressed.value
|
const buttons = gamepadButtonPressed.value
|
||||||
const turbo = buttons[5] ? TURBO_MULTIPLIER : 1
|
const turbo = buttons[5] ? turboMultiplier.value : 1
|
||||||
|
|
||||||
if (buttons[0]) {
|
if (buttons[0]) {
|
||||||
return ZERO_COMMAND
|
return ZERO_COMMAND
|
||||||
}
|
}
|
||||||
|
|
||||||
const lx = roundValue(-normalizeAxis(gamepadAxes.value[1] ?? 0) * KEYBOARD_FORWARD_SPEED * turbo)
|
const lx = roundValue(-normalizeAxis(gamepadAxes.value[1] ?? 0) * forwardSpeed.value * turbo)
|
||||||
const ly = roundValue(-normalizeAxis(gamepadAxes.value[0] ?? 0) * KEYBOARD_STRAFE_SPEED * turbo)
|
const ly = roundValue(-normalizeAxis(gamepadAxes.value[0] ?? 0) * strafeSpeed.value * turbo)
|
||||||
const az = roundValue(-activeTurnAxis() * KEYBOARD_TURN_SPEED * turbo)
|
const az = roundValue(-activeTurnAxis() * turnSpeed.value * turbo)
|
||||||
|
|
||||||
return [lx, ly, 0, 0, 0, az]
|
return [lx, ly, 0, 0, 0, az]
|
||||||
}
|
}
|
||||||
@@ -419,12 +540,13 @@ const commandLabel = computed(() => {
|
|||||||
|
|
||||||
const commandMagnitude = computed(() => {
|
const commandMagnitude = computed(() => {
|
||||||
const { lx, ly, az } = commandValues.value
|
const { lx, ly, az } = commandValues.value
|
||||||
|
const limits = controlLimits.value
|
||||||
return Math.min(
|
return Math.min(
|
||||||
1,
|
1,
|
||||||
Math.max(
|
Math.max(
|
||||||
Math.abs(lx) / KEYBOARD_FORWARD_SPEED,
|
Math.abs(lx) / Math.max(limits.forward, MIN_AXIS_SPEED),
|
||||||
Math.abs(ly) / KEYBOARD_STRAFE_SPEED,
|
Math.abs(ly) / Math.max(limits.strafe, MIN_AXIS_SPEED),
|
||||||
Math.abs(az) / KEYBOARD_TURN_SPEED,
|
Math.abs(az) / Math.max(limits.turn, MIN_AXIS_SPEED),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -440,6 +562,17 @@ const keyboardKeys = computed<KeyFeedback[]>(() =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
const keyboardTurbo = computed(() => pressedKeys.value.has('ShiftLeft') || pressedKeys.value.has('ShiftRight'))
|
const keyboardTurbo = computed(() => pressedKeys.value.has('ShiftLeft') || pressedKeys.value.has('ShiftRight'))
|
||||||
|
const controlTuning = computed<ControlTuning>(() => ({
|
||||||
|
forward: forwardSpeed.value,
|
||||||
|
strafe: strafeSpeed.value,
|
||||||
|
turn: turnSpeed.value,
|
||||||
|
turbo: turboMultiplier.value,
|
||||||
|
}))
|
||||||
|
const controlLimits = computed(() => ({
|
||||||
|
forward: roundValue(forwardSpeed.value * turboMultiplier.value),
|
||||||
|
strafe: roundValue(strafeSpeed.value * turboMultiplier.value),
|
||||||
|
turn: roundValue(turnSpeed.value * turboMultiplier.value),
|
||||||
|
}))
|
||||||
|
|
||||||
const gamepadButtons = computed<ButtonFeedback[]>(() =>
|
const gamepadButtons = computed<ButtonFeedback[]>(() =>
|
||||||
GAMEPAD_BUTTON_LABELS.map((label, index) => ({
|
GAMEPAD_BUTTON_LABELS.map((label, index) => ({
|
||||||
@@ -476,6 +609,10 @@ export function useControlInterface() {
|
|||||||
commandValues,
|
commandValues,
|
||||||
commandLabel,
|
commandLabel,
|
||||||
commandMagnitude,
|
commandMagnitude,
|
||||||
|
controlTuning,
|
||||||
|
controlLimits,
|
||||||
|
setControlTuning,
|
||||||
|
resetControlTuning,
|
||||||
pressedKeysLabel,
|
pressedKeysLabel,
|
||||||
keyboardKeys,
|
keyboardKeys,
|
||||||
keyboardTurbo,
|
keyboardTurbo,
|
||||||
|
|||||||
Reference in New Issue
Block a user