mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-14 21:26:08 +01:00
feat: optimized state handling x2
This commit is contained in:
1 parent
230b2df5fd
commit
586cd84349
13 files changed
+138
-149
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { Card, Flex, Tabs, Theme } from '@radix-ui/themes';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Suspense, use, useEffect, useRef, useState } from 'react';
|
||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
|
||||
@@ -19,6 +20,7 @@ import { TankArmor } from './components/TankArmor';
|
||||
import { TankModel } from './components/TankModel';
|
||||
|
||||
export function TankDisplay() {
|
||||
const router = useRouter();
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
const awaitedModelDefinitions = use(modelDefinitions);
|
||||
const canvasWrapper = useRef<HTMLDivElement>(null);
|
||||
@@ -64,15 +66,24 @@ export function TankDisplay() {
|
||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||
|
||||
mutateTankopedia((draft) => {
|
||||
[draft.model.gunPitch, draft.model.turretYaw] = applyPitchYawLimits(
|
||||
draft.model.gunPitch,
|
||||
draft.model.turretYaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
);
|
||||
[draft.model.physical.gunPitch, draft.model.physical.turretYaw] =
|
||||
applyPitchYawLimits(
|
||||
draft.model.physical.gunPitch,
|
||||
draft.model.physical.turretYaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
);
|
||||
});
|
||||
}, [protagonist?.gun, protagonist?.turret]);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.hash.startsWith('#')) {
|
||||
mutateTankopedia((draft) => {
|
||||
draft.mode = location.hash.match(/#(.+)/)![1] as TankopediaMode;
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!protagonist) return null;
|
||||
|
||||
return (
|
||||
@@ -96,6 +107,8 @@ export function TankDisplay() {
|
||||
<Tabs.Root
|
||||
value={mode}
|
||||
onValueChange={(mode) => {
|
||||
router.push(`#${mode}`);
|
||||
|
||||
mutateTankopedia((draft) => {
|
||||
draft.mode = mode as TankopediaMode;
|
||||
});
|
||||
|
||||
@@ -15,8 +15,10 @@ interface OptionsProps {
|
||||
}
|
||||
|
||||
export function Options({ isFullScreen, canvas }: OptionsProps) {
|
||||
const showGrid = useTankopedia((state) => state.model.showGrid);
|
||||
const showSpacedArmor = useTankopedia((state) => state.model.showSpacedArmor);
|
||||
const showGrid = useTankopedia((state) => state.model.visual.showGrid);
|
||||
const showSpacedArmor = useTankopedia(
|
||||
(state) => state.model.visual.showSpacedArmor,
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@@ -39,7 +41,7 @@ export function Options({ isFullScreen, canvas }: OptionsProps) {
|
||||
checked={showGrid}
|
||||
onCheckedChange={(checked) => {
|
||||
mutateTankopedia((draft) => {
|
||||
draft.model.showGrid = checked;
|
||||
draft.model.visual.showGrid = checked;
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -50,7 +52,7 @@ export function Options({ isFullScreen, canvas }: OptionsProps) {
|
||||
checked={showSpacedArmor}
|
||||
onCheckedChange={(checked) => {
|
||||
mutateTankopedia((draft) => {
|
||||
draft.model.showSpacedArmor = checked;
|
||||
draft.model.visual.showSpacedArmor = checked;
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
import { nameToArmorId } from '../../../../../../../core/blitzkrieg/nameToArmorId';
|
||||
import { resolveArmor } from '../../../../../../../core/blitzkrieg/resolveThickness';
|
||||
import { useTankopedia } from '../../../../../../../stores/tankopedia';
|
||||
import { Lighting } from '../../Lighting';
|
||||
|
||||
interface TankArmorProps extends GroupProps {}
|
||||
|
||||
export function TankArmor({ ...props }: TankArmorProps) {
|
||||
const mode = useTankopedia((state) => state.mode);
|
||||
const wrapper = useRef<Group>(null);
|
||||
const [awaitedModelDefinitions, setAwaitedModelDefinitions] = useState<
|
||||
ModelDefinitions | undefined
|
||||
@@ -26,8 +26,10 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
||||
if (!state.areTanksAssigned) return;
|
||||
return state.protagonist;
|
||||
});
|
||||
const showSpacedArmor = useTankopedia((state) => state.model.showSpacedArmor);
|
||||
const model = useTankopedia((state) => state.model);
|
||||
const showSpacedArmor = useTankopedia(
|
||||
(state) => state.model.visual.showSpacedArmor,
|
||||
);
|
||||
const physical = useTankopedia((state) => state.model.physical);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -35,17 +37,6 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = useTankopedia.subscribe(
|
||||
(state) => state.model.controlsEnabled,
|
||||
(controlsEnabled) => {
|
||||
if (wrapper.current) wrapper.current.visible = controlsEnabled;
|
||||
},
|
||||
);
|
||||
|
||||
return unsubscribe;
|
||||
});
|
||||
|
||||
// it's ok to have hooks after this early termination because this will never be true
|
||||
// it's more for typescript to stop throwing a fit
|
||||
if (!protagonist) return null;
|
||||
@@ -79,8 +70,8 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
||||
).applyAxisAngle(X_AXIS, Math.PI / 2);
|
||||
const turretPosition = new Vector3()
|
||||
.sub(turretOrigin)
|
||||
.applyAxisAngle(new Vector3(0, 0, 1), model.turretYaw);
|
||||
const turretRotation = new Euler(0, 0, model.turretYaw);
|
||||
.applyAxisAngle(new Vector3(0, 0, 1), physical.turretYaw);
|
||||
const turretRotation = new Euler(0, 0, physical.turretYaw);
|
||||
|
||||
if (tankModelDefinition.turretRotation) {
|
||||
const pitch = -tankModelDefinition.turretRotation.pitch * (Math.PI / 180);
|
||||
@@ -100,12 +91,11 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
||||
|
||||
return (
|
||||
<HeadsUpDisplay>
|
||||
<Lighting />
|
||||
|
||||
<group
|
||||
{...props}
|
||||
visible={mode === 'armor'}
|
||||
ref={wrapper}
|
||||
rotation={[-Math.PI / 2, 0, model.hullYaw]}
|
||||
rotation={[-Math.PI / 2, 0, physical.hullYaw]}
|
||||
>
|
||||
{armorNodes.map((node) => {
|
||||
const isHull = node.name.startsWith('hull_');
|
||||
@@ -187,10 +177,10 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
||||
position={new Vector3()
|
||||
.sub(turretOrigin)
|
||||
.sub(gunOrigin)
|
||||
.applyAxisAngle(new Vector3(1, 0, 0), model.gunPitch)
|
||||
.applyAxisAngle(new Vector3(1, 0, 0), physical.gunPitch)
|
||||
.add(turretOrigin)
|
||||
.add(gunOrigin)}
|
||||
rotation={[model.gunPitch, 0, 0]}
|
||||
rotation={[physical.gunPitch, 0, 0]}
|
||||
>
|
||||
{armorNodes.map((node) => {
|
||||
const isCurrentGun = node.name.startsWith(
|
||||
|
||||
@@ -33,7 +33,7 @@ export function TankModel() {
|
||||
if (!state.areTanksAssigned) return;
|
||||
return state.protagonist;
|
||||
});
|
||||
const model = useTankopedia((state) => state.model);
|
||||
const physical = useTankopedia((state) => state.model.physical);
|
||||
const hullContainer = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
@@ -70,8 +70,8 @@ export function TankModel() {
|
||||
).applyAxisAngle(X_AXIS, Math.PI / 2);
|
||||
const turretPosition = new Vector3()
|
||||
.sub(turretOrigin)
|
||||
.applyAxisAngle(new Vector3(0, 0, 1), model.turretYaw);
|
||||
const turretRotation = new Euler(0, 0, model.turretYaw);
|
||||
.applyAxisAngle(new Vector3(0, 0, 1), physical.turretYaw);
|
||||
const turretRotation = new Euler(0, 0, physical.turretYaw);
|
||||
|
||||
if (tankModelDefinition.turretRotation) {
|
||||
const pitch = -tankModelDefinition.turretRotation.pitch * (Math.PI / 180);
|
||||
@@ -90,7 +90,7 @@ export function TankModel() {
|
||||
turretPosition.add(turretOrigin);
|
||||
|
||||
return (
|
||||
<group ref={hullContainer} rotation={[-Math.PI / 2, 0, model.hullYaw]}>
|
||||
<group ref={hullContainer} rotation={[-Math.PI / 2, 0, physical.hullYaw]}>
|
||||
{nodes.map((node) => {
|
||||
const isHull = node.name === 'hull';
|
||||
const isWheel = node.name.startsWith('chassis_wheel_');
|
||||
@@ -101,11 +101,11 @@ export function TankModel() {
|
||||
function handlePointerDown(event: ThreeEvent<PointerEvent>) {
|
||||
event.stopPropagation();
|
||||
mutateTankopedia((draft) => {
|
||||
draft.model.controlsEnabled = false;
|
||||
draft.model.visual.controlsEnabled = false;
|
||||
});
|
||||
|
||||
if (isHull) {
|
||||
draftHullYaw = model.hullYaw;
|
||||
draftHullYaw = physical.hullYaw;
|
||||
|
||||
window.addEventListener('pointermove', handlePointerMoveHull);
|
||||
window.addEventListener('pointerup', handlePointerUpHull);
|
||||
@@ -122,8 +122,8 @@ export function TankModel() {
|
||||
}
|
||||
function handlePointerUpHull() {
|
||||
mutateTankopedia((draft) => {
|
||||
draft.model.controlsEnabled = true;
|
||||
draft.model.hullYaw = normalizeAnglePI(draftHullYaw);
|
||||
draft.model.visual.controlsEnabled = true;
|
||||
draft.model.physical.hullYaw = normalizeAnglePI(draftHullYaw);
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMoveHull);
|
||||
window.removeEventListener('pointerup', handlePointerUpHull);
|
||||
@@ -143,7 +143,7 @@ export function TankModel() {
|
||||
}
|
||||
function handlePointerUpTrack() {
|
||||
mutateTankopedia((draft) => {
|
||||
draft.model.controlsEnabled = true;
|
||||
draft.model.visual.controlsEnabled = true;
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMoveTrack);
|
||||
window.removeEventListener('pointerup', handlePointerUpTrack);
|
||||
@@ -185,11 +185,11 @@ export function TankModel() {
|
||||
event.stopPropagation();
|
||||
|
||||
if (isTurret) {
|
||||
draftYaw = model.turretYaw;
|
||||
draftPitch = model.gunPitch;
|
||||
draftYaw = physical.turretYaw;
|
||||
draftPitch = physical.gunPitch;
|
||||
|
||||
mutateTankopedia((draft) => {
|
||||
draft.model.controlsEnabled = false;
|
||||
draft.model.visual.controlsEnabled = false;
|
||||
});
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
@@ -240,9 +240,9 @@ export function TankModel() {
|
||||
}
|
||||
function handlePointerUp() {
|
||||
mutateTankopedia((state) => {
|
||||
state.model.controlsEnabled = true;
|
||||
state.model.gunPitch = normalizeAnglePI(draftPitch);
|
||||
state.model.turretYaw = normalizeAnglePI(draftYaw);
|
||||
state.model.visual.controlsEnabled = true;
|
||||
state.model.physical.gunPitch = normalizeAnglePI(draftPitch);
|
||||
state.model.physical.turretYaw = normalizeAnglePI(draftYaw);
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
@@ -271,10 +271,10 @@ export function TankModel() {
|
||||
position={new Vector3()
|
||||
.sub(turretOrigin)
|
||||
.sub(gunOrigin)
|
||||
.applyAxisAngle(new Vector3(1, 0, 0), model.gunPitch)
|
||||
.applyAxisAngle(new Vector3(1, 0, 0), physical.gunPitch)
|
||||
.add(turretOrigin)
|
||||
.add(gunOrigin)}
|
||||
rotation={[model.gunPitch, 0, 0]}
|
||||
rotation={[physical.gunPitch, 0, 0]}
|
||||
ref={gunContainer}
|
||||
>
|
||||
{nodes.map((node) => {
|
||||
@@ -294,10 +294,10 @@ export function TankModel() {
|
||||
event.stopPropagation();
|
||||
|
||||
mutateTankopedia((draft) => {
|
||||
draft.model.controlsEnabled = false;
|
||||
draft.model.visual.controlsEnabled = false;
|
||||
});
|
||||
draftPitch = model.gunPitch;
|
||||
draftYaw = model.turretYaw;
|
||||
draftPitch = physical.gunPitch;
|
||||
draftYaw = physical.turretYaw;
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
@@ -346,9 +346,9 @@ export function TankModel() {
|
||||
}
|
||||
function handlePointerUp() {
|
||||
mutateTankopedia((state) => {
|
||||
state.model.controlsEnabled = true;
|
||||
state.model.gunPitch = normalizeAnglePI(draftPitch);
|
||||
state.model.turretYaw = normalizeAnglePI(draftYaw);
|
||||
state.model.visual.controlsEnabled = true;
|
||||
state.model.physical.gunPitch = normalizeAnglePI(draftPitch);
|
||||
state.model.physical.turretYaw = normalizeAnglePI(draftYaw);
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
|
||||
Reference in new issue
Block a user