fixed bugs, optimized node generation

This commit is contained in:
ad044 2021-01-29 19:14:02 +04:00
parent acceaa149a
commit 51cbd41c93
10 changed files with 294 additions and 108 deletions

View file

@ -1,14 +1,17 @@
import React, { useCallback } from "react";
import React, { useEffect, useRef } from "react";
import level_selection_font from "../../../static/sprite/select_level_font.png";
import verticalHud from "../../../static/sprite/select_level_hud_vertical.png";
import horizontalHud from "../../../static/sprite/select_level_hud_horizontal.png";
import levelSelectionText from "../../../static/sprite/select_level_text.png";
import upArrow from "../../../static/sprite/select_level_up_arrow.png";
import downArrow from "../../../static/sprite/select_level_down_arrow.png";
import upArrowActive from "../../../static/sprite/select_level_up_arrow_active.png";
import downArrowActive from "../../../static/sprite/select_level_down_arrow_active.png";
import { useStore } from "../../../store";
import { useLoader } from "react-three-fiber";
import * as THREE from "three";
import { a, useSpring } from "@react-spring/three";
import usePrevious from "../../../hooks/usePrevious";
const LevelSelection = () => {
const levelSelectionFontTex = useLoader(
@ -23,47 +26,108 @@ const LevelSelection = () => {
);
const upArrowTex = useLoader(THREE.TextureLoader, upArrow);
const downArrowTex = useLoader(THREE.TextureLoader, downArrow);
const toggled = useStore(
useCallback((state) => Number(state.mainSubscene === "level_selection"), [])
);
const upArrowActiveTex = useLoader(THREE.TextureLoader, upArrowActive);
const downArrowActiveTex = useLoader(THREE.TextureLoader, downArrowActive);
const selectedLevel = useStore((state) => state.selectedLevel)
.toString()
.padStart(2, "0");
const activeLevel = useStore((state) => state.activeLevel);
const subscene = useStore((state) => state.mainSubscene);
const prevData = usePrevious({ subscene, selectedLevel });
const { levelSelectionToggle } = useSpring({
levelSelectionToggle: toggled,
const [pos, set] = useSpring(() => ({
vertPosY: -2.5,
horizPosX: -4,
config: { duration: 500 },
});
}));
const verticalHudPosY = levelSelectionToggle.to([0, 1], [-2.5, 0]);
const horizontalHudPosX = levelSelectionToggle.to([0, 1], [-4, -0.6]);
const fstNumberRef = useRef<THREE.Mesh>();
const sndNumberRef = useRef<THREE.Mesh>();
const upArrowRef = useRef<THREE.Sprite>();
const downArrowRef = useRef<THREE.Sprite>();
const generateGeom = useCallback((number: number) => {
const geometry = new THREE.PlaneBufferGeometry();
useEffect(() => {
const generateGeom = (number: number) => {
const geometry = new THREE.PlaneBufferGeometry();
const uvAttribute = geometry.attributes.uv;
const uvAttribute = geometry.attributes.uv;
for (let i = 0; i < uvAttribute.count; i++) {
let u = uvAttribute.getX(i);
let v = uvAttribute.getY(i);
for (let i = 0; i < uvAttribute.count; i++) {
let u = uvAttribute.getX(i);
let v = uvAttribute.getY(i);
u = (u * 22) / 240 + number / 10;
u = (u * 22) / 240 + number / 10;
uvAttribute.setXY(i, u, v);
uvAttribute.setXY(i, u, v);
}
return geometry;
};
if (subscene === "level_selection") {
set({ vertPosY: 0, horizPosX: -0.6 });
if (fstNumberRef.current && sndNumberRef.current) {
fstNumberRef.current.geometry = generateGeom(parseInt(activeLevel[0]));
sndNumberRef.current.geometry = generateGeom(parseInt(activeLevel[1]));
}
} else if (
subscene === "site" &&
prevData?.subscene === "level_selection"
) {
set({ vertPosY: -2.5, horizPosX: -4 });
}
return geometry;
}, []);
if (selectedLevel !== prevData?.selectedLevel) {
if (fstNumberRef.current && sndNumberRef.current) {
fstNumberRef.current.geometry = generateGeom(
parseInt(selectedLevel[0])
);
sndNumberRef.current.geometry = generateGeom(
parseInt(selectedLevel[1])
);
if (
prevData?.selectedLevel &&
upArrowRef.current &&
downArrowRef.current
) {
if (selectedLevel > prevData?.selectedLevel) {
upArrowRef.current.material.map = upArrowActiveTex;
upArrowRef.current.material.needsUpdate = true;
setTimeout(() => {
upArrowRef.current!.material.map = upArrowTex;
upArrowRef.current!.material.needsUpdate = true;
}, 100);
} else if (selectedLevel < prevData?.selectedLevel) {
downArrowRef.current.material.map = downArrowActiveTex;
downArrowRef.current.material.needsUpdate = true;
setTimeout(() => {
downArrowRef.current!.material.map = downArrowTex;
downArrowRef.current!.material.needsUpdate = true;
}, 100);
}
}
}
}
}, [
activeLevel,
downArrowActiveTex,
prevData?.selectedLevel,
prevData?.subscene,
selectedLevel,
set,
subscene,
upArrowActiveTex,
upArrowTex,
]);
return (
<group>
<a.group position-y={verticalHudPosY} renderOrder={5}>
<a.group position-y={pos.vertPosY} renderOrder={5}>
<mesh
scale={[0.3, 0.4, 0]}
position={[0.95, 0, 0]}
renderOrder={5}
geometry={generateGeom(parseInt(selectedLevel[0]))}
ref={fstNumberRef}
>
<meshBasicMaterial
map={levelSelectionFontTex}
@ -76,7 +140,7 @@ const LevelSelection = () => {
scale={[0.3, 0.4, 0]}
position={[1.23, 0, 0]}
renderOrder={5}
geometry={generateGeom(parseInt(selectedLevel[1]))}
ref={sndNumberRef}
>
<meshBasicMaterial
map={levelSelectionFontTex}
@ -106,20 +170,28 @@ const LevelSelection = () => {
scale={[0.3, 0.15, 0]}
position={[1.1, -0.35, 0]}
renderOrder={4}
ref={downArrowRef}
>
<spriteMaterial
map={downArrowTex}
attach="material"
transparent={true}
depthTest={false}
color={0xffffff}
/>
</sprite>
<sprite scale={[0.3, 0.15, 0]} position={[1.1, 0.5, 0]} renderOrder={4}>
<sprite
scale={[0.3, 0.15, 0]}
position={[1.1, 0.5, 0]}
renderOrder={4}
ref={upArrowRef}
>
<spriteMaterial
map={upArrowTex}
attach="material"
transparent={true}
depthTest={false}
color={0xffffff}
/>
</sprite>
</a.group>
@ -128,7 +200,7 @@ const LevelSelection = () => {
scale={[3, 0.3, 0]}
position={[-0.6, 0, 0]}
renderOrder={4}
position-x={horizontalHudPosX}
position-x={pos.horizPosX}
>
<spriteMaterial
map={horizontalHudTex}

View file

@ -95,8 +95,8 @@ const Site = (props: SiteProps) => {
rotation-y={siteState.siteRotY}
position-y={siteState.sitePosY}
>
<ActiveLevelNodes />
{/*<InactiveLevelNodes />*/}
<ActiveLevelNodes visibleNodes={visibleNodes} />
<InactiveLevelNodes visibleNodes={visibleNodes} />
<NodeAnimations />
<Rings
activateAllRings={props.shouldIntro ? props.introFinished : true}

View file

@ -9,13 +9,11 @@ import { NodeDataType, SiteType } from "../Site";
import usePrevious from "../../../../hooks/usePrevious";
type ActiveLevelNodesProps = {
visibleNodes: any;
visibleNodes: SiteType;
};
const ActiveLevelNodes = memo((props: ActiveLevelNodesProps) => {
const activeNodeId = useStore((state) => state.activeNode.id);
const gameProgress = useStore((state) => state.gameProgress);
const currentSite = useStore((state) => state.activeSite);
const activeLevel = useStore((state) => state.activeLevel);
const prevData = usePrevious({ activeLevel });
@ -24,7 +22,6 @@ const ActiveLevelNodes = memo((props: ActiveLevelNodesProps) => {
);
useEffect(() => {
const siteData = currentSite === "a" ? site_a : site_b;
if (
prevData?.activeLevel !== activeLevel &&
prevData?.activeLevel !== undefined
@ -32,36 +29,38 @@ const ActiveLevelNodes = memo((props: ActiveLevelNodesProps) => {
const prevLevel = parseInt(prevData?.activeLevel);
const newLevel = parseInt(activeLevel);
if (prevLevel - 1 === newLevel || prevLevel + 1 === newLevel) {
setVisibleNodes(siteData[activeLevel as keyof typeof siteData]);
setVisibleNodes(
props.visibleNodes[activeLevel as keyof typeof props.visibleNodes]
);
} else {
setTimeout(() => {
setVisibleNodes(siteData[activeLevel as keyof typeof siteData]);
setVisibleNodes(
props.visibleNodes[activeLevel as keyof typeof props.visibleNodes]
);
}, 1500);
}
}
}, [activeLevel, currentSite, gameProgress, prevData?.activeLevel]);
}, [activeLevel, prevData?.activeLevel, props, props.visibleNodes]);
return (
<>
{Object.values(visibleNodes).map((node: NodeDataType) => {
if (isNodeVisible(node, gameProgress)) {
return (
<Node
nodeName={node.node_name}
position={
node_positions[node.id.substr(2) as keyof typeof node_positions]
.position
}
rotation={
node_positions[node.id.substr(2) as keyof typeof node_positions]
.rotation
}
key={node.node_name}
active={node.id === activeNodeId}
level={node.id.substr(0, 2)}
/>
);
}
return (
<Node
nodeName={node.node_name}
position={
node_positions[node.id.substr(2) as keyof typeof node_positions]
.position
}
rotation={
node_positions[node.id.substr(2) as keyof typeof node_positions]
.rotation
}
key={node.node_name}
active={node.id === activeNodeId}
level={node.id.substr(0, 2)}
/>
);
})}
</>
);

View file

@ -0,0 +1,84 @@
import React, { memo, useMemo } from "react";
import { useLoader } from "react-three-fiber";
import { a } from "@react-spring/three";
import * as THREE from "three";
import Cou from "../../../../static/sprite/Cou.png";
import Dc from "../../../../static/sprite/Dc.png";
import SSkn from "../../../../static/sprite/SSkn.png";
import Tda from "../../../../static/sprite/Tda.png";
import Dia from "../../../../static/sprite/Dia.png";
import Lda from "../../../../static/sprite/Lda.png";
import MULTI from "../../../../static/sprite/MULTI.png";
import level_y_values from "../../../../resources/level_y_values.json";
type NodeContructorProps = {
nodeName: string;
position: number[];
rotation: number[];
level: string;
};
const InactiveLevelNode = memo((props: NodeContructorProps) => {
const tex = useMemo(() => {
if (props.nodeName.includes("S")) {
return SSkn;
} else if (
props.nodeName.startsWith("P") ||
props.nodeName.startsWith("G") ||
props.nodeName.includes("?")
) {
return MULTI;
} else if (props.nodeName.includes("Dc")) {
return Dc;
} else {
switch (props.nodeName.substr(0, 3)) {
case "Tda":
return Tda;
case "Cou":
return Cou;
case "Dia":
return Dia;
case "Lda":
return Lda;
case "Ere":
case "Ekm":
case "Eda":
case "TaK":
case "Env":
return MULTI;
}
}
}, [props.nodeName]);
const nonActiveTexture = useLoader(THREE.TextureLoader, tex!);
return (
<group
position={[
0,
level_y_values[props.level as keyof typeof level_y_values],
0,
]}
>
<a.mesh
position-x={props.position[0]}
position-y={props.position[1]}
position-z={props.position[2]}
rotation-y={props.rotation[1]}
rotation-z={0}
scale={[0.36, 0.18, 0.36]}
renderOrder={1}
>
<planeBufferGeometry attach="geometry" />
<meshStandardMaterial
attach="material"
map={nonActiveTexture}
side={THREE.DoubleSide}
transparent={true}
/>
</a.mesh>
</group>
);
});
export default InactiveLevelNode;

View file

@ -1,59 +1,60 @@
import React, { useMemo, memo } from "react";
import Node from "./Node";
import React, { memo, useEffect, useState } from "react";
import node_positions from "../../../../resources/node_positions.json";
import { useStore } from "../../../../store";
import { isNodeVisible } from "../../../../core/nodeSelector";
import site_a from "../../../../resources/site_a.json";
import site_b from "../../../../resources/site_b.json";
import { SiteType } from "../Site";
import InactiveLevelNode from "./InactiveLevelNode";
import usePrevious from "../../../../hooks/usePrevious";
import { generateInactiveNodes } from "../../../../core/utils/nodeUtils";
const InactiveLevelNodes = memo(() => {
const gameProgress = useStore((state) => state.gameProgress);
const currentSite = useStore((state) => state.activeSite);
const siteData = useMemo(() => (currentSite === "a" ? site_a : site_b), [
currentSite,
]);
type ActiveLevelNodesProps = {
visibleNodes: SiteType;
};
const InactiveLevelNodes = memo((props: ActiveLevelNodesProps) => {
const activeLevel = useStore((state) => state.activeLevel);
const prevData = usePrevious({ activeLevel });
const visibleNodes = useMemo(() => {
const obj = {};
const activeLevelNr = parseInt(activeLevel);
const visibleLevels = [
(activeLevelNr - 2).toString().padStart(2, "0"),
(activeLevelNr - 1).toString().padStart(2, "0"),
(activeLevelNr + 1).toString().padStart(2, "0"),
(activeLevelNr + 2).toString().padStart(2, "0"),
];
const [visibleNodes, setVisibleNodes] = useState<{}>(
generateInactiveNodes(props.visibleNodes, activeLevel)
);
visibleLevels.forEach((level) => {
Object.assign(obj, siteData[level as keyof typeof siteData]);
});
return obj;
}, [activeLevel, siteData]);
useEffect(() => {
if (
prevData?.activeLevel !== activeLevel &&
prevData?.activeLevel !== undefined
) {
const prevLevel = parseInt(prevData?.activeLevel);
const newLevel = parseInt(activeLevel);
if (prevLevel - 1 === newLevel || prevLevel + 1 === newLevel) {
setVisibleNodes(generateInactiveNodes(props.visibleNodes, activeLevel));
} else {
setTimeout(() => {
setVisibleNodes(
generateInactiveNodes(props.visibleNodes, activeLevel)
);
}, 1500);
}
}
}, [activeLevel, prevData?.activeLevel, props.visibleNodes]);
return (
<>
{Object.entries(visibleNodes).map((node: [string, any]) => {
if (isNodeVisible(node[1], gameProgress)) {
return (
<Node
nodeName={node[1].node_name}
position={
node_positions[node[0].substr(2) as keyof typeof node_positions]
.position
}
rotation={
node_positions[node[0].substr(2) as keyof typeof node_positions]
.rotation
}
key={node[1].node_name}
level={node[0].substr(0, 2)}
/>
);
}
return (
<InactiveLevelNode
nodeName={node[1].node_name}
position={
node_positions[node[0].substr(2) as keyof typeof node_positions]
.position
}
rotation={
node_positions[node[0].substr(2) as keyof typeof node_positions]
.rotation
}
key={node[1].node_name}
level={node[0].substr(0, 2)}
/>
);
})}
</>
);

View file

@ -23,7 +23,7 @@ type NodeContructorProps = {
nodeName: string;
position: number[];
rotation: number[];
active?: boolean;
active: boolean;
level: string;
};

View file

@ -6,8 +6,17 @@ const LevelSelectionManager = (props: StateManagerProps) => {
const setSelectedLevel = useStore((state) => state.setSelectedLevel);
const dispatchObject = useCallback(
(eventState: { event: string; selectedLevelIdx: number }) => {
(eventState: {
event: string;
selectedLevelIdx: number;
level: number;
}) => {
switch (eventState.event) {
case "toggle_level_selection":
return {
action: setSelectedLevel,
value: eventState.level,
};
case "level_selection_up":
case "level_selection_down":
return {

View file

@ -90,7 +90,7 @@ const handleMainSceneEvent = (gameContext: any) => {
}
break;
case "L2":
return { event: "toggle_level_selection" };
return { event: "toggle_level_selection", level: level };
case "TRIANGLE":
return { event: "pause_game" };
case "SPACE":

View file

@ -1,7 +1,4 @@
import {
NodeDataType,
SiteType,
} from "../../components/MainScene/SyncedComponents/Site";
import { SiteType } from "../../components/MainScene/SyncedComponents/Site";
import game_progress from "../../resources/initial_progress.json";
import { isNodeVisible } from "../nodeSelector";
@ -9,10 +6,13 @@ const filterInvisibleNodes = (
siteData: SiteType,
gameProgress: typeof game_progress
) => {
const visibleNodes: NodeDataType[] = [];
Object.values(siteData).forEach((level) => {
Object.values(level).forEach((node) => {
if (isNodeVisible(node, gameProgress)) visibleNodes.push(node);
const visibleNodes: SiteType = {};
Object.entries(siteData).forEach((level) => {
visibleNodes[level[0]] = {};
Object.entries(level[1]).forEach((node) => {
if (isNodeVisible(node[1], gameProgress)) {
visibleNodes[level[0]][node[0]] = node[1];
}
});
});

View file

@ -0,0 +1,21 @@
import { SiteType } from "../../components/MainScene/SyncedComponents/Site";
export const generateInactiveNodes = (
visibleNodes: SiteType,
activeLevel: string
) => {
const obj = {};
const activeLevelNr = parseInt(activeLevel);
const visibleLevels = [
(activeLevelNr - 2).toString().padStart(2, "0"),
(activeLevelNr - 1).toString().padStart(2, "0"),
(activeLevelNr + 1).toString().padStart(2, "0"),
(activeLevelNr + 2).toString().padStart(2, "0"),
];
visibleLevels.forEach((level) => {
Object.assign(obj, visibleNodes[level as keyof typeof visibleNodes]);
});
return obj;
};