refactored mediaplayer and fixed idles

This commit is contained in:
ad044 2021-02-12 21:13:49 +04:00
parent f6ac61ff77
commit 47c65fd497
11 changed files with 206 additions and 246 deletions

View file

@ -2,7 +2,7 @@ import React, { Suspense, useEffect, useMemo } from "react";
import MainScene from "./scenes/MainScene";
import "./static/css/page.css";
import { Canvas } from "react-three-fiber";
import MediaPlayer from "./components/MediaScene/MediaPlayer";
import MediaPlayer from "./components/MediaPlayer";
import MediaScene from "./scenes/MediaScene";
import { useStore } from "./store";
import GateScene from "./scenes/GateScene";

View file

@ -28,6 +28,9 @@ import bootSubsceneManager from "../core/setters/boot/bootSubsceneManager";
import bootManager from "../core/setters/boot/bootManager";
import handleBootSceneKeyPress from "../core/scene-keypress-handlers/handleBootSceneKeyPress";
import soundManager from "../core/setters/soundManager";
import { useFrame } from "react-three-fiber";
import { getRandomIdleLainAnim, getRandomIdleMedia } from "../utils/idle-utils";
import idleManager from "../core/setters/main/idleManager";
const KeyPressHandler = () => {
const mediaSceneSetters = useMemo(
@ -79,8 +82,36 @@ const KeyPressHandler = () => {
);
const scene = useStore((state) => state.currentScene);
const mainSubscene = useStore((state) => state.mainSubscene);
const timePassedSinceLastKeyPress = useRef(-1);
const lainIdleCounter = useRef(-1);
const idleSceneCounter = useRef(-1);
useFrame(() => {
const now = Date.now();
if (
lainIdleCounter.current > -1 &&
idleSceneCounter.current > -1 &&
mainSubscene !== "pause" &&
mainSubscene !== "level_selection" &&
scene === "main"
) {
if (now > lainIdleCounter.current + 10000) {
lainManager({ event: getRandomIdleLainAnim() });
// after one idle animation plays, the second comes sooner than it would after a regular keypress
lainIdleCounter.current = now - 2500;
}
if (now > idleSceneCounter.current + 15000) {
idleManager(getRandomIdleMedia());
sceneManager({ event: "play_idle_media" });
// put it on lock until the next action, since while the idle media plays, the
// Date.now() value keeps increasing, which can result in another idle media playing right after one finishes
// one way to work around this would be to modify the value depending on the last played idle media's duration
// but i'm way too lazy for that
idleSceneCounter.current = -1;
}
}
});
const handleKeyPress = useCallback(
(event) => {
@ -91,7 +122,8 @@ const KeyPressHandler = () => {
const now = Date.now();
if (keyPress) {
timePassedSinceLastKeyPress.current = Date.now();
lainIdleCounter.current = now;
idleSceneCounter.current = now;
const sceneFns = (() => {
switch (scene) {
case "main":
@ -120,7 +152,6 @@ const KeyPressHandler = () => {
};
case "gate":
case "polytan":
case "about":
return {
action: () => useStore.setState({ currentScene: "main" }),
};

View file

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect, useMemo, useRef, useState } from "react";
import TriangleNode from "./NodeRip/TriangleNode";
import { useStore } from "../../../../store";
import RipLine from "./NodeRip/RipLine";
@ -13,7 +13,7 @@ const NodeRip = () => {
const LCG = (a: number, c: number, m: number, s: number) => () =>
(s = (s * a + c) % m);
const lcgInstance = LCG(1664525, 1013904223, 2 ** 32, 2);
const lcgInstance = useMemo(() => LCG(1664525, 1013904223, 2 ** 32, 2), []);
const firstLineSet = Array.from({ length: 25 }, (_, idx) => {
let coordSet = [lcgInstance() / 7000000000, lcgInstance() / 7000000000];

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState, memo } from "react";
import React, { useEffect, useState, memo, useMemo } from "react";
import Star from "./Starfield/Star";
type StarfieldProps = {
@ -10,7 +10,7 @@ const Starfield = memo((props: StarfieldProps) => {
const LCG = (a: number, c: number, m: number, s: number) => () =>
(s = (s * a + c) % m);
const lcgInstance = LCG(1664525, 1013904223, 2 ** 32, 2);
const lcgInstance = useMemo(() => LCG(1664525, 1013904223, 2 ** 32, 2), []);
const [
posesBlueFromRight,

View file

@ -0,0 +1,67 @@
import React, { createRef, useCallback, useEffect, useRef } from "react";
import { useStore } from "../store";
const MediaPlayer = () => {
const setPercentageElapsed = useStore((state) => state.setPercentageElapsed);
const requestRef = useRef();
const videoRef = createRef<HTMLVideoElement>();
const trackRef = createRef<HTMLTrackElement>();
const subtitleRef = createRef<HTMLParagraphElement>();
useEffect(() => {
const handleCueChange = (e: any) => {
const { track } = e.target;
const { activeCues } = track;
const text = [...activeCues].map(
(cue) => cue.getCueAsHTML().textContent
)[0];
if (subtitleRef.current && videoRef.current) {
if (!text || videoRef.current.currentTime === 0)
subtitleRef.current.textContent = text;
else subtitleRef.current.textContent = text;
}
};
if (trackRef.current) {
trackRef.current.addEventListener("cuechange", handleCueChange);
}
}, [subtitleRef, trackRef, videoRef]);
const updateTime = useCallback(() => {
(requestRef.current as any) = requestAnimationFrame(updateTime);
if (videoRef.current) {
const timeElapsed = videoRef.current.currentTime;
const duration = videoRef.current.duration;
const percentageElapsed = Math.floor((timeElapsed / duration) * 100);
if (percentageElapsed % 5 === 0 && percentageElapsed !== 0) {
setPercentageElapsed(percentageElapsed);
if (percentageElapsed === 100 && videoRef.current)
videoRef.current.currentTime = 0;
}
}
}, [setPercentageElapsed, videoRef]);
useEffect(() => {
(requestRef.current as any) = requestAnimationFrame(updateTime);
const curr = requestRef.current;
return () => {
cancelAnimationFrame(curr as any);
setPercentageElapsed(0);
};
}, [setPercentageElapsed, updateTime]);
return (
<>
<video width="800" height="600" id="media" ref={videoRef} controls>
<track id={"track"} ref={trackRef} kind="captions" default />
</video>
<div id={"subtitle-container"}>
<p ref={subtitleRef} id={"subtitle"} />
</div>
</>
);
};
export default MediaPlayer;

View file

@ -1,196 +0,0 @@
import React, {
createRef,
useCallback,
useEffect,
useMemo,
useRef,
} from "react";
import { useStore } from "../../store";
import endroll from "../../static/movie/ENDROLL1.STR[0].webm";
import xa0001 from "../../static/audio/Xa0001.mp4";
import xa0006 from "../../static/audio/Xa0006.mp4";
const MediaPlayer = () => {
const currentScene = useStore((state) => state.currentScene);
const setScene = useStore((state) => state.setScene);
const setPercentageElapsed = useStore((state) => state.setPercentageElapsed);
const nodeName = useStore((state) => state.activeNode.node_name);
const idleMedia = useStore((state) => state.idleMedia);
const nodeMedia = useStore((state) => state.activeNode.media_file);
const triggersFinalVideo = useStore(
(state) => state.activeNode.triggers_final_video
);
const requestRef = useRef();
const videoRef = createRef<HTMLVideoElement>();
const trackRef = createRef<HTMLTrackElement>();
const subtitleRef = createRef<HTMLParagraphElement>();
// end scene specific stuff
const endMediaPlayedCount = useStore((state) => state.endMediaPlayedCount);
const incrementEndMediaPlayedCount = useStore(
(state) => state.incrementEndMediaPlayedCount
);
const resetEndMediaPlayedCount = useStore(
(state) => state.resetEndMediaPlayedCount
);
useEffect(() => {
const handleCueChange = (e: any) => {
const { track } = e.target;
const { activeCues } = track;
const text = [...activeCues].map(
(cue) => cue.getCueAsHTML().textContent
)[0];
if (subtitleRef.current && videoRef.current) {
if (!text || videoRef.current.currentTime === 0)
subtitleRef.current.textContent = text;
else subtitleRef.current.textContent = text;
}
};
if (trackRef.current) {
trackRef.current.addEventListener("cuechange", handleCueChange);
}
}, [subtitleRef, trackRef, videoRef]);
const updateTime = useCallback(() => {
(requestRef.current as any) = requestAnimationFrame(updateTime);
if (videoRef.current) {
const timeElapsed = videoRef.current.currentTime;
const duration = videoRef.current.duration;
const percentageElapsed = Math.floor((timeElapsed / duration) * 100);
if (percentageElapsed % 5 === 0 && percentageElapsed !== 0) {
setPercentageElapsed(percentageElapsed);
if (percentageElapsed === 100) {
if (currentScene === "media") setPercentageElapsed(0);
videoRef.current.currentTime = 0;
if (currentScene === "idle_media") {
videoRef.current.pause();
setScene("main");
} else {
if (currentScene === "end") {
incrementEndMediaPlayedCount();
} else {
if (triggersFinalVideo === 1) {
resetEndMediaPlayedCount();
setScene("end");
} else {
videoRef.current.pause();
}
}
}
}
}
}
}, [
currentScene,
incrementEndMediaPlayedCount,
resetEndMediaPlayedCount,
setPercentageElapsed,
setScene,
triggersFinalVideo,
videoRef,
]);
useEffect(() => {
(requestRef.current as any) = requestAnimationFrame(updateTime);
const curr = requestRef.current;
return () => cancelAnimationFrame(curr as any);
}, [updateTime]);
const endMediaSource = useMemo(() => {
switch (endMediaPlayedCount) {
case 0:
return endroll;
case 1:
return xa0001;
case 2:
return xa0006;
}
}, [endMediaPlayedCount]);
useEffect(() => {
if (currentScene === "end") {
if (endMediaPlayedCount === 0) {
if (videoRef.current) {
videoRef.current.load();
videoRef.current.play().catch((e) => {
console.log(e);
});
}
} else if (endMediaPlayedCount === 1) {
if (videoRef.current) {
videoRef.current.load();
}
} else if (endMediaPlayedCount === 2) {
if (videoRef.current) {
videoRef.current.load();
videoRef.current.play();
}
}
} else {
import("../../static/webvtt/" + nodeName + ".vtt").then((vtt) => {
if (trackRef.current) {
trackRef.current.src = vtt.default;
}
});
if (
currentScene === "media" ||
currentScene === "tak" ||
currentScene === "idle_media"
) {
if (currentScene === "media") setPercentageElapsed(0);
const mediaToPlay =
currentScene === "idle_media" ? idleMedia : nodeMedia;
if (mediaToPlay) {
if (mediaToPlay.includes("XA")) {
import("../../static/audio/" + mediaToPlay + ".ogg").then(
(media) => {
if (videoRef.current) {
videoRef.current.src = media.default;
videoRef.current.load();
}
}
);
} else {
import("../../static/movie/" + mediaToPlay + "[0].webm").then(
(media) => {
if (videoRef.current) {
videoRef.current.src = media.default;
videoRef.current.load();
}
}
);
}
}
}
}
}, [
currentScene,
endMediaPlayedCount,
idleMedia,
nodeMedia,
nodeName,
setPercentageElapsed,
trackRef,
videoRef,
]);
return (
<>
<video width="800" height="600" id="media" ref={videoRef} controls>
<track ref={trackRef} kind="captions" default />
</video>
<div id={"subtitle-container"}>
<p ref={subtitleRef} id={"subtitle"} />
</div>
</>
);
};
export default MediaPlayer;

View file

@ -1,27 +1,13 @@
import { useStore } from "../../../store";
const idleManager = (eventState: any) => {
const setIdleScene = useStore.getState().setIdleScene;
const dispatchAction = (eventState: {
media: string;
images?: { "1": string; "2": string; "3": string };
}) => {
if (eventState.images) {
return {
action: () =>
useStore.setState({
idleMedia: eventState.media,
idleImages: eventState.images,
}),
};
} else {
return {
action: () =>
useStore.setState({
idleMedia: eventState.media,
}),
};
}
};
images: { "1": string; "2": string; "3": string } | undefined;
nodeName: string | undefined;
}) => ({ action: () => setIdleScene(eventState) });
const { action } = { ...dispatchAction(eventState) };

View file

@ -3,16 +3,46 @@ import { useStore } from "../store";
import Images from "../components/MediaScene/Images";
const IdleMediaScene = () => {
const mediaPercentageElapsed = useStore(
(state) => state.mediaPercentageElapsed
);
const setScene = useStore((state) => state.setScene);
const idleMedia = useStore((state) => state.idleMedia);
const idleNodeName = useStore((state) => state.idleNodeName);
useEffect(() => {
if (mediaPercentageElapsed === 100) setScene("main");
}, [mediaPercentageElapsed, setScene]);
useEffect(() => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
const trackElement = document.getElementById("track") as HTMLTrackElement;
if (mediaElement) {
mediaElement.currentTime = 0;
mediaElement.play();
import("../static/webvtt/" + idleNodeName + ".vtt")
.then((vtt) => {
if (vtt) trackElement.src = vtt.default;
})
// some entries have no spoken words, so the file doesnt exist. we catch that here.
.catch((e) => console.log(e));
if (idleMedia.includes("XA")) {
import("../static/audio/" + idleMedia + ".ogg").then((media) => {
mediaElement.src = media.default;
mediaElement.load();
mediaElement.play();
});
} else {
import("../static/movie/" + idleMedia + "[0].webm").then((media) => {
mediaElement.src = media.default;
mediaElement.load();
mediaElement.play();
});
}
}
}, []);
}, [idleMedia, idleNodeName]);
return (
<group visible={idleMedia.includes("XA")}>

View file

@ -21,9 +21,36 @@ const MediaScene = () => {
};
}, []);
useEffect(()=> {
console.log('rend')
}, [])
const nodeMedia = useStore((state) => state.activeNode.media_file);
const nodeName = useStore((state) => state.activeNode.node_name);
useEffect(() => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
const trackElement = document.getElementById("track") as HTMLTrackElement;
if (mediaElement) {
mediaElement.currentTime = 0;
import("../static/webvtt/" + nodeName + ".vtt")
.then((vtt) => {
if (vtt) trackElement.src = vtt.default;
})
// some entries have no spoken words, so the file doesnt exist. we catch that here.
.catch((e) => console.log(e));
if (nodeMedia.includes("XA")) {
import("../static/audio/" + nodeMedia + ".ogg").then((media) => {
mediaElement.src = media.default;
mediaElement.load();
});
} else {
import("../static/movie/" + nodeMedia + "[0].webm").then((media) => {
mediaElement.src = media.default;
mediaElement.load();
});
}
}
}, [nodeMedia, nodeName]);
return (
<perspectiveCamera position-z={3}>
<group position={[0.4, -0.3, 0]}>

View file

@ -61,7 +61,8 @@ type State = {
// idle scene
idleMedia: string;
idleImages: any;
idleImages: { "1": string; "2": string; "3": string } | undefined;
idleNodeName: string | undefined;
// sskn scene
ssknComponentMatrix: ["ok", "cancel"];
@ -122,7 +123,7 @@ export const useStore = create(
combine(
{
// scene data
currentScene: "boot",
currentScene: "main",
// game progress
gameProgress: game_progress,
@ -190,9 +191,10 @@ export const useStore = create(
wordSelected: false,
// idle scene
idleMedia: "INS01.STR",
idleMedia: site_a["00"]["0000"].media_file,
idleNodeName: site_a["00"]["0000"].node_name,
// this may be undefined depending on whether or not the media is audio or not
idleImages: undefined,
idleImages: site_a["00"]["0000"].image_table_indices,
// sskn scene
ssknComponentMatrix: ["ok", "cancel"],
@ -346,8 +348,16 @@ export const useStore = create(
setWordSelected: (to: boolean) => set(() => ({ wordSelected: to })),
// idle media setters
setIdleMedia: (to: any) => set(() => ({ idleMedia: to })),
setIdleImages: (to: any) => set(() => ({ idleImages: to })),
setIdleScene: (to: {
images: { "1": string; "2": string; "3": string } | undefined;
media: string | undefined;
nodeName: string | undefined;
}) =>
set(() => ({
idleMedia: to.media,
idleImages: to.images,
idleNodeName: to.nodeName,
})),
//polytan setters
setPolytanPartUnlocked: (bodyPart: string) =>

View file

@ -3,7 +3,7 @@ import site_b from "../resources/site_b.json";
import { SiteType } from "../components/MainScene/Site";
import { useStore } from "../store";
export const getRandomIdleMedia = (site: string) => {
export const getRandomIdleMedia = () => {
const siteAIdleNodes = {
audio: [
"0000",
@ -56,7 +56,9 @@ export const getRandomIdleMedia = (site: string) => {
],
};
const siteData = site === "a" ? site_a : site_b;
const site = useStore.getState().activeSite;
const siteData: SiteType = site === "a" ? site_a : site_b;
const idleNodes = site === "a" ? siteAIdleNodes : siteBIdleNodes;
if (Math.random() < 0.5) {
@ -65,19 +67,22 @@ export const getRandomIdleMedia = (site: string) => {
const level = nodeToPlay.substr(0, 2);
const images = (siteData as SiteType)[level][nodeToPlay]
.image_table_indices;
const media = (siteData as SiteType)[level][nodeToPlay].media_file;
const images = siteData[level][nodeToPlay].image_table_indices;
const media = siteData[level][nodeToPlay].media_file;
const nodeName = siteData[level][nodeToPlay].node_name;
useStore.setState({
idleImages: images,
idleMedia: media,
});
return {
images: images,
media: media,
nodeName: nodeName,
};
} else {
useStore.setState({
idleMedia:
return {
media:
idleNodes.video[Math.floor(Math.random() * idleNodes.video.length)],
});
nodeName: undefined,
images: undefined,
};
}
};