added second ring, tweaked code, camera functions properly

This commit is contained in:
ad044 2020-08-26 22:10:59 +04:00
parent 25f60c8d13
commit af26c3563c
9 changed files with 225 additions and 135 deletions

BIN
public/models/ring1.glb Normal file

Binary file not shown.

View file

@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import Intro from "./components/Intro";
import Game from "./components/Game";
import "./static/css/main.css";
import "./static/css/hub.css";
const App = () => {
const [moveToGame, setMoveToGame] = useState(false);

View file

@ -10,7 +10,8 @@ import Lain, {
} from "./Lain";
import Hub from "./Hub";
//import Orb from "./Orb";
import { OrbitControls } from "drei";
import { OrbitControls, PerspectiveCamera } from "drei";
import Lights from "./Lights";
type KeyCodeAssociations = {
[keyCode: number]: string;
@ -20,17 +21,34 @@ type FrameCounts = {
[animation: string]: number;
};
// value by which to rotate/move the ring on the y axis
type LowerRingValues = {
[direction: string]: number;
};
const Game = () => {
const [isLainMoving, setLainMoving] = useState(false);
const [lainMoveState, setLainMoveState] = useState(<LainStanding />);
const [lainPosY, setLainPosY] = useState(-0.2);
const [lowerRingRotationY, setLowerRingRotationY] = useState(5);
const [lowerRingPositionY, setLowerRingPositionY] = useState(-0.31);
const [cameraPosY, setCameraPosY] = useState(0);
const [cameraRotationY, setCameraRotationY] = useState(0);
const moveCamera = (value: number, duration: number) => {
const moveInterval = setInterval(() => {
setCameraPosY((prev: number) => prev + value);
setLainPosY((prev: number) => prev - value);
});
setTimeout(() => {
clearInterval(moveInterval);
}, duration);
};
const rotateCamera = (value: number, duration: number) => {
const rotationInterval = setInterval(() => {
setCameraRotationY((prev: number) => prev + value);
});
setTimeout(() => {
clearInterval(rotationInterval);
}, duration);
};
const getKeyValue = <U extends keyof T, T extends object>(key: U) => (
obj: T
@ -93,35 +111,6 @@ const Game = () => {
}, getAnimationDuration(key));
};
const getLowerRingValue = (direction: string) => {
return getKeyValue<keyof LowerRingValues, LowerRingValues>(direction)({
left: 0.002,
right: -0.002,
up: -0.005,
down: 0.005,
});
};
const rotateLowerRing = (key: string, duration: number) => {
const rotationInterval = setInterval(() => {
setLowerRingRotationY((prev) => prev + getLowerRingValue(key));
}, 10);
setTimeout(() => {
clearInterval(rotationInterval);
}, duration);
};
const moveLowerRing = (key: string, duration: number) => {
const moveInterval = setInterval(() => {
setLowerRingPositionY((prev) => prev + getLowerRingValue(key));
});
setTimeout(() => {
clearInterval(moveInterval);
}, duration);
};
const handleUserKeyPress = useCallback(
(event) => {
const { _, keyCode } = event;
@ -129,22 +118,33 @@ const Game = () => {
const key = getKeyCodeAssociation(keyCode);
console.log(key);
if (!isLainMoving) {
setAnimationState(key);
setTimeout(() => {
switch (key) {
case "left":
case "right":
rotateLowerRing(key, 1000);
break;
case "up":
case "down":
moveLowerRing(key, 1000);
break;
default:
break;
}
}, 1200);
switch (key) {
case "left":
setTimeout(() => {
rotateCamera(0.001, 1600);
}, 1100);
break;
case "right":
setTimeout(() => {
rotateCamera(-0.001, 1600);
}, 1100);
break;
case "up":
setTimeout(() => {
moveCamera(-0.005, 1200);
}, 1300);
break;
case "down":
setTimeout(() => {
moveCamera(0.005, 1200);
}, 1300);
break;
default:
break;
}
}
},
[isLainMoving]
@ -153,23 +153,31 @@ const Game = () => {
useEffect(() => {
window.addEventListener("keydown", handleUserKeyPress);
document.getElementsByTagName("canvas")[0].className = "hub-background";
return () => {
window.removeEventListener("keydown", handleUserKeyPress);
document.getElementsByTagName("body")[0].className = "";
};
}, [handleUserKeyPress]);
return (
<>
<Canvas shadowMap concurrent camera={{ position: [0, -0.1, -2] }}>
<Lain isLainMoving={isLainMoving} lainMoveState={lainMoveState} />
<Hub
lowerRingRotationY={lowerRingRotationY}
lowerRingPositionY={lowerRingPositionY}
/>
<ambientLight color={0x808080} />
<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} />
{/* <Canvas camera={{ position: [0, 0, -2] }}> */}
<Canvas>
<PerspectiveCamera
position={[0, cameraPosY, 3]}
rotation={[0, cameraRotationY, 0]}
>
<OrbitControls />
<Lain
isLainMoving={isLainMoving}
lainMoveState={lainMoveState}
lainPosY={lainPosY}
/>
<Hub />
<Lights />
</PerspectiveCamera>
</Canvas>
</>
);

View file

@ -1,19 +1,13 @@
import React, { Suspense } from "react";
import Ring0 from "./Ring0";
import Ring1 from "./Ring1";
type HubProps = {
lowerRingPositionY: number;
lowerRingRotationY: number;
};
const Hub = (props: HubProps) => {
const Hub = (props: any) => {
return (
<>
<Suspense fallback={<React.Fragment>loading...</React.Fragment>}>
<Ring0
lowerRingPositionY={props.lowerRingPositionY}
lowerRingRotationY={props.lowerRingRotationY}
/>
<Suspense fallback={<>loading...</>}>
<Ring1 />
<Ring0 />
</Suspense>
</>
);

View file

@ -4,7 +4,7 @@ import * as THREE from "three";
import introSpriteSheet from "../static/sprites/intro.png";
import moveDownSpriteSheet from "../static/sprites/jump_down.png";
import standingSpriteSheet from "../static/sprites/standing.png";
import moveLeftSpriteSheet from "../static/sprites/move_left1.png";
import moveLeftSpriteSheet from "../static/sprites/move_left.png";
import moveRightSpriteSheet from "../static/sprites/move_right.png";
import moveUpSpriteSheet from "../static/sprites/jump_up.png";
import { PlainSingularAnimator } from "three-plain-animator/lib/plain-singular-animator";
@ -12,26 +12,26 @@ import { PlainSingularAnimator } from "three-plain-animator/lib/plain-singular-a
type LainProps = {
isLainMoving: boolean;
lainMoveState: JSX.Element;
lainPosY: number;
};
type LainConstructorProps = {
sprite: string;
frameCount: number;
framesVertical: number;
framesHorizontal: number;
};
const LainConstructor = (props: LainConstructorProps) => {
// any here temporarily
const spriteTexture: any = useLoader(
THREE.TextureLoader,
props.sprite
);
const spriteTexture: any = useLoader(THREE.TextureLoader, props.sprite);
const [animator] = useState(
() =>
new PlainSingularAnimator(
spriteTexture,
props.frameCount,
1,
props.framesHorizontal,
props.framesVertical,
props.frameCount,
props.frameCount * 0.27
)
@ -42,57 +42,84 @@ const LainConstructor = (props: LainConstructorProps) => {
});
return (
<Suspense fallback={null}>
<sprite position={[0.1, -0.7, 0]} scale={[3.3, 3.3, 3.3]}>
<spriteMaterial attach="material" map={spriteTexture}></spriteMaterial>
<spriteMaterial attach="material" map={spriteTexture}></spriteMaterial>
);
};
export const LainIntro = () => {
return (
<LainConstructor
sprite={standingSpriteSheet}
frameCount={1}
framesHorizontal={8}
framesVertical={6}
/>
);
};
export const LainStanding = () => {
return (
<LainConstructor
sprite={standingSpriteSheet}
frameCount={3}
framesHorizontal={3}
framesVertical={1}
/>
);
};
export const LainMoveDown = () => {
return (
<LainConstructor
sprite={moveDownSpriteSheet}
frameCount={36}
framesHorizontal={6}
framesVertical={6}
/>
);
};
export const LainMoveLeft = () => {
return (
<LainConstructor
sprite={moveLeftSpriteSheet}
frameCount={47}
framesVertical={6}
framesHorizontal={8}
/>
);
};
export const LainMoveRight = () => {
return (
<LainConstructor
sprite={moveRightSpriteSheet}
frameCount={47}
framesHorizontal={8}
framesVertical={6}
/>
);
};
export const LainMoveUp = () => {
return (
<LainConstructor
sprite={moveUpSpriteSheet}
frameCount={36}
framesHorizontal={6}
framesVertical={6}
/>
);
};
const Lain = (props: LainProps) => {
return (
<Suspense fallback={<>loading...</>}>
<sprite position={[0.1, props.lainPosY, 0]} scale={[4.3, 4.3, 4.3]}>
{props.isLainMoving ? props.lainMoveState : <LainStanding />}
</sprite>
</Suspense>
);
};
export const LainIntro = () => {
return <LainConstructor sprite={introSpriteSheet} frameCount={51} />;
};
export const LainStanding = () => {
return <LainConstructor sprite={standingSpriteSheet} frameCount={1} />;
};
export const LainMoveDown = () => {
return <LainConstructor sprite={moveDownSpriteSheet} frameCount={36} />;
};
export const LainMoveLeft = () => {
return <LainConstructor sprite={moveLeftSpriteSheet} frameCount={47} />;
};
export const LainMoveRight = () => {
return <LainConstructor sprite={moveRightSpriteSheet} frameCount={47} />;
};
export const LainMoveUp = () => {
return <LainConstructor sprite={moveUpSpriteSheet} frameCount={36} />;
};
const Lain = (props: LainProps) => {
return (
<>
{/* without a suspense the sprites wont load, and the suspense loading
animation takes about .3 seconds to finish for each sprite resulting in
blinks between each spritesheet. with a nested suspense we can have
LainStanding as the suspense fallback with a fallback of its own (the
loading message) which will only be shown once, when the game loads. */}
<Suspense
fallback={
<Suspense fallback={<React.Fragment>loading...</React.Fragment>}>
<LainStanding />
</Suspense>
}
>
{props.isLainMoving ? props.lainMoveState : <LainStanding />}
</Suspense>
</>
);
};
export default Lain;

14
src/components/Lights.tsx Normal file
View file

@ -0,0 +1,14 @@
import React from "react";
const Lights = () => {
return (
<>
<ambientLight color={0x808080} />
<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} />
</>
);
};
export default Lights;

View file

@ -24,7 +24,7 @@ type GLTFResult = GLTF & {
materials: {};
};
const Ring0 = (props: Ring0Props) => {
const Ring0 = (props: any) => {
const { nodes, materials } = useLoader<GLTFResult>(
GLTFLoader,
"/models/ring0.glb",
@ -33,14 +33,14 @@ const Ring0 = (props: Ring0Props) => {
return (
<group
scale={[1.3, 1.3, 1.3]}
position={[0, props.lowerRingPositionY, 0]}
rotation={[0, props.lowerRingRotationY, 0]}
position={[0, -0.27, 0]}
rotation={[0, 0.26, 0]}
dispose={null}
>
<mesh geometry={nodes.Circle.geometry} rotation={[0, Math.PI / 4, 0]}>
<meshLambertMaterial
attach="material"
transparent={true}
color={0x636363}
side={THREE.DoubleSide}
/>
</mesh>
@ -48,4 +48,4 @@ const Ring0 = (props: Ring0Props) => {
);
};
export default Ring0
export default Ring0;

49
src/components/Ring1.tsx Normal file
View file

@ -0,0 +1,49 @@
import * as THREE from "three";
import React, { useRef, useState, useEffect } from "react";
import { useLoader } from "react-three-fiber";
import { GLTFLoader, GLTF } from "three/examples/jsm/loaders/GLTFLoader";
import { draco } from "drei";
type GLTFResult = GLTF & {
nodes: {
Circle002: THREE.Mesh;
};
materials: {};
};
const Ring1 = (props: JSX.IntrinsicElements["group"]) => {
const [higherRingRotation, setHigherRingRotation] = useState(0);
const { nodes, materials } = useLoader<GLTFResult>(
GLTFLoader,
"/models/ring1.glb",
draco("/draco-gltf/")
);
const ring1PermaRotation = () => {
setHigherRingRotation((prev) => prev + 0.002);
};
useEffect(() => {
setInterval(ring1PermaRotation, 1);
}, []);
return (
<group
position={[0, 0.4, 0]}
rotation={[0, higherRingRotation, 0]}
scale={[1.3, 1.3, 1.3]}
dispose={null}
>
<mesh geometry={nodes.Circle002.geometry} rotation={[0, Math.PI / 4, 0]}>
<meshLambertMaterial
attach="material"
color={0x8b6ff7}
side={THREE.DoubleSide}
/>
</mesh>
</group>
);
};
export default Ring1;

View file

@ -1,3 +0,0 @@
export const getKeyValue = <U extends keyof T, T extends object>(key: U) => (
obj: T
) => obj[key];