mirror of
https://github.com/ad044/lainTSX.git
synced 2024-10-22 15:09:05 +00:00
better intro implementation and suspense handling
This commit is contained in:
parent
91caca2d41
commit
1512996968
5 changed files with 117 additions and 124 deletions
|
@ -20,7 +20,7 @@ const Loading = () => {
|
|||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<group position={[0, 0, 3]}>
|
||||
<sprite scale={[5, 5, 5]} renderOrder={999}>
|
||||
<spriteMaterial color={0x000000} depthTest={false} />
|
||||
</sprite>
|
||||
|
@ -38,7 +38,7 @@ const Loading = () => {
|
|||
>
|
||||
<spriteMaterial map={lifeInstinct} depthTest={false} />
|
||||
</sprite>
|
||||
</>
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React, { Suspense, useEffect, useMemo } from "react";
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { a, useSpring } from "@react-spring/three";
|
||||
import { useStore } from "@/store";
|
||||
import Levels from "./Levels";
|
||||
import Loading from "@canvas/objects/Loading";
|
||||
import { FlattenedSiteLayout, MainSubscene, NodeID } from "@/types";
|
||||
import { getLevelY } from "@/utils/site";
|
||||
import { getRotationForSegment } from "@/utils/site";
|
||||
|
@ -106,18 +105,16 @@ const Site = (props: SiteProps) => {
|
|||
}, [siteLayout]);
|
||||
|
||||
return (
|
||||
<Suspense fallback={props.introFinished ? <Loading /> : null}>
|
||||
<a.group rotation-x={tiltState.tilt}>
|
||||
<a.group rotation-x={rotationSpring.x}>
|
||||
<a.group rotation-y={rotationSpring.y} position-y={positionSpring.y}>
|
||||
<Levels
|
||||
flattenedLayout={layout}
|
||||
activateAllLevels={props.introFinished}
|
||||
/>
|
||||
</a.group>
|
||||
<a.group rotation-x={tiltState.tilt}>
|
||||
<a.group rotation-x={rotationSpring.x}>
|
||||
<a.group rotation-y={rotationSpring.y} position-y={positionSpring.y}>
|
||||
<Levels
|
||||
flattenedLayout={layout}
|
||||
activateAllLevels={props.introFinished}
|
||||
/>
|
||||
</a.group>
|
||||
</a.group>
|
||||
</Suspense>
|
||||
</a.group>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Suspense, useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useStore } from "@/store";
|
||||
import { playAudio } from "@/utils/audio";
|
||||
import LevelSelection from "@canvas/objects/MainScene/LevelSelection";
|
||||
|
@ -8,12 +8,15 @@ import MiddleRing from "@canvas/objects/MainScene/MiddleRing";
|
|||
import Starfield from "@canvas/objects/MainScene/Starfield";
|
||||
import Site from "@canvas/objects/MainScene/Site";
|
||||
import Lain from "@canvas/objects/MainScene/Lain";
|
||||
import Loading from "@canvas/objects/Loading";
|
||||
import usePrevious from "@/hooks/usePrevious";
|
||||
import { a, useSpring } from "@react-spring/three";
|
||||
import {
|
||||
a,
|
||||
useSpring,
|
||||
AnimationResult,
|
||||
} from "@react-spring/three";
|
||||
import Pause from "@canvas/objects/MainScene/Pause";
|
||||
import GrayPlane from "@canvas/objects/MainScene/GrayPlane";
|
||||
import { MainSubscene, Position } from "@/types";
|
||||
import { MainSubscene, Position, Rotation } from "@/types";
|
||||
import { handleEvent } from "@/core";
|
||||
import {
|
||||
resetInputCooldown,
|
||||
|
@ -79,18 +82,14 @@ const MainScene = () => {
|
|||
}
|
||||
}, [wordSelected]);
|
||||
|
||||
const introWrapperRef = useRef<THREE.Group>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (intro) {
|
||||
playAudio("snd_32.mp4");
|
||||
|
||||
setStarfieldIntro(false);
|
||||
setLainIntroAnim(false);
|
||||
setIntroFinished(false);
|
||||
|
||||
starfieldIntroRef.current = false;
|
||||
lainIntroRef.current = false;
|
||||
introFinishedRef.current = false;
|
||||
|
||||
handleEvent(setInputCooldown(-1));
|
||||
} else {
|
||||
handleEvent(resetInputCooldown);
|
||||
|
@ -98,49 +97,10 @@ const MainScene = () => {
|
|||
}, [intro]);
|
||||
|
||||
const [starfieldIntro, setStarfieldIntro] = useState(false);
|
||||
const starfieldIntroRef = useRef(false);
|
||||
const [lainIntroAnim, setLainIntroAnim] = useState(false);
|
||||
const lainIntroRef = useRef(false);
|
||||
const [introFinished, setIntroFinished] = useState(false);
|
||||
const introFinishedRef = useRef(false);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (!introFinished && intro && introWrapperRef.current) {
|
||||
if (introWrapperRef.current.position.z === -10) playAudio("snd_32.mp4");
|
||||
if (
|
||||
Math.round(introWrapperRef.current.position.z) === -3 &&
|
||||
!starfieldIntroRef.current
|
||||
) {
|
||||
setStarfieldIntro(true);
|
||||
starfieldIntroRef.current = true;
|
||||
}
|
||||
if (
|
||||
Math.round(introWrapperRef.current.position.z) === -1 &&
|
||||
!lainIntroRef.current
|
||||
) {
|
||||
setLainIntroAnim(true);
|
||||
lainIntroRef.current = true;
|
||||
}
|
||||
|
||||
if (introWrapperRef.current.position.z < 0) {
|
||||
introWrapperRef.current.position.z += 2.5 * delta;
|
||||
}
|
||||
if (introWrapperRef.current.rotation.x > 0) {
|
||||
introWrapperRef.current.rotation.x -= 0.4 * delta;
|
||||
}
|
||||
|
||||
if (
|
||||
!introFinishedRef.current &&
|
||||
introWrapperRef.current.rotation.x < 0 &&
|
||||
introWrapperRef.current.position.z > 0
|
||||
) {
|
||||
introFinishedRef.current = true;
|
||||
setIntroFinished(true);
|
||||
|
||||
handleEvent(resetInputCooldown);
|
||||
}
|
||||
}
|
||||
|
||||
if (grayPlaneGroupRef.current) {
|
||||
grayPlaneGroupRef.current.rotation.y -= delta / 1.5;
|
||||
}
|
||||
|
@ -175,67 +135,99 @@ const MainScene = () => {
|
|||
)
|
||||
);
|
||||
|
||||
const introSpring = useSpring<{
|
||||
from: { position: Position; rotation: Rotation };
|
||||
to: { position: Position; rotation: Rotation };
|
||||
}>({
|
||||
from: {
|
||||
position: [0, 0, intro ? -8 : 0],
|
||||
rotation: [intro ? Math.PI / 2 : 0, 0, 0],
|
||||
},
|
||||
to: {
|
||||
rotation: [0, 0, 0],
|
||||
position: [0, 0, 0],
|
||||
},
|
||||
config: { duration: 3000 },
|
||||
onChange: (result: AnimationResult) => {
|
||||
const { position } = result.value as {
|
||||
position: Position;
|
||||
rotation: Rotation;
|
||||
};
|
||||
|
||||
if (!starfieldIntro && position[2] > -4) {
|
||||
setStarfieldIntro(true);
|
||||
}
|
||||
|
||||
if (!lainIntroAnim && position[2] > -1) {
|
||||
setLainIntroAnim(true);
|
||||
}
|
||||
},
|
||||
onRest: () => {
|
||||
setIntroFinished(true);
|
||||
handleEvent(resetInputCooldown);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<group position-z={3}>
|
||||
<Suspense fallback={<Loading />}>
|
||||
<LevelSelection />
|
||||
<Pause />
|
||||
<group position={[-0.85, -0.7, 0]} scale={[0.85, 0.85, 0]}>
|
||||
<group position={[1, 0.6, 0]} scale={[1.2, 1.2, 0]}>
|
||||
<Prompt />
|
||||
</group>
|
||||
<About />
|
||||
<PermissionDenied />
|
||||
<SaveStatusDisplay />
|
||||
<LevelSelection />
|
||||
<Pause />
|
||||
<group position={[-0.85, -0.7, 0]} scale={[0.85, 0.85, 0]}>
|
||||
<group position={[1, 0.6, 0]} scale={[1.2, 1.2, 0]}>
|
||||
<Prompt />
|
||||
</group>
|
||||
<NotFound />
|
||||
<a.group
|
||||
visible={intro ? introFinished : true}
|
||||
position={bgSpring.position}
|
||||
>
|
||||
<mesh renderOrder={-5} scale={[5, 1, 0]}>
|
||||
<planeBufferGeometry attach="geometry" />
|
||||
<meshBasicMaterial map={mainSceneBg} depthTest={false} />
|
||||
</mesh>
|
||||
</a.group>
|
||||
<group visible={!paused}>
|
||||
<group visible={!wordSelected && (intro ? introFinished : true)}>
|
||||
<a.group visible={!wordNotFound} position-y={tiltSpring.value}>
|
||||
<HUD />
|
||||
</a.group>
|
||||
<MiddleRing />
|
||||
<group position={[0.1, 0, -2]} ref={grayPlaneGroupRef}>
|
||||
{grayPlanePoses.map((position, idx: number) => (
|
||||
<GrayPlane position={position} key={idx} />
|
||||
))}
|
||||
</group>
|
||||
<About />
|
||||
<PermissionDenied />
|
||||
<SaveStatusDisplay />
|
||||
</group>
|
||||
<NotFound />
|
||||
<a.group
|
||||
visible={intro ? introFinished : true}
|
||||
position={bgSpring.position}
|
||||
>
|
||||
<mesh renderOrder={-5} scale={[5, 1, 0]}>
|
||||
<planeBufferGeometry attach="geometry" />
|
||||
<meshBasicMaterial map={mainSceneBg} depthTest={false} />
|
||||
</mesh>
|
||||
</a.group>
|
||||
<group visible={!paused}>
|
||||
<group visible={!wordSelected && (intro ? introFinished : true)}>
|
||||
<a.group visible={!wordNotFound} position-y={tiltSpring.value}>
|
||||
<HUD />
|
||||
</a.group>
|
||||
<MiddleRing />
|
||||
<group position={[0.1, 0, -2]} ref={grayPlaneGroupRef}>
|
||||
{grayPlanePoses.map((position, idx: number) => (
|
||||
<GrayPlane position={position} key={idx} />
|
||||
))}
|
||||
</group>
|
||||
<group visible={intro ? introFinished : true}>
|
||||
<YellowOrb visible={!paused} />
|
||||
</group>
|
||||
<Starfield
|
||||
shouldIntro={intro}
|
||||
mainVisible={intro ? starfieldIntro : true}
|
||||
/>
|
||||
</group>
|
||||
<a.group visible={!wordSelected} position-y={tiltSpring.value}>
|
||||
<Lain
|
||||
shouldAnimate={lainIntroAnim}
|
||||
introFinished={intro ? introFinished : true}
|
||||
/>
|
||||
</a.group>
|
||||
<group
|
||||
ref={introWrapperRef}
|
||||
position-z={intro ? -10 : 0}
|
||||
rotation-x={intro ? Math.PI / 2 : 0}
|
||||
>
|
||||
<Site introFinished={intro ? introFinished : true} />
|
||||
<group visible={intro ? introFinished : true}>
|
||||
<YellowOrb visible={!paused} />
|
||||
</group>
|
||||
<pointLight color={0xffffff} position={[0, 0, 7]} intensity={1} />
|
||||
<pointLight color={0x7f7f7f} position={[0, 10, 0]} intensity={1.5} />
|
||||
<pointLight color={0xffffff} position={[8, 0, 0]} intensity={0.2} />
|
||||
<pointLight color={0xffffff} position={[-8, 0, 0]} intensity={0.2} />
|
||||
</Suspense>
|
||||
<Starfield
|
||||
shouldIntro={intro}
|
||||
mainVisible={intro ? starfieldIntro : true}
|
||||
/>
|
||||
</group>
|
||||
<a.group visible={!wordSelected} position-y={tiltSpring.value}>
|
||||
<Lain
|
||||
shouldAnimate={lainIntroAnim}
|
||||
introFinished={intro ? introFinished : true}
|
||||
/>
|
||||
</a.group>
|
||||
<a.group
|
||||
position={introSpring.position}
|
||||
// NOTE (cast to any)
|
||||
// throws a type error, but works
|
||||
rotation={introSpring.rotation as any}
|
||||
>
|
||||
<Site introFinished={intro ? introFinished : true} />
|
||||
</a.group>
|
||||
<pointLight color={0xffffff} position={[0, 0, 7]} intensity={1} />
|
||||
<pointLight color={0x7f7f7f} position={[0, 10, 0]} intensity={1.5} />
|
||||
<pointLight color={0xffffff} position={[8, 0, 0]} intensity={0.2} />
|
||||
<pointLight color={0xffffff} position={[-8, 0, 0]} intensity={0.2} />
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -11,7 +11,6 @@ import { useStore } from "@/store";
|
|||
import { GameScene, MediaComponent, Position, TextType } from "@/types";
|
||||
import { createAudioAnalyser } from "@/utils/audio";
|
||||
import Images from "@canvas/objects/Images";
|
||||
import Loading from "@canvas/objects/Loading";
|
||||
import AudioVisualizer from "@canvas/objects/MediaScene/AudioVisualizer";
|
||||
import LeftSide from "@canvas/objects/MediaScene/LeftSide";
|
||||
import NodeNameContainer from "@canvas/objects/MediaScene/NodeNameContainer";
|
||||
|
@ -140,7 +139,7 @@ const MediaScene = () => {
|
|||
|
||||
return (
|
||||
<group position-z={3} ref={mediaSceneGroupRef}>
|
||||
{node && loaded ? (
|
||||
{node && loaded && (
|
||||
<group position={[0.4, -0.3, 0]}>
|
||||
<pointLight intensity={1.2} color={0xffffff} position={[-2, 0, 0]} />
|
||||
<LeftSide />
|
||||
|
@ -178,8 +177,6 @@ const MediaScene = () => {
|
|||
<Images imageTableIndices={node.image_table_indices} />
|
||||
</group>
|
||||
</group>
|
||||
) : (
|
||||
<Loading />
|
||||
)}
|
||||
</group>
|
||||
);
|
||||
|
|
|
@ -24,6 +24,7 @@ import MediaPlayer from "@canvas/objects/MediaPlayer";
|
|||
import { GameScene } from "@/types";
|
||||
import Head from "next/head";
|
||||
import Preloader from "@/components/canvas/objects/Preloader";
|
||||
import Loading from "@/components/canvas/objects/Loading";
|
||||
|
||||
const Game = () => {
|
||||
const scene = useStore((state) => state.scene);
|
||||
|
@ -116,9 +117,15 @@ const Game = () => {
|
|||
linear
|
||||
className="main-canvas"
|
||||
>
|
||||
<Suspense fallback={null}>
|
||||
<Preloader />
|
||||
<Suspense
|
||||
fallback={
|
||||
scene === GameScene.Main || scene === GameScene.Media ? (
|
||||
<Loading />
|
||||
) : null
|
||||
}
|
||||
>
|
||||
{dispatchScene[scene]}
|
||||
<Preloader />
|
||||
<InputHandler />
|
||||
</Suspense>
|
||||
</Canvas>
|
||||
|
|
Loading…
Reference in a new issue