update: add a physic scenne
This commit is contained in:
+33
-13
@@ -1,18 +1,36 @@
|
||||
type Listener<TPayload> = (payload: TPayload) => void;
|
||||
|
||||
// TypeScript cannot narrow mapped-type indexed access by a generic key TKey
|
||||
// (microsoft/TypeScript#30581). The helper below encapsulates the one necessary
|
||||
// cast so the rest of the class stays cast-free.
|
||||
type ListenerMap<TEvents extends Record<string, unknown>> = {
|
||||
[TKey in keyof TEvents]?: Set<Listener<TEvents[TKey]>>;
|
||||
};
|
||||
|
||||
function getListeners<
|
||||
TEvents extends Record<string, unknown>,
|
||||
TKey extends keyof TEvents,
|
||||
>(
|
||||
map: ListenerMap<TEvents>,
|
||||
key: TKey,
|
||||
): Set<Listener<TEvents[TKey]>> | undefined {
|
||||
return map[key] as Set<Listener<TEvents[TKey]>> | undefined;
|
||||
}
|
||||
|
||||
export class EventEmitter<TEvents extends Record<string, unknown>> {
|
||||
private readonly listeners = new Map<
|
||||
keyof TEvents,
|
||||
Set<Listener<TEvents[keyof TEvents]>>
|
||||
>();
|
||||
private readonly listeners: ListenerMap<TEvents> = {};
|
||||
|
||||
on<TKey extends keyof TEvents>(
|
||||
event: TKey,
|
||||
listener: Listener<TEvents[TKey]>,
|
||||
): () => void {
|
||||
const currentListeners = this.listeners.get(event) ?? new Set();
|
||||
currentListeners.add(listener as Listener<TEvents[keyof TEvents]>);
|
||||
this.listeners.set(event, currentListeners);
|
||||
const existing = getListeners(this.listeners, event);
|
||||
|
||||
if (existing) {
|
||||
existing.add(listener);
|
||||
} else {
|
||||
this.listeners[event] = new Set([listener]) as ListenerMap<TEvents>[TKey];
|
||||
}
|
||||
|
||||
return () => {
|
||||
this.off(event, listener);
|
||||
@@ -23,32 +41,34 @@ export class EventEmitter<TEvents extends Record<string, unknown>> {
|
||||
event: TKey,
|
||||
listener: Listener<TEvents[TKey]>,
|
||||
): void {
|
||||
const currentListeners = this.listeners.get(event);
|
||||
const currentListeners = getListeners(this.listeners, event);
|
||||
|
||||
if (!currentListeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentListeners.delete(listener as Listener<TEvents[keyof TEvents]>);
|
||||
currentListeners.delete(listener);
|
||||
|
||||
if (currentListeners.size === 0) {
|
||||
this.listeners.delete(event);
|
||||
delete this.listeners[event];
|
||||
}
|
||||
}
|
||||
|
||||
emit<TKey extends keyof TEvents>(event: TKey, payload: TEvents[TKey]): void {
|
||||
const currentListeners = this.listeners.get(event);
|
||||
const currentListeners = getListeners(this.listeners, event);
|
||||
|
||||
if (!currentListeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentListeners.forEach((listener) => {
|
||||
listener(payload as TEvents[keyof TEvents]);
|
||||
listener(payload);
|
||||
});
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.listeners.clear();
|
||||
for (const key of Object.keys(this.listeners) as (keyof TEvents)[]) {
|
||||
delete this.listeners[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-14
@@ -9,8 +9,12 @@ export class Debug {
|
||||
private readonly folders = new Map<string, GUI>();
|
||||
private readonly registeredFolders = new Set<string>();
|
||||
private readonly listeners = new Set<() => void>();
|
||||
private readonly controls: { cameraMode: CameraMode } = {
|
||||
private readonly controls: {
|
||||
cameraMode: CameraMode;
|
||||
showInteractionSpheres: boolean;
|
||||
} = {
|
||||
cameraMode: "player",
|
||||
showInteractionSpheres: false,
|
||||
};
|
||||
|
||||
static getInstance(): Debug {
|
||||
@@ -28,9 +32,7 @@ export class Debug {
|
||||
if (this.gui) {
|
||||
const folder = this.createFolder("Debug");
|
||||
|
||||
if (!folder) {
|
||||
return;
|
||||
}
|
||||
if (!folder) return;
|
||||
|
||||
folder
|
||||
.add(this.controls, "cameraMode", { Player: "player", Debug: "debug" })
|
||||
@@ -39,6 +41,14 @@ export class Debug {
|
||||
this.controls.cameraMode = value;
|
||||
this.emit();
|
||||
});
|
||||
|
||||
folder
|
||||
.add(this.controls, "showInteractionSpheres")
|
||||
.name("Interaction Spheres")
|
||||
.onChange((value: boolean) => {
|
||||
this.controls.showInteractionSpheres = value;
|
||||
this.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,21 +58,15 @@ export class Debug {
|
||||
* null is returned to avoid duplicating controls under StrictMode double-mount.
|
||||
*/
|
||||
createFolder(name: string): GUI | null {
|
||||
if (!this.gui) {
|
||||
return null;
|
||||
}
|
||||
if (!this.gui) return null;
|
||||
|
||||
if (this.registeredFolders.has(name)) {
|
||||
return null;
|
||||
}
|
||||
if (this.registeredFolders.has(name)) return null;
|
||||
|
||||
this.registeredFolders.add(name);
|
||||
|
||||
const existingFolder = this.folders.get(name);
|
||||
const existing = this.folders.get(name);
|
||||
|
||||
if (existingFolder) {
|
||||
return existingFolder;
|
||||
}
|
||||
if (existing) return existing;
|
||||
|
||||
const folder = this.gui.addFolder(name);
|
||||
this.folders.set(name, folder);
|
||||
@@ -82,6 +86,10 @@ export class Debug {
|
||||
return this.controls.cameraMode;
|
||||
}
|
||||
|
||||
getShowInteractionSpheres(): boolean {
|
||||
return this.controls.showInteractionSpheres;
|
||||
}
|
||||
|
||||
private emit(): void {
|
||||
this.listeners.forEach((listener) => listener());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user