@nimbus2d/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/index.mjs +18433 -0
  2. package/package.json +39 -0
  3. package/src/base/EventEmitter.js +187 -0
  4. package/src/base/Subsystem.js +183 -0
  5. package/src/base/index.js +6 -0
  6. package/src/core/Application/AppState.js +27 -0
  7. package/src/core/Application/Application.js +480 -0
  8. package/src/core/Application/DefaultConfig.js +63 -0
  9. package/src/core/Application/index.js +7 -0
  10. package/src/core/AssetManager/AssetManager.js +711 -0
  11. package/src/core/AssetManager/AssetTypes.js +108 -0
  12. package/src/core/AssetManager/adapters/IResourceAdapter.js +50 -0
  13. package/src/core/AssetManager/adapters/PixiAdapter.js +90 -0
  14. package/src/core/AssetManager/core/RefCounter.js +167 -0
  15. package/src/core/AssetManager/core/ResourceCache.js +281 -0
  16. package/src/core/AssetManager/core/ResourceEntry.js +191 -0
  17. package/src/core/AssetManager/core/ResourceEventBus.js +127 -0
  18. package/src/core/AssetManager/handlers/IResourceHandler.js +45 -0
  19. package/src/core/AssetManager/index.js +26 -0
  20. package/src/core/AssetManager/plugins/IResourcePlugin.js +56 -0
  21. package/src/core/Camera/Camera.js +534 -0
  22. package/src/core/Camera/CameraTypes.js +59 -0
  23. package/src/core/Camera/index.js +13 -0
  24. package/src/core/ConfigSystem/ConfigLayer.js +230 -0
  25. package/src/core/ConfigSystem/ConfigLayerType.js +31 -0
  26. package/src/core/ConfigSystem/ConfigMerger.js +50 -0
  27. package/src/core/ConfigSystem/ConfigPath.js +96 -0
  28. package/src/core/ConfigSystem/ConfigSystem.js +378 -0
  29. package/src/core/ConfigSystem/ConfigTypes.js +29 -0
  30. package/src/core/ConfigSystem/ConfigWatcher.js +84 -0
  31. package/src/core/ConfigSystem/index.js +19 -0
  32. package/src/core/EventBus/EventBus.js +797 -0
  33. package/src/core/EventBus/Interceptor.js +26 -0
  34. package/src/core/EventBus/PerformanceStats.js +105 -0
  35. package/src/core/EventBus/index.js +7 -0
  36. package/src/core/Lifecycle/Lifecycle.js +265 -0
  37. package/src/core/Lifecycle/LifecycleState.js +31 -0
  38. package/src/core/Lifecycle/index.js +6 -0
  39. package/src/core/Logger/ConsoleHandler.js +55 -0
  40. package/src/core/Logger/LogHandler.js +34 -0
  41. package/src/core/Logger/LogLevel.js +38 -0
  42. package/src/core/Logger/Logger.js +234 -0
  43. package/src/core/Logger/index.js +8 -0
  44. package/src/core/PluginSystem/Plugin.js +156 -0
  45. package/src/core/PluginSystem/PluginEntry.js +102 -0
  46. package/src/core/PluginSystem/PluginState.js +30 -0
  47. package/src/core/PluginSystem/PluginSystem.js +530 -0
  48. package/src/core/PluginSystem/index.js +8 -0
  49. package/src/core/PoolManager/DefaultConfig.js +19 -0
  50. package/src/core/PoolManager/ObjectPool.js +326 -0
  51. package/src/core/PoolManager/PoolManager.js +253 -0
  52. package/src/core/PoolManager/PoolTypes.js +92 -0
  53. package/src/core/PoolManager/index.js +15 -0
  54. package/src/core/SceneManager/FadeTransition.js +64 -0
  55. package/src/core/SceneManager/MaskTransition.js +234 -0
  56. package/src/core/SceneManager/Scene.js +221 -0
  57. package/src/core/SceneManager/SceneManager.js +658 -0
  58. package/src/core/SceneManager/SceneState.js +29 -0
  59. package/src/core/SceneManager/SlideTransition.js +113 -0
  60. package/src/core/SceneManager/Transition.js +102 -0
  61. package/src/core/SceneManager/ZoomTransition.js +119 -0
  62. package/src/core/SceneManager/index.js +12 -0
  63. package/src/core/SpriteGPULayer/SpriteGPULayer.js +321 -0
  64. package/src/core/SpriteGPULayer/SpriteGPULayerTypes.js +49 -0
  65. package/src/core/SpriteGPULayer/index.js +11 -0
  66. package/src/core/Timeline/Clip.js +101 -0
  67. package/src/core/Timeline/DefaultConfig.js +41 -0
  68. package/src/core/Timeline/Easing.js +176 -0
  69. package/src/core/Timeline/Timeline.js +458 -0
  70. package/src/core/Timeline/TimelineTypes.js +89 -0
  71. package/src/core/Timeline/Track.js +202 -0
  72. package/src/core/Timeline/index.js +22 -0
  73. package/src/errors/ConfigError.js +47 -0
  74. package/src/errors/LifecycleError.js +58 -0
  75. package/src/errors/NimbusError.js +43 -0
  76. package/src/errors/PluginError.js +138 -0
  77. package/src/errors/ResourceError.js +166 -0
  78. package/src/errors/SceneError.js +124 -0
  79. package/src/errors/SubsystemError.js +68 -0
  80. package/src/errors/index.js +53 -0
  81. package/src/index.js +86 -0
  82. package/src/types/index.js +141 -0
  83. package/src/utils/MemoryChecker.js +374 -0
  84. package/src/utils/Validator.js +60 -0
  85. package/src/utils/index.js +84 -0
@@ -0,0 +1,64 @@
1
+ // ============================================================================
2
+ // Nimbus2D - 淡入淡出过渡效果
3
+ // ----------------------------------------------------------------------------
4
+ // 淡出源场景(alpha 1 → 0),淡入目标场景(alpha 0 → 1)。
5
+ //
6
+ // 继承关系:
7
+ // Transition → FadeTransition
8
+ // ============================================================================
9
+
10
+ import { Transition } from './Transition.js'
11
+
12
+ class FadeTransition extends Transition {
13
+ /**
14
+ * 构造函数
15
+ * @param {Object} [options={}] - 选项(继承自 Transition)
16
+ */
17
+ constructor(options = {}) {
18
+ super(options)
19
+ }
20
+
21
+ /**
22
+ * 执行淡入淡出过渡
23
+ * @param {import('./Scene.js').Scene} fromScene - 源场景
24
+ * @param {import('./Scene.js').Scene} toScene - 目标场景
25
+ * @returns {Promise<void>}
26
+ */
27
+ async execute(fromScene, toScene) {
28
+ // 触发开始回调
29
+ if (this._onTransitionStart) this._onTransitionStart()
30
+
31
+ // 确保目标场景可见且初始 alpha 为 0
32
+ if (toScene && toScene.stage) {
33
+ toScene.stage.alpha = 0
34
+ }
35
+
36
+ // 阶段1:淡出源场景(半段时间)
37
+ const halfDuration = this._duration / 2
38
+ if (fromScene && fromScene.stage) {
39
+ await this._animate(halfDuration, (progress) => {
40
+ fromScene.stage.alpha = 1 - progress
41
+ })
42
+ }
43
+
44
+ // 阶段2:淡入目标场景(半段时间)
45
+ if (toScene && toScene.stage) {
46
+ await this._animate(halfDuration, (progress) => {
47
+ toScene.stage.alpha = progress
48
+ })
49
+ }
50
+
51
+ // 确保最终状态
52
+ if (fromScene && fromScene.stage) {
53
+ fromScene.stage.alpha = 1
54
+ }
55
+ if (toScene && toScene.stage) {
56
+ toScene.stage.alpha = 1
57
+ }
58
+
59
+ // 触发结束回调
60
+ if (this._onTransitionEnd) this._onTransitionEnd()
61
+ }
62
+ }
63
+
64
+ export { FadeTransition }
@@ -0,0 +1,234 @@
1
+ // ============================================================================
2
+ // Nimbus2D - MaskTransition 遮罩转场效果
3
+ // ----------------------------------------------------------------------------
4
+ // 职责:
5
+ // 1. 实现遮罩转场动画(目标场景按遮罩形状逐渐显示)
6
+ // 2. 支持 circle/rect/diamond 三种内置遮罩形状
7
+ // 3. 支持 customMask 自定义遮罩函数(用户返回 Graphics 绘制逻辑)
8
+ // 4. 复用同一 Graphics 实例避免每帧创建(性能优化)
9
+ // 5. 完成后自动清理 Graphics 资源(避免内存泄漏)
10
+ //
11
+ // 继承关系:
12
+ // Transition → MaskTransition
13
+ //
14
+ // 设计决策:
15
+ // - 继承现有 Transition 基类(execute 契约),复用 _animate/_applyEasing
16
+ // - Graphics 复用:同一实例 clear() 后重绘,避免每帧 GC 压力
17
+ // - 遮罩挂载位置:优先挂载到 toStage.parent(pixi stage),降级到 toStage 本身
18
+ // - 资源清理:execute() 完成后自动销毁 Graphics,destroy() 提供强制清理入口
19
+ // - 视口尺寸获取:从 toStage.parent 降级到 800x600 默认值
20
+ //
21
+ // 四期新增(任务 1.5/3.1):对标 Phaser 的 MaskTransition
22
+ // ============================================================================
23
+
24
+ import { Graphics } from 'pixi.js'
25
+ import { Transition } from './Transition.js'
26
+
27
+ class MaskTransition extends Transition {
28
+ /**
29
+ * 构造函数
30
+ * @param {Object} [options={}] - 选项(继承自 Transition)
31
+ * @param {string} [options.maskShape='circle'] - 遮罩形状(circle/rect/diamond)
32
+ * @param {Function} [options.customMask] - 自定义遮罩函数 (graphics, progress, viewWidth, viewHeight) => void
33
+ * 用户提供自定义绘制逻辑时,maskShape 参数被忽略
34
+ */
35
+ constructor(options = {}) {
36
+ super(options)
37
+
38
+ /** @type {string} 遮罩形状(circle/rect/diamond) */
39
+ this._maskShape = options.maskShape ?? 'circle'
40
+
41
+ /**
42
+ * 自定义遮罩函数
43
+ * 签名:(graphics, progress, viewWidth, viewHeight) => void
44
+ * 用户负责在函数内调用 graphics.clear() 并绘制遮罩形状
45
+ * @type {Function|null}
46
+ */
47
+ this._customMask = options.customMask ?? null
48
+
49
+ /**
50
+ * 复用的 Graphics 实例
51
+ * 整个转场过程复用同一实例,避免每帧创建销毁
52
+ * @type {Graphics|null}
53
+ * @private
54
+ */
55
+ this._maskGraphics = new Graphics()
56
+
57
+ /** @type {boolean} Graphics 是否已销毁(防重复销毁) */
58
+ this._graphicsDestroyed = false
59
+ }
60
+
61
+ /**
62
+ * 执行遮罩转场
63
+ * @param {import('./Scene.js').Scene} fromScene - 源场景
64
+ * @param {import('./Scene.js').Scene} toScene - 目标场景
65
+ * @returns {Promise<void>}
66
+ */
67
+ async execute(fromScene, toScene) {
68
+ // 触发开始回调
69
+ if (this._onTransitionStart) this._onTransitionStart()
70
+
71
+ /** @type {import('pixi.js').Container|null} 目标场景 stage */
72
+ const toStage = toScene ? toScene.stage : null
73
+ /** @type {import('pixi.js').Container|null} 源场景 stage */
74
+ const fromStage = fromScene ? fromScene.stage : null
75
+
76
+ // 无目标场景时直接结束(无法应用遮罩)
77
+ if (!toStage) {
78
+ if (this._onTransitionEnd) this._onTransitionEnd()
79
+ return
80
+ }
81
+
82
+ // 获取视口尺寸(从 toStage.parent 降级到默认值 800x600)
83
+ const viewWidth = (toStage.parent && toStage.parent.width) ? toStage.parent.width : 800
84
+ const viewHeight = (toStage.parent && toStage.parent.height) ? toStage.parent.height : 600
85
+
86
+ // 将 Graphics 添加到显示列表(PIXI v8 要求 mask 必须在显示列表中)
87
+ // 优先添加到 toStage.parent(与 toStage 同级),降级到 toStage 本身
88
+ /** @type {import('pixi.js').Container} Graphics 挂载容器 */
89
+ const maskParent = toStage.parent || toStage
90
+ maskParent.addChild(this._maskGraphics)
91
+
92
+ // 应用遮罩到目标场景
93
+ toStage.mask = this._maskGraphics
94
+
95
+ try {
96
+ // 执行遮罩动画
97
+ await this._animate(this._duration, (progress) => {
98
+ // 绘制遮罩(每帧重绘)
99
+ this._drawMask(progress, viewWidth, viewHeight)
100
+ // 源场景同步淡出(增强转场视觉效果)
101
+ if (fromStage) {
102
+ fromStage.alpha = 1 - progress
103
+ }
104
+ })
105
+ } finally {
106
+ // 无论动画成功或失败,都清理遮罩资源并恢复源场景 alpha
107
+ // 避免异常情况下 fromStage.alpha 残留为 0 导致场景不可见
108
+ this._cleanupMask(toStage)
109
+ if (fromStage) {
110
+ fromStage.alpha = 1
111
+ }
112
+ }
113
+
114
+ // 触发结束回调
115
+ if (this._onTransitionEnd) this._onTransitionEnd()
116
+ }
117
+
118
+ // --------------------------------------------------------------------------
119
+ // 私有方法:遮罩绘制
120
+ // --------------------------------------------------------------------------
121
+
122
+ /**
123
+ * 绘制遮罩(清空并重绘复用的 Graphics)
124
+ * @param {number} progress - 进度 0→1
125
+ * @param {number} viewWidth - 视口宽度
126
+ * @param {number} viewHeight - 视口高度
127
+ * @private
128
+ */
129
+ _drawMask(progress, viewWidth, viewHeight) {
130
+ // 自定义遮罩优先
131
+ if (typeof this._customMask === 'function') {
132
+ try {
133
+ this._customMask(this._maskGraphics, progress, viewWidth, viewHeight)
134
+ } catch (e) {
135
+ // 自定义遮罩异常时降级到 circle
136
+ this._drawBuiltinMask(progress, viewWidth, viewHeight)
137
+ }
138
+ return
139
+ }
140
+
141
+ this._drawBuiltinMask(progress, viewWidth, viewHeight)
142
+ }
143
+
144
+ /**
145
+ * 绘制内置遮罩(circle/rect/diamond)
146
+ * @param {number} progress - 进度 0→1
147
+ * @param {number} viewWidth - 视口宽度
148
+ * @param {number} viewHeight - 视口高度
149
+ * @private
150
+ */
151
+ _drawBuiltinMask(progress, viewWidth, viewHeight) {
152
+ this._maskGraphics.clear()
153
+
154
+ // 遮罩尺寸:取视口对角线长度乘以 progress,确保完全覆盖
155
+ /** @type {number} 对角线长度(保证任意形状都能覆盖整个视口) */
156
+ const maxSize = Math.sqrt(viewWidth * viewWidth + viewHeight * viewHeight) * progress
157
+ /** @type {number} 中心 X */
158
+ const cx = viewWidth / 2
159
+ /** @type {number} 中心 Y */
160
+ const cy = viewHeight / 2
161
+
162
+ switch (this._maskShape) {
163
+ case 'circle':
164
+ // 圆形遮罩:从中心向外扩展
165
+ this._maskGraphics.circle(cx, cy, maxSize / 2)
166
+ break
167
+ case 'rect':
168
+ // 矩形遮罩:从中心向外扩展
169
+ this._maskGraphics.rect(
170
+ cx - maxSize / 2,
171
+ cy - maxSize / 2,
172
+ maxSize,
173
+ maxSize
174
+ )
175
+ break
176
+ case 'diamond':
177
+ // 菱形遮罩:从中心向外扩展
178
+ this._maskGraphics.moveTo(cx, cy - maxSize / 2)
179
+ this._maskGraphics.lineTo(cx + maxSize / 2, cy)
180
+ this._maskGraphics.lineTo(cx, cy + maxSize / 2)
181
+ this._maskGraphics.lineTo(cx - maxSize / 2, cy)
182
+ this._maskGraphics.closePath()
183
+ break
184
+ default:
185
+ // 未知形状降级到 circle
186
+ this._maskGraphics.circle(cx, cy, maxSize / 2)
187
+ break
188
+ }
189
+
190
+ // 填充白色(mask 只关心 alpha 通道,颜色不影响)
191
+ this._maskGraphics.fill(0xFFFFFF)
192
+ }
193
+
194
+ /**
195
+ * 清理遮罩资源
196
+ * 移除 mask 引用 + 从父容器移除 Graphics + 销毁 Graphics
197
+ * @param {import('pixi.js').Container|null} toStage - 目标场景 stage
198
+ * @private
199
+ */
200
+ _cleanupMask(toStage) {
201
+ // 移除 mask 引用(避免引用悬挂)
202
+ if (toStage) {
203
+ toStage.mask = null
204
+ }
205
+
206
+ // 从父容器移除 Graphics 并销毁
207
+ if (this._maskGraphics && !this._graphicsDestroyed) {
208
+ if (this._maskGraphics.parent) {
209
+ this._maskGraphics.parent.removeChild(this._maskGraphics)
210
+ }
211
+ try {
212
+ this._maskGraphics.destroy()
213
+ } catch (e) {
214
+ // 销毁异常忽略
215
+ }
216
+ this._maskGraphics = null
217
+ this._graphicsDestroyed = true
218
+ }
219
+ }
220
+
221
+ // --------------------------------------------------------------------------
222
+ // 销毁
223
+ // --------------------------------------------------------------------------
224
+
225
+ /**
226
+ * 销毁遮罩转场(强制清理 Graphics 资源)
227
+ * 正常流程下 execute() 完成后会自动清理,此方法用于异常情况下强制清理
228
+ */
229
+ destroy() {
230
+ this._cleanupMask(null)
231
+ }
232
+ }
233
+
234
+ export { MaskTransition }
@@ -0,0 +1,221 @@
1
+ // ============================================================================
2
+ // Nimbus2D - 场景基类
3
+ // ----------------------------------------------------------------------------
4
+ // 所有游戏场景必须继承此基类,提供场景级事件能力和生命周期钩子。
5
+ //
6
+ // 设计决策:
7
+ // - 继承 EventEmitter,提供场景级事件能力
8
+ // - 构造函数创建 Container(从 pixi.js 导入)作为 stage
9
+ // - _state 由 SceneManager 设置(构造函数不设置,见决策 106)
10
+ // - _assetManager 由 SceneManager 注入(通过 _setAssetManager)
11
+ // - 内置资源引用管理(_retainAsset + _releaseAssets)
12
+ // - 5 个生命周期钩子:onCreate/onStart/onStop/onPause/onResume
13
+ // - 无 onDestroy 方法(资源引用由 destroy 自动释放)
14
+ // ============================================================================
15
+
16
+ import { Container } from 'pixi.js'
17
+ import { EventEmitter } from '../../base/EventEmitter.js'
18
+ import { SceneState } from './SceneState.js'
19
+ import { SceneError } from '../../errors/SceneError.js'
20
+
21
+ class Scene extends EventEmitter {
22
+ /**
23
+ * 构造函数
24
+ * @param {string} name - 场景名称
25
+ */
26
+ constructor(name) {
27
+ super()
28
+
29
+ /** @type {string} 场景名称 */
30
+ this._name = name
31
+
32
+ /** @type {import('pixi.js').Container} 场景舞台(构造函数中创建) */
33
+ this._stage = new Container()
34
+
35
+ /** @type {SceneState|null} 场景状态(由 SceneManager 设置,构造函数不设置) */
36
+ this._state = null
37
+
38
+ /** @type {boolean} 是否可见 */
39
+ this._isVisible = true
40
+
41
+ /** @type {number} 层级 */
42
+ this._zIndex = 0
43
+
44
+ /** @type {import('../AssetManager/AssetManager.js').AssetManager|null} 资源管理器(由 SceneManager 注入) */
45
+ this._assetManager = null
46
+
47
+ /** @type {Set<string>} 已引用的资源 ID 集合 */
48
+ this._retainedAssets = new Set()
49
+ }
50
+
51
+ // --------------------------------------------------------------------------
52
+ // 属性
53
+ // --------------------------------------------------------------------------
54
+
55
+ /** @type {string} 场景名称 */
56
+ get name() { return this._name }
57
+
58
+ /** @type {SceneState|null} 场景状态 */
59
+ get state() { return this._state }
60
+
61
+ /** @type {import('pixi.js').Container} 场景舞台 */
62
+ get stage() { return this._stage }
63
+
64
+ /** @type {boolean} 是否可见 */
65
+ get isVisible() { return this._isVisible }
66
+
67
+ /** @type {number} 层级 */
68
+ get zIndex() { return this._zIndex }
69
+
70
+ // --------------------------------------------------------------------------
71
+ // 生命周期钩子(子类重写)
72
+ // --------------------------------------------------------------------------
73
+
74
+ /**
75
+ * 创建时调用(仅一次,首次 start 时调用)
76
+ * @returns {Promise<void>|void}
77
+ */
78
+ async onCreate() {
79
+ // 基类空实现,子类按需重写
80
+ }
81
+
82
+ /**
83
+ * 启动时调用(每次 start)
84
+ * @returns {Promise<void>|void}
85
+ */
86
+ async onStart() {
87
+ // 基类空实现,子类按需重写
88
+ }
89
+
90
+ /**
91
+ * 停止时调用(每次 stop)
92
+ * @returns {Promise<void>|void}
93
+ */
94
+ async onStop() {
95
+ // 基类空实现,子类按需重写
96
+ }
97
+
98
+ /**
99
+ * 暂停时调用
100
+ * @returns {void}
101
+ */
102
+ onPause() {
103
+ // 基类空实现,子类按需重写
104
+ }
105
+
106
+ /**
107
+ * 恢复时调用
108
+ * @returns {void}
109
+ */
110
+ onResume() {
111
+ // 基类空实现,子类按需重写
112
+ }
113
+
114
+ // --------------------------------------------------------------------------
115
+ // 更新方法(子类重写)
116
+ // --------------------------------------------------------------------------
117
+
118
+ /**
119
+ * 每帧更新
120
+ * @param {number} delta - 帧间隔(秒)
121
+ */
122
+ update(delta) {
123
+ // 基类空实现,子类按需重写
124
+ }
125
+
126
+ // --------------------------------------------------------------------------
127
+ // 资源引用管理
128
+ // --------------------------------------------------------------------------
129
+
130
+ /**
131
+ * 引用资源(异步)
132
+ * @param {string} assetId - 资源 ID
133
+ * @returns {Promise<boolean>} - 是否引用成功
134
+ */
135
+ async _retainAsset(assetId) {
136
+ // 已引用过则直接返回
137
+ if (this._retainedAssets.has(assetId)) return true
138
+
139
+ // AssetManager 必须先注入
140
+ if (!this._assetManager) {
141
+ throw new SceneError(`Scene ${this._name} 未注入 AssetManager,请通过 SceneManager.register 注册场景`)
142
+ }
143
+
144
+ const data = await this._assetManager.retain(assetId, this._name)
145
+
146
+ // 使用 !== undefined && !== null 判断成功,避免 falsy 值误判
147
+ const ok = data !== undefined && data !== null
148
+ if (ok) {
149
+ this._retainedAssets.add(assetId)
150
+ }
151
+ return ok
152
+ }
153
+
154
+ /**
155
+ * 释放所有引用的资源(由 destroy 自动调用)
156
+ */
157
+ _releaseAssets() {
158
+ if (!this._assetManager) return
159
+ for (const assetId of this._retainedAssets) {
160
+ try {
161
+ this._assetManager.release(assetId, this._name)
162
+ } catch (e) {
163
+ // 忽略释放错误
164
+ }
165
+ }
166
+ this._retainedAssets.clear()
167
+ }
168
+
169
+ // --------------------------------------------------------------------------
170
+ // 内部方法(由 SceneManager 调用)
171
+ // --------------------------------------------------------------------------
172
+
173
+ /**
174
+ * 注入 AssetManager 引用(由 SceneManager.register 时调用)
175
+ * @param {import('../AssetManager/AssetManager.js').AssetManager} assetManager - 资源管理器实例
176
+ * @private
177
+ */
178
+ _setAssetManager(assetManager) {
179
+ this._assetManager = assetManager
180
+ }
181
+
182
+ /**
183
+ * 设置场景状态(由 SceneManager 调用)
184
+ * @param {SceneState} state - 新状态
185
+ * @private
186
+ */
187
+ _setState(state) {
188
+ this._state = state
189
+ }
190
+
191
+ // --------------------------------------------------------------------------
192
+ // 销毁
193
+ // --------------------------------------------------------------------------
194
+
195
+ /**
196
+ * 销毁场景(自动释放资源引用 + 销毁 stage)
197
+ * @returns {Promise<void>}
198
+ */
199
+ async destroy() {
200
+ // 释放所有资源引用
201
+ this._releaseAssets()
202
+
203
+ // 销毁场景舞台 Container
204
+ if (this._stage) {
205
+ this._stage.destroy({ children: true })
206
+ this._stage = null
207
+ }
208
+
209
+ this._state = SceneState.DESTROYED
210
+ this._assetManager = null
211
+ this._retainedAssets.clear()
212
+
213
+ // 先 emit destroyed 事件(super.destroy 后 emit 无效)
214
+ this.emit('destroyed', { name: this._name })
215
+
216
+ // 调用父类 destroy 设置 _destroyed = true 并清理监听器
217
+ super.destroy()
218
+ }
219
+ }
220
+
221
+ export { Scene }