diff --git a/packages/frontend/src/world/engine.ts b/packages/frontend/src/world/engine.ts index e1742408b2..ee4e0e50b4 100644 --- a/packages/frontend/src/world/engine.ts +++ b/packages/frontend/src/world/engine.ts @@ -8,7 +8,7 @@ import { AxesViewer } from '@babylonjs/core/Debug/axesViewer'; import { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic'; import { EventEmitter } from 'eventemitter3'; import tinycolor from 'tinycolor2'; -import { HorizontalCameraKeyboardMoveInput, RecyvlingTextGrid, WORLD_SCALE, camelToKebab, cm, createPlaneUvMapper, normalizeUvToSquare, randomRange } from './utility.js'; +import { HorizontalCameraKeyboardMoveInput, RecyvlingTextGrid, Timer, WORLD_SCALE, camelToKebab, cm, createPlaneUvMapper, normalizeUvToSquare, randomRange } from './utility.js'; import { TIME_MAP } from './utility.js'; import { genId } from '@/utility/id.js'; import { deepClone } from '@/utility/clone.js'; @@ -34,8 +34,6 @@ export class WorldEngine extends EventEmitter { public scene: BABYLON.Scene; private shadowGeneratorForSunLight: BABYLON.ShadowGenerator; public camera: BABYLON.UniversalCamera; - public intervalIds: number[] = []; - public timeoutIds: number[] = []; private time: 0 | 1 | 2 = 0; // 0: 昼, 1: 夕, 2: 夜 private envMap: BABYLON.CubeTexture; public lightContainer: BABYLON.ClusteredLightContainer; @@ -44,6 +42,7 @@ export class WorldEngine extends EventEmitter { private textMaterial: BABYLON.StandardMaterial; private translucentTextMaterial: BABYLON.StandardMaterial; private reflectionProbe: BABYLON.ReflectionProbe; + public timer: Timer = new Timer(); public isSitting = false; private fps: number | null = null; @@ -314,7 +313,7 @@ export class WorldEngine extends EventEmitter { let currentTextIndex = 1; - setInterval(() => { + this.timer.setInterval(() => { const textToShow = texts[currentTextIndex]; currentTextIndex = (currentTextIndex + 1) % texts.length; text.writeWithAnimation(textToShow); @@ -343,7 +342,7 @@ export class WorldEngine extends EventEmitter { messageRing.animations = [anim]; this.scene.beginAnimation(messageRing, 0, 10000, true); - setInterval(() => { + this.timer.setInterval(() => { text.write(Date.now().toString()); }, 10); } @@ -369,7 +368,7 @@ export class WorldEngine extends EventEmitter { messageRing.animations = [anim]; this.scene.beginAnimation(messageRing, 0, 10000, true); - setInterval(() => { + this.timer.setInterval(() => { const now = new Date(); const hours = now.getHours().toString().padStart(2, '0'); const minutes = now.getMinutes().toString().padStart(2, '0'); @@ -399,7 +398,7 @@ export class WorldEngine extends EventEmitter { messageRing.animations = [anim]; this.scene.beginAnimation(messageRing, 0, 10000, true); - setInterval(() => { + this.timer.setInterval(() => { const now = new Date(); const years = now.getFullYear().toString(); const months = (now.getMonth() + 1).toString().padStart(2, '0'); @@ -510,7 +509,7 @@ export class WorldEngine extends EventEmitter { const _7days = _1h * 24 * 7; const _30days = _1h * 24 * 30; - setInterval(() => { + this.timer.setInterval(() => { const time = Date.now(); worldRingH.rotation.x = ((time % _12h) / _12h) * Math.PI * 2; worldRingM.rotation.y = -(((time % _1h) / _1h) * Math.PI); @@ -519,7 +518,7 @@ export class WorldEngine extends EventEmitter { const screenMeshes = envObj.meshes.filter(m => m.name.includes('__SCREEN__')); const screenMaterial = screenMeshes[0].material as BABYLON.PBRMaterial; - setTimeout(() => { + this.timer.setTimeout(() => { const tex = new BABYLON.VideoTexture('', 'http://syu-win.local:3000/files/931c02c3-6238-4c29-9371-06bab78950bb', this.scene, true, true); tex.level = 0.5; tex.video.loop = true; @@ -593,14 +592,7 @@ export class WorldEngine extends EventEmitter { } public destroy() { - for (const id of this.intervalIds) { - window.clearInterval(id); - } - for (const id of this.timeoutIds) { - window.clearTimeout(id); - } - this.intervalIds = []; - this.timeoutIds = []; + this.timer.dispose(); this.engine.dispose(); this.disposed = true; } @@ -628,7 +620,7 @@ class MessageRing { messageRing.animations = [anim]; this.scene.beginAnimation(messageRing, 0, 10000, true); - setInterval(() => { + this.timer.setInterval(() => { const now = new Date(); const hours = now.getHours().toString().padStart(2, '0'); const minutes = now.getMinutes().toString().padStart(2, '0'); diff --git a/packages/frontend/src/world/room/engine.ts b/packages/frontend/src/world/room/engine.ts index 978345efd9..79c0655cc6 100644 --- a/packages/frontend/src/world/room/engine.ts +++ b/packages/frontend/src/world/room/engine.ts @@ -14,7 +14,7 @@ import { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic'; import { BoundingBoxRenderer } from '@babylonjs/core/Rendering/boundingBoxRenderer'; import { GridMaterial } from '@babylonjs/materials'; import { EventEmitter } from 'eventemitter3'; -import { TIME_MAP, scaleMorph, HorizontalCameraKeyboardMoveInput, camelToKebab, cm, WORLD_SCALE, getMeshesBoundingBox } from '../utility.js'; +import { TIME_MAP, scaleMorph, HorizontalCameraKeyboardMoveInput, camelToKebab, cm, WORLD_SCALE, getMeshesBoundingBox, Timer } from '../utility.js'; import { getObjectDef } from './object-defs.js'; import { findMaterial, ModelManager, SYSTEM_MESH_NAMES } from './utility.js'; import type { ObjectDef, RoomObjectInstance, RoomStateObject } from './object.js'; @@ -137,8 +137,6 @@ export class RoomEngine extends EventEmitter { private shadowGeneratorForRoomLight: BABYLON.ShadowGenerator; private shadowGeneratorForSunLight: BABYLON.ShadowGenerator; public camera: BABYLON.UniversalCamera; - public intervalIds: number[] = []; - public timeoutIds: number[] = []; public objectEntities: Map; @@ -207,6 +205,7 @@ export class RoomEngine extends EventEmitter { private zGridPreviewPlane: BABYLON.Mesh; private selectionOutlineLayer: BABYLON.SelectionOutlineLayer; public sr: BABYLON.SnapshotRenderingHelper; + public timer: Timer = new Timer(); private _isEditMode = false; get isEditMode() { @@ -1267,12 +1266,10 @@ export class RoomEngine extends EventEmitter { }, }; - const intervalId = setInterval(() => { + this.timer.setInterval(() => { this.handleGrabbing(); }, 10); - this.intervalIds.push(intervalId); - this.playSfxUrl('/client-assets/room/sfx/grab.mp3', { volume: 1, playbackRate: 1, @@ -1479,12 +1476,10 @@ export class RoomEngine extends EventEmitter { }, }; - const intervalId = setInterval(() => { + this.timer.setInterval(() => { this.handleGrabbing(); }, 10); - this.intervalIds.push(intervalId); - this.playSfxUrl('/client-assets/room/sfx/grab.mp3', { volume: 1, playbackRate: 1, @@ -1614,14 +1609,7 @@ export class RoomEngine extends EventEmitter { } public destroy() { - for (const id of this.intervalIds) { - window.clearInterval(id); - } - for (const id of this.timeoutIds) { - window.clearTimeout(id); - } - this.intervalIds = []; - this.timeoutIds = []; + this.timer.dispose(); this.engine.dispose(); this.disposed = true; } diff --git a/packages/frontend/src/world/room/objects/tabletopDigitalClock.ts b/packages/frontend/src/world/room/objects/tabletopDigitalClock.ts index 04fb4fab04..81403a7bd1 100644 --- a/packages/frontend/src/world/room/objects/tabletopDigitalClock.ts +++ b/packages/frontend/src/world/room/objects/tabletopDigitalClock.ts @@ -100,7 +100,7 @@ export const tabletopDigitalClock = defineObject({ applyBodyColor(); applyLcdColor(); - room.intervalIds.push(setInterval(() => { + room.timer.setInterval(() => { const onMeshes = get7segMeshesOfCurrentTime(segmentMeshes); for (const mesh of Object.values(segmentMeshes)) { @@ -118,7 +118,7 @@ export const tabletopDigitalClock = defineObject({ } room?.sr.updateMesh([...Object.values(segmentMeshes), ...colonMeshes]); - }, 1000)); + }, 1000); }, onOptionsUpdated: ([k, v]) => { if (k === 'bodyColor') { diff --git a/packages/frontend/src/world/room/objects/wallClock.ts b/packages/frontend/src/world/room/objects/wallClock.ts index 0c6d345edb..c367faebe5 100644 --- a/packages/frontend/src/world/room/objects/wallClock.ts +++ b/packages/frontend/src/world/room/objects/wallClock.ts @@ -39,7 +39,7 @@ export const wallClock = defineObject({ return { onInited: () => { - room.intervalIds.push(setInterval(() => { + room.timer.setInterval(() => { const now = new Date(); const hours = now.getHours() % 12; const minutes = now.getMinutes(); @@ -48,7 +48,7 @@ export const wallClock = defineObject({ hourHand.rotation = new BABYLON.Vector3(0, 0, hAngle); minuteHand.rotation = new BABYLON.Vector3(0, 0, mAngle); room?.sr.updateMesh([hourHand, minuteHand]); - }, 1000)); + }, 1000); }, onOptionsUpdated: ([k, v]) => { applyFrameColor(); diff --git a/packages/frontend/src/world/room/utility.ts b/packages/frontend/src/world/room/utility.ts index 640fff8298..652a60789d 100644 --- a/packages/frontend/src/world/room/utility.ts +++ b/packages/frontend/src/world/room/utility.ts @@ -146,11 +146,9 @@ export function initTv(room: RoomEngine, screenMesh: BABYLON.Mesh) { uvs[uvIndexes[3] + 1] = dy; screenMesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs); - const timeoutId = window.setTimeout(() => { - room.timeoutIds = room.timeoutIds.filter(id => id !== timeoutId); + room.timer.setTimeout(() => { applyTvTexture((tlIndex + 1) % tvProgram.timeline.length); }, duration); - room.timeoutIds.push(timeoutId); }; applyTvTexture(0); diff --git a/packages/frontend/src/world/utility.ts b/packages/frontend/src/world/utility.ts index bb50e28537..bef7de736a 100644 --- a/packages/frontend/src/world/utility.ts +++ b/packages/frontend/src/world/utility.ts @@ -660,3 +660,33 @@ export class RecyvlingTextGrid { export function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } + +export class Timer { + private timeoutIds: number[] = []; + private intervalIds: number[] = []; + + public setTimeout(callback: () => void, ms: number) { + const id = window.setTimeout(() => { + this.timeoutIds = this.timeoutIds.filter(i => i !== id); + callback(); + }, ms); + this.timeoutIds.push(id); + } + + public setInterval(callback: () => void, ms: number) { + const id = window.setInterval(callback, ms); + this.intervalIds.push(id); + } + + public dispose() { + for (const id of this.timeoutIds) { + window.clearTimeout(id); + } + this.timeoutIds = []; + + for (const id of this.intervalIds) { + window.clearInterval(id); + } + this.intervalIds = []; + } +}