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",
default: 1,
});
export const hudVisibilityAtom = atom({
key: "hudVisibilityAtom",
default: false,
});

View file

@ -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) => {

View file

@ -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} />

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 { 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>

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

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

View file

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

View file

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