Files
echo-nexus/src/hooks/useGameLoop.ts
T

67 lines
2.2 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
// 回响星核 / Echo Nexus — 主循环 hook
import { useEffect, useRef } from "react";
import { useGameStore } from "@/store/gameStore";
/** 250ms tick:产能 + 自动产晶体 + 自动解码 + 星潮 + 成就检测 */
export function useGameLoop() {
const tick = useGameStore((s) => s.tick);
const autoDecodeTick = useGameStore((s) => s.autoDecodeTick);
const tickTide = useGameStore((s) => s.tickTide);
const checkAchievements = useGameStore((s) => s.checkAchievements);
const init = useGameStore((s) => s.init);
const inited = useRef(false);
useEffect(() => {
// 等待 persist rehydrate 完成后再 initv0.5.2 修复离线收益不弹窗 BUG)
// Zustand persist 即使用同步 localStoragerehydrate 也是异步(Promise.resolve 包装)
const doInit = () => {
if (!inited.current) {
init();
inited.current = true;
}
};
let unsub: (() => void) | null = null;
let fallback: ReturnType<typeof setTimeout> | null = null;
if (useGameStore.persist.hasHydrated()) {
doInit();
} else {
unsub = useGameStore.persist.onFinishHydration(doInit);
// 兜底:500ms 后强制 init(防止 hydrated 状态异常)
fallback = setTimeout(doInit, 500);
}
let counter = 0;
const id = setInterval(() => {
const now = Date.now();
tick(now);
autoDecodeTick();
// 星潮检测每 tick 都查(结束/触发判定需及时)
tickTide(now);
counter++;
// 成就检测每秒执行一次(降低开销)
if (counter % 4 === 0) {
checkAchievements();
}
}, 250);
return () => {
clearInterval(id);
if (unsub) unsub();
if (fallback) clearTimeout(fallback);
};
}, [tick, autoDecodeTick, tickTide, checkAchievements, init]);
// 页面可见性:切回时补 tick + 星潮 + 成就
useEffect(() => {
const onVis = () => {
if (document.visibilityState === "visible") {
tick(Date.now());
tickTide(Date.now());
checkAchievements();
}
};
document.addEventListener("visibilitychange", onVis);
return () => document.removeEventListener("visibilitychange", onVis);
}, [tick, tickTide, checkAchievements]);
}