bugfixes, more stable intro implementation i think

This commit is contained in:
ad044 2021-02-04 20:39:38 +04:00
parent dc545ba522
commit a80cf992bb
9 changed files with 179 additions and 139 deletions

View file

@ -40,6 +40,7 @@ type LainConstructorProps = {
framesVertical: number;
framesHorizontal: number;
fps?: number;
shouldAnimate?: boolean;
};
export const LainConstructor = (props: LainConstructorProps) => {
@ -58,7 +59,9 @@ export const LainConstructor = (props: LainConstructorProps) => {
});
useFrame(() => {
animator.animate();
if (props.shouldAnimate !== false) {
animator.animate();
}
});
return (
@ -71,12 +74,14 @@ export const LainConstructor = (props: LainConstructorProps) => {
);
};
export const LainIntro = () => (
export const LainIntro = (props: { shouldAnimate: boolean }) => (
<LainConstructor
sprite={introSpriteSheet}
frameCount={50}
framesHorizontal={10}
framesVertical={5}
frameCount={3}
framesHorizontal={3}
framesVertical={1}
fps={10}
shouldAnimate={props.shouldAnimate}
/>
);
@ -350,7 +355,8 @@ export const LainPlayWithHair = () => (
);
type LainProps = {
shouldIntro: boolean;
shouldAnimate: boolean;
introFinished: boolean;
};
const Lain = (props: LainProps) => {
@ -392,28 +398,17 @@ const Lain = (props: LainProps) => {
return anims[lainMoveState as keyof typeof anims];
}, [lainMoveState]);
const [introFinished, setIntroFinished] = useState(false);
useEffect(() => {
setTimeout(() => {
setIntroFinished(true);
}, 3900);
}, []);
const stopIntroAnim = useMemo(() => {
return props.shouldIntro ? introFinished : true;
}, [introFinished, props.shouldIntro]);
const lainRef = useRef<THREE.Sprite>();
const glowColor = useMemo(() => new THREE.Color(2, 2, 2), []);
const regularColor = useMemo(() => new THREE.Color(1, 1, 1), []);
useEffect(() => {
if (wordSelected)
if (wordSelected) {
setTimeout(() => {
if (lainRef.current) lainRef.current.material.color = glowColor;
}, 3100);
}
}, [glowColor, wordSelected]);
useFrame(() => {
@ -425,7 +420,11 @@ const Lain = (props: LainProps) => {
return (
<Suspense fallback={null}>
<sprite scale={[4.5, 4.5, 4.5]} position={[0, -0.15, 0]} ref={lainRef}>
{stopIntroAnim ? lainAnimationDispatch : <LainIntro />}
{props.introFinished ? (
lainAnimationDispatch
) : (
<LainIntro shouldAnimate={props.shouldAnimate} />
)}
</sprite>
</Suspense>
);

View file

@ -43,7 +43,6 @@ export type SiteType = {
};
type SiteProps = {
shouldIntro: boolean;
introFinished: boolean;
};
@ -52,13 +51,17 @@ const Site = (props: SiteProps) => {
const [rotState, setRot] = useSpring(() => ({
x: wordSelected ? 0 : useStore.getState().siteRot[0],
y: wordSelected ? 0 : useStore.getState().siteRot[1],
y: wordSelected
? useStore.getState().oldSiteRot[1]
: useStore.getState().siteRot[1],
config: { duration: 1200 },
}));
const [posState, setPos] = useSpring(() => ({
y: wordSelected
? 0
? -level_y_values[
useStore.getState().oldLevel as keyof typeof level_y_values
]
: -level_y_values[
useStore.getState().activeLevel as keyof typeof level_y_values
],
@ -77,28 +80,6 @@ const Site = (props: SiteProps) => {
}));
}, [setPos, setRot]);
const introWrapperRef = useRef<THREE.Object3D>();
// imperative because having a spring here seemed to behave clunkily if that's even a word
// the site would pop back after having done the intro anim sometimes
useFrame(() => {
if (introWrapperRef.current) {
if (introWrapperRef.current.position.z < 0) {
introWrapperRef.current.position.z += 0.05;
}
if (introWrapperRef.current.rotation.x > 0) {
introWrapperRef.current.rotation.x -= 0.008;
}
}
});
useEffect(() => {
if (props.shouldIntro && introWrapperRef.current) {
introWrapperRef.current.rotation.x = Math.PI / 2;
introWrapperRef.current.position.z = -10;
}
}, [props.shouldIntro]);
const currentSite = useStore((state) => state.activeSite);
const gameProgress = useStore((state) => state.gameProgress);
@ -110,17 +91,13 @@ const Site = (props: SiteProps) => {
return (
<Suspense fallback={null}>
<a.group ref={introWrapperRef}>
<a.group rotation-x={rotState.x}>
<a.group rotation-y={rotState.y} position-y={posState.y}>
<ActiveLevelNodes visibleNodes={visibleNodes} />
<InactiveLevelNodes visibleNodes={visibleNodes} />
<Rings
activateAllRings={props.shouldIntro ? props.introFinished : true}
/>
</a.group>
<NodeAnimations />
<a.group rotation-x={rotState.x}>
<a.group rotation-y={rotState.y} position-y={posState.y}>
<ActiveLevelNodes visibleNodes={visibleNodes} />
<InactiveLevelNodes visibleNodes={visibleNodes} />
<Rings activateAllRings={props.introFinished} />
</a.group>
<NodeAnimations />
</a.group>
</Suspense>
);

View file

@ -3,7 +3,7 @@ import Star from "./Starfield/Star";
type StarfieldProps = {
shouldIntro: boolean;
introFinished: boolean;
mainVisible: boolean;
};
const Starfield = memo((props: StarfieldProps) => {
@ -39,13 +39,9 @@ const Starfield = memo((props: StarfieldProps) => {
])
);
const [mainVisible, setMainVisible] = useState(false);
const [introVisible, setIntroVisible] = useState(true);
useEffect(() => {
setTimeout(() => {
setMainVisible(true);
}, 2800);
setTimeout(() => {
setIntroVisible(false);
}, 3200);
@ -53,10 +49,7 @@ const Starfield = memo((props: StarfieldProps) => {
return (
<>
<group
position={[0, -1, 2]}
visible={props.shouldIntro ? mainVisible : true}
>
<group position={[0, -1, 2]} visible={props.mainVisible}>
<group rotation={[0, 0.75, Math.PI / 2]} position={[-0.7, -1, -5]}>
{posesBlueFromLeft.map((poses, idx) => (
<Star

View file

@ -104,6 +104,7 @@ const handleMainSceneEvent = (mainSceneContext: any) => {
event: `${eventAnimation}_media`,
scene: "media",
siteRotY: siteRotY,
level: level.toString().padStart(2, "0"),
};
case 6:
if (activeNode.node_name.substr(0, 3) === "TaK") {
@ -117,6 +118,7 @@ const handleMainSceneEvent = (mainSceneContext: any) => {
event: `${eventAnimation}_media`,
scene: "media",
siteRotY: siteRotY,
level: level.toString().padStart(2, "0"),
};
}
case 8:

View file

@ -2,9 +2,15 @@ import { useStore } from "../../../../store";
const levelManager = (eventState: any) => {
const setActiveLevel = useStore.getState().setActiveLevel;
const setOldLevel = useStore.getState().setOldLevel;
const dispatchAction = (eventState: any) => {
switch (eventState.event) {
case "throw_node_media":
case "rip_node_media":
return {
action: () => setOldLevel(eventState.level),
};
case "site_up":
case "site_down":
case "select_level_up":

View file

@ -3,9 +3,15 @@ import { useStore } from "../../../../store";
const siteManager = (eventState: any) => {
const setRotY = useStore.getState().setSiteRotY;
const setRotX = useStore.getState().setSiteRotX;
const setOldRot = useStore.getState().setOldSiteRot;
const dispatchAction = (eventState: any) => {
switch (eventState.event) {
case "throw_node_media":
case "rip_node_media":
return {
action: () => setOldRot([0, eventState.siteRotY, 0]),
};
case "site_left":
case "site_right":
case "media_fstWord_select":

View file

@ -1,5 +1,5 @@
import { OrbitControls } from "@react-three/drei";
import React, { Suspense, useEffect, useState } from "react";
import React, { Suspense, useEffect, useRef, useState } from "react";
import { useStore } from "../store";
import Pause from "../components/MainScene/PauseSubscene/Pause";
import LevelSelection from "../components/MainScene/SyncedComponents/LevelSelection";
@ -11,8 +11,11 @@ import GrayPlanes from "../components/MainScene/SyncedComponents/GrayPlanes";
import Starfield from "../components/MainScene/SyncedComponents/Starfield";
import Site from "../components/MainScene/SyncedComponents/Site";
import Lain from "../components/MainScene/Lain";
import * as THREE from "three";
import { useFrame } from "react-three-fiber";
const MainScene = () => {
const intro = useStore((state) => state.intro);
const [paused, setPaused] = useState(false);
const subscene = useStore((state) => state.mainSubscene);
@ -37,23 +40,84 @@ const MainScene = () => {
}
}, [setWordSelected, wordSelected]);
useEffect(() => {
if (intro) {
if (introWrapperRef.current) {
introWrapperRef.current.rotation.x = Math.PI / 2;
introWrapperRef.current.position.z = -10;
}
setStarfieldIntro(false);
setLainIntroAnim(false);
setIntroFinished(false);
}
}, [intro]);
const [starfieldIntro, setStarfieldIntro] = useState(false);
const [lainIntroAnim, setLainIntroAnim] = useState(false);
const [introFinished, setIntroFinished] = useState(false);
const introWrapperRef = useRef<THREE.Group>();
useFrame(() => {
if (intro && introWrapperRef.current) {
if (
Math.round(introWrapperRef.current.position.z) === -3 &&
!starfieldIntro
) {
setStarfieldIntro(true);
}
if (
Math.round(introWrapperRef.current.position.z) === -1 &&
!lainIntroAnim
) {
setLainIntroAnim(true);
}
if (
Math.round(introWrapperRef.current.position.z) === 0 &&
Math.round(introWrapperRef.current.rotation.x) === 0 &&
!introFinished
) {
setIntroFinished(true);
}
if (introWrapperRef.current.position.z < 0) {
introWrapperRef.current.position.z += 0.05;
}
if (introWrapperRef.current.rotation.x > 0) {
introWrapperRef.current.rotation.x -= 0.008;
}
}
});
return (
<perspectiveCamera position-z={3}>
<Suspense fallback={null}>
<LevelSelection />
<Pause />
<group visible={!paused}>
<group visible={!wordSelected}>
<group visible={!wordSelected && (intro ? introFinished : true)}>
<HUD />
<YellowTextRenderer />
<MiddleRing />
<GrayPlanes />
<Lain shouldIntro={false} />
</group>
<YellowOrb visible={!paused} />
<Starfield shouldIntro={false} introFinished={true} />
<group visible={intro ? introFinished : true}>
<YellowOrb visible={!paused} />
</group>
<Starfield
shouldIntro={intro}
mainVisible={intro ? starfieldIntro : true}
/>
</group>
<group visible={!wordSelected}>
<Lain
shouldAnimate={lainIntroAnim}
introFinished={intro ? introFinished : true}
/>
</group>
<group ref={introWrapperRef}>
<Site introFinished={intro ? introFinished : true} />
</group>
<Site shouldIntro={false} introFinished={true} />
<OrbitControls />
<pointLight color={0xffffff} position={[0, 0, 7]} intensity={1} />
<pointLight color={0x7f7f7f} position={[0, 10, 0]} intensity={1.5} />

View file

@ -31,9 +31,11 @@ type State = {
// site
activeSite: "a" | "b";
siteRot: number[];
oldSiteRot: number[];
// level
activeLevel: string;
oldLevel: string;
// level selection
selectedLevel: number;
@ -96,7 +98,6 @@ type State = {
endMediaPlayedCount: number;
// save state
siteSaveState: {
a: {
activeNode: NodeDataType;
@ -171,9 +172,13 @@ export const useStore = create(
// site
activeSite: "a",
siteRot: [0, 0, 0],
// this one is used for word selection animation to start from the correct point
oldSiteRot: [0, 0, 0],
// level
activeLevel: "04",
// this one is used for word selection animation to start from the correct point
oldLevel: "04",
// level selection
selectedLevel: 4,
@ -301,9 +306,14 @@ export const useStore = create(
nextRot[0] = to;
return { siteRot: nextRot };
}),
setOldSiteRot: (to: number[]) =>
set(() => ({
oldSiteRot: to,
})),
// level setters
setActiveLevel: (to: string) => set(() => ({ activeLevel: to })),
setOldLevel: (to: string) => set(() => ({ oldLevel: to })),
// level selection setters
setSelectedLevel: (to: number) => set(() => ({ selectedLevel: to })),

View file

@ -60,29 +60,19 @@ export const getNodeHud = (nodeMatrixIndices: {
] as keyof typeof node_huds
];
};
//visible = (global_final_viewcount > 0) && (req_final_viewcount <= global_final_viewcount + 1)
export const isNodeVisible = (
node: NodeDataType,
gameProgress: typeof unlocked_nodes
) => {
if (node) {
const unlockedBy = node.unlocked_by;
let unlocked;
if (unlockedBy === "") unlocked = true;
else
unlocked =
gameProgress[unlockedBy as keyof typeof gameProgress].is_viewed;
// visible = (global_final_viewcount > 0) && (req_final_viewcount <= global_final_viewcount + 1)
return (
unlocked &&
gameProgress[node.node_name as keyof typeof gameProgress].is_visible
);
} else {
return false;
}
};
) =>
node
? (node.unlocked_by === "" ||
gameProgress[node.unlocked_by as keyof typeof gameProgress]
.is_viewed) &&
gameProgress[node.node_name as keyof typeof gameProgress].is_visible &&
node.required_final_video_viewcount < 1
: false;
export const getVisibleNodesMatrix = (
matrixIdx: number,
@ -134,99 +124,92 @@ function* nextPos_left([row, col]: [number, number]) {
const p = RowPrecedence(row);
for (let c = col - 1; c > -1; c--)
for (let r = 0; r < 3; r++)
yield [p[r], c];
for (let r = 0; r < 3; r++) yield [p[r], c];
}
function* nextPos_right([row, col]: [number, number]) {
const p = RowPrecedence(row);
for (let c = col + 1; c < 4; c++)
for (let r = 0; r < 3; r++)
yield [p[r], c];
for (let c = col + 1; c < 4; c++) for (let r = 0; r < 3; r++) yield [p[r], c];
}
function* nextPos_up([row, col]: [number, number]) {
const p = ColPrecedence(col);
for (let r = row - 1; r > -1; r--)
for (let c = 0; c < 4; c++)
yield [r, p[c]];
for (let c = 0; c < 4; c++) yield [r, p[c]];
}
function* nextPos_down([row, col]: [number, number]) {
const p = ColPrecedence(col);
for (let r = row + 1; r < 3; r++)
for (let c = 0; c < 4; c++)
yield [r, p[c]];
for (let r = row + 1; r < 3; r++) for (let c = 0; c < 4; c++) yield [r, p[c]];
}
function move(direction: string, [matrix, level]: [number, number]) {
switch (direction) {
case "left": matrix = matrix + 1 > 8 ? 1 : matrix + 1; break;
case "right": matrix = matrix - 1 < 1 ? 8 : matrix - 1; break;
case "up": level++; break;
case "down": level--; break;
case "left":
matrix = matrix + 1 > 8 ? 1 : matrix + 1;
break;
case "right":
matrix = matrix - 1 < 1 ? 8 : matrix - 1;
break;
case "up":
level++;
break;
case "down":
level--;
break;
}
return [matrix, level];
}
export function findNode
(
export function findNode(
direction: string,
{matrixIdx, rowIdx, colIdx}:
{matrixIdx: number, rowIdx: number, colIdx: number},
{
matrixIdx,
rowIdx,
colIdx,
}: { matrixIdx: number; rowIdx: number; colIdx: number },
level: number,
currentSite: string,
gameProgress: any
)
: any | undefined
{
): any | undefined {
const funcs: any = {
left: [
nextPos_left,
([r]: [number, number]) => nextPos_right([r, -1])
],
right: [
nextPos_right,
([r]: [number, number]) => nextPos_left([r, 4])
],
up: [
nextPos_up,
([, c]: [number, number]) => nextPos_up([3, c])
],
down: [
nextPos_down,
([, c]: [number, number]) => nextPos_down([-1, c])
]
left: [nextPos_left, ([r]: [number, number]) => nextPos_right([r, -1])],
right: [nextPos_right, ([r]: [number, number]) => nextPos_left([r, 4])],
up: [nextPos_up, ([, c]: [number, number]) => nextPos_up([3, c])],
down: [nextPos_down, ([, c]: [number, number]) => nextPos_down([-1, c])],
};
const nextPos = funcs[direction];
for (let i = 0; i < 2; i++) {
const nodes = getVisibleNodesMatrix(
matrixIdx, level,
currentSite, gameProgress
matrixIdx,
level,
currentSite,
gameProgress
);
for (const [r, c] of nextPos[i]([rowIdx, colIdx])) {
const node = nodes[r][c];
if (node) return {
node,
if (node)
return {
node,
matrixIndices: {
matrixIdx,
rowIdx: r, colIdx: c
},
matrixIndices: {
matrixIdx,
rowIdx: r,
colIdx: c,
},
didMove: Boolean(i)
};
didMove: Boolean(i),
};
}
[matrixIdx, level] = move(direction, [matrixIdx, level]);