mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: parity size
This commit is contained in:
1 parent
74ac5b5ac2
commit
26d3bdaac7
2 files changed
+114
-68
No files matched your search
@@ -95,6 +95,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
top="50%"
|
top="50%"
|
||||||
left="50%"
|
left="50%"
|
||||||
style={{
|
style={{
|
||||||
|
zIndex: 2,
|
||||||
backgroundImage: `url(${imgur('27Gwth4')})`,
|
backgroundImage: `url(${imgur('27Gwth4')})`,
|
||||||
backgroundSize: 'contain',
|
backgroundSize: 'contain',
|
||||||
transform: 'translateX(-50%)',
|
transform: 'translateX(-50%)',
|
||||||
|
|||||||
+113
-68
@@ -1,39 +1,48 @@
|
|||||||
import { I_HAT, J_HAT } from '@blitzkit/core';
|
import { asset, I_HAT, imgur, J_HAT } from '@blitzkit/core';
|
||||||
import { Box } from '@radix-ui/themes';
|
import { Box } from '@radix-ui/themes';
|
||||||
import { Html } from '@react-three/drei';
|
import { Html } from '@react-three/drei';
|
||||||
import { useFrame, useLoader } from '@react-three/fiber';
|
import { useFrame, useLoader } from '@react-three/fiber';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
ArrowHelper,
|
|
||||||
Group,
|
Group,
|
||||||
MeshStandardMaterial,
|
MeshStandardMaterial,
|
||||||
|
PerspectiveCamera,
|
||||||
TextureLoader,
|
TextureLoader,
|
||||||
Vector2,
|
Vector2,
|
||||||
Vector3,
|
Vector3,
|
||||||
} from 'three';
|
} from 'three';
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/Addons.js';
|
||||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||||
|
import { awaitableEquipmentDefinitions } from '../../../../../../core/awaitables/equipmentDefinitions';
|
||||||
import { awaitableModelDefinitions } from '../../../../../../core/awaitables/modelDefinitions';
|
import { awaitableModelDefinitions } from '../../../../../../core/awaitables/modelDefinitions';
|
||||||
|
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 { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
|
||||||
|
import { tankCharacteristics } from '../../../../../../core/blitzkit/tankCharacteristics';
|
||||||
import { Var } from '../../../../../../core/radix/var';
|
import { Var } from '../../../../../../core/radix/var';
|
||||||
import { Duel } from '../../../../../../stores/duel';
|
import { Duel } from '../../../../../../stores/duel';
|
||||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||||
import { TankopediaPersistent } from '../../../../../../stores/tankopediaPersistent';
|
import { TankopediaPersistent } from '../../../../../../stores/tankopediaPersistent';
|
||||||
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
|
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
|
||||||
|
|
||||||
|
const [modelDefinitions, equipmentDefinitions, provisionDefinitions] =
|
||||||
|
await Promise.all([
|
||||||
|
awaitableModelDefinitions,
|
||||||
|
awaitableEquipmentDefinitions,
|
||||||
|
awaitableProvisionDefinitions,
|
||||||
|
]);
|
||||||
|
|
||||||
const emptyVector = new Vector2();
|
const emptyVector = new Vector2();
|
||||||
const modelDefinitions = await awaitableModelDefinitions;
|
|
||||||
const FAR_DISTANCE = 1000;
|
const FAR_DISTANCE = 1000;
|
||||||
|
|
||||||
export function SceneProps() {
|
export function SceneProps() {
|
||||||
const shellPathHelper = useRef<ArrowHelper>(null);
|
|
||||||
const show = TankopediaPersistent.use(
|
const show = TankopediaPersistent.use(
|
||||||
(state) => state.showGrid && !state.showEnvironment,
|
(state) => state.showGrid && !state.showEnvironment,
|
||||||
);
|
);
|
||||||
const targetCircle = useRef<Group>(null);
|
const targetCircle = useRef<Group>(null);
|
||||||
const targetCircleDots = useRef<HTMLDivElement>(null);
|
const targetCircleDots = useRef<HTMLDivElement>(null);
|
||||||
const texture = useLoader(TextureLoader, 'https://i.imgur.com/C28Z8nU.png');
|
const texture = useLoader(TextureLoader, imgur('C28Z8nU'));
|
||||||
|
|
||||||
texture.anisotropy = 2;
|
texture.anisotropy = 2;
|
||||||
|
|
||||||
@@ -42,43 +51,66 @@ export function SceneProps() {
|
|||||||
const material = useRef<MeshStandardMaterial>(null);
|
const material = useRef<MeshStandardMaterial>(null);
|
||||||
const display = TankopediaEphemeral.use((state) => state.display);
|
const display = TankopediaEphemeral.use((state) => state.display);
|
||||||
const playground = useRef<Group>(null);
|
const playground = useRef<Group>(null);
|
||||||
const protagonistTurret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
const protagonistTrack = Duel.use((state) => state.protagonist.track);
|
const track = Duel.use((state) => state.protagonist.track);
|
||||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
const protagonistGun = Duel.use((state) => state.protagonist.gun);
|
const gun = Duel.use((state) => state.protagonist.gun);
|
||||||
const protagonistModelDefinition =
|
const shell = Duel.use((state) => state.protagonist.shell);
|
||||||
modelDefinitions.models[protagonistTank.id];
|
const engine = Duel.use((state) => state.protagonist.engine);
|
||||||
const protagonistTrackModelDefinition =
|
const tankModelDefinition = modelDefinitions.models[tank.id];
|
||||||
modelDefinitions.models[protagonistTank.id].tracks[protagonistTrack.id];
|
const trackModelDefinition =
|
||||||
const protagonistGunModelDefinition =
|
modelDefinitions.models[tank.id].tracks[track.id];
|
||||||
protagonistModelDefinition.turrets[protagonistTurret.id].guns[
|
const gunModelDefinition =
|
||||||
protagonistGun.gun_type!.value.base.id
|
tankModelDefinition.turrets[turret.id].guns[gun.gun_type!.value.base.id];
|
||||||
];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const protagonistTurretModelDefinition =
|
const hullOrigin = new Vector3(
|
||||||
protagonistModelDefinition.turrets[protagonistTurret.id];
|
trackModelDefinition.origin.x,
|
||||||
const protagonistHullOrigin = new Vector3(
|
trackModelDefinition.origin.y,
|
||||||
protagonistTrackModelDefinition.origin.x,
|
-trackModelDefinition.origin.z,
|
||||||
protagonistTrackModelDefinition.origin.y,
|
|
||||||
-protagonistTrackModelDefinition.origin.z,
|
|
||||||
);
|
);
|
||||||
const protagonistTurretOrigin = new Vector3(
|
const turretOrigin = new Vector3(
|
||||||
protagonistModelDefinition.turret_origin.x,
|
tankModelDefinition.turret_origin.x,
|
||||||
protagonistModelDefinition.turret_origin.y,
|
tankModelDefinition.turret_origin.y,
|
||||||
-protagonistModelDefinition.turret_origin.z,
|
-tankModelDefinition.turret_origin.z,
|
||||||
);
|
|
||||||
const protagonistGunOriginOnlyY = new Vector3(
|
|
||||||
0,
|
|
||||||
protagonistTurretModelDefinition.gun_origin.y,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const turretRotationSpeed = degToRad(protagonistTurret.traverse_speed);
|
|
||||||
const gunRotationSpeed = degToRad(
|
|
||||||
protagonistGun.gun_type!.value.base.rotation_speed,
|
|
||||||
);
|
);
|
||||||
|
const gunOriginOnlyY = new Vector3(0, turretModelDefinition.gun_origin.y, 0);
|
||||||
|
const turretRotationSpeed = degToRad(turret.traverse_speed);
|
||||||
|
const gunRotationSpeed = degToRad(gun.gun_type!.value.base.rotation_speed);
|
||||||
const shellOrigin = new Vector3()
|
const shellOrigin = new Vector3()
|
||||||
.add(protagonistHullOrigin)
|
.add(hullOrigin)
|
||||||
.add(protagonistTurretOrigin)
|
.add(turretOrigin)
|
||||||
.add(protagonistGunOriginOnlyY);
|
.add(gunOriginOnlyY);
|
||||||
|
const camouflage = Duel.use((state) => state.protagonist.camouflage);
|
||||||
|
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||||
|
const consumables = Duel.use((state) => state.protagonist.consumables);
|
||||||
|
const provisions = Duel.use((state) => state.protagonist.provisions);
|
||||||
|
const equipmentMatrix = Duel.use(
|
||||||
|
(state) => state.protagonist.equipmentMatrix,
|
||||||
|
);
|
||||||
|
const characteristics = tankCharacteristics(
|
||||||
|
{
|
||||||
|
tank,
|
||||||
|
turret,
|
||||||
|
gun,
|
||||||
|
shell,
|
||||||
|
consumables,
|
||||||
|
applyDynamicArmor: false,
|
||||||
|
applyReactiveArmor: false,
|
||||||
|
applySpallLiner: false,
|
||||||
|
camouflage,
|
||||||
|
crewSkills,
|
||||||
|
engine,
|
||||||
|
equipmentMatrix,
|
||||||
|
provisions,
|
||||||
|
stockEngine: tank.engines[0],
|
||||||
|
stockGun: tank.turrets[0].guns[0],
|
||||||
|
stockTurret: tank.turrets[0],
|
||||||
|
stockTrack: tank.tracks[0],
|
||||||
|
track,
|
||||||
|
},
|
||||||
|
{ equipmentDefinitions, provisionDefinitions, tankModelDefinition },
|
||||||
|
);
|
||||||
|
const mockTank = useLoader(GLTFLoader, asset(`3d/tanks/models/12305.glb`));
|
||||||
|
|
||||||
useFrame(({ camera }) => {
|
useFrame(({ camera }) => {
|
||||||
if (!material.current) return;
|
if (!material.current) return;
|
||||||
@@ -88,11 +120,10 @@ export function SceneProps() {
|
|||||||
let lastTime = 0;
|
let lastTime = 0;
|
||||||
let DEBUG_disable = false;
|
let DEBUG_disable = false;
|
||||||
|
|
||||||
useFrame(({ raycaster, camera, clock }) => {
|
useFrame(({ raycaster, camera, clock, gl }) => {
|
||||||
if (
|
if (
|
||||||
display !== TankopediaDisplay.ShootingRange ||
|
display !== TankopediaDisplay.ShootingRange ||
|
||||||
!playground.current ||
|
!playground.current ||
|
||||||
!shellPathHelper.current ||
|
|
||||||
!targetCircle.current ||
|
!targetCircle.current ||
|
||||||
DEBUG_disable ||
|
DEBUG_disable ||
|
||||||
!targetCircleDots.current
|
!targetCircleDots.current
|
||||||
@@ -120,9 +151,6 @@ export function SceneProps() {
|
|||||||
direction = path.clone().normalize();
|
direction = path.clone().normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// shellPathHelper.current.setDirection(direction);
|
|
||||||
// shellPathHelper.current.setLength(length);
|
|
||||||
|
|
||||||
const deltaT = clock.elapsedTime - lastTime;
|
const deltaT = clock.elapsedTime - lastTime;
|
||||||
lastTime = clock.elapsedTime;
|
lastTime = clock.elapsedTime;
|
||||||
const desiredPitch = Math.asin(direction.y);
|
const desiredPitch = Math.asin(direction.y);
|
||||||
@@ -139,8 +167,8 @@ export function SceneProps() {
|
|||||||
const [pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
lastPitch + maxDeltaPitch,
|
lastPitch + maxDeltaPitch,
|
||||||
lastYaw + maxDeltaYaw,
|
lastYaw + maxDeltaYaw,
|
||||||
protagonistGunModelDefinition.pitch,
|
gunModelDefinition.pitch,
|
||||||
protagonistTurretModelDefinition.yaw,
|
turretModelDefinition.yaw,
|
||||||
);
|
);
|
||||||
|
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.emit({ pitch, yaw });
|
||||||
@@ -151,9 +179,6 @@ export function SceneProps() {
|
|||||||
.applyAxisAngle(I_HAT, pitch)
|
.applyAxisAngle(I_HAT, pitch)
|
||||||
.applyAxisAngle(J_HAT, yaw);
|
.applyAxisAngle(J_HAT, yaw);
|
||||||
|
|
||||||
shellPathHelper.current.setLength(FAR_DISTANCE);
|
|
||||||
shellPathHelper.current.setDirection(gunDirection);
|
|
||||||
|
|
||||||
raycaster.set(shellOrigin, gunDirection);
|
raycaster.set(shellOrigin, gunDirection);
|
||||||
|
|
||||||
const gunIntersections = raycaster.intersectObjects(
|
const gunIntersections = raycaster.intersectObjects(
|
||||||
@@ -173,15 +198,22 @@ export function SceneProps() {
|
|||||||
|
|
||||||
targetCircle.current.position.copy(gunTarget);
|
targetCircle.current.position.copy(gunTarget);
|
||||||
|
|
||||||
const targetCircleDotsSize = `${gunTarget.distanceTo(shellOrigin)}px`;
|
const distanceToTarget = gunTarget.distanceTo(shellOrigin);
|
||||||
|
const dispersionRadius =
|
||||||
|
characteristics.dispersion * (distanceToTarget / 100);
|
||||||
|
const dispersionSize = 2 * dispersionRadius;
|
||||||
|
const targetDistanceFromCamera = gunTarget.distanceTo(camera.position);
|
||||||
|
document.title = `${targetDistanceFromCamera}`;
|
||||||
|
const angularSize =
|
||||||
|
2 * Math.atan(dispersionSize / (2 * targetDistanceFromCamera));
|
||||||
|
const verticalScreenSize =
|
||||||
|
2 * Math.tan(degToRad((camera as PerspectiveCamera).fov) / 2);
|
||||||
|
const screenSpaceSize =
|
||||||
|
gl.domElement.clientHeight * (angularSize / verticalScreenSize);
|
||||||
|
const targetCircleDotsSize = `${Math.max(38, screenSpaceSize)}px`;
|
||||||
|
|
||||||
targetCircleDots.current.style.width = targetCircleDotsSize;
|
targetCircleDots.current.style.width = targetCircleDotsSize;
|
||||||
targetCircleDots.current.style.height = targetCircleDotsSize;
|
targetCircleDots.current.style.height = targetCircleDotsSize;
|
||||||
|
|
||||||
// window.addEventListener('keydown', (event) => {
|
|
||||||
// if (event.key === 'Enter') {
|
|
||||||
// DEBUG_disable = true;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -195,26 +227,32 @@ export function SceneProps() {
|
|||||||
|
|
||||||
{display === TankopediaDisplay.ShootingRange && (
|
{display === TankopediaDisplay.ShootingRange && (
|
||||||
<>
|
<>
|
||||||
<arrowHelper ref={shellPathHelper} position={shellOrigin} />
|
|
||||||
|
|
||||||
<group ref={targetCircle}>
|
<group ref={targetCircle}>
|
||||||
{/* <sphereGeometry args={[0.1, 32, 32]} />
|
<Html center occlude={false} zIndexRange={[0, 0]}>
|
||||||
<meshStandardMaterial
|
|
||||||
depthTest={false}
|
|
||||||
depthWrite={false}
|
|
||||||
color="red"
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
<Html center occlude={false}>
|
|
||||||
<Box
|
<Box
|
||||||
ref={targetCircleDots}
|
ref={targetCircleDots}
|
||||||
width="10em"
|
width="10em"
|
||||||
height="10em"
|
height="10em"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
border: `0.25rem dotted ${Var('gray-12')}`,
|
border: `0.125rem solid ${Var('green-9')}`,
|
||||||
}}
|
}}
|
||||||
/>
|
position="relative"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
width="1rem"
|
||||||
|
height="1rem"
|
||||||
|
position="absolute"
|
||||||
|
top="50%"
|
||||||
|
left="50%"
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url(${imgur('kU9Xejd')})`,
|
||||||
|
backgroundSize: 'contain',
|
||||||
|
transform: 'translateX(-50%) scale(90%)',
|
||||||
|
borderRadius: '50%',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
</Html>
|
</Html>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
@@ -238,6 +276,13 @@ export function SceneProps() {
|
|||||||
<sphereGeometry args={[0.1, 32, 32]} />
|
<sphereGeometry args={[0.1, 32, 32]} />
|
||||||
<meshStandardMaterial depthTest={false} />
|
<meshStandardMaterial depthTest={false} />
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
|
<group
|
||||||
|
position={[0, 0, -182]}
|
||||||
|
rotation={[-Math.PI / 2, 0, Math.PI]}
|
||||||
|
>
|
||||||
|
<primitive object={mockTank.scene} />
|
||||||
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in new issue
Block a user