word sleection animation

This commit is contained in:
ad044 2021-02-03 22:54:58 +04:00
parent 1f3126e1b9
commit dc545ba522
12 changed files with 129 additions and 121 deletions

View file

@ -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 { useFrame, useLoader } from "react-three-fiber";
import * as THREE from "three"; import * as THREE from "three";
import { PlainSingularAnimator } from "three-plain-animator/lib/plain-singular-animator"; import { PlainSingularAnimator } from "three-plain-animator/lib/plain-singular-animator";
@ -66,6 +66,7 @@ export const LainConstructor = (props: LainConstructorProps) => {
attach="material" attach="material"
map={lainSpriteTexture} map={lainSpriteTexture}
alphaTest={0.01} alphaTest={0.01}
color={0xffffff}
/> />
); );
}; };
@ -355,6 +356,8 @@ type LainProps = {
const Lain = (props: LainProps) => { const Lain = (props: LainProps) => {
const lainMoveState = useStore((state) => state.lainMoveState); const lainMoveState = useStore((state) => state.lainMoveState);
const wordSelected = useStore((state) => state.wordSelected);
const lainAnimationDispatch = useMemo(() => { const lainAnimationDispatch = useMemo(() => {
const anims = { const anims = {
standing: <LainStanding />, standing: <LainStanding />,
@ -401,9 +404,27 @@ const Lain = (props: LainProps) => {
return props.shouldIntro ? introFinished : true; return props.shouldIntro ? introFinished : true;
}, [introFinished, props.shouldIntro]); }, [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 ( return (
<Suspense fallback={null}> <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 />} {stopIntroAnim ? lainAnimationDispatch : <LainIntro />}
</sprite> </sprite>
</Suspense> </Suspense>

View file

@ -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;

View file

@ -158,6 +158,8 @@ const MiddleRing = () => {
const clock = new THREE.Clock(); const clock = new THREE.Clock();
const wordSelected = useStore((state) => state.wordSelected);
const [wobbleAmp, setWobbleAmp] = useState(0); const [wobbleAmp, setWobbleAmp] = useState(0);
const [noiseAmp, setNoiseAmp] = useState(0.03); const [noiseAmp, setNoiseAmp] = useState(0.03);
const [rotating, setRotating] = useState(true); const [rotating, setRotating] = useState(true);
@ -352,6 +354,16 @@ const MiddleRing = () => {
}, 950); }, 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) { if (prevData?.siteRotY !== undefined && prevData?.siteRotY !== siteRotY) {
rotate(prevData?.siteRotY > siteRotY ? [-0.07, 0.03] : [0.07, -0.03]); rotate(prevData?.siteRotY > siteRotY ? [-0.07, 0.03] : [0.07, -0.03]);
} else if ( } else if (
@ -367,6 +379,8 @@ const MiddleRing = () => {
pause(); pause();
} else if (subscene === "site" && prevData?.subscene === "pause") { } else if (subscene === "site" && prevData?.subscene === "pause") {
unpause(); unpause();
} else if (wordSelected) {
afterWordSelection();
} }
}, [ }, [
activeLevel, activeLevel,
@ -377,6 +391,7 @@ const MiddleRing = () => {
setRot, setRot,
siteRotY, siteRotY,
subscene, subscene,
wordSelected,
]); ]);
return ( return (

View file

@ -10,8 +10,8 @@ import * as THREE from "three";
import site_a from "../../../resources/site_a.json"; import site_a from "../../../resources/site_a.json";
import site_b from "../../../resources/site_b.json"; import site_b from "../../../resources/site_b.json";
import level_y_values from "../../../resources/level_y_values.json"; import level_y_values from "../../../resources/level_y_values.json";
import usePrevious from "../../../hooks/usePrevious";
import { filterInvisibleNodes } from "../../../utils/node-utils"; import { filterInvisibleNodes } from "../../../utils/node-utils";
import usePrevious from "../../../hooks/usePrevious";
export type NodeDataType = { export type NodeDataType = {
id: string; id: string;
@ -48,33 +48,34 @@ type SiteProps = {
}; };
const Site = (props: SiteProps) => { const Site = (props: SiteProps) => {
const activeLevel = useStore((state) => state.activeLevel); const wordSelected = useStore((state) => state.wordSelected);
const [state, setState] = useSpring(() => ({ const [rotState, setRot] = useSpring(() => ({
posY: -level_y_values[ x: wordSelected ? 0 : useStore.getState().siteRot[0],
useStore.getState().activeLevel as keyof typeof level_y_values y: wordSelected ? 0 : useStore.getState().siteRot[1],
], config: { duration: 1200 },
rotX: useStore.getState().siteRot[0], }));
rotY: useStore.getState().siteRot[1],
const [posState, setPos] = useSpring(() => ({
y: wordSelected
? 0
: -level_y_values[
useStore.getState().activeLevel as keyof typeof level_y_values
],
delay: 1300,
config: { duration: 1200 }, config: { duration: 1200 },
})); }));
useEffect(() => { useEffect(() => {
setTimeout(() => { useStore.subscribe(setRot, (state) => ({
setState({ x: state.siteRot[0],
posY: -level_y_values[activeLevel as keyof typeof level_y_values], y: state.siteRot[1],
}); }));
}, 1200); useStore.subscribe(setPos, (state) => ({
}, [activeLevel, setState]); y: -level_y_values[state.activeLevel as keyof typeof level_y_values],
delay: 1300,
useEffect(() => { }));
useStore.subscribe(setState, (state) => { }, [setPos, setRot]);
return {
rotX: state.siteRot[0],
rotY: state.siteRot[1],
};
});
}, [setState]);
const introWrapperRef = useRef<THREE.Object3D>(); const introWrapperRef = useRef<THREE.Object3D>();
@ -110,8 +111,8 @@ const Site = (props: SiteProps) => {
return ( return (
<Suspense fallback={null}> <Suspense fallback={null}>
<a.group ref={introWrapperRef}> <a.group ref={introWrapperRef}>
<a.group rotation-x={state.rotX}> <a.group rotation-x={rotState.x}>
<a.group rotation-y={state.rotY} position-y={state.posY}> <a.group rotation-y={rotState.y} position-y={posState.y}>
<ActiveLevelNodes visibleNodes={visibleNodes} /> <ActiveLevelNodes visibleNodes={visibleNodes} />
<InactiveLevelNodes visibleNodes={visibleNodes} /> <InactiveLevelNodes visibleNodes={visibleNodes} />
<Rings <Rings

View file

@ -2,7 +2,6 @@ import React, { useEffect, useState, memo } from "react";
import Star from "./Starfield/Star"; import Star from "./Starfield/Star";
type StarfieldProps = { type StarfieldProps = {
visible: boolean;
shouldIntro: boolean; shouldIntro: boolean;
introFinished: boolean; introFinished: boolean;
}; };
@ -53,7 +52,7 @@ const Starfield = memo((props: StarfieldProps) => {
}, []); }, []);
return ( return (
<group visible={props.visible}> <>
<group <group
position={[0, -1, 2]} position={[0, -1, 2]}
visible={props.shouldIntro ? mainVisible : true} visible={props.shouldIntro ? mainVisible : true}
@ -126,7 +125,7 @@ const Starfield = memo((props: StarfieldProps) => {
) : ( ) : (
<></> <></>
)} )}
</group> </>
); );
}); });

View file

@ -156,13 +156,13 @@ const nodeManager = (eventState: any) => {
case "site_right": case "site_right":
case "select_level_up": case "select_level_up":
case "select_level_down": case "select_level_down":
case "media_fstWord_select":
case "media_sndWord_select":
case "media_thirdWord_select":
return { return {
action: () => updateActiveNode(eventState.node, 3900), action: () => updateActiveNode(eventState.node, 3900),
}; };
case "change_node": case "change_node":
case "media_fstWord_select":
case "media_sndWord_select":
case "media_thirdWord_select":
return { return {
action: () => updateActiveNode(eventState.node), action: () => updateActiveNode(eventState.node),
}; };

View file

@ -5,7 +5,6 @@ const siteManager = (eventState: any) => {
const setRotX = useStore.getState().setSiteRotX; const setRotX = useStore.getState().setSiteRotX;
const dispatchAction = (eventState: any) => { const dispatchAction = (eventState: any) => {
console.log(eventState.siteRotY);
switch (eventState.event) { switch (eventState.event) {
case "site_left": case "site_left":
case "site_right": case "site_right":

View file

@ -7,6 +7,8 @@ const mediaManager = (eventState: any) => {
const setLeftComponentMatrixIdx = useStore.getState() const setLeftComponentMatrixIdx = useStore.getState()
.setMediaLeftComponentMatrixIdx; .setMediaLeftComponentMatrixIdx;
const setWordSelected = useStore.getState().setWordSelected;
const updateRightSide = useStore.getState().updateRightSide; const updateRightSide = useStore.getState().updateRightSide;
const resetScene = useStore.getState().resetMediaScene; const resetScene = useStore.getState().resetMediaScene;
@ -71,7 +73,12 @@ const mediaManager = (eventState: any) => {
case "media_fstWord_select": case "media_fstWord_select":
case "media_sndWord_select": case "media_sndWord_select":
case "media_thirdWord_select": case "media_thirdWord_select":
return { action: () => undefined }; return {
action: () => {
exitMedia();
setWordSelected(true);
},
};
} }
}; };

View file

@ -21,7 +21,7 @@
], ],
"5": [ "5": [
["20", "23", "16", "19"], ["20", "23", "16", "19"],
["12", "15", "09", "11"], ["12", "15", "08", "11"],
["04", "07", "00", "03"] ["04", "07", "00", "03"]
], ],
"6": [ "6": [

View file

@ -1,30 +1,59 @@
import { OrbitControls } from "@react-three/drei"; import { OrbitControls } from "@react-three/drei";
import React, { Suspense, useEffect, useMemo } from "react"; import React, { Suspense, useEffect, useState } from "react";
import Preloader from "../components/Preloader";
import { useStore } from "../store"; import { useStore } from "../store";
import Pause from "../components/MainScene/PauseSubscene/Pause"; import Pause from "../components/MainScene/PauseSubscene/Pause";
import SyncedComponentLoader from "../components/MainScene/SyncedComponentLoader";
import LevelSelection from "../components/MainScene/SyncedComponents/LevelSelection"; 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 MainScene = () => {
const currentSubscene = useStore((state) => state.mainSubscene); const [paused, setPaused] = useState(false);
const shouldIntro = useStore((state) => state.intro); const subscene = useStore((state) => state.mainSubscene);
const isPaused = useMemo(() => currentSubscene === "pause", [ const wordSelected = useStore((state) => state.wordSelected);
currentSubscene, const setWordSelected = useStore((state) => state.setWordSelected);
]);
useEffect(() => { useEffect(() => {
return () => { if (subscene === "pause") {
document.getElementsByTagName("canvas")[0].className = ""; setTimeout(() => {
}; setPaused(true);
}, []); }, 3400);
} else {
setPaused(false);
}
}, [subscene]);
useEffect(() => {
if (wordSelected) {
setTimeout(() => {
setWordSelected(false);
}, 3100);
}
}, [setWordSelected, wordSelected]);
return ( return (
<perspectiveCamera position-z={3}> <perspectiveCamera position-z={3}>
<Suspense fallback={null}> <Suspense fallback={null}>
<LevelSelection /> <LevelSelection />
<Pause /> <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 /> <OrbitControls />
<pointLight color={0xffffff} position={[0, 0, 7]} intensity={1} /> <pointLight color={0xffffff} position={[0, 0, 7]} intensity={1} />
<pointLight color={0x7f7f7f} position={[0, 10, 0]} intensity={1.5} /> <pointLight color={0x7f7f7f} position={[0, 10, 0]} intensity={1.5} />

View file

@ -53,6 +53,7 @@ type State = {
rightSideIdx: 0 | 1 | 2; rightSideIdx: 0 | 1 | 2;
}; };
mediaWordPosStateIdx: number; mediaWordPosStateIdx: number;
wordSelected: boolean;
// idle scene // idle scene
idleMedia: string; idleMedia: string;
@ -114,7 +115,7 @@ export const useStore = create(
combine( combine(
{ {
// scene data // scene data
currentScene: "media", currentScene: "main",
// game progress // game progress
gameProgress: game_progress, gameProgress: game_progress,
@ -198,6 +199,7 @@ export const useStore = create(
rightSideIdx: 0, rightSideIdx: 0,
}, },
mediaWordPosStateIdx: 1, mediaWordPosStateIdx: 1,
wordSelected: false,
// idle scene // idle scene
idleMedia: "INS01.STR", idleMedia: "INS01.STR",
@ -350,6 +352,7 @@ export const useStore = create(
rightSideIdx: 0, rightSideIdx: 0,
}, },
})), })),
setWordSelected: (to: boolean) => set(() => ({ wordSelected: to })),
// idle media setters // idle media setters
setIdleMedia: (to: any) => set(() => ({ idleMedia: to })), setIdleMedia: (to: any) => set(() => ({ idleMedia: to })),

View file

@ -46,7 +46,9 @@ export const findNodeFromWord = (
const matrixIndices = Object.entries(node_matrices).flatMap((matrixData) => const matrixIndices = Object.entries(node_matrices).flatMap((matrixData) =>
matrixData[1].flatMap((row, idx) => 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]; )[0];
@ -66,7 +68,8 @@ export const findNodeFromWord = (
...chosenNode, ...chosenNode,
matrixIndices: matrixIndices, 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), level: chosenNode.id.substr(0, 2),
}; };
}; };