transient updates on text renderer, adding gate scene

This commit is contained in:
ad044 2020-11-03 16:44:40 +04:00
parent 83fc6e1e53
commit ea87f6f97f
17 changed files with 299 additions and 70 deletions

View file

@ -8,6 +8,7 @@ import MediaPlayer from "./components/MediaScene/MediaPlayer";
import MediaScene from "./scenes/MediaScene";
import EventManager from "./components/StateManagers/EventManager";
import { useSceneStore } from "./store";
import GateScene from "./scenes/GateScene";
const App = () => {
const [moveToGame, setMoveToGame] = useState(false);
@ -24,6 +25,7 @@ const App = () => {
return {
main: <MainScene />,
media: <MediaScene />,
gate: <GateScene />,
};
}, []);

View file

@ -0,0 +1,56 @@
import React, { useMemo } from "react";
import * as THREE from "three";
const GateMiddle = () => {
const uniforms = useMemo(
() => ({
color1: {
value: new THREE.Color("white"),
},
color2: {
value: new THREE.Color("red"),
},
}),
[]
);
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
uniform vec3 color1;
uniform vec3 color2;
uniform float alpha;
varying vec2 vUv;
void main() {
float alpha = smoothstep(0.0, 1.0, vUv.y);
float colorMix = smoothstep(1.0, 2.0, 1.8);
gl_FragColor = vec4(mix(color1, color2, colorMix), alpha);
}
`;
return (
<>
<mesh scale={[2,2,2]}>
<planeBufferGeometry attach="geometry"></planeBufferGeometry>
<shaderMaterial
attach="material"
vertexShader={vertexShader}
fragmentShader={fragmentShader}
uniforms={uniforms}
/>
</mesh>
</>
);
};
export default GateMiddle;

View file

@ -0,0 +1,54 @@
import React, { useMemo, useRef } from "react";
import blueBinary from "../../static/sprite/blue_binary.png";
import { useFrame, useLoader } from "react-three-fiber";
import * as THREE from "three";
const GateSide = () => {
const blueBinaryTex = useLoader(THREE.TextureLoader, blueBinary);
const texture = useMemo(() => {
blueBinaryTex.wrapS = THREE.RepeatWrapping;
blueBinaryTex.wrapT = THREE.RepeatWrapping;
blueBinaryTex.repeat.set(5, 5);
return blueBinaryTex;
}, [blueBinaryTex]);
useFrame(() => {
if (Date.now() % 2 === 0) {
texture.offset.y += 0.5;
}
});
return (
<>
<mesh
rotation={[0, 0.2, 0]}
position={[-1.7, 0, 1.5]}
scale={[3, 1.5, 0]}
>
<planeBufferGeometry attach="geometry"></planeBufferGeometry>
<meshBasicMaterial
attach="material"
map={texture}
transparent={true}
opacity={0.6}
/>
</mesh>
<mesh
rotation={[0, -0.2, 0]}
position={[1.7, 0, 1.5]}
scale={[3, 1.5, 0]}
>
<planeBufferGeometry attach="geometry"></planeBufferGeometry>
<meshBasicMaterial
attach="material"
map={texture}
transparent={true}
opacity={0.6}
/>
</mesh>
</>
);
};
export default GateSide;

View file

@ -1,4 +1,4 @@
import React, { memo, Suspense, useMemo } from "react";
import React, { memo, Suspense } from "react";
import site_a from "../../resources/site_a.json";
import Level from "./Level";
import level_y_values from "../../resources/level_y_values.json";
@ -6,8 +6,6 @@ import blue_orb_positions from "../../resources/blue_orb_positions.json";
import BlueOrb from "./BlueOrb";
import { a, useSpring } from "@react-spring/three";
import { useBlueOrbStore, useLevelStore, useSiteStore } from "../../store";
import { useLoader } from "react-three-fiber";
import * as THREE from "three";
const Site = memo(() => {
const activeBlueOrbId = useBlueOrbStore((state) => state.activeBlueOrbId);

View file

@ -21,13 +21,15 @@ type BlueOrbDispatcher = {
move_left: BlueOrbDispatchData;
move_right: BlueOrbDispatchData;
change_blue_orb: BlueOrbDispatchData;
throw_blue_orb: BlueOrbDispatchData;
throw_blue_orb_media: BlueOrbDispatchData;
throw_blue_orb_gate: BlueOrbDispatchData;
};
const BlueOrbManager = (props: StateManagerProps) => {
const setActiveBlueOrb = useBlueOrbStore((state) => state.setActiveBlueOrbId);
const setBlueOrbRowIdx = useBlueOrbStore((state) => state.setBlueOrbRowIdx);
const setBlueOrbColIdx = useBlueOrbStore((state) => state.setBlueOrbColIdx);
const setBlueOrbMatrixIndices = useBlueOrbStore(
(state) => state.setBlueOrbMatrixIndices
);
const setIsActiveBlueOrbInteractedWith: SetIsActiveBlueOrbInteractedWith = useBlueOrbStore(
(state) => state.setIsActiveBlueOrbInteractedWith
@ -76,18 +78,21 @@ const BlueOrbManager = (props: StateManagerProps) => {
const updateActiveBlueOrb = useCallback(
(
delay: number,
isMoving: boolean,
newActiveBlueOrbId: string,
newBlueOrbColIdx: number,
newBlueOrbRowIdx: number
) => {
setActiveBlueOrb("");
if (isMoving) setActiveBlueOrb("");
setTimeout(() => {
setActiveBlueOrb(newActiveBlueOrbId);
setBlueOrbColIdx(newBlueOrbColIdx);
setBlueOrbRowIdx(newBlueOrbRowIdx);
setBlueOrbMatrixIndices({
rowIdx: newBlueOrbRowIdx,
colIdx: newBlueOrbColIdx,
});
}, delay);
},
[setActiveBlueOrb, setBlueOrbColIdx, setBlueOrbRowIdx]
[setActiveBlueOrb, setBlueOrbMatrixIndices]
);
const dispatchObject = useCallback(
@ -102,6 +107,7 @@ const BlueOrbManager = (props: StateManagerProps) => {
action: updateActiveBlueOrb,
value: [
3903.704,
true,
newActiveBlueOrbId,
newBlueOrbColIdx,
newBlueOrbRowIdx,
@ -111,6 +117,7 @@ const BlueOrbManager = (props: StateManagerProps) => {
action: updateActiveBlueOrb,
value: [
3903.704,
true,
newActiveBlueOrbId,
newBlueOrbColIdx,
newBlueOrbRowIdx,
@ -120,6 +127,7 @@ const BlueOrbManager = (props: StateManagerProps) => {
action: updateActiveBlueOrb,
value: [
3903.704,
true,
newActiveBlueOrbId,
newBlueOrbColIdx,
newBlueOrbRowIdx,
@ -129,6 +137,7 @@ const BlueOrbManager = (props: StateManagerProps) => {
action: updateActiveBlueOrb,
value: [
3903.704,
true,
newActiveBlueOrbId,
newBlueOrbColIdx,
newBlueOrbRowIdx,
@ -136,9 +145,19 @@ const BlueOrbManager = (props: StateManagerProps) => {
},
change_blue_orb: {
action: updateActiveBlueOrb,
value: [0, newActiveBlueOrbId, newBlueOrbColIdx, newBlueOrbRowIdx],
value: [
0,
false,
newActiveBlueOrbId,
newBlueOrbColIdx,
newBlueOrbRowIdx,
],
},
throw_blue_orb: {
throw_blue_orb_media: {
action: animateActiveBlueOrbThrow,
value: [0, true],
},
throw_blue_orb_gate: {
action: animateActiveBlueOrbThrow,
value: [0, true],
},

View file

@ -48,8 +48,7 @@ export type StateManagerProps = {
export type GameContext = {
keyPress?: string;
scene: string;
blueOrbRowIdx: number;
blueOrbColIdx: number;
blueOrbMatrixIndices: { rowIdx: number; colIdx: number };
currentLevel: string;
siteRotIdx: string;
activeMediaComponent: string;
@ -59,8 +58,9 @@ const EventManager = () => {
const currentScene = useSceneStore((state) => state.currentScene);
// main scene
const blueOrbRowIdx = useBlueOrbStore((state) => state.blueOrbRowIdx);
const blueOrbColIdx = useBlueOrbStore((state) => state.blueOrbColIdx);
const blueOrbMatrixIndices = useBlueOrbStore(
(state) => state.blueOrbMatrixIndices
);
const siteRotIdx = useSiteStore((state) => state.siteRotIdx);
const currentLevel = useLevelStore((state) => state.currentLevel);
@ -77,15 +77,13 @@ const EventManager = () => {
() => ({
scene: currentScene,
siteRotIdx: siteRotIdx,
blueOrbRowIdx: blueOrbRowIdx,
blueOrbColIdx: blueOrbColIdx,
blueOrbMatrixIndices: blueOrbMatrixIndices,
currentLevel: currentLevel,
activeMediaComponent: activeMediaComponent,
}),
[
activeMediaComponent,
blueOrbColIdx,
blueOrbRowIdx,
blueOrbMatrixIndices,
currentLevel,
currentScene,
siteRotIdx,

View file

@ -84,7 +84,7 @@ const GreenTextManager = (props: StateManagerProps) => {
action: toggleAndSetGreenText,
value: [newActiveBlueOrbId, newActiveHudId, 500],
},
throw_blue_orb: {
throw_blue_orb_media: {
action: initializeGreenTextForMediaScene,
value: [],
},

View file

@ -28,7 +28,12 @@ const LainManager = (props: StateManagerProps) => {
value: "move_right",
duration: 3904.704,
},
throw_blue_orb: {
throw_blue_orb_media: {
action: setLainMoveState,
value: "throwBlueOrb",
duration: 3904.704,
},
throw_blue_orb_gate: {
action: setLainMoveState,
value: "throwBlueOrb",
duration: 3904.704,

View file

@ -101,7 +101,7 @@ const MediaComponentManager = (props: StateManagerProps) => {
action: switchToLeftSide,
value: "thirdWord",
},
throw_blue_orb: {
throw_blue_orb_media: {
action: setActiveMediaComponent,
value: "play",
},

View file

@ -49,7 +49,7 @@ const MediaImageManager = (props: StateManagerProps) => {
const dispatchObject = useCallback(
(event: string, newActiveBlueOrbId: string) => {
const dispatcherObjects = {
throw_blue_orb: {
throw_blue_orb_media: {
action: updateSceneImages,
value: newActiveBlueOrbId,
},

View file

@ -14,7 +14,7 @@ type MediaWordDispatchData = {
sndWord_up: MediaWordDispatcher;
thirdWord_down: MediaWordDispatcher;
thirdWord_up: MediaWordDispatcher;
throw_blue_orb: MediaWordDispatcher;
throw_blue_orb_media: MediaWordDispatcher;
};
const MediaWordManager = (props: StateManagerProps) => {
@ -52,7 +52,7 @@ const MediaWordManager = (props: StateManagerProps) => {
action: addToWordPositionDataStructIdx,
value: -1,
},
throw_blue_orb: {
throw_blue_orb_media: {
action: resetWordPositionDataStructIdx,
},
};

View file

@ -8,7 +8,12 @@ const SceneManager = (props: StateManagerProps) => {
const dispatchObject = useCallback(
(event: string, newScene: string) => {
const dispatcherObjects = {
throw_blue_orb: {
throw_blue_orb_media: {
action: setScene,
value: newScene,
delay: 3904.704,
},
throw_blue_orb_gate: {
action: setScene,
value: newScene,
delay: 3904.704,

View file

@ -41,7 +41,7 @@ type YellowTextDispatcher = {
change_blue_orb: YellowTextDispatchData;
play_down: YellowTextDispatchData;
exit_up: YellowTextDispatchData;
throw_blue_orb: YellowTextDispatchData;
throw_blue_orb_media: YellowTextDispatchData;
exit_media_scene: YellowTextDispatchData;
};
@ -258,7 +258,7 @@ const YellowTextManager = (props: any) => {
action: animateMediaYellowText,
value: ["Exit", [-0.8, -0.08, 0.6]],
},
throw_blue_orb: {
throw_blue_orb_media: {
action: initializeYellowTextForMediaScene,
},
exit_media_scene: {

View file

@ -1,6 +1,10 @@
import { a, useSpring, useTrail } from "@react-spring/three";
import React, { useEffect, useMemo } from "react";
import { useSiteStore, useTextRendererStore } from "../../store";
import React, { useEffect, useRef } from "react";
import {
TextRendererState,
useSiteStore,
useTextRendererStore,
} from "../../store";
import BigLetter from "./BigLetter";
import MediumLetter from "./MediumLetter";
@ -14,22 +18,34 @@ const TextRenderer = () => {
const isSiteChangingY = useSiteStore((state) => state.isSiteChangingY);
// ======================================= YELLOW TEXT ======================================
const yellowText = useTextRendererStore((state) => state.yellowText);
const yellowTextArr = useMemo(() => yellowText.split(""), [yellowText]);
const yellowTextPosY = useTextRendererStore((state) => state.yellowTextPosY);
// yellow text posx and posy need to be updated reactively aswell when changing site rotation/y value
const yellowTextPosX = useTextRendererStore((state) => state.yellowTextPosX);
const yellowTextPosY = useTextRendererStore((state) => state.yellowTextPosY);
const yellowTextOffsetXCoeff = useTextRendererStore(
(state) => state.yellowTextOffsetXCoeff
);
const yellowTextArrRef = useRef(
useTextRendererStore.getState().yellowText.split("")
);
const yellowTextPosXRef = useRef(
useTextRendererStore.getState().yellowTextPosX
);
const yellowTextPosYRef = useRef(
useTextRendererStore.getState().yellowTextPosY
);
// this is used to animate the letters moving one after another
const yellowLetterTrail = useTrail(yellowText.length, {
yellowLetterPosX: yellowTextPosX,
yellowLetterPosY: yellowTextPosY,
const yellowLetterTrail = useTrail(yellowTextArrRef.current.length, {
yellowLetterPosX: yellowTextPosXRef.current,
yellowLetterPosY: yellowTextPosYRef.current,
config: { duration: 280 },
});
// this is used when the GROUP itself has to be animated in a "static" manner
// this is used when the whole GROUP itself needs to be animated
const yellowLetterSpring = useSpring({
yellowLetterPosX: yellowTextPosX,
yellowLetterPosY: yellowTextPosY,
@ -38,14 +54,21 @@ const TextRenderer = () => {
// ==================================== GREEN TEXT ============================================
const greenText = useTextRendererStore((state) => state.greenText);
const greenTextPosY = useTextRendererStore((state) => state.greenTextPosY);
const greenTextPosXObj = useTextRendererStore((state) => state.greenTextPosX);
const greenTextArr = useMemo(() => greenText.split(""), [greenText]);
const greenTextActive = useTextRendererStore(
(state) => state.greenTextActive
);
const greenTextPosYRef = useRef(
useTextRendererStore.getState().greenTextPosY
);
const greenTextPosXRef = useRef(
useTextRendererStore.getState().greenTextPosX
);
const greenTextArrRef = useRef(
useTextRendererStore.getState().greenText.split("")
);
const { greenTextPosXToggle } = useSpring({
greenTextPosXToggle: greenTextActive,
config: { duration: 500 },
@ -53,13 +76,34 @@ const TextRenderer = () => {
const greenTextPosX = greenTextPosXToggle.to(
[0, 1],
[greenTextPosXObj.initial, greenTextPosXObj.final]
[greenTextPosXRef.current.initial, greenTextPosXRef.current.final]
);
// subscribing to state and updating transiently
useEffect(
() =>
useTextRendererStore.subscribe(
(state) => {
yellowTextPosXRef.current = (state as TextRendererState).yellowTextPosX;
yellowTextPosYRef.current = (state as TextRendererState).yellowTextPosY;
yellowTextArrRef.current = (state as TextRendererState).yellowText.split(
""
);
greenTextPosYRef.current = (state as TextRendererState).greenTextPosY;
greenTextPosXRef.current = (state as TextRendererState).greenTextPosX;
greenTextArrRef.current = (state as TextRendererState).greenText.split(
""
);
},
(state) => state
),
[]
);
return (
<group position={[0, 0, 10]}>
{isSiteChangingY
? yellowTextArr.map((letter, idx) => (
? yellowTextArrRef.current.map((letter, idx) => (
<a.group
key={idx}
position-x={yellowLetterSpring.yellowLetterPosX}
@ -69,8 +113,8 @@ const TextRenderer = () => {
>
<BigLetter
color={"yellow"}
yellowTextOffsetXCoeff={yellowTextOffsetXCoeff}
letter={yellowTextArr[idx]}
yellowTextOffsetXCoeff={0}
letter={yellowTextArrRef.current[idx]}
letterIdx={idx}
key={idx}
/>
@ -88,7 +132,7 @@ const TextRenderer = () => {
<BigLetter
color={"yellow"}
yellowTextOffsetXCoeff={yellowTextOffsetXCoeff}
letter={yellowTextArr[idx]}
letter={yellowTextArrRef.current[idx]}
letterIdx={idx}
key={idx}
/>
@ -98,11 +142,11 @@ const TextRenderer = () => {
<a.group
position-x={greenTextPosX}
position-y={greenTextPosY}
position-y={greenTextPosYRef.current}
position-z={-8.7}
scale={[0.02, 0.035, 0.02]}
>
{greenTextArr.map((letter, idx) => (
{greenTextArrRef.current.map((letter, idx) => (
<MediumLetter
color={"yellow"}
letter={letter}

View file

@ -1,5 +1,6 @@
import { GameContext } from "../components/StateManagers/EventManager";
import available_blue_orbs_on_projection from "../resources/available_blue_orbs_on_projection.json";
import site_a from "../resources/site_a.json";
const hudAssocs = {
"00": "fg_hud_1",
@ -21,26 +22,31 @@ const handleMainSceneEvent = (gameContext: GameContext) => {
const keyPress = gameContext.keyPress;
let newBlueOrbColIdx = gameContext.blueOrbColIdx;
let newBlueOrbRowIdx = gameContext.blueOrbRowIdx;
const blueOrbColIdx = gameContext.blueOrbMatrixIndices.colIdx;
const blueOrbRowIdx = gameContext.blueOrbMatrixIndices.rowIdx;
let newBlueOrbColIdx = gameContext.blueOrbMatrixIndices.colIdx;
let newBlueOrbRowIdx = gameContext.blueOrbMatrixIndices.rowIdx;
let newLevel = gameContext.currentLevel;
let newSiteRotIdx: string | number = gameContext.siteRotIdx;
let newSiteRotIdx = gameContext.siteRotIdx;
let newScene = gameContext.scene;
switch (keyPress) {
case "left":
newBlueOrbColIdx = gameContext.blueOrbColIdx - 1;
newBlueOrbColIdx = blueOrbColIdx - 1;
if (newBlueOrbColIdx < 0) {
event = "move_left";
newSiteRotIdx = parseInt(gameContext.siteRotIdx) + 1;
if (newSiteRotIdx > 8) newSiteRotIdx = "1";
newSiteRotIdx =
parseInt(gameContext.siteRotIdx) + 1 > 8
? "1"
: (parseInt(gameContext.siteRotIdx) + 1).toString();
newBlueOrbColIdx = 0;
} else {
event = "change_blue_orb";
}
break;
case "down":
newBlueOrbRowIdx = gameContext.blueOrbRowIdx + 1;
newBlueOrbRowIdx = blueOrbRowIdx + 1;
if (newBlueOrbRowIdx > 2) {
event = "move_down";
@ -53,7 +59,7 @@ const handleMainSceneEvent = (gameContext: GameContext) => {
}
break;
case "up":
newBlueOrbRowIdx = gameContext.blueOrbRowIdx - 1;
newBlueOrbRowIdx = blueOrbRowIdx - 1;
if (newBlueOrbRowIdx < 0) {
event = "move_up";
@ -67,18 +73,45 @@ const handleMainSceneEvent = (gameContext: GameContext) => {
}
break;
case "right":
newBlueOrbColIdx = gameContext.blueOrbColIdx + 1;
newBlueOrbColIdx = blueOrbColIdx + 1;
if (newBlueOrbColIdx > 3) {
event = "move_right";
newSiteRotIdx = (parseInt(gameContext.siteRotIdx) - 1).toString();
newSiteRotIdx =
parseInt(gameContext.siteRotIdx) - 1 < 1
? "8"
: (parseInt(gameContext.siteRotIdx) - 1).toString();
newBlueOrbColIdx = 3;
} else {
event = "change_blue_orb";
}
break;
case "select":
event = "throw_blue_orb";
newScene = "media";
// in this case we have to check the type of the blue orb
// and dispatch an action depending on that, so we have to precalculate the
// new active blue orb here.
const newActiveBlueOrbId =
newLevel +
available_blue_orbs_on_projection[
newSiteRotIdx as keyof typeof available_blue_orbs_on_projection
][newBlueOrbRowIdx as number][newBlueOrbColIdx as number];
const blueOrbType =
site_a[newActiveBlueOrbId as keyof typeof site_a].type;
const eventAnimation = "throw_blue_orb_";
switch (parseInt(blueOrbType)) {
case 0:
case 2:
event = eventAnimation + "media";
newScene = "media";
break;
case 8:
event = eventAnimation + "gate";
newScene = "gate";
break;
}
}
const newActiveBlueOrbId =
@ -91,6 +124,7 @@ const handleMainSceneEvent = (gameContext: GameContext) => {
hudAssocs[
`${newBlueOrbRowIdx}${newBlueOrbColIdx}` as keyof typeof hudAssocs
];
return {
event: event,
newBlueOrbColIdx: newBlueOrbColIdx,

16
src/scenes/GateScene.tsx Normal file
View file

@ -0,0 +1,16 @@
import React from "react";
import GateSide from "../components/GateScene/GateSide";
import { OrbitControls } from "drei";
import GateMiddle from "../components/GateScene/GateMiddle";
const GateScene = () => {
return (
<perspectiveCamera position-z={3}>
<OrbitControls />
<pointLight intensity={5.2} color={0xffffff} position={[-2, 0, 0]} />
<GateSide />
<GateMiddle/>
</perspectiveCamera>
);
};
export default GateScene;

View file

@ -26,10 +26,8 @@ type BlueOrbState = {
setActiveBlueOrbRotZ: (to: number) => void;
setActiveBlueOrbId: (to: string) => void;
setIsActiveBlueOrbInteractedWith: (to: boolean) => void;
blueOrbRowIdx: number;
blueOrbColIdx: number;
setBlueOrbRowIdx: (to: number) => void;
setBlueOrbColIdx: (to: number) => void;
blueOrbMatrixIndices: { rowIdx: number; colIdx: number };
setBlueOrbMatrixIndices: (to: { rowIdx: number; colIdx: number }) => void;
};
type LainState = {
@ -128,7 +126,7 @@ type MediaState = {
setMediaPercentageElapsed: (to: number) => void;
};
type TextRendererState = {
export type TextRendererState = {
yellowText: string;
yellowTextPosY: number;
yellowTextPosX: number;
@ -209,9 +207,9 @@ export const useBlueOrbStore = create<BlueOrbState>((set) => ({
setIsActiveBlueOrbInteractedWith: (to) =>
set(() => ({ isActiveBlueOrbInteractedWith: to })),
blueOrbRowIdx: 0,
setBlueOrbRowIdx: (to) => set(() => ({ blueOrbRowIdx: to })),
blueOrbColIdx: 0,
setBlueOrbColIdx: (to) => set(() => ({ blueOrbColIdx: to })),
blueOrbMatrixIndices: { rowIdx: 0, colIdx: 0 },
setBlueOrbMatrixIndices: (to) => set(() => ({ blueOrbMatrixIndices: to })),
}));
export const useLainStore = create<LainState>((set) => ({
@ -382,7 +380,7 @@ export const useMediaWordStore = create<MediaWordState>((set) => ({
}));
export const useSceneStore = create<SceneState>((set) => ({
currentScene: "main",
currentScene: "gate",
setScene: (to) => set(() => ({ currentScene: to })),
}));