mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: pitch and yaw limits for inputs
This commit is contained in:
1 parent
21122c620b
commit
14fb62bda4
4 files changed
+113
-150
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { ThreeEvent, useThree } from '@react-three/fiber';
|
||||
import { clamp } from 'lodash';
|
||||
import { RefObject, forwardRef, useImperativeHandle, useRef } from 'react';
|
||||
import { Group, Mesh, Object3D, Vector3 } from 'three';
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import {
|
||||
InitialTurretRotation,
|
||||
PitchLimits,
|
||||
@@ -78,77 +78,11 @@ export const GunContainer = forwardRef<Group, GunContainerProps>(
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
if (yawLimits) {
|
||||
draftYaw = clamp(
|
||||
draftYaw + event.movementX * (Math.PI / canvas.width),
|
||||
-yawLimits.max * (Math.PI / 180),
|
||||
-yawLimits.min * (Math.PI / 180),
|
||||
);
|
||||
} else {
|
||||
draftYaw += event.movementX * ((2 * Math.PI) / canvas.width);
|
||||
}
|
||||
|
||||
let lowerPitch = -pitchLimits.max * (Math.PI / 180);
|
||||
let upperPitch = -pitchLimits.min * (Math.PI / 180);
|
||||
const transition = (pitchLimits.transition ?? 10) * (Math.PI / 180);
|
||||
|
||||
if (pitchLimits.back) {
|
||||
const range = pitchLimits.back.range * (Math.PI / 180);
|
||||
const yawRotatedAbs = Math.abs(
|
||||
normalizeAnglePI(draftYaw - Math.PI),
|
||||
);
|
||||
|
||||
if (yawRotatedAbs <= range + transition) {
|
||||
if (yawRotatedAbs <= range) {
|
||||
// inside range
|
||||
lowerPitch = -pitchLimits.back.max * (Math.PI / 180);
|
||||
upperPitch = -pitchLimits.back.min * (Math.PI / 180);
|
||||
} else {
|
||||
// inside transition
|
||||
const transitionProgress =
|
||||
(yawRotatedAbs - range) / transition;
|
||||
const maxInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.back.max +
|
||||
transitionProgress * pitchLimits.max;
|
||||
const minInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.back.min +
|
||||
transitionProgress * pitchLimits.min;
|
||||
|
||||
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
|
||||
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pitchLimits.front) {
|
||||
const range = pitchLimits.front.range * (Math.PI / 180);
|
||||
const yawAbs = Math.abs(normalizeAnglePI(draftYaw));
|
||||
|
||||
if (yawAbs <= range + transition) {
|
||||
if (yawAbs <= range) {
|
||||
// inside range
|
||||
lowerPitch = -pitchLimits.front.max * (Math.PI / 180);
|
||||
upperPitch = -pitchLimits.front.min * (Math.PI / 180);
|
||||
} else {
|
||||
// inside transition
|
||||
const transitionProgress = (yawAbs - range) / transition;
|
||||
const maxInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.front.max +
|
||||
transitionProgress * pitchLimits.max;
|
||||
const minInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.front.min +
|
||||
transitionProgress * pitchLimits.min;
|
||||
|
||||
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
|
||||
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draftPitch = clamp(
|
||||
[draftPitch, draftYaw] = applyPitchYawLimits(
|
||||
draftPitch - event.movementY * (Math.PI / canvas.height),
|
||||
lowerPitch,
|
||||
upperPitch,
|
||||
draftYaw + event.movementX * (Math.PI / canvas.width),
|
||||
pitchLimits,
|
||||
yawLimits,
|
||||
);
|
||||
|
||||
if (gunContainer.current) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Flex, TextField } from '@radix-ui/themes';
|
||||
import { clamp } from 'lodash';
|
||||
import { use, useEffect, useRef } from 'react';
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
|
||||
import { normalizeAnglePI } from '../../../../../core/math/normalizeAngle180';
|
||||
import mutateTankopedia, {
|
||||
@@ -94,13 +94,19 @@ export function RotationInputs({
|
||||
<TextField.Input
|
||||
defaultValue={Math.round(model.turretYaw * (180 / Math.PI))}
|
||||
onBlur={() => {
|
||||
const normalized = normalizeAnglePI(
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
model.gunPitch,
|
||||
-Number(turretYawInput.current?.value) * (Math.PI / 180),
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
);
|
||||
mutateTankopedia((state) => {
|
||||
state.model.turretYaw = normalized;
|
||||
state.model.gunPitch = pitch;
|
||||
state.model.turretYaw = yaw;
|
||||
});
|
||||
turretYawInput.current!.value = `${Math.round(normalized)}`;
|
||||
turretYawInput.current!.value = `${Math.round(
|
||||
yaw * (180 / Math.PI),
|
||||
)}`;
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
@@ -124,15 +130,19 @@ export function RotationInputs({
|
||||
<TextField.Input
|
||||
defaultValue={-Math.round(model.gunPitch * (180 / Math.PI))}
|
||||
onBlur={() => {
|
||||
const clamped = clamp(
|
||||
Number(gunPitchInput.current?.value),
|
||||
gunModelDefinition.pitch.min,
|
||||
gunModelDefinition.pitch.max,
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
-Number(gunPitchInput.current?.value) * (Math.PI / 180),
|
||||
model.turretYaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
);
|
||||
mutateTankopedia((state) => {
|
||||
state.model.gunPitch = -clamped * (Math.PI / 180);
|
||||
state.model.gunPitch = pitch;
|
||||
state.model.turretYaw = yaw;
|
||||
});
|
||||
gunPitchInput.current!.value = `${Math.round(clamped)}`;
|
||||
gunPitchInput.current!.value = `${Math.round(
|
||||
pitch * (180 / Math.PI),
|
||||
)}`;
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ThreeEvent, useThree } from '@react-three/fiber';
|
||||
import { clamp } from 'lodash';
|
||||
import {
|
||||
ReactNode,
|
||||
RefObject,
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { Euler, Group, Mesh, Object3D, Vector3 } from 'three';
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import {
|
||||
InitialTurretRotation,
|
||||
PitchLimits,
|
||||
@@ -99,74 +99,12 @@ export const TurretContainer = forwardRef(
|
||||
}
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
if (yawLimits) {
|
||||
draftYaw = clamp(
|
||||
draftYaw + event.movementX * (Math.PI / canvas.width),
|
||||
-yawLimits.max * (Math.PI / 180),
|
||||
-yawLimits.min * (Math.PI / 180),
|
||||
);
|
||||
} else {
|
||||
draftYaw += event.movementX * ((2 * Math.PI) / canvas.width);
|
||||
}
|
||||
|
||||
let lowerPitch = -pitchLimits.max * (Math.PI / 180);
|
||||
let upperPitch = -pitchLimits.min * (Math.PI / 180);
|
||||
const transition = (pitchLimits.transition ?? 10) * (Math.PI / 180);
|
||||
|
||||
if (pitchLimits.back) {
|
||||
const range = pitchLimits.back.range * (Math.PI / 180);
|
||||
const yawRotatedAbs = Math.abs(
|
||||
normalizeAnglePI(draftYaw - Math.PI),
|
||||
);
|
||||
|
||||
if (yawRotatedAbs <= range + transition) {
|
||||
if (yawRotatedAbs <= range) {
|
||||
// inside range
|
||||
lowerPitch = -pitchLimits.back.max * (Math.PI / 180);
|
||||
upperPitch = -pitchLimits.back.min * (Math.PI / 180);
|
||||
} else {
|
||||
// inside transition
|
||||
const transitionProgress =
|
||||
(yawRotatedAbs - range) / transition;
|
||||
const maxInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.back.max +
|
||||
transitionProgress * pitchLimits.max;
|
||||
const minInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.back.min +
|
||||
transitionProgress * pitchLimits.min;
|
||||
|
||||
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
|
||||
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pitchLimits.front) {
|
||||
const range = pitchLimits.front.range * (Math.PI / 180);
|
||||
const yawAbs = Math.abs(normalizeAnglePI(draftYaw));
|
||||
|
||||
if (yawAbs <= range + transition) {
|
||||
if (yawAbs <= range) {
|
||||
// inside range
|
||||
lowerPitch = -pitchLimits.front.max * (Math.PI / 180);
|
||||
upperPitch = -pitchLimits.front.min * (Math.PI / 180);
|
||||
} else {
|
||||
// inside transition
|
||||
const transitionProgress = (yawAbs - range) / transition;
|
||||
const maxInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.front.max +
|
||||
transitionProgress * pitchLimits.max;
|
||||
const minInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.front.min +
|
||||
transitionProgress * pitchLimits.min;
|
||||
|
||||
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
|
||||
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draftPitch = clamp(draftPitch, lowerPitch, upperPitch);
|
||||
[draftPitch, draftYaw] = applyPitchYawLimits(
|
||||
draftPitch,
|
||||
draftYaw + event.movementX * (Math.PI / canvas.width),
|
||||
pitchLimits,
|
||||
yawLimits,
|
||||
);
|
||||
|
||||
if (gunContainer.current) {
|
||||
gunContainer.current.position
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { clamp } from 'lodash';
|
||||
import { PitchLimits, YawLimits } from '../blitzkrieg/modelDefinitions';
|
||||
import { normalizeAnglePI } from '../math/normalizeAngle180';
|
||||
|
||||
export function applyPitchYawLimits(
|
||||
pitch: number, // rad
|
||||
yaw: number, // rad
|
||||
pitchLimits: PitchLimits, // deg
|
||||
yawLimits?: YawLimits, // deg
|
||||
) {
|
||||
let newPitch = pitch;
|
||||
let newYaw = yaw;
|
||||
|
||||
if (yawLimits) {
|
||||
newYaw = normalizeAnglePI(
|
||||
clamp(
|
||||
newYaw,
|
||||
-yawLimits.max * (Math.PI / 180),
|
||||
-yawLimits.min * (Math.PI / 180),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let lowerPitch = -pitchLimits.max * (Math.PI / 180);
|
||||
let upperPitch = -pitchLimits.min * (Math.PI / 180);
|
||||
const transition = (pitchLimits.transition ?? 10) * (Math.PI / 180);
|
||||
|
||||
if (pitchLimits.back) {
|
||||
const range = pitchLimits.back.range * (Math.PI / 180);
|
||||
const yawRotatedAbs = Math.abs(normalizeAnglePI(newYaw - Math.PI));
|
||||
|
||||
if (yawRotatedAbs <= range + transition) {
|
||||
if (yawRotatedAbs <= range) {
|
||||
// inside range
|
||||
lowerPitch = -pitchLimits.back.max * (Math.PI / 180);
|
||||
upperPitch = -pitchLimits.back.min * (Math.PI / 180);
|
||||
} else {
|
||||
// inside transition
|
||||
const transitionProgress = (yawRotatedAbs - range) / transition;
|
||||
const maxInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.back.max +
|
||||
transitionProgress * pitchLimits.max;
|
||||
const minInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.back.min +
|
||||
transitionProgress * pitchLimits.min;
|
||||
|
||||
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
|
||||
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pitchLimits.front) {
|
||||
const range = pitchLimits.front.range * (Math.PI / 180);
|
||||
const yawAbs = Math.abs(normalizeAnglePI(newYaw));
|
||||
|
||||
if (yawAbs <= range + transition) {
|
||||
if (yawAbs <= range) {
|
||||
// inside range
|
||||
lowerPitch = -pitchLimits.front.max * (Math.PI / 180);
|
||||
upperPitch = -pitchLimits.front.min * (Math.PI / 180);
|
||||
} else {
|
||||
// inside transition
|
||||
const transitionProgress = (yawAbs - range) / transition;
|
||||
const maxInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.front.max +
|
||||
transitionProgress * pitchLimits.max;
|
||||
const minInterpolatedPitch =
|
||||
(1 - transitionProgress) * pitchLimits.front.min +
|
||||
transitionProgress * pitchLimits.min;
|
||||
|
||||
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
|
||||
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newPitch = clamp(newPitch, lowerPitch, upperPitch);
|
||||
|
||||
return [newPitch, newYaw];
|
||||
}
|
||||
Reference in new issue
Block a user