mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: he splash visual
This commit is contained in:
1 parent
16500793a5
commit
8b5638f82d
4 files changed
+91
-11
No files matched your search
Vendored
+1
@@ -2,6 +2,7 @@
|
|||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"AABBOX",
|
"AABBOX",
|
||||||
"Abhi",
|
"Abhi",
|
||||||
|
"absarc",
|
||||||
"accountinfo",
|
"accountinfo",
|
||||||
"adsense",
|
"adsense",
|
||||||
"aeson",
|
"aeson",
|
||||||
|
|||||||
@@ -1,9 +1,21 @@
|
|||||||
import { Box, Flex, Text } from '@radix-ui/themes';
|
import { Box, Flex, Text } from '@radix-ui/themes';
|
||||||
import { Html } from '@react-three/drei';
|
import { Html } from '@react-three/drei';
|
||||||
import { useFrame } from '@react-three/fiber';
|
import { useFrame } from '@react-three/fiber';
|
||||||
import { ComponentProps, useRef } from 'react';
|
import { clamp } from 'lodash';
|
||||||
import { Euler, Mesh, Quaternion } from 'three';
|
import { ComponentProps, useEffect, useRef } from 'react';
|
||||||
import { J_HAT } from '../../../constants/axis';
|
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 * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
|
||||||
import { ShotDisplayCard } from './ShotDisplayCard';
|
import { ShotDisplayCard } from './ShotDisplayCard';
|
||||||
|
|
||||||
@@ -26,20 +38,65 @@ export function ShotDisplay() {
|
|||||||
const shot = TankopediaEphemeral.use((state) => state.shot);
|
const shot = TankopediaEphemeral.use((state) => state.shot);
|
||||||
const inTracer = useRef<Mesh>(null);
|
const inTracer = useRef<Mesh>(null);
|
||||||
const outTracer = useRef<Mesh>(null);
|
const outTracer = useRef<Mesh>(null);
|
||||||
|
const splashRadiusWrapper = useRef<Group>(null);
|
||||||
|
const splashRadiusMaterial = useRef<MeshBasicMaterial>(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 }) => {
|
useFrame(({ clock }) => {
|
||||||
if (!shot || !inTracer.current) return;
|
if (!shot) return;
|
||||||
|
|
||||||
const t1 = clock.elapsedTime % 1;
|
const t = clock.elapsedTime;
|
||||||
const t2 = (clock.elapsedTime + 0.5) % 1;
|
|
||||||
|
|
||||||
inTracer.current.scale.set(1, 1 - 2 * Math.abs(t1 - 0.5), 1);
|
if (inTracer.current) {
|
||||||
inTracer.current.position.set(0, (1 - t1) * inLength, 0);
|
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);
|
if (outTracer.current) {
|
||||||
outTracer.current.position.set(0, t2 * outLength, 0);
|
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;
|
if (!shot) return null;
|
||||||
@@ -68,6 +125,26 @@ export function ShotDisplay() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<group>
|
<group>
|
||||||
|
{shot.splashRadius && (
|
||||||
|
<group
|
||||||
|
position={inLast.point}
|
||||||
|
ref={splashRadiusWrapper}
|
||||||
|
rotation={new Euler().setFromQuaternion(
|
||||||
|
new Quaternion().setFromUnitVectors(K_HAT, shot.in.surfaceNormal),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<mesh>
|
||||||
|
<circleGeometry />
|
||||||
|
<meshBasicMaterial
|
||||||
|
side={DoubleSide}
|
||||||
|
color="orange"
|
||||||
|
transparent
|
||||||
|
ref={splashRadiusMaterial}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
</group>
|
||||||
|
)}
|
||||||
|
|
||||||
<Html
|
<Html
|
||||||
position={inLast.point}
|
position={inLast.point}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export function SpacedArmorSceneComponent({
|
|||||||
const shell = antagonist.shell;
|
const shell = antagonist.shell;
|
||||||
const cameraNormal = camera.position.clone().sub(point).normalize();
|
const cameraNormal = camera.position.clone().sub(point).normalize();
|
||||||
const shot: TankopediaEphemeral.Shot = {
|
const shot: TankopediaEphemeral.Shot = {
|
||||||
|
splashRadius: shell.explosionRadius,
|
||||||
damage: -1,
|
damage: -1,
|
||||||
containsGaps: shell.type === ShellType.HEAT,
|
containsGaps: shell.type === ShellType.HEAT,
|
||||||
in: {
|
in: {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export type ShotLayer = ShotLayerExternal | ShotLayerNonExternal | ShotLayerGap;
|
|||||||
export type Shot = {
|
export type Shot = {
|
||||||
containsGaps: boolean;
|
containsGaps: boolean;
|
||||||
damage: number;
|
damage: number;
|
||||||
|
splashRadius?: number;
|
||||||
|
|
||||||
in: {
|
in: {
|
||||||
surfaceNormal: Vector3;
|
surfaceNormal: Vector3;
|
||||||
|
|||||||
Reference in new issue
Block a user