separated main intro scene into its own file, ported the animation to react-spring

This commit is contained in:
ad044 2020-09-12 21:45:51 +04:00
parent 7d2d114ecc
commit ba56e5451f
11 changed files with 168 additions and 86 deletions

View file

@ -4,3 +4,8 @@ export const hudActiveAtom = atom({
key: "hudActiveAtom", key: "hudActiveAtom",
default: 1, default: 1,
}); });
export const hudVisibilityAtom = atom({
key: "hudVisibilityAtom",
default: false,
});

View file

@ -53,7 +53,7 @@ const InputHandler = () => {
const setCurrentHUDElement = useSetRecoilState(currentHUDAtom); const setCurrentHUDElement = useSetRecoilState(currentHUDAtom);
const sethudActive = useSetRecoilState(hudActiveAtom); const setHudActive = useSetRecoilState(hudActiveAtom);
const setCamPosY = useSetRecoilState(camPosYAtom); const setCamPosY = useSetRecoilState(camPosYAtom);
const setCamRotY = useSetRecoilState(camRotYAtom); const setCamRotY = useSetRecoilState(camRotYAtom);
@ -136,8 +136,8 @@ const InputHandler = () => {
); );
const updateHUD = useCallback(() => { const updateHUD = useCallback(() => {
sethudActive((prev: number) => Number(!prev)); setHudActive((prev: number) => Number(!prev));
}, [sethudActive]); }, [setHudActive]);
const moveDispatcher = useCallback( const moveDispatcher = useCallback(
(move: string, key: string) => { (move: string, key: string) => {

View file

@ -1,13 +1,9 @@
import React, { memo } from "react"; import React, { memo } from "react";
type LightProps = { const Lights = memo(() => {
ambientLightVal: number;
};
const Lights = memo((props: LightProps) => {
return ( 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={0xffffff} position={[0, 0, 700]} intensity={0.5} />
<pointLight color={0x7f7f7f} position={[0, 1000, 0]} intensity={1} /> <pointLight color={0x7f7f7f} position={[0, 1000, 0]} intensity={1} />
<pointLight color={0xffffff} position={[0, 0, 0]} intensity={0.1} /> <pointLight color={0xffffff} position={[0, 0, 0]} intensity={0.1} />

View 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,
});

View file

@ -1,77 +1,51 @@
import { a, useSpring } from "@react-spring/three"; import { a, useSpring } from "@react-spring/three";
import { OrbitControls } from "drei"; import { OrbitControls } from "drei";
import React, { Suspense, useEffect, useRef, useState } from "react"; import React, { Suspense, useEffect, useRef, useState } from "react";
import { useFrame } from "react-three-fiber";
import Hub from "../Hub"; import Hub from "../Hub";
import Lain, { LainIntro, LainStanding } from "../Lain/Lain"; import Lain, { LainIntro } from "../Lain/Lain";
import Lights from "../Lights"; import Lights from "../Lights";
import OrthoCamera from "../OrthoCamera/OrthoCamera"; import OrthoCamera from "../OrthoCamera/OrthoCamera";
import Preloader from "../Preloader"; import Preloader from "../Preloader";
import Starfield from "../Starfield/Starfield"; import Starfield from "../Starfield/Starfield";
import * as THREE from "three";
import { useRecoilValue, useSetRecoilState } from "recoil"; import { useRecoilValue, useSetRecoilState } from "recoil";
import { lainMoveStateAtom, lainMovingAtom } from "../Lain/LainAtom"; import { lainMoveStateAtom, lainMovingAtom } from "../Lain/LainAtom";
import { camPosYAtom, camRotYAtom } from "./CameraAtom"; import { camPosYAtom, camRotYAtom } from "./CameraAtom";
import InputHandler from "../InputHandler"; import InputHandler from "../InputHandler";
import MainSceneIntro from "./MainSceneIntro";
import {
mainGroupPosYAtom,
mainGroupPosZAtom,
mainGroupRotXAtom,
} from "./MainGroupAtom";
const MainScene = () => { const MainScene = () => {
const setLainMoving = useSetRecoilState(lainMovingAtom); const setLainMoving = useSetRecoilState(lainMovingAtom);
const setLainMoveState = useSetRecoilState(lainMoveStateAtom); const setLainMoveState = useSetRecoilState(lainMoveStateAtom);
const mainGroupPosY = useRecoilValue(mainGroupPosYAtom);
const [isIntro, setIsIntro] = useState(true); const mainGroupPosZ = useRecoilValue(mainGroupPosZAtom);
const mainGroupRotX = useRecoilValue(mainGroupRotXAtom);
const camPosY = useRecoilValue(camPosYAtom); const camPosY = useRecoilValue(camPosYAtom);
const camRotY = useRecoilValue(camRotYAtom); const camRotY = useRecoilValue(camRotYAtom);
const [ambientLightVal, setAmbientLightVal] = useState(0.4);
const cameraState = useSpring({ const cameraState = useSpring({
camPosY: camPosY, camPosY: camPosY,
camRotY: camRotY, camRotY: camRotY,
config: { duration: 1200 }, config: { duration: 1200 },
}); });
const groupRef = useRef<THREE.Object3D>(); const mainGroupStatePos = useSpring({
mainGroupPosY: mainGroupPosY,
useFrame(() => { mainGroupPosZ: mainGroupPosZ,
if (isIntro) { config: { duration: 3644 },
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 mainGroupStateRot = useSpring({
mainGroupRotX: mainGroupRotX,
config: { duration: 1500 },
});
// set lain intro spritesheet before the page loads fully
useEffect(() => { useEffect(() => {
setLainMoving(true); setLainMoving(true);
setLainMoveState(<LainIntro />); setLainMoveState(<LainIntro />);
@ -84,15 +58,20 @@ const MainScene = () => {
rotation-y={cameraState.camRotY} rotation-y={cameraState.camRotY}
> >
<Suspense fallback={null}> <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 /> <InputHandler />
<Preloader /> <Preloader />
<Hub /> <Hub />
<OrthoCamera orbVisibility={!isIntro} hudVisibility={!isIntro} /> <OrthoCamera />
<Starfield introStarfieldVisible={isIntro} /> <Starfield />
<Lights ambientLightVal={ambientLightVal} /> <Lights />
<OrbitControls /> <OrbitControls />
</group> </a.group>
<Lain /> <Lain />
</Suspense> </Suspense>
</a.perspectiveCamera> </a.perspectiveCamera>

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

View file

@ -1,7 +1,7 @@
import React, { memo, useRef, useState } from "react"; import React, { memo, 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 orbSprite from "../static/sprites/orb.png"; import orbSprite from "../../static/sprites/orb.png";
// initialize outside the component otherwise it gets overwritten when it rerenders // initialize outside the component otherwise it gets overwritten when it rerenders
let orbIdx = 0; let orbIdx = 0;

View file

@ -0,0 +1,6 @@
import { atom } from "recoil";
export const orbVisibilityAtom = atom({
key: "orbVisibilityAtom",
default: false,
});

View file

@ -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 { useFrame, useThree } from "react-three-fiber";
import { Scene } from "three"; import { Scene } from "three";
import HUDElement from "../HUD/HUDElement"; import HUDElement from "../HUD/HUDElement";
import Orb from "../Orb"; import Orb from "../Orb/Orb";
import { useRecoilValue } from "recoil"; import { useRecoilValue } from "recoil";
import { orthoCamPosYAtom } from "./OrthoCameraAtom"; import { orthoCamPosYAtom } from "./OrthoCameraAtom";
import { useSpring, a } from "@react-spring/three"; import { useSpring, a } from "@react-spring/three";
import { orbVisibilityAtom } from "../Orb/OrbAtom";
import { hudVisibilityAtom } from "../HUD/HUDActiveAtom";
interface OrthoCameraProps { const OrthoCamera = memo(() => {
orbVisibility: boolean;
hudVisibility: boolean;
}
const OrthoCamera = (props: OrthoCameraProps) => {
const { gl, scene, camera } = useThree(); const { gl, scene, camera } = useThree();
const virtualScene = useMemo(() => new Scene(), []); const virtualScene = useMemo(() => new Scene(), []);
const virtualCam = useRef(); const virtualCam = useRef();
const orthoCameraPosY = useRecoilValue(orthoCamPosYAtom); const orthoCameraPosY = useRecoilValue(orthoCamPosYAtom);
const orbVisible = useRecoilValue(orbVisibilityAtom);
const hudVisible = useRecoilValue(hudVisibilityAtom);
const orthoCameraState = useSpring({ const orthoCameraState = useSpring({
orthoCameraPosY: orthoCameraPosY, orthoCameraPosY: orthoCameraPosY,
config: { duration: 1200 }, config: { duration: 1200 },
@ -38,10 +39,10 @@ const OrthoCamera = (props: OrthoCameraProps) => {
position={[0, 0, 10]} position={[0, 0, 10]}
position-y={orthoCameraState.orthoCameraPosY} position-y={orthoCameraState.orthoCameraPosY}
> >
<HUDElement key={1} hudVisibility={props.hudVisibility} /> <HUDElement key={1} hudVisibility={hudVisible} />
<Orb orbVisibility={props.orbVisibility} /> <Orb orbVisibility={orbVisible} />
</a.orthographicCamera> </a.orthographicCamera>
); );
}; });
export default OrthoCamera; export default OrthoCamera;

View file

@ -1,4 +1,4 @@
import { a, Interpolation, useSpring } from "@react-spring/three"; import { a, useSpring } from "@react-spring/three";
import React, { import React, {
createRef, createRef,
memo, memo,
@ -10,7 +10,10 @@ import React, {
} from "react"; } from "react";
import { useFrame } from "react-three-fiber"; import { useFrame } from "react-three-fiber";
import * as THREE from "three"; import * as THREE from "three";
import { starfieldPosYAtom } from "./StarfieldAtom"; import {
introStarfieldVisibilityAtom,
starfieldPosYAtom,
} from "./StarfieldAtom";
import { useRecoilValue } from "recoil"; import { useRecoilValue } from "recoil";
type StarRefsAndInitialPoses = [ type StarRefsAndInitialPoses = [
@ -18,10 +21,6 @@ type StarRefsAndInitialPoses = [
number[][] number[][]
][]; ][];
type StarfieldProps = {
introStarfieldVisible: boolean;
};
type StarfieldObjectData = { type StarfieldObjectData = {
starPoses: number[][]; starPoses: number[][];
ref: React.MutableRefObject<React.RefObject<THREE.Object3D>[]>; ref: React.MutableRefObject<React.RefObject<THREE.Object3D>[]>;
@ -40,13 +39,15 @@ type IntroStarfieldObjectData = {
| undefined; | undefined;
}; };
const Starfield = memo((props: StarfieldProps) => { const Starfield = memo(() => {
const introStarfieldGroupRef = useRef<THREE.Object3D>(); const introStarfieldGroupRef = useRef<THREE.Object3D>();
const [mainStarfieldVisible, setMainStarfieldVisible] = useState(false); const [mainStarfieldVisible, setMainStarfieldVisible] = useState(false);
const starfieldPosY = useRecoilValue(starfieldPosYAtom); const starfieldPosY = useRecoilValue(starfieldPosYAtom);
const introStarfieldVisible = useRecoilValue(introStarfieldVisibilityAtom);
const starfieldState = useSpring({ const starfieldState = useSpring({
starfieldPosY: starfieldPosY, starfieldPosY: starfieldPosY,
config: { duration: 1200 }, config: { duration: 1200 },
@ -173,7 +174,7 @@ const Starfield = memo((props: StarfieldProps) => {
); );
useFrame(() => { useFrame(() => {
if (props.introStarfieldVisible) { if (introStarfieldVisible) {
introStarfieldGroupRef.current!.position.y += 0.2; introStarfieldGroupRef.current!.position.y += 0.2;
} }
if (mainStarfieldVisible) { if (mainStarfieldVisible) {
@ -281,21 +282,21 @@ const Starfield = memo((props: StarfieldProps) => {
<> <>
<a.group <a.group
ref={introStarfieldGroupRef} ref={introStarfieldGroupRef}
position={[-2, -20, -2]} position={[-2, -20, -3.2]}
rotation={[0, 0, 0]} rotation={[0, 0, 0]}
visible={props.introStarfieldVisible} visible={introStarfieldVisible}
> >
{introStarfieldObjects.map((obj: IntroStarfieldObjectData) => {introStarfieldObjects.map((obj: IntroStarfieldObjectData) =>
obj.starPoses.map((pos: number[], idx: number) => { obj.starPoses.map((pos: number[], idx: number) => {
return ( return (
<mesh <mesh
ref={obj.ref.current[idx]} ref={obj.ref.current[idx]}
scale={[0.01, 2, -0.5]} scale={[0.005, 2, 0.005]}
position={[pos[0], pos[1], pos[2]]} position={[pos[0], pos[1], pos[2]]}
key={pos[0]} key={pos[0]}
renderOrder={-1} renderOrder={-1}
> >
<planeBufferGeometry attach="geometry" /> <boxBufferGeometry attach="geometry" />
<shaderMaterial <shaderMaterial
attach="material" attach="material"
uniforms={obj.uniform} uniforms={obj.uniform}

View file

@ -1,7 +1,11 @@
import { atom } from "recoil"; import { atom } from "recoil";
export const starfieldPosYAtom = atom({ export const starfieldPosYAtom = atom({
key: "starfieldPosYAtom", key: "starfieldPosYAtom",
default: -1, default: -1,
}); });
export const introStarfieldVisibilityAtom = atom({
key: "introStarfieldVisibilityAtom",
default: true,
});