mirror of
https://github.com/ad044/lainTSX.git
synced 2024-10-22 23:19:06 +00:00
node explosion almost done, imported node rip anim
This commit is contained in:
parent
715c924cde
commit
2c4dff77b3
10 changed files with 214 additions and 55 deletions
|
@ -13,6 +13,7 @@ import ripMiddleRingSpriteSheet from "../../static/sprite/rip_middle_ring.png";
|
|||
import knockSpriteSheet from "../../static/sprite/knock.png";
|
||||
import knockAndFallSpriteSheet from "../../static/sprite/knock_and_fall.png";
|
||||
import touchAndScareSpriteSheet from "../../static/sprite/touch_and_scare.png";
|
||||
import ripNodeSpriteSheet from "../../static/sprite/rip_node.png";
|
||||
import { useLainStore, useMainSceneStore } from "../../store";
|
||||
|
||||
type LainConstructorProps = {
|
||||
|
@ -162,6 +163,18 @@ export const LainTouchAndScare = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const LainRipNode = () => {
|
||||
return (
|
||||
<LainConstructor
|
||||
sprite={ripNodeSpriteSheet}
|
||||
frameCount={60}
|
||||
framesHorizontal={8}
|
||||
framesVertical={8}
|
||||
fps={60 * 0.17}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type LainProps = {
|
||||
shouldIntro: boolean;
|
||||
};
|
||||
|
@ -179,7 +192,7 @@ const Lain = (props: LainProps) => {
|
|||
select_level_up: <LainMoveUp />,
|
||||
throw_node: <LainThrowNode />,
|
||||
pause_game: <LainRipMiddleRing />,
|
||||
test: <LainTouchAndScare />,
|
||||
test: <LainRipNode />,
|
||||
};
|
||||
|
||||
const [introFinished, setIntroFinished] = useState(false);
|
||||
|
|
|
@ -74,17 +74,14 @@ const Node = (props: NodeContructorProps) => {
|
|||
|
||||
const nonActiveTexture = useLoader(THREE.TextureLoader, sprite[0]);
|
||||
const activeTexture = useLoader(THREE.TextureLoader, sprite[1]);
|
||||
const goldTexture = useLoader(THREE.TextureLoader, sprite[2]);
|
||||
|
||||
const uniforms = useMemo(
|
||||
() => ({
|
||||
tex1: { type: "t", value: nonActiveTexture },
|
||||
tex2: { type: "t", value: activeTexture },
|
||||
tex3: { type: "t", value: goldTexture },
|
||||
goldTextureBias: { value: 0 },
|
||||
timeMSeconds: { value: (Date.now() % (Math.PI * 2000)) / 1000.0 },
|
||||
}),
|
||||
[nonActiveTexture, activeTexture, goldTexture]
|
||||
[nonActiveTexture, activeTexture]
|
||||
);
|
||||
|
||||
const vertexShader = `
|
||||
|
@ -101,9 +98,7 @@ const Node = (props: NodeContructorProps) => {
|
|||
|
||||
uniform sampler2D tex1;
|
||||
uniform sampler2D tex2;
|
||||
uniform sampler2D tex3;
|
||||
uniform float timeMSeconds;
|
||||
uniform float goldTextureBias;
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
|
@ -112,9 +107,8 @@ const Node = (props: NodeContructorProps) => {
|
|||
void main() {
|
||||
vec4 t1 = texture2D(tex1,vUv);
|
||||
vec4 t2 = texture2D(tex2,vUv);
|
||||
vec4 t3 = texture2D(tex3,vUv);
|
||||
float bias = abs(sin(timeMSeconds / (1.6 / M_PI)));
|
||||
gl_FragColor = mix(mix(t1, t2, bias), t3, goldTextureBias);
|
||||
gl_FragColor = mix(t1, t2, bias);
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -127,8 +121,7 @@ const Node = (props: NodeContructorProps) => {
|
|||
activeNodePosY,
|
||||
activeNodePosZ,
|
||||
activeNodeRotZ,
|
||||
activeNodeRotY,
|
||||
goldTextureBias,
|
||||
activeNodeVisible,
|
||||
},
|
||||
set,
|
||||
] = useSpring(() => ({
|
||||
|
@ -145,10 +138,7 @@ const Node = (props: NodeContructorProps) => {
|
|||
activeNodeRotZ: useNodeStore.getState().activeNodeState.interactedWith
|
||||
? useNodeStore.getState().activeNodeState.rotZ
|
||||
: 0,
|
||||
activeNodeRotY: useNodeStore.getState().activeNodeState.interactedWith
|
||||
? useNodeStore.getState().activeNodeState.rotY
|
||||
: props.rotation[1],
|
||||
goldTextureBias: useNodeStore.getState().activeNodeState.goldTextureBias,
|
||||
activeNodeVisible: true,
|
||||
config: { duration: 800 },
|
||||
}));
|
||||
|
||||
|
@ -166,10 +156,7 @@ const Node = (props: NodeContructorProps) => {
|
|||
activeNodeRotZ: useNodeStore.getState().activeNodeState.interactedWith
|
||||
? state.activeNodeState.rotZ
|
||||
: 0,
|
||||
activeNodeRotY: useNodeStore.getState().activeNodeState.interactedWith
|
||||
? state.activeNodeState.rotY
|
||||
: props.rotation[1],
|
||||
goldTextureBias: useNodeStore.getState().activeNodeState.goldTextureBias,
|
||||
activeNodeVisible: useNodeStore.getState().activeNodeState.visible,
|
||||
}));
|
||||
}, [
|
||||
props.level,
|
||||
|
@ -185,7 +172,6 @@ const Node = (props: NodeContructorProps) => {
|
|||
if (materialRef.current) {
|
||||
materialRef.current.uniforms.timeMSeconds.value =
|
||||
(Date.now() % (Math.PI * 2000)) / 1000.0;
|
||||
materialRef.current.uniforms.goldTextureBias.value = goldTextureBias.get();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -203,7 +189,8 @@ const Node = (props: NodeContructorProps) => {
|
|||
position-y={activeNodePosY}
|
||||
position-z={activeNodePosZ}
|
||||
rotation-z={activeNodeRotZ}
|
||||
rotation-y={activeNodeRotY}
|
||||
rotation-y={props.rotation[1]}
|
||||
visible={activeNodeVisible}
|
||||
scale={[0.36, 0.18, 0.36]}
|
||||
renderOrder={1}
|
||||
>
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
import React, { useMemo, useRef, useState } from "react";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import ExplosionLine from "./NodeExplosion/ExplosionLine";
|
||||
import node_explosion_line_positions from "../../../../resources/node_explosion_line_positions.json";
|
||||
|
||||
import { useFrame } from "react-three-fiber";
|
||||
import GoldNode from "./NodeExplosion/GoldNode";
|
||||
import { useNodeStore } from "../../../../store";
|
||||
|
||||
const NodeExplosion = () => {
|
||||
const explosionVisible = useNodeStore(
|
||||
(state) => state.activeNodeState.exploding
|
||||
);
|
||||
|
||||
const [shouldAnimate, setShouldAnimate] = useState(false);
|
||||
const [shouldRotate, setShouldRotate] = useState(false);
|
||||
const [currentFrame, setCurrentFrame] = useState(1);
|
||||
|
||||
const linePoses = useMemo(
|
||||
|
@ -17,26 +26,53 @@ const NodeExplosion = () => {
|
|||
const lastTime = useRef(0);
|
||||
|
||||
useFrame(() => {
|
||||
if (shouldAnimate) {
|
||||
const now = Date.now();
|
||||
if (now > lastTime.current + 2400) {
|
||||
if (now > lastTime.current + 100) {
|
||||
if (currentFrame < 6) {
|
||||
setCurrentFrame(currentFrame + 1);
|
||||
lastTime.current = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
useEffect(() => {
|
||||
if (explosionVisible) {
|
||||
setTimeout(() => {
|
||||
setShouldRotate(true);
|
||||
}, 300);
|
||||
setTimeout(() => {
|
||||
setShouldAnimate(true);
|
||||
}, 1100);
|
||||
} else {
|
||||
setShouldAnimate(false);
|
||||
setShouldRotate(false);
|
||||
setCurrentFrame(1);
|
||||
}
|
||||
}, [explosionVisible]);
|
||||
|
||||
return explosionVisible ? (
|
||||
<group position={[-0.5, 0.45, 0]}>
|
||||
{Object.values(linePoses).map((entry) => (
|
||||
<group
|
||||
visible={shouldAnimate}
|
||||
position={[-0.1, 0.1, 0]}
|
||||
scale={[1.2, 1.2, 1.2]}
|
||||
>
|
||||
{Object.values(linePoses).map((entry, idx) => (
|
||||
<ExplosionLine
|
||||
rotation={entry.rotation as [number, number, number]}
|
||||
position={entry.position as [number, number, number]}
|
||||
color={entry.color}
|
||||
length={entry.length}
|
||||
key={idx}
|
||||
/>
|
||||
))}
|
||||
</group>
|
||||
<GoldNode visible={shouldRotate} goldTexture={shouldAnimate} />
|
||||
</group>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ const ExplosionLine = (props: LineProps) => {
|
|||
float alpha = smoothstep(1.0, 0.0, vUv.y);
|
||||
float colorMix = smoothstep(1.0, 2.0, 1.8);
|
||||
|
||||
gl_FragColor = vec4(mix(color1, color2, colorMix), alpha) * 0.8;
|
||||
gl_FragColor = vec4(mix(color1, color2, colorMix), alpha) * 0.7;
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import * as THREE from "three";
|
||||
import { useFrame, useLoader } from "react-three-fiber";
|
||||
import Cou from "../../../../../static/sprite/Cou.png";
|
||||
import CouGold from "../../../../../static/sprite/Cou_gold.png";
|
||||
import Dc from "../../../../../static/sprite/Dc.png";
|
||||
import DcGold from "../../../../../static/sprite/Dc_gold.png";
|
||||
import SSkn from "../../../../../static/sprite/SSkn.png";
|
||||
import SSKnGold from "../../../../../static/sprite/SSkn_gold.png";
|
||||
import Tda from "../../../../../static/sprite/Tda.png";
|
||||
import TdaGold from "../../../../../static/sprite/Tda_gold.png";
|
||||
import Dia from "../../../../../static/sprite/Dia.png";
|
||||
import DiaGold from "../../../../../static/sprite/Dia_gold.png";
|
||||
import Lda from "../../../../../static/sprite/Lda.png";
|
||||
import LdaGold from "../../../../../static/sprite/Lda_gold.png";
|
||||
import MULTI from "../../../../../static/sprite/MULTI.png";
|
||||
import MULTIGold from "../../../../../static/sprite/MULTI_gold.png";
|
||||
import {
|
||||
useLevelStore,
|
||||
useNodeStore,
|
||||
useSiteStore,
|
||||
} from "../../../../../store";
|
||||
import site_a from "../../../../../resources/site_a.json";
|
||||
import site_b from "../../../../../resources/site_b.json";
|
||||
import { SiteType } from "../../Site";
|
||||
|
||||
type GLTFResult = GLTF & {
|
||||
nodes: {
|
||||
Cube: THREE.Mesh;
|
||||
};
|
||||
materials: {
|
||||
Material: THREE.MeshStandardMaterial;
|
||||
};
|
||||
};
|
||||
|
||||
type GoldNodeProps = {
|
||||
visible: boolean;
|
||||
goldTexture: boolean;
|
||||
};
|
||||
|
||||
const GoldNode = (props: GoldNodeProps) => {
|
||||
const { nodes } = useLoader<GLTFResult>(GLTFLoader, "models/goldNode.glb");
|
||||
const activeNodeId = useNodeStore((state) => state.activeNodeState.id);
|
||||
const activeLevel = useLevelStore((state) => state.activeLevel);
|
||||
|
||||
const currentSite = useSiteStore((state) => state.currentSite);
|
||||
|
||||
const siteData = currentSite === "a" ? site_a : site_b;
|
||||
|
||||
const activeNodeData = (siteData as SiteType)[activeLevel][activeNodeId];
|
||||
const activeNodeName = activeNodeData.node_name;
|
||||
|
||||
const activeNodeNameToPath = useMemo(() => {
|
||||
if (activeNodeName.includes("S")) {
|
||||
return [SSkn, SSKnGold];
|
||||
} else if (
|
||||
activeNodeName.startsWith("P") ||
|
||||
activeNodeName.startsWith("G") ||
|
||||
activeNodeName.includes("?")
|
||||
) {
|
||||
return [MULTI, MULTIGold];
|
||||
} else if (activeNodeName.includes("Dc")) {
|
||||
return [Dc, DcGold];
|
||||
} else {
|
||||
const activeNodeNameAssocs = {
|
||||
Tda: [Tda, TdaGold],
|
||||
Cou: [Cou, CouGold],
|
||||
Dia: [Dia, DiaGold],
|
||||
Lda: [Lda, LdaGold],
|
||||
Ere: [MULTI, MULTIGold],
|
||||
Ekm: [MULTI, MULTIGold],
|
||||
Eda: [MULTI, MULTIGold],
|
||||
TaK: [MULTI, MULTIGold],
|
||||
Env: [MULTI, MULTIGold],
|
||||
};
|
||||
|
||||
return activeNodeNameAssocs[
|
||||
activeNodeName.substr(0, 3) as keyof typeof activeNodeNameAssocs
|
||||
];
|
||||
}
|
||||
}, [activeNodeName]);
|
||||
|
||||
const r = useRef<THREE.Object3D>();
|
||||
|
||||
const regularTex = useLoader(THREE.TextureLoader, activeNodeNameToPath[0]);
|
||||
const goldTex = useLoader(THREE.TextureLoader, activeNodeNameToPath[1]);
|
||||
|
||||
useEffect(() => {
|
||||
if (r.current && !props.visible) {
|
||||
r.current.rotation.y = -1.2;
|
||||
r.current.rotation.z = 0;
|
||||
}
|
||||
}, [props.visible]);
|
||||
|
||||
useFrame(() => {
|
||||
if (r.current && props.visible) {
|
||||
r.current.rotation.y -= 0.01;
|
||||
r.current.rotation.z += 0.01;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh
|
||||
// geometry={nodes.Cube.geometry}
|
||||
position={[-0.18, -0.45, 0]}
|
||||
rotation={[0, -1.2, 0]}
|
||||
scale={[0.24, 0.19, 0.34]}
|
||||
ref={r}
|
||||
>
|
||||
<boxBufferGeometry attach="geometry" />
|
||||
<meshBasicMaterial
|
||||
attach="material"
|
||||
map={props.goldTexture ? goldTex : regularTex}
|
||||
transparent={true}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoldNode;
|
|
@ -12,6 +12,7 @@ import boringHudSpriteSheet from "../static/sprite/long_hud_boring.png";
|
|||
import boringHudMirroredSpriteSheet from "../static/sprite/long_hud_boring_mirrored.png";
|
||||
import throwNodeSpriteSheet from "../static/sprite/throw_node.png";
|
||||
import ripMiddleRingSpriteSheet from "../static/sprite/rip_middle_ring.png";
|
||||
import ripNodeSpriteSheet from "../static/sprite/rip_node.png";
|
||||
|
||||
import * as THREE from "three";
|
||||
import { useLoader, useThree } from "react-three-fiber";
|
||||
|
@ -46,6 +47,7 @@ const Preloader = () => {
|
|||
THREE.TextureLoader,
|
||||
ripMiddleRingSpriteSheet
|
||||
);
|
||||
const ripNode = useLoader(THREE.TextureLoader, ripNodeSpriteSheet);
|
||||
|
||||
const { gl } = useThree();
|
||||
useLayoutEffect(() => {
|
||||
|
@ -63,6 +65,7 @@ const Preloader = () => {
|
|||
gl.initTexture(boringHudMirrored);
|
||||
gl.initTexture(throwNode);
|
||||
gl.initTexture(ripMiddleRing);
|
||||
gl.initTexture(ripNode);
|
||||
}, [
|
||||
moveDown,
|
||||
moveUp,
|
||||
|
@ -79,6 +82,7 @@ const Preloader = () => {
|
|||
intro,
|
||||
throwNode,
|
||||
ripMiddleRing,
|
||||
ripNode,
|
||||
]);
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -101,19 +101,23 @@ const NodeManager = (props: StateManagerProps) => {
|
|||
setActiveNodeState(0, "posY");
|
||||
|
||||
setTimeout(() => {
|
||||
setActiveNodeState(Math.PI, "rotZ");
|
||||
setActiveNodeState(Math.PI, "rotY");
|
||||
setActiveNodeState(Math.PI, "rotX");
|
||||
setActiveNodeState(1, "goldTextureBias");
|
||||
setActiveNodeState(true, "exploding");
|
||||
}, 1200);
|
||||
|
||||
setTimeout(() => {
|
||||
setActiveNodeState(false, "visible");
|
||||
setActiveNodeState(false, "interactedWith");
|
||||
setActiveNodeState(0, "rotZ");
|
||||
setActiveNodeState(0, "rotY");
|
||||
setActiveNodeState(0, "rotX");
|
||||
setActiveNodeState(0, "goldTextureBias");
|
||||
}, 2500);
|
||||
}, 1400);
|
||||
|
||||
setTimeout(() => {
|
||||
setActiveNodeState(false, "exploding");
|
||||
}, 3150);
|
||||
|
||||
setTimeout(() => {
|
||||
setActiveNodeState(true, "visible");
|
||||
}, 3500);
|
||||
},
|
||||
[setActiveNodeState]
|
||||
);
|
||||
|
|
|
@ -128,12 +128,6 @@
|
|||
"length": 0.9,
|
||||
"color": "yellow"
|
||||
},
|
||||
{
|
||||
"position": [-0.5, -0.1, 0],
|
||||
"rotation": [0, 0, -0.65],
|
||||
"length": 0.8,
|
||||
"color": "yellow"
|
||||
},
|
||||
{
|
||||
"position": [0.4, -0.25, 0],
|
||||
"rotation": [0, 0, -1.15],
|
||||
|
|
|
@ -39,7 +39,7 @@ const MainScene = () => {
|
|||
<pointLight color={0xffffff} position={[-8, 0, 0]} intensity={0.2} />
|
||||
<NodeExplosion />
|
||||
</a.group>
|
||||
{/*<Lain shouldIntro={shouldIntro} />*/}
|
||||
<Lain shouldIntro={shouldIntro} />
|
||||
</Suspense>
|
||||
</perspectiveCamera>
|
||||
);
|
||||
|
|
|
@ -62,10 +62,10 @@ type NodeState = {
|
|||
posX: number;
|
||||
posZ: number;
|
||||
posY: number;
|
||||
rotY: number;
|
||||
rotZ: number;
|
||||
interactedWith: boolean;
|
||||
goldTextureBias: number;
|
||||
exploding: boolean;
|
||||
visible: boolean;
|
||||
};
|
||||
nodeMatrixIndices: { matrixIdx: number; rowIdx: number; colIdx: number };
|
||||
gameProgress: typeof game_progress;
|
||||
|
@ -295,10 +295,10 @@ export const useNodeStore = create(
|
|||
posX: 0,
|
||||
posZ: 0,
|
||||
rotZ: 0,
|
||||
rotY: 0,
|
||||
posY: 0,
|
||||
interactedWith: false,
|
||||
goldTextureBias: 0,
|
||||
exploding: false,
|
||||
visible: true,
|
||||
},
|
||||
nodeMatrixIndices: { matrixIdx: 7, rowIdx: 0, colIdx: 0 },
|
||||
gameProgress: game_progress,
|
||||
|
|
Loading…
Reference in a new issue