mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-01-19 20:25:01 +00:00
86 lines
2.1 KiB
TypeScript
86 lines
2.1 KiB
TypeScript
import {
|
|
createContext,
|
|
ReactNode,
|
|
useCallback,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useRef,
|
|
} from "react";
|
|
import { useFrame } from "@react-three/fiber";
|
|
|
|
/** Ticks per second, matching the Torque engine tick rate. */
|
|
export const TICK_RATE = 32;
|
|
const TICK_INTERVAL = 1 / TICK_RATE;
|
|
|
|
export type TickCallback = (tick: number) => void;
|
|
|
|
interface TickContextValue {
|
|
subscribe: (callback: TickCallback) => () => void;
|
|
getTick: () => number;
|
|
}
|
|
|
|
const TickContext = createContext<TickContextValue | null>(null);
|
|
|
|
interface TickProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
export function TickProvider({ children }: TickProviderProps) {
|
|
const callbacksRef = useRef<Set<TickCallback> | undefined>(undefined);
|
|
const accumulatorRef = useRef(0);
|
|
const tickRef = useRef(0);
|
|
|
|
useFrame((_, delta) => {
|
|
accumulatorRef.current += delta;
|
|
|
|
while (accumulatorRef.current >= TICK_INTERVAL) {
|
|
accumulatorRef.current -= TICK_INTERVAL;
|
|
tickRef.current++;
|
|
|
|
if (callbacksRef.current) {
|
|
for (const callback of callbacksRef.current) {
|
|
callback(tickRef.current);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const subscribe = useCallback((callback: TickCallback) => {
|
|
callbacksRef.current ??= new Set();
|
|
callbacksRef.current.add(callback);
|
|
return () => {
|
|
callbacksRef.current!.delete(callback);
|
|
};
|
|
}, []);
|
|
|
|
const getTick = useCallback(() => tickRef.current, []);
|
|
|
|
const context = useMemo(() => ({ subscribe, getTick }), [subscribe, getTick]);
|
|
|
|
return (
|
|
<TickContext.Provider value={context}>{children}</TickContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useTick(callback: TickCallback) {
|
|
const context = useContext(TickContext);
|
|
if (!context) {
|
|
throw new Error("useTick must be used within a TickProvider");
|
|
}
|
|
const callbackRef = useRef(callback);
|
|
callbackRef.current = callback;
|
|
|
|
useEffect(() => {
|
|
return context.subscribe((tick) => callbackRef.current(tick));
|
|
}, [context]);
|
|
}
|
|
|
|
export function useGetTick() {
|
|
const context = useContext(TickContext);
|
|
if (!context) {
|
|
throw new Error("useGetTick must be used within a TickProvider");
|
|
}
|
|
return context.getTick;
|
|
}
|