new help section explaining classes and their mechanics
This commit is contained in:
@@ -1,8 +1,19 @@
|
||||
import type * as THREE from "three";
|
||||
import type { GamePhase, GameplayActivity } from "../game/types";
|
||||
|
||||
export type SceneRenderMode = "active" | "outcome" | "static" | "suspended";
|
||||
export type OutcomePhase = Extract<GamePhase, "victory" | "defeat" | "intermission">;
|
||||
|
||||
// Visible modes stay in R3F's demand clock domain. Suspended mode blocks loader
|
||||
// and host-commit invalidations as well as the explicit gameplay request loop.
|
||||
export function sceneCanvasFrameloop(mode: SceneRenderMode): "demand" | "never" {
|
||||
return mode === "suspended" ? "never" : "demand";
|
||||
}
|
||||
|
||||
export function resetSceneClockForMode(clock: Pick<THREE.Clock, "start">, mode: SceneRenderMode) {
|
||||
if (mode === "active" || mode === "outcome") clock.start();
|
||||
}
|
||||
|
||||
export const GAMEPLAY_FRAME_INTERVAL_MS = 1_000 / 60;
|
||||
export const OUTCOME_FRAME_INTERVAL_MS = 1_000 / 30;
|
||||
export const FRAME_INTERVAL_JITTER_MS = 1.5;
|
||||
@@ -76,13 +87,11 @@ export function consumeSimulationSteps(
|
||||
export interface SceneFrameSample {
|
||||
elapsedMs: number;
|
||||
elapsedSeconds: number;
|
||||
manualTimeSeconds: number;
|
||||
outcomeElapsedSeconds: number;
|
||||
}
|
||||
|
||||
export interface SceneFrameLoopOptions {
|
||||
mode: "active" | "outcome";
|
||||
initialManualTimeSeconds?: number;
|
||||
initialOutcomeElapsedSeconds?: number;
|
||||
requestFrame: (callback: FrameRequestCallback) => number;
|
||||
cancelFrame: (handle: number) => void;
|
||||
@@ -91,12 +100,11 @@ export interface SceneFrameLoopOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the bounded manual R3F loop used by active gameplay and finite outcome
|
||||
* animation tails. Static and suspended modes intentionally have no manual loop.
|
||||
* Starts the bounded request loop used for active gameplay and finite outcome
|
||||
* animation tails. Static and suspended modes intentionally have no loop.
|
||||
*/
|
||||
export function startSceneFrameLoop({
|
||||
mode,
|
||||
initialManualTimeSeconds = 0,
|
||||
initialOutcomeElapsedSeconds = 0,
|
||||
requestFrame,
|
||||
cancelFrame,
|
||||
@@ -107,7 +115,6 @@ export function startSceneFrameLoop({
|
||||
let frameHandle: number | null = null;
|
||||
let stopped = false;
|
||||
let lastRenderedAt: number | null = null;
|
||||
let manualTimeSeconds = Math.max(0, initialManualTimeSeconds);
|
||||
let outcomeElapsedSeconds = mode === "outcome" ? Math.max(0, initialOutcomeElapsedSeconds) : 0;
|
||||
|
||||
const scheduleNext = () => {
|
||||
@@ -120,7 +127,7 @@ export function startSceneFrameLoop({
|
||||
const previous = lastRenderedAt;
|
||||
if (previous === null) {
|
||||
lastRenderedAt = now;
|
||||
onFrame({ elapsedMs: 0, elapsedSeconds: 0, manualTimeSeconds, outcomeElapsedSeconds });
|
||||
onFrame({ elapsedMs: 0, elapsedSeconds: 0, outcomeElapsedSeconds });
|
||||
} else {
|
||||
const elapsedMs = now - previous;
|
||||
if (elapsedMs + FRAME_INTERVAL_JITTER_MS >= intervalMs) {
|
||||
@@ -129,9 +136,8 @@ export function startSceneFrameLoop({
|
||||
const elapsedSeconds = mode === "active"
|
||||
? Math.min(MAX_FRAME_DELTA_SECONDS, wallElapsedSeconds)
|
||||
: wallElapsedSeconds;
|
||||
manualTimeSeconds += elapsedSeconds;
|
||||
if (mode === "outcome") outcomeElapsedSeconds += wallElapsedSeconds;
|
||||
onFrame({ elapsedMs, elapsedSeconds, manualTimeSeconds, outcomeElapsedSeconds });
|
||||
onFrame({ elapsedMs, elapsedSeconds, outcomeElapsedSeconds });
|
||||
|
||||
if (mode === "outcome" && outcomeElapsedSeconds >= OUTCOME_RENDER_TAIL_SECONDS) {
|
||||
stopped = true;
|
||||
|
||||
Reference in New Issue
Block a user