From 8b5638f82da20a7ff1dd011c7556e4ebc06fec99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Tue, 23 Jul 2024 21:01:32 -0500 Subject: [PATCH] feat: he splash visual --- .vscode/settings.json | 1 + .../Armor/components/ShotDisplay.tsx | 99 ++++++++++++++++--- .../SpacedArmorSceneComponent/index.tsx | 1 + src/stores/tankopediaEphemeral.ts | 1 + 4 files changed, 91 insertions(+), 11 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 1c2e5ac5f..b663f9824 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "cSpell.words": [ "AABBOX", "Abhi", + "absarc", "accountinfo", "adsense", "aeson", diff --git a/src/components/Armor/components/ShotDisplay.tsx b/src/components/Armor/components/ShotDisplay.tsx index 60544b58a..0c2502c35 100644 --- a/src/components/Armor/components/ShotDisplay.tsx +++ b/src/components/Armor/components/ShotDisplay.tsx @@ -1,9 +1,21 @@ import { Box, Flex, Text } from '@radix-ui/themes'; import { Html } from '@react-three/drei'; import { useFrame } from '@react-three/fiber'; -import { ComponentProps, useRef } from 'react'; -import { Euler, Mesh, Quaternion } from 'three'; -import { J_HAT } from '../../../constants/axis'; +import { clamp } from 'lodash'; +import { ComponentProps, useEffect, useRef } from 'react'; +import { + BufferGeometry, + DoubleSide, + Euler, + Group, + Line, + LineDashedMaterial, + Mesh, + MeshBasicMaterial, + Path, + Quaternion, +} from 'three'; +import { J_HAT, K_HAT } from '../../../constants/axis'; import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral'; import { ShotDisplayCard } from './ShotDisplayCard'; @@ -26,20 +38,65 @@ export function ShotDisplay() { const shot = TankopediaEphemeral.use((state) => state.shot); const inTracer = useRef(null); const outTracer = useRef(null); + const splashRadiusWrapper = useRef(null); + const splashRadiusMaterial = useRef(null); + let animationStartTime: number | null = null; + + useEffect(() => { + if (shot && shot.splashRadius !== undefined) { + const outlineGeometry = new BufferGeometry().setFromPoints( + new Path().absarc(0, 0, 1, 0, Math.PI * 2).getSpacedPoints(50), + ); + const outlineMaterial = new LineDashedMaterial({ + color: 'orange', + dashSize: 0.05, + gapSize: 0.025, + }); + const outline = new Line( + outlineGeometry, + outlineMaterial, + ).computeLineDistances(); + + splashRadiusWrapper.current?.add(outline); + + return () => { + splashRadiusWrapper.current?.remove(outline); + }; + } + }); useFrame(({ clock }) => { - if (!shot || !inTracer.current) return; + if (!shot) return; - const t1 = clock.elapsedTime % 1; - const t2 = (clock.elapsedTime + 0.5) % 1; + const t = clock.elapsedTime; - inTracer.current.scale.set(1, 1 - 2 * Math.abs(t1 - 0.5), 1); - inTracer.current.position.set(0, (1 - t1) * inLength, 0); + if (inTracer.current) { + const tracerT1 = t % 1; + const tracerT2 = (t + 0.5) % 1; - if (!outTracer.current) return; + inTracer.current.scale.set(1, 1 - 2 * Math.abs(tracerT1 - 0.5), 1); + inTracer.current.position.set(0, (1 - tracerT1) * inLength, 0); - outTracer.current.scale.set(1, 1 - 2 * Math.abs(t2 - 0.5), 1); - outTracer.current.position.set(0, t2 * outLength, 0); + if (outTracer.current) { + outTracer.current.scale.set(1, 1 - 2 * Math.abs(tracerT2 - 0.5), 1); + outTracer.current.position.set(0, tracerT2 * outLength, 0); + } + } + + if ( + shot.splashRadius !== undefined && + splashRadiusWrapper.current && + splashRadiusMaterial.current + ) { + if (animationStartTime === null) animationStartTime = t; + + const scale = + clamp(4 * (t - animationStartTime), 0, 1) * shot.splashRadius; + const opacity = 1 - clamp(2 * (t - animationStartTime), 0, 1); + + splashRadiusWrapper.current.scale.set(scale, scale, scale); + splashRadiusMaterial.current.opacity = opacity; + } }); if (!shot) return null; @@ -68,6 +125,26 @@ export function ShotDisplay() { return ( + {shot.splashRadius && ( + + + + + + + )} +