Files
esengine/packages/platform-web/src/BrowserRuntime.ts
YHH d7454e3ca4 feat(engine): 添加编辑器模式标志控制编辑器UI显示 (#274)
* feat(engine): 添加编辑器模式标志控制编辑器UI显示

- 在 Rust 引擎中添加 isEditor 标志,控制网格、gizmos、坐标轴指示器的显示
- 运行时模式下自动隐藏所有编辑器专用 UI
- 编辑器预览和浏览器运行时通过 setEditorMode(false) 禁用编辑器 UI
- 添加 Scene.isEditorMode 延迟组件生命周期回调,直到 begin() 调用
- 修复用户组件注册到 Core ComponentRegistry 以支持序列化
- 修复 Run in Browser 时用户组件加载问题

* fix: 复制引擎模块的类型定义文件到 dist/engine

* fix: 修复用户项目 tsconfig paths 类型定义路径

- 从 module.json 读取实际包名而不是使用目录名
- 修复 .d.ts 文件复制逻辑,支持 .mjs 扩展名
2025-12-04 22:43:26 +08:00

240 lines
6.4 KiB
TypeScript
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.
/**
* Browser Runtime
* 浏览器运行时
*
* Lightweight runtime for web game builds.
* Uses dynamic plugin loading via import maps.
*
* 轻量级 Web 游戏运行时。
* 通过 import maps 动态加载插件。
*/
import { Core } from '@esengine/ecs-framework';
import {
GameRuntime,
createGameRuntime,
BrowserPlatformAdapter,
runtimePluginManager,
BrowserFileSystemService,
type IPlugin
} from '@esengine/runtime-core';
import type { IAssetManager } from '@esengine/asset-system';
import { BrowserAssetReader } from './BrowserAssetReader';
/**
* Runtime configuration
* 运行时配置
*/
export interface RuntimeConfig {
/** Canvas element ID */
canvasId: string;
/** Canvas width (defaults to window.innerWidth) */
width?: number;
/** Canvas height (defaults to window.innerHeight) */
height?: number;
/** Asset catalog file URL (defaults to '/asset-catalog.json') */
assetCatalogUrl?: string;
/** Asset base URL (defaults to '/assets') */
assetBaseUrl?: string;
}
/**
* Browser Runtime
* 浏览器运行时
*
* Main entry point for running games in browser.
* Supports dynamic plugin registration.
*/
export class BrowserRuntime {
private _runtime: GameRuntime | null = null;
private _canvasId: string;
private _width: number;
private _height: number;
private _assetCatalogUrl: string;
private _assetBaseUrl: string;
private _fileSystem: BrowserFileSystemService | null = null;
private _assetReader: BrowserAssetReader | null = null;
private _initialized = false;
constructor(config: RuntimeConfig) {
this._canvasId = config.canvasId;
this._width = config.width ?? window.innerWidth;
this._height = config.height ?? window.innerHeight;
this._assetCatalogUrl = config.assetCatalogUrl ?? '/asset-catalog.json';
this._assetBaseUrl = config.assetBaseUrl ?? '/assets';
}
/**
* Register a plugin dynamically
* 动态注册插件
*
* Call this before initialize() to register plugins.
*/
registerPlugin(plugin: IPlugin): void {
if (plugin) {
runtimePluginManager.register(plugin);
runtimePluginManager.enable(plugin.manifest.id);
console.log(`[Runtime] Registered plugin: ${plugin.manifest.id}`);
}
}
/**
* Register multiple plugins
* 注册多个插件
*/
registerPlugins(plugins: IPlugin[]): void {
for (const plugin of plugins) {
this.registerPlugin(plugin);
}
}
/**
* Initialize the runtime
* 初始化运行时
*
* @param wasmModule - Optional WASM module (from es_engine.js)
*/
async initialize(wasmModule?: unknown): Promise<void> {
if (this._initialized) {
console.warn('[Runtime] Already initialized');
return;
}
// Initialize browser file system service
this._fileSystem = new BrowserFileSystemService({
baseUrl: this._assetBaseUrl,
catalogUrl: this._assetCatalogUrl,
enableCache: true
});
await this._fileSystem.initialize();
// Initialize asset reader
this._assetReader = new BrowserAssetReader(this._assetBaseUrl);
// Create browser platform adapter
const platform = new BrowserPlatformAdapter({
wasmModule: wasmModule ?? undefined
});
// Create game runtime
this._runtime = createGameRuntime({
platform,
canvasId: this._canvasId,
width: this._width,
height: this._height,
autoStartRenderLoop: false
});
await this._runtime.initialize();
// Register file system service
const IFileSystemServiceKey = Symbol.for('IFileSystemService');
if (!Core.services.isRegistered(IFileSystemServiceKey)) {
Core.services.registerInstance(IFileSystemServiceKey, this._fileSystem);
}
// Set asset reader for AssetManager
if (this._runtime.assetManager && this._assetReader) {
this._runtime.assetManager.setReader(this._assetReader);
}
// Disable editor mode (hides grid, gizmos, axis indicator)
// 禁用编辑器模式隐藏网格、gizmos、坐标轴指示器
this._runtime.setEditorMode(false);
this._initialized = true;
console.log('[Runtime] Initialized');
}
/**
* Load a scene from URL
* 从 URL 加载场景
*/
async loadScene(sceneUrl: string): Promise<void> {
if (!this._runtime) {
throw new Error('Runtime not initialized. Call initialize() first.');
}
await this._runtime.loadSceneFromUrl(sceneUrl);
}
/**
* Start the game loop
* 启动游戏循环
*/
start(): void {
this._runtime?.start();
}
/**
* Stop the game loop
* 停止游戏循环
*/
stop(): void {
this._runtime?.stop();
}
/**
* Handle window resize
* 处理窗口大小变化
*/
handleResize(width: number, height: number): void {
this._runtime?.resize(width, height);
}
/**
* Get the underlying GameRuntime
* 获取底层 GameRuntime
*/
get gameRuntime(): GameRuntime | null {
return this._runtime;
}
/**
* Get the asset manager
* 获取资产管理器
*
* @returns The asset manager instance, or null if not initialized
*/
get assetManager(): IAssetManager | null {
return this._runtime?.assetManager ?? null;
}
/**
* Check if runtime is initialized
* 检查运行时是否已初始化
*/
get isInitialized(): boolean {
return this._initialized;
}
}
/**
* Create a browser runtime instance
* 创建浏览器运行时实例
*/
export function create(config: RuntimeConfig): BrowserRuntime {
return new BrowserRuntime(config);
}
/**
* Default export for convenient usage
* 默认导出,便于使用
*
* Usage in game HTML:
* ```js
* const ECSRuntime = (await import('@esengine/platform-web')).default;
* const runtime = ECSRuntime.create({ canvasId: 'game-canvas' });
* ```
*/
export default {
create,
BrowserRuntime,
BrowserAssetReader,
// Re-export useful types from dependencies
Core,
GameRuntime,
createGameRuntime,
BrowserPlatformAdapter,
runtimePluginManager
};