mirror of
https://github.com/ad044/lainTSX.git
synced 2024-10-22 23:19:06 +00:00
word sleection animation
This commit is contained in:
parent
1f3126e1b9
commit
dc545ba522
12 changed files with 129 additions and 121 deletions
|
@ -1,4 +1,4 @@
|
|||
import React, { Suspense, useEffect, useMemo, useState } from "react";
|
||||
import React, { Suspense, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useLoader } from "react-three-fiber";
|
||||
import * as THREE from "three";
|
||||
import { PlainSingularAnimator } from "three-plain-animator/lib/plain-singular-animator";
|
||||
|
@ -66,6 +66,7 @@ export const LainConstructor = (props: LainConstructorProps) => {
|
|||
attach="material"
|
||||
map={lainSpriteTexture}
|
||||
alphaTest={0.01}
|
||||
color={0xffffff}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -355,6 +356,8 @@ type LainProps = {
|
|||
const Lain = (props: LainProps) => {
|
||||
const lainMoveState = useStore((state) => state.lainMoveState);
|
||||
|
||||
const wordSelected = useStore((state) => state.wordSelected);
|
||||
|
||||
const lainAnimationDispatch = useMemo(() => {
|
||||
const anims = {
|
||||
standing: <LainStanding />,
|
||||
|
@ -401,9 +404,27 @@ const Lain = (props: LainProps) => {
|
|||
return props.shouldIntro ? introFinished : true;
|
||||
}, [introFinished, props.shouldIntro]);
|
||||
|
||||
const lainRef = useRef<THREE.Sprite>();
|
||||
|
||||
const glowColor = useMemo(() => new THREE.Color(2, 2, 2), []);
|
||||
const regularColor = useMemo(() => new THREE.Color(1, 1, 1), []);
|
||||
|
||||
useEffect(() => {
|
||||
if (wordSelected)
|
||||
setTimeout(() => {
|
||||
if (lainRef.current) lainRef.current.material.color = glowColor;
|
||||
}, 3100);
|
||||
}, [glowColor, wordSelected]);
|
||||
|
||||
useFrame(() => {
|
||||
if (lainRef.current) {
|
||||
lainRef.current.material.color.lerp(regularColor, 0.07);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<sprite scale={[4.5, 4.5, 4.5]} position={[0, -0.15, 0]}>
|
||||
<sprite scale={[4.5, 4.5, 4.5]} position={[0, -0.15, 0]} ref={lainRef}>
|
||||
{stopIntroAnim ? lainAnimationDispatch : <LainIntro />}
|
||||
</sprite>
|
||||
</Suspense>
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import HUD from "./SyncedComponents/HUD";
|
||||
import YellowTextRenderer from "../TextRenderer/YellowTextRenderer";
|
||||
import YellowOrb from "./SyncedComponents/YellowOrb";
|
||||
import GrayPlanes from "./SyncedComponents/GrayPlanes";
|
||||
import Starfield from "./SyncedComponents/Starfield";
|
||||
import Site from "./SyncedComponents/Site";
|
||||
import MiddleRing from "./SyncedComponents/MiddleRing";
|
||||
import Lain from "./Lain";
|
||||
import { useStore } from "../../store";
|
||||
|
||||
type SyncedComponentLoaderProps = {
|
||||
paused: boolean;
|
||||
shouldIntro: boolean;
|
||||
};
|
||||
|
||||
const SyncedComponentLoader = (props: SyncedComponentLoaderProps) => {
|
||||
const [introFinished, setIntroFinished] = useState(false);
|
||||
const [paused, setPaused] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.shouldIntro) {
|
||||
document.getElementsByTagName("canvas")[0].className =
|
||||
"main-scene-background";
|
||||
}
|
||||
setTimeout(() => {
|
||||
setIntroFinished(true);
|
||||
document.getElementsByTagName("canvas")[0].className =
|
||||
"main-scene-background";
|
||||
}, 4000);
|
||||
}, [props.shouldIntro]);
|
||||
|
||||
const subscene = useStore((state) => state.mainSubscene);
|
||||
|
||||
useEffect(() => {
|
||||
if (subscene === "pause") {
|
||||
setTimeout(() => {
|
||||
setPaused(true);
|
||||
}, 3400);
|
||||
} else {
|
||||
setPaused(false);
|
||||
}
|
||||
}, [subscene]);
|
||||
|
||||
const visible = useMemo(() => {
|
||||
return props.shouldIntro ? introFinished : true;
|
||||
}, [introFinished, props.shouldIntro]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<group visible={visible && !paused}>
|
||||
<HUD />
|
||||
<YellowTextRenderer />
|
||||
<YellowOrb visible={visible && !paused} />
|
||||
<MiddleRing />
|
||||
<GrayPlanes />
|
||||
</group>
|
||||
<Starfield
|
||||
visible={!paused}
|
||||
shouldIntro={props.shouldIntro}
|
||||
introFinished={introFinished}
|
||||
/>
|
||||
<Site shouldIntro={props.shouldIntro} introFinished={introFinished} />
|
||||
<Lain shouldIntro={props.shouldIntro} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SyncedComponentLoader;
|
|
@ -158,6 +158,8 @@ const MiddleRing = () => {
|
|||
|
||||
const clock = new THREE.Clock();
|
||||
|
||||
const wordSelected = useStore((state) => state.wordSelected);
|
||||
|
||||
const [wobbleAmp, setWobbleAmp] = useState(0);
|
||||
const [noiseAmp, setNoiseAmp] = useState(0.03);
|
||||
const [rotating, setRotating] = useState(true);
|
||||
|
@ -352,6 +354,16 @@ const MiddleRing = () => {
|
|||
}, 950);
|
||||
};
|
||||
|
||||
const afterWordSelection = () => {
|
||||
setRotating(true);
|
||||
setRot({ rotX: -0.4 });
|
||||
|
||||
// reset the rotation value to 0
|
||||
setTimeout(() => {
|
||||
setRot({ rotZ: 0, rotX: 0 });
|
||||
}, 3100);
|
||||
};
|
||||
|
||||
if (prevData?.siteRotY !== undefined && prevData?.siteRotY !== siteRotY) {
|
||||
rotate(prevData?.siteRotY > siteRotY ? [-0.07, 0.03] : [0.07, -0.03]);
|
||||
} else if (
|
||||
|
@ -367,6 +379,8 @@ const MiddleRing = () => {
|
|||
pause();
|
||||
} else if (subscene === "site" && prevData?.subscene === "pause") {
|
||||
unpause();
|
||||
} else if (wordSelected) {
|
||||
afterWordSelection();
|
||||
}
|
||||
}, [
|
||||
activeLevel,
|
||||
|
@ -377,6 +391,7 @@ const MiddleRing = () => {
|
|||
setRot,
|
||||
siteRotY,
|
||||
subscene,
|
||||
wordSelected,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -10,8 +10,8 @@ import * as THREE from "three";
|
|||
import site_a from "../../../resources/site_a.json";
|
||||
import site_b from "../../../resources/site_b.json";
|
||||
import level_y_values from "../../../resources/level_y_values.json";
|
||||
import usePrevious from "../../../hooks/usePrevious";
|
||||
import { filterInvisibleNodes } from "../../../utils/node-utils";
|
||||
import usePrevious from "../../../hooks/usePrevious";
|
||||
|
||||
export type NodeDataType = {
|
||||
id: string;
|
||||
|
@ -48,33 +48,34 @@ type SiteProps = {
|
|||
};
|
||||
|
||||
const Site = (props: SiteProps) => {
|
||||
const activeLevel = useStore((state) => state.activeLevel);
|
||||
const wordSelected = useStore((state) => state.wordSelected);
|
||||
|
||||
const [state, setState] = useSpring(() => ({
|
||||
posY: -level_y_values[
|
||||
useStore.getState().activeLevel as keyof typeof level_y_values
|
||||
],
|
||||
rotX: useStore.getState().siteRot[0],
|
||||
rotY: useStore.getState().siteRot[1],
|
||||
const [rotState, setRot] = useSpring(() => ({
|
||||
x: wordSelected ? 0 : useStore.getState().siteRot[0],
|
||||
y: wordSelected ? 0 : useStore.getState().siteRot[1],
|
||||
config: { duration: 1200 },
|
||||
}));
|
||||
|
||||
const [posState, setPos] = useSpring(() => ({
|
||||
y: wordSelected
|
||||
? 0
|
||||
: -level_y_values[
|
||||
useStore.getState().activeLevel as keyof typeof level_y_values
|
||||
],
|
||||
delay: 1300,
|
||||
config: { duration: 1200 },
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setState({
|
||||
posY: -level_y_values[activeLevel as keyof typeof level_y_values],
|
||||
});
|
||||
}, 1200);
|
||||
}, [activeLevel, setState]);
|
||||
|
||||
useEffect(() => {
|
||||
useStore.subscribe(setState, (state) => {
|
||||
return {
|
||||
rotX: state.siteRot[0],
|
||||
rotY: state.siteRot[1],
|
||||
};
|
||||
});
|
||||
}, [setState]);
|
||||
useStore.subscribe(setRot, (state) => ({
|
||||
x: state.siteRot[0],
|
||||
y: state.siteRot[1],
|
||||
}));
|
||||
useStore.subscribe(setPos, (state) => ({
|
||||
y: -level_y_values[state.activeLevel as keyof typeof level_y_values],
|
||||
delay: 1300,
|
||||
}));
|
||||
}, [setPos, setRot]);
|
||||
|
||||
const introWrapperRef = useRef<THREE.Object3D>();
|
||||
|
||||
|
@ -110,8 +111,8 @@ const Site = (props: SiteProps) => {
|
|||
return (
|
||||
<Suspense fallback={null}>
|
||||
<a.group ref={introWrapperRef}>
|
||||
<a.group rotation-x={state.rotX}>
|
||||
<a.group rotation-y={state.rotY} position-y={state.posY}>
|
||||
<a.group rotation-x={rotState.x}>
|
||||
<a.group rotation-y={rotState.y} position-y={posState.y}>
|
||||
<ActiveLevelNodes visibleNodes={visibleNodes} />
|
||||
<InactiveLevelNodes visibleNodes={visibleNodes} />
|
||||
<Rings
|
||||
|
|
|
@ -2,7 +2,6 @@ import React, { useEffect, useState, memo } from "react";
|
|||
import Star from "./Starfield/Star";
|
||||
|
||||
type StarfieldProps = {
|
||||
visible: boolean;
|
||||
shouldIntro: boolean;
|
||||
introFinished: boolean;
|
||||
};
|
||||
|
@ -53,7 +52,7 @@ const Starfield = memo((props: StarfieldProps) => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<group visible={props.visible}>
|
||||
<>
|
||||
<group
|
||||
position={[0, -1, 2]}
|
||||
visible={props.shouldIntro ? mainVisible : true}
|
||||
|
@ -126,7 +125,7 @@ const Starfield = memo((props: StarfieldProps) => {
|
|||
) : (
|
||||
<></>
|
||||
)}
|
||||
</group>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -156,13 +156,13 @@ const nodeManager = (eventState: any) => {
|
|||
case "site_right":
|
||||
case "select_level_up":
|
||||
case "select_level_down":
|
||||
case "media_fstWord_select":
|
||||
case "media_sndWord_select":
|
||||
case "media_thirdWord_select":
|
||||
return {
|
||||
action: () => updateActiveNode(eventState.node, 3900),
|
||||
};
|
||||
case "change_node":
|
||||
case "media_fstWord_select":
|
||||
case "media_sndWord_select":
|
||||
case "media_thirdWord_select":
|
||||
return {
|
||||
action: () => updateActiveNode(eventState.node),
|
||||
};
|
||||
|
|
|
@ -5,7 +5,6 @@ const siteManager = (eventState: any) => {
|
|||
const setRotX = useStore.getState().setSiteRotX;
|
||||
|
||||
const dispatchAction = (eventState: any) => {
|
||||
console.log(eventState.siteRotY);
|
||||
switch (eventState.event) {
|
||||
case "site_left":
|
||||
case "site_right":
|
||||
|
|
|
@ -7,6 +7,8 @@ const mediaManager = (eventState: any) => {
|
|||
const setLeftComponentMatrixIdx = useStore.getState()
|
||||
.setMediaLeftComponentMatrixIdx;
|
||||
|
||||
const setWordSelected = useStore.getState().setWordSelected;
|
||||
|
||||
const updateRightSide = useStore.getState().updateRightSide;
|
||||
|
||||
const resetScene = useStore.getState().resetMediaScene;
|
||||
|
@ -71,7 +73,12 @@ const mediaManager = (eventState: any) => {
|
|||
case "media_fstWord_select":
|
||||
case "media_sndWord_select":
|
||||
case "media_thirdWord_select":
|
||||
return { action: () => undefined };
|
||||
return {
|
||||
action: () => {
|
||||
exitMedia();
|
||||
setWordSelected(true);
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
],
|
||||
"5": [
|
||||
["20", "23", "16", "19"],
|
||||
["12", "15", "09", "11"],
|
||||
["12", "15", "08", "11"],
|
||||
["04", "07", "00", "03"]
|
||||
],
|
||||
"6": [
|
||||
|
|
|
@ -1,30 +1,59 @@
|
|||
import { OrbitControls } from "@react-three/drei";
|
||||
import React, { Suspense, useEffect, useMemo } from "react";
|
||||
import Preloader from "../components/Preloader";
|
||||
import React, { Suspense, useEffect, useState } from "react";
|
||||
import { useStore } from "../store";
|
||||
import Pause from "../components/MainScene/PauseSubscene/Pause";
|
||||
import SyncedComponentLoader from "../components/MainScene/SyncedComponentLoader";
|
||||
import LevelSelection from "../components/MainScene/SyncedComponents/LevelSelection";
|
||||
import HUD from "../components/MainScene/SyncedComponents/HUD";
|
||||
import YellowTextRenderer from "../components/TextRenderer/YellowTextRenderer";
|
||||
import YellowOrb from "../components/MainScene/SyncedComponents/YellowOrb";
|
||||
import MiddleRing from "../components/MainScene/SyncedComponents/MiddleRing";
|
||||
import GrayPlanes from "../components/MainScene/SyncedComponents/GrayPlanes";
|
||||
import Starfield from "../components/MainScene/SyncedComponents/Starfield";
|
||||
import Site from "../components/MainScene/SyncedComponents/Site";
|
||||
import Lain from "../components/MainScene/Lain";
|
||||
|
||||
const MainScene = () => {
|
||||
const currentSubscene = useStore((state) => state.mainSubscene);
|
||||
const shouldIntro = useStore((state) => state.intro);
|
||||
const [paused, setPaused] = useState(false);
|
||||
const subscene = useStore((state) => state.mainSubscene);
|
||||
|
||||
const isPaused = useMemo(() => currentSubscene === "pause", [
|
||||
currentSubscene,
|
||||
]);
|
||||
const wordSelected = useStore((state) => state.wordSelected);
|
||||
const setWordSelected = useStore((state) => state.setWordSelected);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
document.getElementsByTagName("canvas")[0].className = "";
|
||||
};
|
||||
}, []);
|
||||
if (subscene === "pause") {
|
||||
setTimeout(() => {
|
||||
setPaused(true);
|
||||
}, 3400);
|
||||
} else {
|
||||
setPaused(false);
|
||||
}
|
||||
}, [subscene]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wordSelected) {
|
||||
setTimeout(() => {
|
||||
setWordSelected(false);
|
||||
}, 3100);
|
||||
}
|
||||
}, [setWordSelected, wordSelected]);
|
||||
|
||||
return (
|
||||
<perspectiveCamera position-z={3}>
|
||||
<Suspense fallback={null}>
|
||||
<LevelSelection />
|
||||
<Pause />
|
||||
<SyncedComponentLoader paused={isPaused} shouldIntro={shouldIntro} />
|
||||
<group visible={!paused}>
|
||||
<group visible={!wordSelected}>
|
||||
<HUD />
|
||||
<YellowTextRenderer />
|
||||
<MiddleRing />
|
||||
<GrayPlanes />
|
||||
<Lain shouldIntro={false} />
|
||||
</group>
|
||||
<YellowOrb visible={!paused} />
|
||||
<Starfield shouldIntro={false} introFinished={true} />
|
||||
</group>
|
||||
<Site shouldIntro={false} introFinished={true} />
|
||||
<OrbitControls />
|
||||
<pointLight color={0xffffff} position={[0, 0, 7]} intensity={1} />
|
||||
<pointLight color={0x7f7f7f} position={[0, 10, 0]} intensity={1.5} />
|
||||
|
|
|
@ -53,6 +53,7 @@ type State = {
|
|||
rightSideIdx: 0 | 1 | 2;
|
||||
};
|
||||
mediaWordPosStateIdx: number;
|
||||
wordSelected: boolean;
|
||||
|
||||
// idle scene
|
||||
idleMedia: string;
|
||||
|
@ -114,7 +115,7 @@ export const useStore = create(
|
|||
combine(
|
||||
{
|
||||
// scene data
|
||||
currentScene: "media",
|
||||
currentScene: "main",
|
||||
|
||||
// game progress
|
||||
gameProgress: game_progress,
|
||||
|
@ -198,6 +199,7 @@ export const useStore = create(
|
|||
rightSideIdx: 0,
|
||||
},
|
||||
mediaWordPosStateIdx: 1,
|
||||
wordSelected: false,
|
||||
|
||||
// idle scene
|
||||
idleMedia: "INS01.STR",
|
||||
|
@ -350,6 +352,7 @@ export const useStore = create(
|
|||
rightSideIdx: 0,
|
||||
},
|
||||
})),
|
||||
setWordSelected: (to: boolean) => set(() => ({ wordSelected: to })),
|
||||
|
||||
// idle media setters
|
||||
setIdleMedia: (to: any) => set(() => ({ idleMedia: to })),
|
||||
|
|
|
@ -46,7 +46,9 @@ export const findNodeFromWord = (
|
|||
|
||||
const matrixIndices = Object.entries(node_matrices).flatMap((matrixData) =>
|
||||
matrixData[1].flatMap((row, idx) =>
|
||||
row[0] === pos ? { matrixIdx: matrixData[0], rowIdx: idx, colIdx: 0 } : []
|
||||
row[0] === pos
|
||||
? { matrixIdx: parseInt(matrixData[0]), rowIdx: idx, colIdx: 0 }
|
||||
: []
|
||||
)
|
||||
)[0];
|
||||
|
||||
|
@ -66,7 +68,8 @@ export const findNodeFromWord = (
|
|||
...chosenNode,
|
||||
matrixIndices: matrixIndices,
|
||||
},
|
||||
siteRotY: rotValues[matrixIndices.matrixIdx as keyof typeof rotValues],
|
||||
siteRotY:
|
||||
rotValues[matrixIndices.matrixIdx.toString() as keyof typeof rotValues],
|
||||
level: chosenNode.id.substr(0, 2),
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue