This commit is contained in:
ad044 2020-09-11 20:09:38 +04:00
parent 2b203fc835
commit 29ed34cd1e
21 changed files with 652 additions and 664 deletions

5
package-lock.json generated
View file

@ -10827,6 +10827,11 @@
"util.promisify": "^1.0.0"
}
},
"recoil": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.0.10.tgz",
"integrity": "sha512-+9gRqehw3yKETmoZbhSnWu4GO10HDb5xYf1CjLF1oXGK2uT6GX5Lu9mfTXwjxV/jXxEKx8MIRUUbgPxvbJ8SEw=="
},
"recursive-readdir": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",

View file

@ -17,6 +17,7 @@
"react-dom": "^16.13.1",
"react-scripts": "3.4.3",
"react-three-fiber": "^4.2.20",
"recoil": "0.0.10",
"three": "^0.119.1",
"three-plain-animator": "^1.0.2",
"typescript": "^3.7.5"

View file

@ -1,8 +1,9 @@
import React, { useEffect, useState } from "react";
import Game from "./components/Game";
import MainScene from "./components/MainScene/MainScene";
import "./static/css/hub.css";
import "./static/css/main.css";
import { Canvas } from "react-three-fiber";
import { RecoilRoot } from "recoil";
const App = () => {
const [moveToGame, setMoveToGame] = useState(false);
@ -17,10 +18,12 @@ const App = () => {
return (
<div id="game-root" className="game">
{/* {moveToGame ? <Game /> : <Intro setMoveToGame={setMoveToGame} />} */}
{/* {moveToGame ? <MainScene /> : <Intro setMoveToGame={setMoveToGame} />} */}
{/* <Intro /> */}
<Canvas concurrent>
<Game />
<RecoilRoot>
<MainScene />
</RecoilRoot>
</Canvas>
</div>
);

View file

@ -1,457 +0,0 @@
import { a, useSpring } from "@react-spring/three";
import { OrbitControls } from "drei";
import React, {
Suspense,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import { useFrame } from "react-three-fiber";
import lain_animations from "../resources/lain_animations.json";
import level_sprite_directions from "../resources/level_sprite_directions.json";
import level_sprite_huds from "../resources/level_sprite_huds.json";
import Hub from "./Hub";
import Lain, {
LainMoveDown,
LainMoveLeft,
LainMoveRight,
LainMoveUp,
LainStanding,
LainIntro,
} from "./Lain";
import Lights from "./Lights";
import OrthoCamera from "./OrthoCamera";
import Preloader from "./Preloader";
import Starfield from "./Starfield";
import * as THREE from "three";
type KeyCodeAssociations = {
[keyCode: number]: string;
};
type SpriteDirectionData = {
[direction: string]: string;
};
type SpriteDirections = {
[sprite_id: string]: SpriteDirectionData;
};
type HudShapeData = {
position: number[];
scale: number[];
type: string;
initial_position: number[];
};
type SpriteHudData = {
long: HudShapeData;
boring: HudShapeData;
big: HudShapeData;
};
type SpriteHuds = {
[sprite_hud_id: string]: SpriteHudData;
};
type LainAnimationData = {
frame_count: number;
duration: number;
};
type LainAnimations = {
[sprite: string]: LainAnimationData;
};
const Game = () => {
const [isIntro, setIsIntro] = useState(true);
const [isLainMoving, setLainMoving] = useState(true);
const [lainMoveState, setLainMoveState] = useState(<LainIntro />);
const [orthoCameraPosY, setOrthoCameraPosY] = useState(0);
const [currentSprite, setCurrentSprite] = useState("0422");
const [currentSpriteHUD, setCurrentSpriteHUD] = useState<SpriteHudData>(
(level_sprite_huds as SpriteHuds)[currentSprite.substr(2)]
);
const [spriteUpdateCooldown, setSpriteUpdateCooldown] = useState(false);
const [mainStarfieldVisible, setMainStarfieldVisible] = useState(false);
const [HUDActive, setHUDActive] = useState(1);
const { bigHUDPositionX } = useSpring({
bigHUDPositionX: HUDActive,
config: { duration: 500 },
});
const { longHUDPositionX } = useSpring({
longHUDPositionX: HUDActive,
config: { duration: 500 },
});
const { boringHUDPositionX } = useSpring({
boringHUDPositionX: HUDActive,
config: { duration: 500 },
});
const bigHUDPosX = bigHUDPositionX.to(
[0, 1],
[
currentSpriteHUD["big"]["initial_position"][0],
currentSpriteHUD["big"]["position"][0],
]
);
const boringHUDPosX = boringHUDPositionX.to(
[0, 1],
[
currentSpriteHUD["boring"]["initial_position"][0],
currentSpriteHUD["boring"]["position"][0],
]
);
const longHUDPosX = longHUDPositionX.to(
[0, 1],
[
currentSpriteHUD["long"]["initial_position"][0],
currentSpriteHUD["long"]["position"][0],
]
);
const [{ cameraRotationY }, setCameraRotationY] = useSpring(
() => ({
cameraRotationY: 0,
config: { precision: 0.0001, duration: 1600 },
}),
[]
);
const [{ cameraPositionY }, setCameraPositionY] = useSpring(
() => ({
cameraPositionY: 0,
config: { precision: 0.0001, duration: 1200 },
}),
[]
);
const [{ lainPositionY }, setLainPositionY] = useSpring(
() => ({
lainPositionY: -0.06,
config: { precision: 0.0001, duration: 1200 },
}),
[]
);
const [{ starfieldPositionY }, setStarfieldPositionY] = useSpring(
() => ({
starfieldPositionY: -0.5,
config: { precision: 0.0001, duration: 1200 },
}),
[]
);
const moveCamera = useCallback(
(val: number) =>
void setTimeout(
() =>
setCameraPositionY(() => ({
cameraPositionY: cameraPositionY.get() + val,
})),
1300
),
[cameraPositionY, setCameraPositionY]
);
const moveStarfield = useCallback(
(val: number) =>
void setTimeout(
() =>
setStarfieldPositionY(() => ({
starfieldPositionY: starfieldPositionY.get() + val,
})),
1300
),
[starfieldPositionY, setStarfieldPositionY]
);
const moveLain = useCallback(
(val: number) =>
void setTimeout(
() =>
setLainPositionY(() => ({
lainPositionY: lainPositionY.get() + val,
})),
1300
),
[setLainPositionY, lainPositionY]
);
const rotateCamera = useCallback(
(val: number) =>
void setTimeout(
() =>
setCameraRotationY(() => ({
cameraRotationY: cameraRotationY.get() + val,
})),
1100
),
[setCameraRotationY, cameraRotationY]
);
const camRotY = cameraRotationY.to([0, 1], [0, Math.PI]);
const camPosY = cameraPositionY.to([0, 1], [0, Math.PI]);
const lainPosY = lainPositionY.to([0, 1], [0, Math.PI]);
const starfieldPosY = starfieldPositionY.to([0, 1], [0, Math.PI]);
const getMove = (currentLoc: string, key: string): string => {
return (level_sprite_directions as SpriteDirections)[currentLoc][key];
};
const getKeyCodeAssociation = (keyCode: number): string => {
return ({
40: "down",
37: "left",
38: "up",
39: "right",
} as KeyCodeAssociations)[keyCode];
};
const setAnimationState = useCallback(
(key: string) => {
switch (key) {
case "down":
moveCamera(0.6);
moveStarfield(-0.6);
moveLain(-0.6);
setLainMoveState(<LainMoveDown />);
break;
case "left":
rotateCamera(0.15);
setLainMoveState(<LainMoveLeft />);
break;
case "up":
moveCamera(-0.6);
moveStarfield(0.6);
moveLain(0.6);
setLainMoveState(<LainMoveUp />);
break;
case "right":
rotateCamera(-0.1);
setLainMoveState(<LainMoveRight />);
break;
default:
break;
}
// set moving to true to avoid player from overlapping animations and stuff
// by waiting for the anim to finish
setLainMoving(true);
// wait for the anim to finish, set lain to standing state, release the move lock
setTimeout(() => {
setLainMoving(false);
setLainMoveState(<LainStanding />);
}, (lain_animations as LainAnimations)[key]["duration"]);
},
[moveCamera, moveLain, rotateCamera, moveStarfield]
);
const updateHUD = useCallback(() => {
setHUDActive((prev: number) => Number(!prev));
}, [setHUDActive]);
const moveDispatcher = useCallback(
(move: string, key: string) => {
console.log(move[0]);
switch (move[0]) {
// do nothing / cant move
case undefined:
break;
// "+" in the json denotes that the sprite chosen by getMove is not currently on screen,
// therefore lain should first do a move (up/down/left/right) and then that sprite
// will be chosen.
case "+":
if (!spriteUpdateCooldown) {
// hide the hud
updateHUD();
// disable glow on current sprite
setCurrentSprite("");
setSpriteUpdateCooldown(true);
setAnimationState(key);
setTimeout(() => {
setOrthoCameraPosY(1.88);
updateHUD();
setCurrentSprite(move.substr(1));
setCurrentSpriteHUD(
(level_sprite_huds as SpriteHuds)[move.substr(3)]
);
}, (lain_animations as LainAnimations)[key]["duration"] + 200);
setTimeout(() => {
setSpriteUpdateCooldown(false);
}, 1000);
}
break;
// only change sprite focus
default:
if (!spriteUpdateCooldown) {
setCurrentSprite(move);
setSpriteUpdateCooldown(true);
// toggle hud to go back in
updateHUD();
setTimeout(() => {
// change hud while its hidden
setCurrentSpriteHUD(
(level_sprite_huds as SpriteHuds)[move.substr(2)]
);
// toggle it again to be shown in the new position
updateHUD();
}, 500);
setTimeout(() => {
setSpriteUpdateCooldown(false);
}, 1000);
}
}
},
[setAnimationState, updateHUD, spriteUpdateCooldown]
);
const handleKeyPress = useCallback(
(event) => {
const { keyCode } = event;
const key = getKeyCodeAssociation(keyCode);
if (key && !isLainMoving) {
const move = getMove(currentSprite, key);
console.log(key);
moveDispatcher(move, key);
}
},
[isLainMoving, currentSprite, moveDispatcher]
);
useEffect(() => {
window.addEventListener("keydown", handleKeyPress);
return () => {
window.removeEventListener("keydown", handleKeyPress);
document.getElementsByTagName("body")[0].className = "";
};
}, [handleKeyPress]);
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);
document.getElementsByTagName("canvas")[0].className =
"hub-background";
}, 300);
}
// when pos z reaches this point, make the main starfield appear
if (parseFloat(groupRef.current!.position.z.toPrecision(2)) === -1.4) {
if (!mainStarfieldVisible) {
setMainStarfieldVisible(true);
}
}
}
}
});
// pos-z ? => 3
// rot-x 1.5 => 0
// grp rot/pos both 0,0,0 => ?
return (
<a.perspectiveCamera
position-z={3}
position-y={camPosY}
rotation-y={camRotY}
>
<Suspense fallback={null}>
<group rotation={[2.3, 0, 0]} position={[0, 1.5, -7.5]} ref={groupRef}>
<Preloader />
<Hub currentSprite={currentSprite} />
<OrthoCamera
longHUDPosX={longHUDPosX}
bigHUDPosX={bigHUDPosX}
boringHUDPosX={boringHUDPosX}
longHUDPosYZ={[
currentSpriteHUD!["long"]["position"][1],
currentSpriteHUD!["long"]["position"][2],
]}
boringHUDPosYZ={[
currentSpriteHUD!["boring"]["position"][1],
currentSpriteHUD!["boring"]["position"][2],
]}
bigHUDPosYZ={[
currentSpriteHUD!["big"]["position"][1],
currentSpriteHUD!["big"]["position"][2],
]}
longHUDType={currentSpriteHUD!["long"]["type"]}
boringHUDType={currentSpriteHUD!["boring"]["type"]}
bigHUDType={currentSpriteHUD!["big"]["type"]}
longHUDScale={
currentSpriteHUD!["long"]["scale"] as [number, number, number]
}
boringHUDScale={
currentSpriteHUD!["boring"]["scale"] as [number, number, number]
}
bigHUDScale={
currentSpriteHUD!["big"]["scale"] as [number, number, number]
}
orthoCameraPosY={orthoCameraPosY}
id={currentSprite}
orbVisibility={!isIntro}
hudVisibility={!isIntro}
/>
<Starfield
starfieldPosY={starfieldPosY}
mainStarfieldVisible={mainStarfieldVisible}
introStarfieldVisible={isIntro}
/>
<Lights />
<OrbitControls />
</group>
<Lain
isLainMoving={isLainMoving}
lainMoveState={lainMoveState}
lainPosY={lainPosY}
/>
</Suspense>
</a.perspectiveCamera>
);
};
export default Game;

View file

@ -0,0 +1,8 @@
import {atom} from "recoil";
import level_sprite_huds from "../../resources/level_sprite_huds.json";
import {SpriteHuds} from "./HUDElement";
export const currentHUDAtom = atom({
key: "currentHUDAtom",
default: (level_sprite_huds as SpriteHuds)["22"],
});

View file

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

View file

@ -0,0 +1,163 @@
import React, {memo} from "react";
import {useLoader} from "react-three-fiber";
import * as THREE from "three";
import bigHud from "../../static/sprites/big_hud.png";
import bigHudMirrored from "../../static/sprites/big_hud_mirrored.png";
import longHud from "../../static/sprites/long_hud.png";
import longHudMirrored from "../../static/sprites/long_hud_mirrored.png";
import boringHud from "../../static/sprites/long_hud_boring.png";
import boringHudMirrored from "../../static/sprites/long_hud_boring_mirrored.png";
import {a, useSpring} from "@react-spring/three";
import {useRecoilValue} from "recoil";
import {hudActiveAtom} from "./HUDActiveAtom";
import {currentHUDAtom} from "./CurrentHUDAtom";
export type HUDElementProps = {
hudVisibility: boolean;
};
type HudShapeData = {
position: number[];
scale: number[];
type: string;
initial_position: number[];
};
export type SpriteHudData = {
long: HudShapeData;
boring: HudShapeData;
big: HudShapeData;
};
export type SpriteHuds = {
[sprite_hud_id: string]: SpriteHudData;
};
const HUDElement = memo((props: HUDElementProps) => {
const currentSpriteHUD = useRecoilValue(currentHUDAtom);
const hudActive = useRecoilValue(hudActiveAtom);
const { bigHUDPositionX } = useSpring({
bigHUDPositionX: hudActive,
config: { duration: 500 },
});
const { longHUDPositionX } = useSpring({
longHUDPositionX: hudActive,
config: { duration: 500 },
});
const { boringHUDPositionX } = useSpring({
boringHUDPositionX: hudActive,
config: { duration: 500 },
});
const bigHUDPosX = bigHUDPositionX.to(
[0, 1],
[
currentSpriteHUD["big"]["initial_position"][0],
currentSpriteHUD["big"]["position"][0],
]
);
const boringHUDPosX = boringHUDPositionX.to(
[0, 1],
[
currentSpriteHUD["boring"]["initial_position"][0],
currentSpriteHUD["boring"]["position"][0],
]
);
const longHUDPosX = longHUDPositionX.to(
[0, 1],
[
currentSpriteHUD["long"]["initial_position"][0],
currentSpriteHUD["long"]["position"][0],
]
);
// these hud elements come in all shapes and forms, some of them are mirrored, rotated
// you name it. this function allows me to specify whether i want a normal texture
// for the sprite or the mirrored/rotated one.
const spriteTypeToSprite = (spriteType: string, hudElement: string) => {
switch (spriteType) {
case "normal":
switch (hudElement) {
case "long":
return longHud;
case "boring":
return boringHud;
case "big":
return bigHud;
}
break;
case "mirrored":
switch (hudElement) {
case "big":
return bigHudMirrored;
case "long":
return longHudMirrored;
case "boring":
return boringHudMirrored;
}
}
};
const longHudTexture = useLoader(
THREE.TextureLoader,
spriteTypeToSprite(currentSpriteHUD["long"]["type"], "long")!
);
const longHudBoringTexture = useLoader(
THREE.TextureLoader,
spriteTypeToSprite(currentSpriteHUD["boring"]["type"], "boring")!
);
const bigHudTexture = useLoader(
THREE.TextureLoader,
spriteTypeToSprite(currentSpriteHUD["big"]["type"], "big")!
);
return (
<group visible={props.hudVisibility}>
<a.sprite
position-x={longHUDPosX}
position-y={currentSpriteHUD["long"]["position"][1]}
position-z={currentSpriteHUD["long"]["position"][2]}
scale={currentSpriteHUD["long"]["scale"] as [number, number, number]}
>
<spriteMaterial
attach="material"
map={longHudTexture}
transparent={true}
/>
</a.sprite>
<a.sprite
position-x={boringHUDPosX}
position-y={currentSpriteHUD!["boring"]["position"][1]}
position-z={currentSpriteHUD!["boring"]["position"][2]}
scale={currentSpriteHUD!["boring"]["scale"] as [number, number, number]}
>
<spriteMaterial
attach="material"
map={longHudBoringTexture}
transparent={true}
/>
</a.sprite>
<a.sprite
position-x={bigHUDPosX}
position-y={currentSpriteHUD!["big"]["position"][1]}
position-z={currentSpriteHUD!["big"]["position"][2]}
scale={currentSpriteHUD!["big"]["scale"] as [number, number, number]}
>
<spriteMaterial
attach="material"
map={bigHudTexture}
transparent={true}
/>
</a.sprite>
</group>
);
});
export default HUDElement;

View file

@ -1,117 +0,0 @@
import React, { memo } from "react";
import { useLoader } from "react-three-fiber";
import * as THREE from "three";
import bigHud from "../static/sprites/big_hud.png";
import bigHudMirrored from "../static/sprites/big_hud_mirrored.png";
import longHud from "../static/sprites/long_hud.png";
import longHudMirrored from "../static/sprites/long_hud_mirrored.png";
import boringHud from "../static/sprites/long_hud_boring.png";
import boringHudMirrored from "../static/sprites/long_hud_boring_mirrored.png";
import { a, Interpolation } from "@react-spring/three";
export type HUDElementProps = {
longHUDType: string;
boringHUDType: string;
bigHUDType: string;
longHUDPosYZ: [number, number];
longHUDPosX: Interpolation<number, number>;
longHUDScale: [number, number, number];
boringHUDPosX: Interpolation<number, number>;
boringHUDPosYZ: [number, number];
boringHUDScale: [number, number, number];
bigHUDPosX: Interpolation<number, number>;
bigHUDPosYZ: [number, number];
bigHUDScale: [number, number, number];
hudVisibility: boolean;
};
const HUDElement = memo((props: HUDElementProps) => {
// these hud elements come in all shapes and forms, some of them are mirrored, rotated
// you name it. this function allows me to specify whether i want a normal texture
// for the sprite or the mirrored/rotated one.
const spriteTypeToSprite = (spriteType: string, hudElement: string) => {
switch (spriteType) {
case "normal":
switch (hudElement) {
case "long":
return longHud;
case "boring":
return boringHud;
case "big":
return bigHud;
}
break;
case "mirrored":
switch (hudElement) {
case "big":
return bigHudMirrored;
case "long":
return longHudMirrored;
case "boring":
return boringHudMirrored;
}
}
};
const longHudTexture = useLoader(
THREE.TextureLoader,
spriteTypeToSprite(props.longHUDType, "long")!
);
const longHudBoringTexture = useLoader(
THREE.TextureLoader,
spriteTypeToSprite(props.boringHUDType, "boring")!
);
const bigHudTexture = useLoader(
THREE.TextureLoader,
spriteTypeToSprite(props.bigHUDType, "big")!
);
return (
<group visible={props.hudVisibility}>
<a.sprite
position-y={props.longHUDPosYZ[0]}
position-z={props.longHUDPosYZ[1]}
position-x={props.longHUDPosX}
scale={props.longHUDScale}
>
<spriteMaterial
attach="material"
map={longHudTexture}
transparent={true}
/>
</a.sprite>
<a.sprite
position-x={props.boringHUDPosX}
position-y={props.boringHUDPosYZ[0]}
position-z={props.boringHUDPosYZ[1]}
scale={props.boringHUDScale}
>
<spriteMaterial
attach="material"
map={longHudBoringTexture}
transparent={true}
/>
</a.sprite>
<a.sprite
position-x={props.bigHUDPosX}
position-y={props.bigHUDPosYZ[0]}
position-z={props.bigHUDPosYZ[1]}
scale={props.bigHUDScale}
>
<spriteMaterial
attach="material"
map={bigHudTexture}
transparent={true}
/>
</a.sprite>
</group>
);
});
export default HUDElement;

View file

@ -0,0 +1,227 @@
import React, { useCallback, useEffect, useState } from "react";
import {
LainMoveDown,
LainMoveLeft,
LainMoveRight,
LainMoveUp,
LainStanding,
} from "./Lain/Lain";
import { useRecoilState, useSetRecoilState } from "recoil";
import { hudActiveAtom } from "./HUD/HUDActiveAtom";
import { currentSpriteAtom } from "./LevelSprite/CurrentSpriteAtom";
import lain_animations from "../resources/lain_animations.json";
import level_sprite_huds from "../resources/level_sprite_huds.json";
import level_sprite_directions from "../resources/level_sprite_directions.json";
import {
lainMoveStateAtom,
lainMovingAtom,
lainPosYAtom,
} from "./Lain/LainAtom";
import { camPosYAtom, camRotYAtom } from "./MainScene/CameraAtom";
import { starfieldPosYAtom } from "./Starfield/StarfieldAtom";
import { currentHUDAtom } from "./HUD/CurrentHUDAtom";
import { SpriteHuds } from "./HUD/HUDElement";
type KeyCodeAssociations = {
[keyCode: number]: string;
};
type SpriteDirectionData = {
[direction: string]: string;
};
type SpriteDirections = {
[sprite_id: string]: SpriteDirectionData;
};
type LainAnimationData = {
frame_count: number;
duration: number;
};
type LainAnimations = {
[sprite: string]: LainAnimationData;
};
const InputHandler = () => {
const [currentSprite, setCurrentSprite] = useRecoilState(currentSpriteAtom);
const [lainMoving, setLainMoving] = useRecoilState(lainMovingAtom);
const setLainMoveState = useSetRecoilState(lainMoveStateAtom);
const [spriteUpdateCooldown, setSpriteUpdateCooldown] = useState(false);
const setCurrentHUDElement = useSetRecoilState(currentHUDAtom);
const sethudActive = useSetRecoilState(hudActiveAtom);
const setCamPosY = useSetRecoilState(camPosYAtom);
const setCamRotY = useSetRecoilState(camRotYAtom);
const setLainPosY = useSetRecoilState(lainPosYAtom);
const setStarfieldPosY = useSetRecoilState(starfieldPosYAtom);
const moveCamera = useCallback(
(val: number) => {
setCamPosY((prev: number) => prev + val);
setLainPosY((prev: number) => prev - val);
setStarfieldPosY((prev: number) => prev + val);
},
[setCamPosY, setLainPosY, setStarfieldPosY]
);
const rotateCamera = useCallback(
(val: number) => {
setCamRotY((prev: number) => prev + val);
},
[setCamRotY]
);
const getMove = (currentLoc: string, key: string): string => {
return (level_sprite_directions as SpriteDirections)[currentLoc][key];
};
const getKeyCodeAssociation = (keyCode: number): string => {
return ({
40: "down",
37: "left",
38: "up",
39: "right",
} as KeyCodeAssociations)[keyCode];
};
const setAnimationState = useCallback(
(key: string) => {
switch (key) {
case "down":
setLainMoveState(<LainMoveDown />);
setTimeout(() => {
moveCamera(1.87);
}, 1300);
break;
case "left":
rotateCamera(0.15);
setLainMoveState(<LainMoveLeft />);
break;
case "up":
setLainMoveState(<LainMoveUp />);
setTimeout(() => {
moveCamera(-1.87);
}, 1300);
break;
case "right":
rotateCamera(-0.1);
setLainMoveState(<LainMoveRight />);
break;
default:
break;
}
// set moving to true to avoid player from overlapping animations and stuff
// by waiting for the anim to finish
setLainMoving(true);
// wait for the anim to finish, set lain to standing state, release the move lock
setTimeout(() => {
setLainMoving(false);
setLainMoveState(<LainStanding />);
}, (lain_animations as LainAnimations)[key]["duration"]);
},
[moveCamera, rotateCamera, setLainMoveState]
);
const updateHUD = useCallback(() => {
sethudActive((prev: number) => Number(!prev));
}, [sethudActive]);
const moveDispatcher = useCallback(
(move: string, key: string) => {
console.log(move[0]);
switch (move[0]) {
// do nothing / cant move
case undefined:
break;
// "+" in the json denotes that the sprite chosen by getMove is not currently on screen,
// therefore lain should first do a move (up/down/left/right) and then that sprite
// will be chosen.
case "+":
if (!spriteUpdateCooldown) {
// hide the hud
updateHUD();
// disable glow on current sprite
setCurrentSprite("");
setSpriteUpdateCooldown(true);
setAnimationState(key);
setTimeout(() => {
updateHUD();
// skip the "+"
setCurrentSprite(move.substr(1));
setCurrentHUDElement(
(level_sprite_huds as SpriteHuds)[move.substr(3)]
);
}, (lain_animations as LainAnimations)[key]["duration"] + 200);
setTimeout(() => {
setSpriteUpdateCooldown(false);
}, 1000);
}
break;
// only change sprite focus
default:
if (!spriteUpdateCooldown) {
setCurrentSprite(move);
setSpriteUpdateCooldown(true);
// toggle hud to go back in
updateHUD();
setTimeout(() => {
// change hud while its hidden
setCurrentHUDElement(
(level_sprite_huds as SpriteHuds)[move.substr(2)]
);
// toggle it again to be shown in the new position
updateHUD();
}, 500);
setTimeout(() => {
setSpriteUpdateCooldown(false);
}, 1000);
}
}
},
[
setAnimationState,
updateHUD,
spriteUpdateCooldown,
setCurrentHUDElement,
setCurrentSprite,
]
);
const handleKeyPress = useCallback(
(event) => {
const { keyCode } = event;
const key = getKeyCodeAssociation(keyCode);
if (key && !lainMoving) {
const move = getMove(currentSprite, key);
console.log(key);
moveDispatcher(move, key);
}
},
[lainMoving, currentSprite, moveDispatcher]
);
useEffect(() => {
window.addEventListener("keydown", handleKeyPress);
return () => {
window.removeEventListener("keydown", handleKeyPress);
};
}, [handleKeyPress]);
return <></>;
};
export default InputHandler;

View file

@ -1,20 +1,16 @@
import { a, Interpolation } from "@react-spring/three";
import { a, Interpolation, useSpring } from "@react-spring/three";
import React, { Suspense, useState } from "react";
import { useFrame, useLoader } from "react-three-fiber";
import * as THREE from "three";
import { PlainSingularAnimator } from "three-plain-animator/lib/plain-singular-animator";
import moveDownSpriteSheet from "../static/sprites/jump_down.png";
import moveUpSpriteSheet from "../static/sprites/jump_up.png";
import moveLeftSpriteSheet from "../static/sprites/move_left.png";
import moveRightSpriteSheet from "../static/sprites/move_right.png";
import standingSpriteSheet from "../static/sprites/standing.png";
import introSpriteSheet from "../static/sprites/intro.png";
type LainProps = {
isLainMoving: boolean;
lainMoveState: JSX.Element;
lainPosY: Interpolation<number, number>;
};
import moveDownSpriteSheet from "../../static/sprites/jump_down.png";
import moveUpSpriteSheet from "../../static/sprites/jump_up.png";
import moveLeftSpriteSheet from "../../static/sprites/move_left.png";
import moveRightSpriteSheet from "../../static/sprites/move_right.png";
import standingSpriteSheet from "../../static/sprites/standing.png";
import introSpriteSheet from "../../static/sprites/intro.png";
import { useRecoilValue } from "recoil";
import { lainMoveStateAtom, lainMovingAtom, lainPosYAtom } from "./LainAtom";
type LainConstructorProps = {
sprite: string;
@ -23,7 +19,6 @@ type LainConstructorProps = {
framesHorizontal: number;
};
const LainConstructor = (props: LainConstructorProps) => {
// any here temporarily
const lainSpriteTexture: any = useLoader(THREE.TextureLoader, props.sprite);
@ -112,15 +107,24 @@ export const LainMoveUp = () => {
);
};
const Lain = (props: LainProps) => {
const Lain = () => {
const lainMoving = useRecoilValue(lainMovingAtom);
const lainMoveState = useRecoilValue(lainMoveStateAtom);
const lainPosY = useRecoilValue(lainPosYAtom);
const lainPosYState = useSpring({
lainPosY: lainPosY,
config: { duration: 1200 },
});
return (
<Suspense fallback={<>loading...</>}>
<a.sprite
position-x={0.1}
position-y={props.lainPosY}
position-y={lainPosYState.lainPosY}
scale={[4.9, 4.9, 4.9]}
>
{props.isLainMoving ? props.lainMoveState : <LainStanding />}
{lainMoving ? lainMoveState : <LainStanding />}
</a.sprite>
</Suspense>
);

View file

@ -0,0 +1,17 @@
import { atom } from "recoil";
import React from "react";
export const lainPosYAtom = atom({
key: "lainPosYAtom",
default: -0.15,
});
export const lainMovingAtom = atom({
key: "lainMovingAtom",
default: false,
});
export const lainMoveStateAtom = atom({
key: "lainMoveStateAtom",
default: <></>,
});

View file

@ -0,0 +1,6 @@
import { atom } from 'recoil'
export const currentSpriteAtom = atom({
key: 'currentSpriteAtom',
default: "0422"
})

View file

@ -1,14 +1,14 @@
import React, { useRef, useMemo, memo } from "react";
import { useFrame, useLoader } from "react-three-fiber";
import * as THREE from "three";
import Cou from "../static/sprites/Cou.png";
import CouActive from "../static/sprites/Cou_active.png";
import Dc from "../static/sprites/Dc.png";
import DcActive from "../static/sprites/Dc_active.png";
import SSkn from "../static/sprites/SSkn.png";
import SSKnActive from "../static/sprites/SSkn_active.png";
import Tda from "../static/sprites/Tda.png";
import TdaActive from "../static/sprites/Tda_active.png";
import Cou from "../../static/sprites/Cou.png";
import CouActive from "../../static/sprites/Cou_active.png";
import Dc from "../../static/sprites/Dc.png";
import DcActive from "../../static/sprites/Dc_active.png";
import SSkn from "../../static/sprites/SSkn.png";
import SSKnActive from "../../static/sprites/SSkn_active.png";
import Tda from "../../static/sprites/Tda.png";
import TdaActive from "../../static/sprites/Tda_active.png";
type LevelSpriteConstructorProps = {
sprite: string;

View file

@ -0,0 +1,12 @@
import { atom } from "recoil";
export const camPosYAtom = atom({
key: "camPosYAtom",
default: 0,
});
export const camRotYAtom = atom({
key: "camRotYAtom",
default: 0,
});

View file

@ -0,0 +1,102 @@
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 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";
const MainScene = () => {
const setLainMoving = useSetRecoilState(lainMovingAtom);
const setLainMoveState = useSetRecoilState(lainMoveStateAtom);
const [isIntro, setIsIntro] = useState(true);
const [mainStarfieldVisible, setMainStarfieldVisible] = useState(false);
const camPosY = useRecoilValue(camPosYAtom);
const camRotY = useRecoilValue(camRotYAtom);
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);
document.getElementsByTagName("canvas")[0].className =
"hub-background";
}, 300);
}
}
}
});
useEffect(() => {
setLainMoving(true);
setLainMoveState(<LainIntro />);
}, [setLainMoveState, setLainMoving]);
return (
<a.perspectiveCamera
position-z={3}
position-y={cameraState.camPosY}
rotation-y={cameraState.camRotY}
>
<Suspense fallback={null}>
<group rotation={[2.3, 0, 0]} position={[0, 1.5, -7.5]} ref={groupRef}>
<InputHandler />
<Preloader />
<Hub />
<OrthoCamera orbVisibility={!isIntro} hudVisibility={!isIntro} />
<Starfield
mainStarfieldVisible={mainStarfieldVisible}
introStarfieldVisible={isIntro}
/>
<Lights />
<OrbitControls />
</group>
<Lain />
</Suspense>
</a.perspectiveCamera>
);
};
export default MainScene;

View file

@ -1,53 +0,0 @@
import React, { useMemo, useRef } from "react";
import { useFrame, useThree } from "react-three-fiber";
import { Scene } from "three";
import HUDElement, { HUDElementProps } from "./HUDElement";
import Orb from "./Orb";
interface OrthoCameraProps extends HUDElementProps {
id: string;
orthoCameraPosY: number;
orbVisibility: boolean;
}
const OrthoCamera = (props: OrthoCameraProps) => {
const { gl, scene, camera } = useThree();
const virtualScene = useMemo(() => new Scene(), []);
const virtualCam = useRef();
useFrame(() => {
gl.autoClear = false;
gl.clear();
gl.render(scene, camera);
gl.clearDepth();
gl.render(virtualScene, virtualCam.current!);
}, 1);
//-0.6
return (
<orthographicCamera
ref={virtualCam}
position={[0, props.orthoCameraPosY, 10]}
>
<HUDElement
longHUDType={props.longHUDType}
boringHUDType={props.boringHUDType}
bigHUDType={props.bigHUDType}
longHUDPosYZ={props.longHUDPosYZ}
longHUDPosX={props.longHUDPosX}
longHUDScale={props.longHUDScale}
boringHUDPosYZ={props.boringHUDPosYZ}
boringHUDPosX={props.boringHUDPosX}
boringHUDScale={props.boringHUDScale}
bigHUDPosYZ={props.bigHUDPosYZ}
bigHUDPosX={props.bigHUDPosX}
bigHUDScale={props.bigHUDScale}
key={props.id}
hudVisibility={props.hudVisibility}
/>
<Orb orbVisibility={props.orbVisibility} />
</orthographicCamera>
);
};
export default OrthoCamera;

View file

@ -0,0 +1,37 @@
import React, {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 {useRecoilValue} from "recoil";
import {orthoCamPosYAtom} from "./OrthoCameraAtom";
interface OrthoCameraProps {
orbVisibility: boolean;
hudVisibility: boolean;
}
const OrthoCamera = (props: OrthoCameraProps) => {
const { gl, scene, camera } = useThree();
const virtualScene = useMemo(() => new Scene(), []);
const virtualCam = useRef();
const orthoCameraPosY = useRecoilValue(orthoCamPosYAtom);
useFrame(() => {
gl.autoClear = false;
gl.clear();
gl.render(scene, camera);
gl.clearDepth();
gl.render(virtualScene, virtualCam.current!);
}, 1);
//-0.6
return (
<orthographicCamera ref={virtualCam} position={[0, orthoCameraPosY, 10]}>
<HUDElement key={1} hudVisibility={props.hudVisibility} />
<Orb orbVisibility={props.orbVisibility} />
</orthographicCamera>
);
};
export default OrthoCamera;

View file

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

View file

@ -1,7 +1,8 @@
import { a, Interpolation } from "@react-spring/three";
import React, { createRef, memo, RefObject, useMemo, useRef } from "react";
import { a, Interpolation, useSpring } from "@react-spring/three";
import React, {createRef, memo, RefObject, useEffect, useMemo, useRef} from "react";
import { useFrame } from "react-three-fiber";
import * as THREE from "three";
import { starfieldPosYAtom } from "./StarfieldAtom";
type StarRefsAndIncrementors = [
React.MutableRefObject<React.RefObject<THREE.Object3D>[]>,
@ -9,7 +10,6 @@ type StarRefsAndIncrementors = [
][];
type StarfieldProps = {
starfieldPosY: Interpolation<number, number>;
introStarfieldVisible: boolean;
mainStarfieldVisible: boolean;
};
@ -35,6 +35,11 @@ type IntroStarfieldObjectData = {
const Starfield = memo((props: StarfieldProps) => {
const introStarfieldGroupRef = useRef<THREE.Object3D>();
const starfieldState = useSpring({
starfieldPosY: starfieldPosYAtom,
config: { duration: 1200 },
});
const uniformConstructor = (col: string) => {
return {
color1: {
@ -237,6 +242,10 @@ const Starfield = memo((props: StarfieldProps) => {
},
];
useEffect(() => {
console.log('ssd')
}, [])
return (
<>
<a.group
@ -273,7 +282,7 @@ const Starfield = memo((props: StarfieldProps) => {
<a.group
position={[-0.7, 0, -5]}
rotation={[0, 0, 0]}
position-y={props.starfieldPosY}
position-y={starfieldPosYAtom}
visible={props.mainStarfieldVisible}
>
{mainStarfieldObjects.map((obj: StarfieldObjectData) =>

View file

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

View file

@ -1,13 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { RecoilRoot } from "recoil";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change