mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
feat: remove reactive pitch and yaw
This commit is contained in:
1 parent
4ddf77fc7f
commit
94cba0c9ba
12 files changed
+141
-190
No files matched your search
@@ -1,4 +1,3 @@
|
|||||||
import { normalizeAngleRad } from '@blitzkit/core';
|
|
||||||
import { useThree, type ThreeEvent } from '@react-three/fiber';
|
import { useThree, type ThreeEvent } from '@react-three/fiber';
|
||||||
import { memo, useRef } from 'react';
|
import { memo, useRef } from 'react';
|
||||||
import { Group, Plane, Vector2, Vector3 } from 'three';
|
import { Group, Plane, Vector2, Vector3 } from 'three';
|
||||||
@@ -153,15 +152,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
|
|
||||||
const position = new Vector2();
|
const position = new Vector2();
|
||||||
const delta = new Vector2();
|
const delta = new Vector2();
|
||||||
let pitch = 0;
|
|
||||||
let yaw = 0;
|
|
||||||
|
|
||||||
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
position.set(event.clientX, event.clientY);
|
position.set(event.clientX, event.clientY);
|
||||||
yaw = protagonist.yaw;
|
|
||||||
pitch = protagonist.pitch;
|
|
||||||
|
|
||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
draft.controlsEnabled = false;
|
draft.controlsEnabled = false;
|
||||||
@@ -190,9 +185,10 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
delta.set(event.clientX, event.clientY).sub(position);
|
delta.set(event.clientX, event.clientY).sub(position);
|
||||||
position.set(event.clientX, event.clientY);
|
position.set(event.clientX, event.clientY);
|
||||||
|
|
||||||
[pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
pitch,
|
modelTransformEvent.last!.pitch,
|
||||||
yaw + delta.x * (Math.PI / boundingRect.width),
|
modelTransformEvent.last!.yaw +
|
||||||
|
delta.x * (Math.PI / boundingRect.width),
|
||||||
gunModelDefinition.pitch,
|
gunModelDefinition.pitch,
|
||||||
turretModelDefinition.yaw,
|
turretModelDefinition.yaw,
|
||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
@@ -201,10 +197,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
modelTransformEvent.dispatch({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}
|
}
|
||||||
function handlePointerUp() {
|
function handlePointerUp() {
|
||||||
mutateDuel((draft) => {
|
|
||||||
draft.protagonist.pitch = normalizeAngleRad(pitch);
|
|
||||||
draft.protagonist.yaw = normalizeAngleRad(yaw);
|
|
||||||
});
|
|
||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
draft.controlsEnabled = true;
|
draft.controlsEnabled = true;
|
||||||
});
|
});
|
||||||
@@ -253,8 +245,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
|
|
||||||
const position = new Vector2();
|
const position = new Vector2();
|
||||||
const delta = new Vector2();
|
const delta = new Vector2();
|
||||||
let pitch = 0;
|
|
||||||
let yaw = 0;
|
|
||||||
|
|
||||||
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@@ -268,8 +258,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
position.set(event.clientX, event.clientY);
|
position.set(event.clientX, event.clientY);
|
||||||
pitch = protagonist.pitch;
|
|
||||||
yaw = protagonist.yaw;
|
|
||||||
|
|
||||||
window.addEventListener('pointermove', handlePointerMove);
|
window.addEventListener('pointermove', handlePointerMove);
|
||||||
window.addEventListener('pointerup', handlePointerUp);
|
window.addEventListener('pointerup', handlePointerUp);
|
||||||
@@ -290,9 +278,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
delta.set(event.clientX, event.clientY).sub(position);
|
delta.set(event.clientX, event.clientY).sub(position);
|
||||||
position.set(event.clientX, event.clientY);
|
position.set(event.clientX, event.clientY);
|
||||||
|
|
||||||
[pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
pitch - delta.y * (Math.PI / boundingRect.height),
|
modelTransformEvent.last!.pitch -
|
||||||
yaw + delta.x * (Math.PI / boundingRect.width),
|
delta.y * (Math.PI / boundingRect.height),
|
||||||
|
modelTransformEvent.last!.yaw +
|
||||||
|
delta.x * (Math.PI / boundingRect.width),
|
||||||
gunModelDefinition.pitch,
|
gunModelDefinition.pitch,
|
||||||
turretModelDefinition.yaw,
|
turretModelDefinition.yaw,
|
||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
@@ -304,10 +294,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
draft.controlsEnabled = true;
|
draft.controlsEnabled = true;
|
||||||
});
|
});
|
||||||
mutateDuel((draft) => {
|
|
||||||
draft.protagonist.pitch = normalizeAngleRad(pitch);
|
|
||||||
draft.protagonist.yaw = normalizeAngleRad(yaw);
|
|
||||||
});
|
|
||||||
window.removeEventListener('pointermove', handlePointerMove);
|
window.removeEventListener('pointermove', handlePointerMove);
|
||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
@@ -341,8 +327,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
|
|
||||||
const position = new Vector2();
|
const position = new Vector2();
|
||||||
const delta = new Vector2();
|
const delta = new Vector2();
|
||||||
let pitch = 0;
|
|
||||||
let yaw = 0;
|
|
||||||
|
|
||||||
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@@ -356,9 +340,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
position.set(event.clientX, event.clientY);
|
position.set(event.clientX, event.clientY);
|
||||||
pitch = protagonist.pitch;
|
|
||||||
yaw = protagonist.yaw;
|
|
||||||
|
|
||||||
window.addEventListener('pointermove', handlePointerMove);
|
window.addEventListener('pointermove', handlePointerMove);
|
||||||
window.addEventListener('pointerup', handlePointerUp);
|
window.addEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
@@ -378,9 +359,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
delta.set(event.clientX, event.clientY).sub(position);
|
delta.set(event.clientX, event.clientY).sub(position);
|
||||||
position.set(event.clientX, event.clientY);
|
position.set(event.clientX, event.clientY);
|
||||||
|
|
||||||
[pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
pitch - delta.y * (Math.PI / boundingRect.height),
|
modelTransformEvent.last!.pitch -
|
||||||
yaw + delta.x * (Math.PI / boundingRect.width),
|
delta.y * (Math.PI / boundingRect.height),
|
||||||
|
modelTransformEvent.last!.yaw +
|
||||||
|
delta.x * (Math.PI / boundingRect.width),
|
||||||
gunModelDefinition.pitch,
|
gunModelDefinition.pitch,
|
||||||
turretModelDefinition.yaw,
|
turretModelDefinition.yaw,
|
||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
@@ -392,10 +375,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
draft.controlsEnabled = true;
|
draft.controlsEnabled = true;
|
||||||
});
|
});
|
||||||
mutateDuel((draft) => {
|
|
||||||
draft.protagonist.pitch = normalizeAngleRad(pitch);
|
|
||||||
draft.protagonist.yaw = normalizeAngleRad(yaw);
|
|
||||||
});
|
|
||||||
window.removeEventListener('pointermove', handlePointerMove);
|
window.removeEventListener('pointermove', handlePointerMove);
|
||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { I_HAT, J_HAT } from '@blitzkit/core';
|
import { I_HAT, J_HAT } from '@blitzkit/core';
|
||||||
import { type MeshProps, useThree } from '@react-three/fiber';
|
import { type MeshProps, useThree } from '@react-three/fiber';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
|
import type { QuicklimeEvent } from 'quicklime';
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Box3,
|
Box3,
|
||||||
@@ -186,16 +187,18 @@ export function StaticArmorSceneComponent({
|
|||||||
const distanceToBarrel = clip.distanceToPoint(barrelOrigin);
|
const distanceToBarrel = clip.distanceToPoint(barrelOrigin);
|
||||||
const point = new Vector3();
|
const point = new Vector3();
|
||||||
|
|
||||||
function handleModelTransform(data: ModelTransformEventData) {
|
function handleModelTransform(
|
||||||
|
event: QuicklimeEvent<ModelTransformEventData>,
|
||||||
|
) {
|
||||||
clip.normal
|
clip.normal
|
||||||
.set(0, 0, -1)
|
.set(0, 0, -1)
|
||||||
.applyAxisAngle(I_HAT, data.pitch)
|
.applyAxisAngle(I_HAT, event.data.pitch)
|
||||||
.applyAxisAngle(J_HAT, data.yaw ?? 0);
|
.applyAxisAngle(J_HAT, event.data.yaw);
|
||||||
clip.constant = 0;
|
clip.constant = 0;
|
||||||
|
|
||||||
point
|
point
|
||||||
.copy(gunOrigin)
|
.copy(gunOrigin)
|
||||||
.applyAxisAngle(J_HAT, data.yaw ?? 0)
|
.applyAxisAngle(J_HAT, event.data.yaw)
|
||||||
.add(clip.normal.multiplyScalar(-distanceToBarrel))
|
.add(clip.normal.multiplyScalar(-distanceToBarrel))
|
||||||
.add(neckOrigin);
|
.add(neckOrigin);
|
||||||
clip.normal.normalize();
|
clip.normal.normalize();
|
||||||
|
|||||||
+11
-10
@@ -3,6 +3,7 @@ import { useEffect } from 'react';
|
|||||||
import { awaitableEquipmentDefinitions } from '../../../../../core/awaitables/equipmentDefinitions';
|
import { awaitableEquipmentDefinitions } from '../../../../../core/awaitables/equipmentDefinitions';
|
||||||
import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions';
|
import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions';
|
||||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||||
|
import { modelTransformEvent } from '../../../../../core/blitzkit/modelTransform';
|
||||||
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
||||||
import { useEquipment } from '../../../../../hooks/useEquipment';
|
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||||
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
||||||
@@ -75,16 +76,16 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
mutateDuel((draft) => {
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
[draft.protagonist.pitch, draft.protagonist.yaw] = applyPitchYawLimits(
|
modelTransformEvent.last!.pitch,
|
||||||
draft.protagonist.pitch,
|
modelTransformEvent.last!.yaw,
|
||||||
draft.protagonist.yaw,
|
gunModelDefinition.pitch,
|
||||||
gunModelDefinition.pitch,
|
turretModelDefinition.yaw,
|
||||||
turretModelDefinition.yaw,
|
hasImprovedVerticalStabilizer,
|
||||||
hasImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
);
|
||||||
);
|
|
||||||
});
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}, [hasImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer]);
|
}, [hasImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+20
-32
@@ -1,5 +1,6 @@
|
|||||||
import { HeightIcon, WidthIcon } from '@radix-ui/react-icons';
|
import { HeightIcon, WidthIcon } from '@radix-ui/react-icons';
|
||||||
import { Flex, TextField } from '@radix-ui/themes';
|
import { Flex, TextField } from '@radix-ui/themes';
|
||||||
|
import type { QuicklimeEvent } from 'quicklime';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { degToRad, radToDeg } from 'three/src/math/MathUtils.js';
|
import { degToRad, radToDeg } from 'three/src/math/MathUtils.js';
|
||||||
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
@@ -28,31 +29,19 @@ export function QuickInputs() {
|
|||||||
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!yawInput.current) return;
|
function handleTransformEvent(
|
||||||
|
event: QuicklimeEvent<ModelTransformEventData>,
|
||||||
yawInput.current.value = radToDeg(protagonist.yaw).toFixed(1);
|
) {
|
||||||
}, [protagonist.yaw]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!pitchInput.current) return;
|
|
||||||
|
|
||||||
pitchInput.current.value = (
|
|
||||||
-radToDeg(protagonist.pitch) + initialGunPitch
|
|
||||||
).toFixed(1);
|
|
||||||
}, [protagonist.pitch]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function handleTransformEvent({ pitch, yaw }: ModelTransformEventData) {
|
|
||||||
if (!pitchInput.current || !yawInput.current) return;
|
if (!pitchInput.current || !yawInput.current) return;
|
||||||
|
|
||||||
pitchInput.current.value = (
|
pitchInput.current.value = (
|
||||||
-radToDeg(pitch) +
|
-radToDeg(event.data.pitch) +
|
||||||
(tankModelDefinition.initial_turret_rotation?.pitch ?? 0)
|
(tankModelDefinition.initial_turret_rotation?.pitch ?? 0)
|
||||||
).toFixed(1);
|
).toFixed(1);
|
||||||
|
|
||||||
if (yaw === undefined) return;
|
if (event.data.yaw === undefined) return;
|
||||||
|
|
||||||
yawInput.current.value = radToDeg(yaw).toFixed(1);
|
yawInput.current.value = radToDeg(event.data.yaw).toFixed(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
modelTransformEvent.on(handleTransformEvent);
|
modelTransformEvent.on(handleTransformEvent);
|
||||||
@@ -85,24 +74,27 @@ export function QuickInputs() {
|
|||||||
defaultValue="0.0"
|
defaultValue="0.0"
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
const value = Number(yawInput.current!.value);
|
const value = Number(yawInput.current!.value);
|
||||||
|
|
||||||
if (isNaN(value)) {
|
if (isNaN(value)) {
|
||||||
yawInput.current!.value = radToDeg(protagonist.yaw).toFixed(1);
|
yawInput.current!.value = radToDeg(
|
||||||
|
modelTransformEvent.last!.yaw,
|
||||||
|
).toFixed(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
protagonist.pitch,
|
modelTransformEvent.last!.pitch,
|
||||||
degToRad(value),
|
degToRad(value),
|
||||||
gunModelDefinition.pitch,
|
gunModelDefinition.pitch,
|
||||||
turretModelDefinition.yaw,
|
turretModelDefinition.yaw,
|
||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
|
|
||||||
modelTransformEvent.dispatch({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
mutateDuel((state) => {
|
yawInput.current!.value = radToDeg(
|
||||||
state.protagonist.pitch = pitch;
|
modelTransformEvent.last!.yaw,
|
||||||
state.protagonist.yaw = yaw;
|
).toFixed(1);
|
||||||
});
|
|
||||||
yawInput.current!.value = radToDeg(protagonist.yaw).toFixed(1);
|
|
||||||
}}
|
}}
|
||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
@@ -128,25 +120,21 @@ export function QuickInputs() {
|
|||||||
const value = Number(pitchInput.current!.value);
|
const value = Number(pitchInput.current!.value);
|
||||||
if (isNaN(value)) {
|
if (isNaN(value)) {
|
||||||
pitchInput.current!.value = (
|
pitchInput.current!.value = (
|
||||||
-radToDeg(protagonist.pitch) + initialGunPitch
|
-radToDeg(modelTransformEvent.last!.pitch) + initialGunPitch
|
||||||
).toFixed(1);
|
).toFixed(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
degToRad(-value + initialGunPitch),
|
degToRad(-value + initialGunPitch),
|
||||||
protagonist.yaw,
|
modelTransformEvent.last!.yaw,
|
||||||
gunModelDefinition.pitch,
|
gunModelDefinition.pitch,
|
||||||
turretModelDefinition.yaw,
|
turretModelDefinition.yaw,
|
||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
modelTransformEvent.dispatch({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
mutateDuel((state) => {
|
|
||||||
state.protagonist.pitch = pitch;
|
|
||||||
state.protagonist.yaw = yaw;
|
|
||||||
});
|
|
||||||
pitchInput.current!.value = (
|
pitchInput.current!.value = (
|
||||||
-radToDeg(protagonist.pitch) + initialGunPitch
|
-radToDeg(modelTransformEvent.last!.pitch) + initialGunPitch
|
||||||
).toFixed(1);
|
).toFixed(1);
|
||||||
}}
|
}}
|
||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||||
|
import { awaitableModelDefinitions } from '../../../../../../core/awaitables/modelDefinitions';
|
||||||
|
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
|
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
|
||||||
|
import { Duel } from '../../../../../../stores/duel';
|
||||||
|
|
||||||
|
const modelDefinitions = await awaitableModelDefinitions;
|
||||||
|
|
||||||
|
export function InitialAligner() {
|
||||||
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
|
const gun = Duel.use((state) => state.protagonist.gun);
|
||||||
|
|
||||||
|
const tankModel = modelDefinitions.models[tank.id];
|
||||||
|
const turretModel = tankModel.turrets[turret.id];
|
||||||
|
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
|
degToRad(8),
|
||||||
|
degToRad(25),
|
||||||
|
gunModel.pitch,
|
||||||
|
turretModel.yaw,
|
||||||
|
);
|
||||||
|
|
||||||
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
+6
-11
@@ -39,9 +39,6 @@ export function SceneProps() {
|
|||||||
const material = useRef<MeshStandardMaterial>(null);
|
const material = useRef<MeshStandardMaterial>(null);
|
||||||
const playground = useRef<Group>(null);
|
const playground = useRef<Group>(null);
|
||||||
|
|
||||||
let lastPitch = Duel.use((state) => state.protagonist.pitch);
|
|
||||||
let lastYaw = Duel.use((state) => state.protagonist.yaw);
|
|
||||||
const display = TankopediaEphemeral.use((state) => state.display);
|
|
||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
const track = Duel.use((state) => state.protagonist.track);
|
const track = Duel.use((state) => state.protagonist.track);
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
@@ -152,8 +149,8 @@ export function SceneProps() {
|
|||||||
|
|
||||||
const desiredPitch = Math.asin(direction.y);
|
const desiredPitch = Math.asin(direction.y);
|
||||||
const desiredYaw = Math.atan2(-direction.x, -direction.z);
|
const desiredYaw = Math.atan2(-direction.x, -direction.z);
|
||||||
const deltaPitch = desiredPitch - lastPitch;
|
const deltaPitch = desiredPitch - modelTransformEvent.last!.pitch;
|
||||||
const deltaYaw = desiredYaw - lastYaw;
|
const deltaYaw = desiredYaw - modelTransformEvent.last!.yaw;
|
||||||
const deltaAngle = Math.sqrt(deltaPitch ** 2 + deltaYaw ** 2);
|
const deltaAngle = Math.sqrt(deltaPitch ** 2 + deltaYaw ** 2);
|
||||||
const maxPossibleRotationInTime = gunRotationSpeed * deltaT;
|
const maxPossibleRotationInTime = gunRotationSpeed * deltaT;
|
||||||
const clampedDeltaAngle = Math.min(maxPossibleRotationInTime, deltaAngle);
|
const clampedDeltaAngle = Math.min(maxPossibleRotationInTime, deltaAngle);
|
||||||
@@ -163,14 +160,14 @@ export function SceneProps() {
|
|||||||
deltaAngle === 0 ? 0 : deltaYaw * (clampedDeltaAngle / deltaAngle);
|
deltaAngle === 0 ? 0 : deltaYaw * (clampedDeltaAngle / deltaAngle);
|
||||||
|
|
||||||
const [pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
lastPitch + clampedDeltaPitch,
|
modelTransformEvent.last!.pitch + clampedDeltaPitch,
|
||||||
lastYaw + clampedDeltaYaw,
|
modelTransformEvent.last!.yaw + clampedDeltaYaw,
|
||||||
gunModelDefinition.pitch,
|
gunModelDefinition.pitch,
|
||||||
turretModelDefinition.yaw,
|
turretModelDefinition.yaw,
|
||||||
);
|
);
|
||||||
|
|
||||||
const actualDeltaPitch = pitch - lastPitch;
|
const actualDeltaPitch = pitch - modelTransformEvent.last!.pitch;
|
||||||
const actualDeltaYaw = yaw - lastYaw;
|
const actualDeltaYaw = yaw - modelTransformEvent.last!.yaw;
|
||||||
const deltaRotation = Math.abs(actualDeltaPitch) + Math.abs(actualDeltaYaw);
|
const deltaRotation = Math.abs(actualDeltaPitch) + Math.abs(actualDeltaYaw);
|
||||||
const rotationSpeed = deltaRotation / deltaT;
|
const rotationSpeed = deltaRotation / deltaT;
|
||||||
const turretTraversePenalty =
|
const turretTraversePenalty =
|
||||||
@@ -190,8 +187,6 @@ export function SceneProps() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
modelTransformEvent.dispatch({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
lastPitch = pitch;
|
|
||||||
lastYaw = yaw;
|
|
||||||
|
|
||||||
const gunDirection = new Vector3(0, 0, -1)
|
const gunDirection = new Vector3(0, 0, -1)
|
||||||
.applyAxisAngle(I_HAT, pitch)
|
.applyAxisAngle(I_HAT, pitch)
|
||||||
|
|||||||
+6
-19
@@ -1,4 +1,3 @@
|
|||||||
import { normalizeAngleRad } from '@blitzkit/core';
|
|
||||||
import { invalidate, useThree, type ThreeEvent } from '@react-three/fiber';
|
import { invalidate, useThree, type ThreeEvent } from '@react-three/fiber';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { Group, Mesh, MeshStandardMaterial, Vector2 } from 'three';
|
import { Group, Mesh, MeshStandardMaterial, Vector2 } from 'three';
|
||||||
@@ -125,17 +124,12 @@ export function TankModel() {
|
|||||||
|
|
||||||
if (!isVisible) return null;
|
if (!isVisible) return null;
|
||||||
|
|
||||||
let pitch = 0;
|
|
||||||
let yaw = 0;
|
|
||||||
|
|
||||||
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
if (!isTurret) return;
|
if (!isTurret) return;
|
||||||
|
|
||||||
position.set(event.clientX, event.clientY);
|
position.set(event.clientX, event.clientY);
|
||||||
yaw = protagonist.yaw;
|
|
||||||
pitch = protagonist.pitch;
|
|
||||||
|
|
||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
draft.controlsEnabled = false;
|
draft.controlsEnabled = false;
|
||||||
@@ -164,9 +158,10 @@ export function TankModel() {
|
|||||||
delta.set(event.clientX, event.clientY).sub(position);
|
delta.set(event.clientX, event.clientY).sub(position);
|
||||||
position.set(event.clientX, event.clientY);
|
position.set(event.clientX, event.clientY);
|
||||||
|
|
||||||
[pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
pitch,
|
modelTransformEvent.last!.pitch,
|
||||||
yaw + delta.x * (Math.PI / boundingRect.width),
|
modelTransformEvent.last!.yaw +
|
||||||
|
delta.x * (Math.PI / boundingRect.width),
|
||||||
gunModelDefinition.pitch,
|
gunModelDefinition.pitch,
|
||||||
turretModelDefinition.yaw,
|
turretModelDefinition.yaw,
|
||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
@@ -175,10 +170,6 @@ export function TankModel() {
|
|||||||
modelTransformEvent.dispatch({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}
|
}
|
||||||
function handlePointerUp() {
|
function handlePointerUp() {
|
||||||
mutateDuel((draft) => {
|
|
||||||
draft.protagonist.pitch = normalizeAngleRad(pitch);
|
|
||||||
draft.protagonist.yaw = normalizeAngleRad(yaw);
|
|
||||||
});
|
|
||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
draft.controlsEnabled = true;
|
draft.controlsEnabled = true;
|
||||||
});
|
});
|
||||||
@@ -231,8 +222,8 @@ export function TankModel() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
position.set(event.clientX, event.clientY);
|
position.set(event.clientX, event.clientY);
|
||||||
pitch = protagonist.pitch;
|
pitch = modelTransformEvent.last!.pitch;
|
||||||
yaw = protagonist.yaw;
|
yaw = modelTransformEvent.last!.yaw;
|
||||||
|
|
||||||
window.addEventListener('pointermove', handlePointerMove);
|
window.addEventListener('pointermove', handlePointerMove);
|
||||||
window.addEventListener('pointerup', handlePointerUp);
|
window.addEventListener('pointerup', handlePointerUp);
|
||||||
@@ -267,10 +258,6 @@ export function TankModel() {
|
|||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
draft.controlsEnabled = true;
|
draft.controlsEnabled = true;
|
||||||
});
|
});
|
||||||
mutateDuel((draft) => {
|
|
||||||
draft.protagonist.pitch = normalizeAngleRad(pitch);
|
|
||||||
draft.protagonist.yaw = normalizeAngleRad(yaw);
|
|
||||||
});
|
|
||||||
window.removeEventListener('pointermove', handlePointerMove);
|
window.removeEventListener('pointermove', handlePointerMove);
|
||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-46
@@ -28,6 +28,7 @@ import {
|
|||||||
import { SmartCanvas } from '../../../../SmartCanvas';
|
import { SmartCanvas } from '../../../../SmartCanvas';
|
||||||
import { AutoClear } from './components/AutoClear';
|
import { AutoClear } from './components/AutoClear';
|
||||||
import { Controls } from './components/Control';
|
import { Controls } from './components/Control';
|
||||||
|
import { InitialAligner } from './components/InitialAligner';
|
||||||
import { InitialFogReveal } from './components/InitialFogReveal';
|
import { InitialFogReveal } from './components/InitialFogReveal';
|
||||||
import { Lighting } from './components/Lighting';
|
import { Lighting } from './components/Lighting';
|
||||||
import { SceneProps } from './components/SceneProps';
|
import { SceneProps } from './components/SceneProps';
|
||||||
@@ -114,56 +115,44 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
function handlePoseEvent(pose: Pose) {
|
function handlePoseEvent(pose: Pose) {
|
||||||
switch (pose) {
|
switch (pose) {
|
||||||
case Pose.HullDown: {
|
case Pose.HullDown: {
|
||||||
mutateDuel((draft) => {
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
const [pitch, yaw] = applyPitchYawLimits(
|
-Infinity,
|
||||||
-Infinity,
|
0,
|
||||||
0,
|
gunModelDefinition.pitch,
|
||||||
gunModelDefinition.pitch,
|
turretModelDefinition.yaw,
|
||||||
turretModelDefinition.yaw,
|
hasImprovedVerticalStabilizer,
|
||||||
hasImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
modelTransformEvent.dispatch({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
draft.protagonist.pitch = pitch;
|
|
||||||
draft.protagonist.yaw = yaw;
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Pose.FaceHug: {
|
case Pose.FaceHug: {
|
||||||
mutateDuel((draft) => {
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
const [pitch, yaw] = applyPitchYawLimits(
|
Infinity,
|
||||||
Infinity,
|
0,
|
||||||
0,
|
gunModelDefinition.pitch,
|
||||||
gunModelDefinition.pitch,
|
turretModelDefinition.yaw,
|
||||||
turretModelDefinition.yaw,
|
hasImprovedVerticalStabilizer,
|
||||||
hasImprovedVerticalStabilizer,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
modelTransformEvent.dispatch({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
draft.protagonist.pitch = pitch;
|
|
||||||
draft.protagonist.yaw = yaw;
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Pose.Default:
|
case Pose.Default:
|
||||||
mutateDuel((draft) => {
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
const [pitch, yaw] = applyPitchYawLimits(
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
gunModelDefinition.pitch,
|
||||||
gunModelDefinition.pitch,
|
turretModelDefinition.yaw,
|
||||||
turretModelDefinition.yaw,
|
hasImprovedVerticalStabilizer,
|
||||||
hasImprovedVerticalStabilizer,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
modelTransformEvent.dispatch({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
draft.protagonist.pitch = pitch;
|
|
||||||
draft.protagonist.yaw = yaw;
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -177,15 +166,15 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
mutateDuel((draft) => {
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
[draft.protagonist.pitch, draft.protagonist.yaw] = applyPitchYawLimits(
|
modelTransformEvent.last!.pitch,
|
||||||
draft.protagonist.pitch,
|
modelTransformEvent.last!.yaw,
|
||||||
draft.protagonist.yaw,
|
gunModelDefinition.pitch,
|
||||||
gunModelDefinition.pitch,
|
turretModelDefinition.yaw,
|
||||||
turretModelDefinition.yaw,
|
hasImprovedVerticalStabilizer,
|
||||||
hasImprovedVerticalStabilizer,
|
);
|
||||||
);
|
|
||||||
});
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}, [protagonistGun, protagonistTurret]);
|
}, [protagonistGun, protagonistTurret]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -246,6 +235,8 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
)}
|
)}
|
||||||
<Lighting display={display} />
|
<Lighting display={display} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
||||||
|
<InitialAligner />
|
||||||
</SmartCanvas>
|
</SmartCanvas>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { Quicklime } from 'quicklime';
|
import { Quicklime } from 'quicklime';
|
||||||
|
|
||||||
export interface ModelTransformEventData {
|
export interface ModelTransformEventData {
|
||||||
yaw?: number;
|
yaw: number;
|
||||||
pitch: number;
|
pitch: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const modelTransformEvent = new Quicklime<ModelTransformEventData>();
|
export const modelTransformEvent = new Quicklime<ModelTransformEventData>({
|
||||||
|
pitch: 0,
|
||||||
|
yaw: 0,
|
||||||
|
});
|
||||||
@@ -4,10 +4,8 @@ import {
|
|||||||
type ProvisionDefinitions,
|
type ProvisionDefinitions,
|
||||||
type TankDefinition,
|
type TankDefinition,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
|
||||||
import type { DuelMember } from '../../stores/duel';
|
import type { DuelMember } from '../../stores/duel';
|
||||||
import { genericDefaultEquipmentMatrix } from '../../stores/duel/constants';
|
import { genericDefaultEquipmentMatrix } from '../../stores/duel/constants';
|
||||||
import { applyPitchYawLimits } from '../blitz/applyPitchYawLimits';
|
|
||||||
|
|
||||||
export function tankToDuelMember(
|
export function tankToDuelMember(
|
||||||
tank: TankDefinition,
|
tank: TankDefinition,
|
||||||
@@ -15,16 +13,7 @@ export function tankToDuelMember(
|
|||||||
provisionDefinitions: ProvisionDefinitions,
|
provisionDefinitions: ProvisionDefinitions,
|
||||||
) {
|
) {
|
||||||
const turret = tank.turrets.at(-1)!;
|
const turret = tank.turrets.at(-1)!;
|
||||||
const turretModel = model.turrets[turret.id];
|
|
||||||
const gun = turret.guns.at(-1)!;
|
const gun = turret.guns.at(-1)!;
|
||||||
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
|
|
||||||
|
|
||||||
const [pitch, yaw] = applyPitchYawLimits(
|
|
||||||
degToRad(8),
|
|
||||||
degToRad(25),
|
|
||||||
gunModel.pitch,
|
|
||||||
turretModel.yaw,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
camouflage: true,
|
camouflage: true,
|
||||||
@@ -37,8 +26,6 @@ export function tankToDuelMember(
|
|||||||
shell: tank.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0],
|
shell: tank.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0],
|
||||||
tank,
|
tank,
|
||||||
track: tank.tracks.at(-1)!,
|
track: tank.tracks.at(-1)!,
|
||||||
yaw,
|
|
||||||
pitch,
|
|
||||||
turret,
|
turret,
|
||||||
assaultDistance:
|
assaultDistance:
|
||||||
(gun.gun_type!.value.base.assault_ranges?.ranges[0].distance ?? 0) / 2,
|
(gun.gun_type!.value.base.assault_ranges?.ranges[0].distance ?? 0) / 2,
|
||||||
|
|||||||
@@ -45,9 +45,8 @@ export function useTankTransform(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleModelTransformInner(data?: ModelTransformEventData) {
|
function handleModelTransformInner(data?: ModelTransformEventData) {
|
||||||
const duel = duelStore.getState();
|
const yaw = data?.yaw ?? modelTransformEvent.last!.yaw;
|
||||||
const yaw = data?.yaw ?? duel.protagonist.yaw;
|
const pitch = data?.pitch ?? modelTransformEvent.last!.pitch;
|
||||||
const pitch = data?.pitch ?? duel.protagonist.pitch;
|
|
||||||
|
|
||||||
gunPosition
|
gunPosition
|
||||||
.set(0, 0, 0)
|
.set(0, 0, 0)
|
||||||
@@ -102,20 +101,8 @@ export function useTankTransform(
|
|||||||
|
|
||||||
modelTransformEvent.on(handleModelTransform);
|
modelTransformEvent.on(handleModelTransform);
|
||||||
|
|
||||||
const unsubscribes = [
|
|
||||||
duelStore.subscribe(
|
|
||||||
(state) => state.protagonist.pitch,
|
|
||||||
() => handleModelTransformInner(),
|
|
||||||
),
|
|
||||||
duelStore.subscribe(
|
|
||||||
(state) => state.protagonist.yaw,
|
|
||||||
() => handleModelTransformInner(),
|
|
||||||
),
|
|
||||||
() => modelTransformEvent.off(handleModelTransform),
|
|
||||||
];
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
modelTransformEvent.off(handleModelTransform);
|
||||||
};
|
};
|
||||||
}, [track, turret]);
|
}, [track, turret]);
|
||||||
}
|
}
|
||||||
@@ -33,8 +33,6 @@ export interface DuelMember {
|
|||||||
shell: ShellDefinition;
|
shell: ShellDefinition;
|
||||||
track: TrackDefinition;
|
track: TrackDefinition;
|
||||||
equipmentMatrix: EquipmentMatrix;
|
equipmentMatrix: EquipmentMatrix;
|
||||||
pitch: number;
|
|
||||||
yaw: number;
|
|
||||||
consumables: number[];
|
consumables: number[];
|
||||||
provisions: number[];
|
provisions: number[];
|
||||||
camouflage: boolean;
|
camouflage: boolean;
|
||||||
|
|||||||
Reference in new issue
Block a user