mirror of
https://github.com/ad044/lainTSX.git
synced 2024-10-22 23:19:06 +00:00
separated main intro scene into its own file, ported the animation to react-spring
This commit is contained in:
parent
7d2d114ecc
commit
ba56e5451f
11 changed files with 168 additions and 86 deletions
|
@ -4,3 +4,8 @@ export const hudActiveAtom = atom({
|
|||
key: "hudActiveAtom",
|
||||
default: 1,
|
||||
});
|
||||
|
||||
export const hudVisibilityAtom = atom({
|
||||
key: "hudVisibilityAtom",
|
||||
default: false,
|
||||
});
|
||||
|
|
|
@ -53,7 +53,7 @@ const InputHandler = () => {
|
|||
|
||||
const setCurrentHUDElement = useSetRecoilState(currentHUDAtom);
|
||||
|
||||
const sethudActive = useSetRecoilState(hudActiveAtom);
|
||||
const setHudActive = useSetRecoilState(hudActiveAtom);
|
||||
|
||||
const setCamPosY = useSetRecoilState(camPosYAtom);
|
||||
const setCamRotY = useSetRecoilState(camRotYAtom);
|
||||
|
@ -136,8 +136,8 @@ const InputHandler = () => {
|
|||
);
|
||||
|
||||
const updateHUD = useCallback(() => {
|
||||
sethudActive((prev: number) => Number(!prev));
|
||||
}, [sethudActive]);
|
||||
setHudActive((prev: number) => Number(!prev));
|
||||
}, [setHudActive]);
|
||||
|
||||
const moveDispatcher = useCallback(
|
||||
(move: string, key: string) => {
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import React, { memo } from "react";
|
||||
|
||||
type LightProps = {
|
||||
ambientLightVal: number;
|
||||
};
|
||||
|
||||
const Lights = memo((props: LightProps) => {
|
||||
const Lights = memo(() => {
|
||||
return (
|
||||
<>
|
||||
<ambientLight color={0x808080} intensity={props.ambientLightVal} />
|
||||
<ambientLight color={0x808080} intensity={1.0} />
|
||||
<pointLight color={0xffffff} position={[0, 0, 700]} intensity={0.5} />
|
||||
<pointLight color={0x7f7f7f} position={[0, 1000, 0]} intensity={1} />
|
||||
<pointLight color={0xffffff} position={[0, 0, 0]} intensity={0.1} />
|
||||
|
|
16
src/components/MainScene/MainGroupAtom.tsx
Normal file
16
src/components/MainScene/MainGroupAtom.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { atom } from "recoil";
|
||||
|
||||
export const mainGroupPosYAtom = atom({
|
||||
key: "mainGroupPosYAtom",
|
||||
default: -2.5,
|
||||
});
|
||||
|
||||
export const mainGroupPosZAtom = atom({
|
||||
key: "mainGroupPosZAtom",
|
||||
default: -9.5,
|
||||
});
|
||||
|
||||
export const mainGroupRotXAtom = atom({
|
||||
key: "mainGroupRotXAtom",
|
||||
default: 1.5,
|
||||
});
|
|
@ -1,77 +1,51 @@
|
|||
import { a, useSpring } from "@react-spring/three";
|
||||
import { OrbitControls } from "drei";
|
||||
import React, { Suspense, useEffect, useRef, useState } from "react";
|
||||
import { useFrame } from "react-three-fiber";
|
||||
import Hub from "../Hub";
|
||||
import Lain, { LainIntro, LainStanding } from "../Lain/Lain";
|
||||
import Lain, { LainIntro } from "../Lain/Lain";
|
||||
import Lights from "../Lights";
|
||||
import OrthoCamera from "../OrthoCamera/OrthoCamera";
|
||||
import Preloader from "../Preloader";
|
||||
import Starfield from "../Starfield/Starfield";
|
||||
import * as THREE from "three";
|
||||
import { useRecoilValue, useSetRecoilState } from "recoil";
|
||||
import { lainMoveStateAtom, lainMovingAtom } from "../Lain/LainAtom";
|
||||
import { camPosYAtom, camRotYAtom } from "./CameraAtom";
|
||||
import InputHandler from "../InputHandler";
|
||||
import MainSceneIntro from "./MainSceneIntro";
|
||||
import {
|
||||
mainGroupPosYAtom,
|
||||
mainGroupPosZAtom,
|
||||
mainGroupRotXAtom,
|
||||
} from "./MainGroupAtom";
|
||||
|
||||
const MainScene = () => {
|
||||
const setLainMoving = useSetRecoilState(lainMovingAtom);
|
||||
const setLainMoveState = useSetRecoilState(lainMoveStateAtom);
|
||||
|
||||
const [isIntro, setIsIntro] = useState(true);
|
||||
const mainGroupPosY = useRecoilValue(mainGroupPosYAtom);
|
||||
const mainGroupPosZ = useRecoilValue(mainGroupPosZAtom);
|
||||
const mainGroupRotX = useRecoilValue(mainGroupRotXAtom);
|
||||
|
||||
const camPosY = useRecoilValue(camPosYAtom);
|
||||
const camRotY = useRecoilValue(camRotYAtom);
|
||||
|
||||
const [ambientLightVal, setAmbientLightVal] = useState(0.4);
|
||||
|
||||
const cameraState = useSpring({
|
||||
camPosY: camPosY,
|
||||
camRotY: camRotY,
|
||||
config: { duration: 1200 },
|
||||
});
|
||||
|
||||
const groupRef = useRef<THREE.Object3D>();
|
||||
|
||||
useFrame(() => {
|
||||
if (isIntro) {
|
||||
if (groupRef.current) {
|
||||
if (groupRef.current!.rotation.x > 0) {
|
||||
if (groupRef.current!.position.z > -1) {
|
||||
groupRef.current!.rotation.x -= 0.015;
|
||||
} else {
|
||||
// if the position z is at a certain point speed up the rotation
|
||||
groupRef.current!.rotation.x -= 0.01;
|
||||
}
|
||||
}
|
||||
if (groupRef.current!.position.y > 0) {
|
||||
groupRef.current!.position.y -= 0.015;
|
||||
}
|
||||
|
||||
if (groupRef.current!.position.z < 0) {
|
||||
groupRef.current!.position.z += 0.04;
|
||||
}
|
||||
|
||||
// if the rotation hits this value that means that the intro is finished.
|
||||
// using a settimeout or something similar resulted in clunkiness, since it was dependant
|
||||
// on load times.
|
||||
if (
|
||||
parseFloat(groupRef.current!.rotation.x.toPrecision(2)) === -0.005
|
||||
) {
|
||||
setLainMoving(false);
|
||||
setLainMoveState(<LainStanding />);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsIntro(false);
|
||||
setAmbientLightVal(1.0);
|
||||
document.getElementsByTagName("canvas")[0].className =
|
||||
"hub-background";
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
const mainGroupStatePos = useSpring({
|
||||
mainGroupPosY: mainGroupPosY,
|
||||
mainGroupPosZ: mainGroupPosZ,
|
||||
config: { duration: 3644 },
|
||||
});
|
||||
|
||||
const mainGroupStateRot = useSpring({
|
||||
mainGroupRotX: mainGroupRotX,
|
||||
config: { duration: 1500 },
|
||||
});
|
||||
|
||||
// set lain intro spritesheet before the page loads fully
|
||||
useEffect(() => {
|
||||
setLainMoving(true);
|
||||
setLainMoveState(<LainIntro />);
|
||||
|
@ -84,15 +58,20 @@ const MainScene = () => {
|
|||
rotation-y={cameraState.camRotY}
|
||||
>
|
||||
<Suspense fallback={null}>
|
||||
<group rotation={[2.3, 0, 0]} position={[0, 1.5, -7.5]} ref={groupRef}>
|
||||
<MainSceneIntro />
|
||||
<a.group
|
||||
rotation-x={mainGroupStateRot.mainGroupRotX}
|
||||
position-y={mainGroupStatePos.mainGroupPosY}
|
||||
position-z={mainGroupStatePos.mainGroupPosZ}
|
||||
>
|
||||
<InputHandler />
|
||||
<Preloader />
|
||||
<Hub />
|
||||
<OrthoCamera orbVisibility={!isIntro} hudVisibility={!isIntro} />
|
||||
<Starfield introStarfieldVisible={isIntro} />
|
||||
<Lights ambientLightVal={ambientLightVal} />
|
||||
<OrthoCamera />
|
||||
<Starfield />
|
||||
<Lights />
|
||||
<OrbitControls />
|
||||
</group>
|
||||
</a.group>
|
||||
<Lain />
|
||||
</Suspense>
|
||||
</a.perspectiveCamera>
|
||||
|
|
74
src/components/MainScene/MainSceneIntro.tsx
Normal file
74
src/components/MainScene/MainSceneIntro.tsx
Normal file
|
@ -0,0 +1,74 @@
|
|||
import React, { memo, useEffect } from "react";
|
||||
import { useSetRecoilState } from "recoil";
|
||||
import { hudActiveAtom, hudVisibilityAtom } from "../HUD/HUDActiveAtom";
|
||||
import {
|
||||
mainGroupPosYAtom,
|
||||
mainGroupPosZAtom,
|
||||
mainGroupRotXAtom,
|
||||
} from "./MainGroupAtom";
|
||||
import { LainStanding } from "../Lain/Lain";
|
||||
import { lainMoveStateAtom, lainMovingAtom } from "../Lain/LainAtom";
|
||||
import { orbVisibilityAtom } from "../Orb/OrbAtom";
|
||||
import { introStarfieldVisibilityAtom } from "../Starfield/StarfieldAtom";
|
||||
|
||||
// ghost component to manipulate the intro animation for the main scene.
|
||||
|
||||
// we separate this file because having something like this
|
||||
// inside <Suspense> tags makes it behave in a more stable manner
|
||||
// by waiting for the components to load and synchronously calling the functions.
|
||||
const MainSceneIntro = memo(() => {
|
||||
const setHudActive = useSetRecoilState(hudActiveAtom);
|
||||
|
||||
const setHudVisible = useSetRecoilState(hudVisibilityAtom);
|
||||
const setOrbVisible = useSetRecoilState(orbVisibilityAtom);
|
||||
|
||||
const setLainMoving = useSetRecoilState(lainMovingAtom);
|
||||
const setLainMoveState = useSetRecoilState(lainMoveStateAtom);
|
||||
|
||||
const setIntroStarfieldVisible = useSetRecoilState(
|
||||
introStarfieldVisibilityAtom
|
||||
);
|
||||
|
||||
const setMainGroupPosY = useSetRecoilState(mainGroupPosYAtom);
|
||||
const setMainGroupPosZ = useSetRecoilState(mainGroupPosZAtom);
|
||||
const setMainGroupRotX = useSetRecoilState(mainGroupRotXAtom);
|
||||
|
||||
useEffect(() => {
|
||||
setMainGroupPosY(0);
|
||||
setMainGroupPosZ(0);
|
||||
setTimeout(() => {
|
||||
setMainGroupRotX(0);
|
||||
}, 2400);
|
||||
|
||||
setHudActive((prev: number) => Number(!prev));
|
||||
setTimeout(() => {
|
||||
setLainMoving(false);
|
||||
setLainMoveState(<LainStanding />);
|
||||
|
||||
setOrbVisible(true);
|
||||
setHudVisible(true);
|
||||
|
||||
setIntroStarfieldVisible(false);
|
||||
|
||||
setHudActive((prev: number) => Number(!prev));
|
||||
|
||||
setTimeout(() => {
|
||||
document.getElementsByTagName("canvas")[0].className = "hub-background";
|
||||
}, 300);
|
||||
}, 3860);
|
||||
}, [
|
||||
setHudVisible,
|
||||
setOrbVisible,
|
||||
setIntroStarfieldVisible,
|
||||
setHudActive,
|
||||
setMainGroupRotX,
|
||||
setMainGroupPosZ,
|
||||
setMainGroupPosY,
|
||||
setLainMoving,
|
||||
setLainMoveState,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
});
|
||||
|
||||
export default MainSceneIntro;
|
|
@ -1,7 +1,7 @@
|
|||
import React, { memo, useRef, useState } from "react";
|
||||
import { useFrame, useLoader } from "react-three-fiber";
|
||||
import * as THREE from "three";
|
||||
import orbSprite from "../static/sprites/orb.png";
|
||||
import orbSprite from "../../static/sprites/orb.png";
|
||||
|
||||
// initialize outside the component otherwise it gets overwritten when it rerenders
|
||||
let orbIdx = 0;
|
6
src/components/Orb/OrbAtom.tsx
Normal file
6
src/components/Orb/OrbAtom.tsx
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { atom } from "recoil";
|
||||
|
||||
export const orbVisibilityAtom = atom({
|
||||
key: "orbVisibilityAtom",
|
||||
default: false,
|
||||
});
|
|
@ -1,23 +1,24 @@
|
|||
import React, { useMemo, useRef } from "react";
|
||||
import React, { memo, useMemo, useRef } from "react";
|
||||
import { useFrame, useThree } from "react-three-fiber";
|
||||
import { Scene } from "three";
|
||||
import HUDElement from "../HUD/HUDElement";
|
||||
import Orb from "../Orb";
|
||||
import Orb from "../Orb/Orb";
|
||||
import { useRecoilValue } from "recoil";
|
||||
import { orthoCamPosYAtom } from "./OrthoCameraAtom";
|
||||
import { useSpring, a } from "@react-spring/three";
|
||||
import { orbVisibilityAtom } from "../Orb/OrbAtom";
|
||||
import { hudVisibilityAtom } from "../HUD/HUDActiveAtom";
|
||||
|
||||
interface OrthoCameraProps {
|
||||
orbVisibility: boolean;
|
||||
hudVisibility: boolean;
|
||||
}
|
||||
|
||||
const OrthoCamera = (props: OrthoCameraProps) => {
|
||||
const OrthoCamera = memo(() => {
|
||||
const { gl, scene, camera } = useThree();
|
||||
const virtualScene = useMemo(() => new Scene(), []);
|
||||
const virtualCam = useRef();
|
||||
const orthoCameraPosY = useRecoilValue(orthoCamPosYAtom);
|
||||
|
||||
const orbVisible = useRecoilValue(orbVisibilityAtom);
|
||||
|
||||
const hudVisible = useRecoilValue(hudVisibilityAtom);
|
||||
|
||||
const orthoCameraState = useSpring({
|
||||
orthoCameraPosY: orthoCameraPosY,
|
||||
config: { duration: 1200 },
|
||||
|
@ -38,10 +39,10 @@ const OrthoCamera = (props: OrthoCameraProps) => {
|
|||
position={[0, 0, 10]}
|
||||
position-y={orthoCameraState.orthoCameraPosY}
|
||||
>
|
||||
<HUDElement key={1} hudVisibility={props.hudVisibility} />
|
||||
<Orb orbVisibility={props.orbVisibility} />
|
||||
<HUDElement key={1} hudVisibility={hudVisible} />
|
||||
<Orb orbVisibility={orbVisible} />
|
||||
</a.orthographicCamera>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default OrthoCamera;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { a, Interpolation, useSpring } from "@react-spring/three";
|
||||
import { a, useSpring } from "@react-spring/three";
|
||||
import React, {
|
||||
createRef,
|
||||
memo,
|
||||
|
@ -10,7 +10,10 @@ import React, {
|
|||
} from "react";
|
||||
import { useFrame } from "react-three-fiber";
|
||||
import * as THREE from "three";
|
||||
import { starfieldPosYAtom } from "./StarfieldAtom";
|
||||
import {
|
||||
introStarfieldVisibilityAtom,
|
||||
starfieldPosYAtom,
|
||||
} from "./StarfieldAtom";
|
||||
import { useRecoilValue } from "recoil";
|
||||
|
||||
type StarRefsAndInitialPoses = [
|
||||
|
@ -18,10 +21,6 @@ type StarRefsAndInitialPoses = [
|
|||
number[][]
|
||||
][];
|
||||
|
||||
type StarfieldProps = {
|
||||
introStarfieldVisible: boolean;
|
||||
};
|
||||
|
||||
type StarfieldObjectData = {
|
||||
starPoses: number[][];
|
||||
ref: React.MutableRefObject<React.RefObject<THREE.Object3D>[]>;
|
||||
|
@ -40,13 +39,15 @@ type IntroStarfieldObjectData = {
|
|||
| undefined;
|
||||
};
|
||||
|
||||
const Starfield = memo((props: StarfieldProps) => {
|
||||
const Starfield = memo(() => {
|
||||
const introStarfieldGroupRef = useRef<THREE.Object3D>();
|
||||
|
||||
const [mainStarfieldVisible, setMainStarfieldVisible] = useState(false);
|
||||
|
||||
const starfieldPosY = useRecoilValue(starfieldPosYAtom);
|
||||
|
||||
const introStarfieldVisible = useRecoilValue(introStarfieldVisibilityAtom);
|
||||
|
||||
const starfieldState = useSpring({
|
||||
starfieldPosY: starfieldPosY,
|
||||
config: { duration: 1200 },
|
||||
|
@ -173,7 +174,7 @@ const Starfield = memo((props: StarfieldProps) => {
|
|||
);
|
||||
|
||||
useFrame(() => {
|
||||
if (props.introStarfieldVisible) {
|
||||
if (introStarfieldVisible) {
|
||||
introStarfieldGroupRef.current!.position.y += 0.2;
|
||||
}
|
||||
if (mainStarfieldVisible) {
|
||||
|
@ -281,21 +282,21 @@ const Starfield = memo((props: StarfieldProps) => {
|
|||
<>
|
||||
<a.group
|
||||
ref={introStarfieldGroupRef}
|
||||
position={[-2, -20, -2]}
|
||||
position={[-2, -20, -3.2]}
|
||||
rotation={[0, 0, 0]}
|
||||
visible={props.introStarfieldVisible}
|
||||
visible={introStarfieldVisible}
|
||||
>
|
||||
{introStarfieldObjects.map((obj: IntroStarfieldObjectData) =>
|
||||
obj.starPoses.map((pos: number[], idx: number) => {
|
||||
return (
|
||||
<mesh
|
||||
ref={obj.ref.current[idx]}
|
||||
scale={[0.01, 2, -0.5]}
|
||||
scale={[0.005, 2, 0.005]}
|
||||
position={[pos[0], pos[1], pos[2]]}
|
||||
key={pos[0]}
|
||||
renderOrder={-1}
|
||||
>
|
||||
<planeBufferGeometry attach="geometry" />
|
||||
<boxBufferGeometry attach="geometry" />
|
||||
<shaderMaterial
|
||||
attach="material"
|
||||
uniforms={obj.uniform}
|
||||
|
|
|
@ -5,3 +5,7 @@ export const starfieldPosYAtom = atom({
|
|||
default: -1,
|
||||
});
|
||||
|
||||
export const introStarfieldVisibilityAtom = atom({
|
||||
key: "introStarfieldVisibilityAtom",
|
||||
default: true,
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue