mirror of
https://github.com/ad044/lainTSX.git
synced 2024-10-22 23:19:06 +00:00
wtf
This commit is contained in:
parent
2b203fc835
commit
29ed34cd1e
21 changed files with 652 additions and 664 deletions
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -10827,6 +10827,11 @@
|
||||||
"util.promisify": "^1.0.0"
|
"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": {
|
"recursive-readdir": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-scripts": "3.4.3",
|
"react-scripts": "3.4.3",
|
||||||
"react-three-fiber": "^4.2.20",
|
"react-three-fiber": "^4.2.20",
|
||||||
|
"recoil": "0.0.10",
|
||||||
"three": "^0.119.1",
|
"three": "^0.119.1",
|
||||||
"three-plain-animator": "^1.0.2",
|
"three-plain-animator": "^1.0.2",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import React, { useEffect, useState } from "react";
|
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/hub.css";
|
||||||
import "./static/css/main.css";
|
import "./static/css/main.css";
|
||||||
import { Canvas } from "react-three-fiber";
|
import { Canvas } from "react-three-fiber";
|
||||||
|
import { RecoilRoot } from "recoil";
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [moveToGame, setMoveToGame] = useState(false);
|
const [moveToGame, setMoveToGame] = useState(false);
|
||||||
|
@ -17,10 +18,12 @@ const App = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="game-root" className="game">
|
<div id="game-root" className="game">
|
||||||
{/* {moveToGame ? <Game /> : <Intro setMoveToGame={setMoveToGame} />} */}
|
{/* {moveToGame ? <MainScene /> : <Intro setMoveToGame={setMoveToGame} />} */}
|
||||||
{/* <Intro /> */}
|
{/* <Intro /> */}
|
||||||
<Canvas concurrent>
|
<Canvas concurrent>
|
||||||
<Game />
|
<RecoilRoot>
|
||||||
|
<MainScene />
|
||||||
|
</RecoilRoot>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
|
8
src/components/HUD/CurrentHUDAtom.tsx
Normal file
8
src/components/HUD/CurrentHUDAtom.tsx
Normal 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"],
|
||||||
|
});
|
6
src/components/HUD/HUDActiveAtom.tsx
Normal file
6
src/components/HUD/HUDActiveAtom.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { atom } from "recoil";
|
||||||
|
|
||||||
|
export const hudActiveAtom = atom({
|
||||||
|
key: "hudActiveAtom",
|
||||||
|
default: 1,
|
||||||
|
});
|
163
src/components/HUD/HUDElement.tsx
Normal file
163
src/components/HUD/HUDElement.tsx
Normal 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;
|
|
@ -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;
|
|
227
src/components/InputHandler.tsx
Normal file
227
src/components/InputHandler.tsx
Normal 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;
|
|
@ -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 React, { Suspense, 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 { PlainSingularAnimator } from "three-plain-animator/lib/plain-singular-animator";
|
import { PlainSingularAnimator } from "three-plain-animator/lib/plain-singular-animator";
|
||||||
import moveDownSpriteSheet from "../static/sprites/jump_down.png";
|
import moveDownSpriteSheet from "../../static/sprites/jump_down.png";
|
||||||
import moveUpSpriteSheet from "../static/sprites/jump_up.png";
|
import moveUpSpriteSheet from "../../static/sprites/jump_up.png";
|
||||||
import moveLeftSpriteSheet from "../static/sprites/move_left.png";
|
import moveLeftSpriteSheet from "../../static/sprites/move_left.png";
|
||||||
import moveRightSpriteSheet from "../static/sprites/move_right.png";
|
import moveRightSpriteSheet from "../../static/sprites/move_right.png";
|
||||||
import standingSpriteSheet from "../static/sprites/standing.png";
|
import standingSpriteSheet from "../../static/sprites/standing.png";
|
||||||
import introSpriteSheet from "../static/sprites/intro.png";
|
import introSpriteSheet from "../../static/sprites/intro.png";
|
||||||
|
import { useRecoilValue } from "recoil";
|
||||||
type LainProps = {
|
import { lainMoveStateAtom, lainMovingAtom, lainPosYAtom } from "./LainAtom";
|
||||||
isLainMoving: boolean;
|
|
||||||
lainMoveState: JSX.Element;
|
|
||||||
lainPosY: Interpolation<number, number>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type LainConstructorProps = {
|
type LainConstructorProps = {
|
||||||
sprite: string;
|
sprite: string;
|
||||||
|
@ -23,7 +19,6 @@ type LainConstructorProps = {
|
||||||
framesHorizontal: number;
|
framesHorizontal: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const LainConstructor = (props: LainConstructorProps) => {
|
const LainConstructor = (props: LainConstructorProps) => {
|
||||||
// any here temporarily
|
// any here temporarily
|
||||||
const lainSpriteTexture: any = useLoader(THREE.TextureLoader, props.sprite);
|
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 (
|
return (
|
||||||
<Suspense fallback={<>loading...</>}>
|
<Suspense fallback={<>loading...</>}>
|
||||||
<a.sprite
|
<a.sprite
|
||||||
position-x={0.1}
|
position-x={0.1}
|
||||||
position-y={props.lainPosY}
|
position-y={lainPosYState.lainPosY}
|
||||||
scale={[4.9, 4.9, 4.9]}
|
scale={[4.9, 4.9, 4.9]}
|
||||||
>
|
>
|
||||||
{props.isLainMoving ? props.lainMoveState : <LainStanding />}
|
{lainMoving ? lainMoveState : <LainStanding />}
|
||||||
</a.sprite>
|
</a.sprite>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
17
src/components/Lain/LainAtom.tsx
Normal file
17
src/components/Lain/LainAtom.tsx
Normal 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: <></>,
|
||||||
|
});
|
6
src/components/LevelSprite/CurrentSpriteAtom.tsx
Normal file
6
src/components/LevelSprite/CurrentSpriteAtom.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { atom } from 'recoil'
|
||||||
|
|
||||||
|
export const currentSpriteAtom = atom({
|
||||||
|
key: 'currentSpriteAtom',
|
||||||
|
default: "0422"
|
||||||
|
})
|
|
@ -1,14 +1,14 @@
|
||||||
import React, { useRef, useMemo, memo } from "react";
|
import React, { useRef, useMemo, memo } 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 Cou from "../static/sprites/Cou.png";
|
import Cou from "../../static/sprites/Cou.png";
|
||||||
import CouActive from "../static/sprites/Cou_active.png";
|
import CouActive from "../../static/sprites/Cou_active.png";
|
||||||
import Dc from "../static/sprites/Dc.png";
|
import Dc from "../../static/sprites/Dc.png";
|
||||||
import DcActive from "../static/sprites/Dc_active.png";
|
import DcActive from "../../static/sprites/Dc_active.png";
|
||||||
import SSkn from "../static/sprites/SSkn.png";
|
import SSkn from "../../static/sprites/SSkn.png";
|
||||||
import SSKnActive from "../static/sprites/SSkn_active.png";
|
import SSKnActive from "../../static/sprites/SSkn_active.png";
|
||||||
import Tda from "../static/sprites/Tda.png";
|
import Tda from "../../static/sprites/Tda.png";
|
||||||
import TdaActive from "../static/sprites/Tda_active.png";
|
import TdaActive from "../../static/sprites/Tda_active.png";
|
||||||
|
|
||||||
type LevelSpriteConstructorProps = {
|
type LevelSpriteConstructorProps = {
|
||||||
sprite: string;
|
sprite: string;
|
12
src/components/MainScene/CameraAtom.tsx
Normal file
12
src/components/MainScene/CameraAtom.tsx
Normal 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,
|
||||||
|
});
|
||||||
|
|
102
src/components/MainScene/MainScene.tsx
Normal file
102
src/components/MainScene/MainScene.tsx
Normal 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;
|
|
@ -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;
|
|
37
src/components/OrthoCamera/OrthoCamera.tsx
Normal file
37
src/components/OrthoCamera/OrthoCamera.tsx
Normal 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;
|
7
src/components/OrthoCamera/OrthoCameraAtom.tsx
Normal file
7
src/components/OrthoCamera/OrthoCameraAtom.tsx
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { atom } from "recoil";
|
||||||
|
|
||||||
|
export const orthoCamPosYAtom = atom({
|
||||||
|
key: "orthoCamPosYAtom",
|
||||||
|
default: 0,
|
||||||
|
});
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { a, Interpolation } from "@react-spring/three";
|
import { a, Interpolation, useSpring } from "@react-spring/three";
|
||||||
import React, { createRef, memo, RefObject, useMemo, useRef } from "react";
|
import React, {createRef, memo, RefObject, useEffect, useMemo, useRef} 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";
|
||||||
|
|
||||||
type StarRefsAndIncrementors = [
|
type StarRefsAndIncrementors = [
|
||||||
React.MutableRefObject<React.RefObject<THREE.Object3D>[]>,
|
React.MutableRefObject<React.RefObject<THREE.Object3D>[]>,
|
||||||
|
@ -9,7 +10,6 @@ type StarRefsAndIncrementors = [
|
||||||
][];
|
][];
|
||||||
|
|
||||||
type StarfieldProps = {
|
type StarfieldProps = {
|
||||||
starfieldPosY: Interpolation<number, number>;
|
|
||||||
introStarfieldVisible: boolean;
|
introStarfieldVisible: boolean;
|
||||||
mainStarfieldVisible: boolean;
|
mainStarfieldVisible: boolean;
|
||||||
};
|
};
|
||||||
|
@ -35,6 +35,11 @@ type IntroStarfieldObjectData = {
|
||||||
const Starfield = memo((props: StarfieldProps) => {
|
const Starfield = memo((props: StarfieldProps) => {
|
||||||
const introStarfieldGroupRef = useRef<THREE.Object3D>();
|
const introStarfieldGroupRef = useRef<THREE.Object3D>();
|
||||||
|
|
||||||
|
const starfieldState = useSpring({
|
||||||
|
starfieldPosY: starfieldPosYAtom,
|
||||||
|
config: { duration: 1200 },
|
||||||
|
});
|
||||||
|
|
||||||
const uniformConstructor = (col: string) => {
|
const uniformConstructor = (col: string) => {
|
||||||
return {
|
return {
|
||||||
color1: {
|
color1: {
|
||||||
|
@ -237,6 +242,10 @@ const Starfield = memo((props: StarfieldProps) => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('ssd')
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<a.group
|
<a.group
|
||||||
|
@ -273,7 +282,7 @@ const Starfield = memo((props: StarfieldProps) => {
|
||||||
<a.group
|
<a.group
|
||||||
position={[-0.7, 0, -5]}
|
position={[-0.7, 0, -5]}
|
||||||
rotation={[0, 0, 0]}
|
rotation={[0, 0, 0]}
|
||||||
position-y={props.starfieldPosY}
|
position-y={starfieldPosYAtom}
|
||||||
visible={props.mainStarfieldVisible}
|
visible={props.mainStarfieldVisible}
|
||||||
>
|
>
|
||||||
{mainStarfieldObjects.map((obj: StarfieldObjectData) =>
|
{mainStarfieldObjects.map((obj: StarfieldObjectData) =>
|
7
src/components/Starfield/StarfieldAtom.tsx
Normal file
7
src/components/Starfield/StarfieldAtom.tsx
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { atom } from "recoil";
|
||||||
|
|
||||||
|
export const starfieldPosYAtom = atom({
|
||||||
|
key: "starfieldPosYAtom",
|
||||||
|
default: 0,
|
||||||
|
});
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from "react-dom";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
import * as serviceWorker from './serviceWorker';
|
import * as serviceWorker from "./serviceWorker";
|
||||||
|
import { RecoilRoot } from "recoil";
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root')
|
document.getElementById("root")
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want your app to work offline and load faster, you can change
|
// If you want your app to work offline and load faster, you can change
|
||||||
|
|
Loading…
Reference in a new issue