2025-12-03 22:15:22 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Browser Runtime
|
|
|
|
|
|
* 浏览器运行时
|
|
|
|
|
|
*
|
|
|
|
|
|
* Lightweight runtime for web game builds.
|
|
|
|
|
|
* Uses dynamic plugin loading via import maps.
|
|
|
|
|
|
*
|
|
|
|
|
|
* 轻量级 Web 游戏运行时。
|
|
|
|
|
|
* 通过 import maps 动态加载插件。
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2025-12-08 21:26:35 +08:00
|
|
|
|
import { Core } from '@esengine/ecs-framework';
|
2025-12-03 22:15:22 +08:00
|
|
|
|
import {
|
|
|
|
|
|
GameRuntime,
|
|
|
|
|
|
createGameRuntime,
|
|
|
|
|
|
BrowserPlatformAdapter,
|
|
|
|
|
|
runtimePluginManager,
|
|
|
|
|
|
BrowserFileSystemService,
|
2025-12-16 12:46:14 +08:00
|
|
|
|
RuntimeSceneManager,
|
|
|
|
|
|
RuntimeSceneManagerToken,
|
2025-12-19 17:48:18 +08:00
|
|
|
|
type IRuntimePlugin,
|
2025-12-16 12:46:14 +08:00
|
|
|
|
type IRuntimeSceneManager
|
2025-12-03 22:15:22 +08:00
|
|
|
|
} from '@esengine/runtime-core';
|
2025-12-19 15:45:14 +08:00
|
|
|
|
import { isValidGUID, setGlobalAssetFileLoader, type IAssetManager, type IAssetFileLoader } from '@esengine/asset-system';
|
2025-12-03 22:15:22 +08:00
|
|
|
|
import { BrowserAssetReader } from './BrowserAssetReader';
|
2025-12-13 19:44:08 +08:00
|
|
|
|
import { WebInputSubsystem } from './subsystems/WebInputSubsystem';
|
2025-12-03 22:15:22 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 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;
|
2025-12-16 12:46:14 +08:00
|
|
|
|
private _sceneManager: RuntimeSceneManager | null = null;
|
2025-12-03 22:15:22 +08:00
|
|
|
|
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.
|
|
|
|
|
|
*/
|
2025-12-19 17:48:18 +08:00
|
|
|
|
registerPlugin(plugin: IRuntimePlugin): void {
|
2025-12-03 22:15:22 +08:00
|
|
|
|
if (plugin) {
|
|
|
|
|
|
runtimePluginManager.register(plugin);
|
|
|
|
|
|
runtimePluginManager.enable(plugin.manifest.id);
|
|
|
|
|
|
console.log(`[Runtime] Registered plugin: ${plugin.manifest.id}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Register multiple plugins
|
|
|
|
|
|
* 注册多个插件
|
|
|
|
|
|
*/
|
2025-12-19 17:48:18 +08:00
|
|
|
|
registerPlugins(plugins: IRuntimePlugin[]): void {
|
2025-12-03 22:15:22 +08:00
|
|
|
|
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
|
2025-12-13 19:44:08 +08:00
|
|
|
|
// 创建输入子系统,用于处理键盘、鼠标、触摸事件
|
|
|
|
|
|
// Create input subsystem for keyboard, mouse, touch events
|
|
|
|
|
|
// 使用 'direct' 模式,因为独立 Web 构建不使用 Tauri 代理
|
|
|
|
|
|
// Use 'direct' mode since standalone web builds don't use Tauri proxy
|
2025-12-03 22:15:22 +08:00
|
|
|
|
const platform = new BrowserPlatformAdapter({
|
2025-12-13 19:44:08 +08:00
|
|
|
|
wasmModule: wasmModule ?? undefined,
|
|
|
|
|
|
inputSubsystemFactory: () => new WebInputSubsystem(),
|
|
|
|
|
|
assetBaseUrl: this._assetBaseUrl,
|
|
|
|
|
|
pathResolveMode: 'direct'
|
2025-12-03 22:15:22 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-13 19:44:08 +08:00
|
|
|
|
// Set asset reader for AssetManager
|
|
|
|
|
|
// 设置资产读取器
|
|
|
|
|
|
if (this._assetReader && this._runtime.assetManager) {
|
|
|
|
|
|
this._runtime.assetManager.setReader(this._assetReader);
|
2025-12-07 01:00:35 +08:00
|
|
|
|
|
2025-12-10 18:23:29 +08:00
|
|
|
|
// Initialize AssetManager with catalog from BrowserFileSystemService
|
|
|
|
|
|
// 使用 BrowserFileSystemService 的 catalog 初始化 AssetManager
|
|
|
|
|
|
// Catalog format is now unified - no conversion needed
|
|
|
|
|
|
// 目录格式已统一 - 无需转换
|
2025-12-07 01:00:35 +08:00
|
|
|
|
if (this._fileSystem?.catalog) {
|
2025-12-10 18:23:29 +08:00
|
|
|
|
const catalog = this._fileSystem.catalog;
|
2025-12-13 19:44:08 +08:00
|
|
|
|
this._runtime.assetManager.initializeFromCatalog(catalog);
|
2025-12-07 01:00:35 +08:00
|
|
|
|
}
|
2025-12-19 15:45:14 +08:00
|
|
|
|
|
|
|
|
|
|
// Set global asset file loader for UI atlas and other subsystems
|
|
|
|
|
|
// 设置全局资产文件加载器供 UI 图集和其他子系统使用
|
|
|
|
|
|
const assetFileLoader: IAssetFileLoader = {
|
|
|
|
|
|
loadImage: (assetPath: string) => this._assetReader!.loadImage(assetPath),
|
|
|
|
|
|
loadText: (assetPath: string) => this._assetReader!.readText(assetPath),
|
|
|
|
|
|
loadBinary: (assetPath: string) => this._assetReader!.readBinary(assetPath),
|
|
|
|
|
|
exists: (assetPath: string) => this._assetReader!.exists(assetPath)
|
|
|
|
|
|
};
|
|
|
|
|
|
setGlobalAssetFileLoader(assetFileLoader);
|
2025-12-03 22:15:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-04 22:43:26 +08:00
|
|
|
|
// Disable editor mode (hides grid, gizmos, axis indicator)
|
|
|
|
|
|
// 禁用编辑器模式(隐藏网格、gizmos、坐标轴指示器)
|
|
|
|
|
|
this._runtime.setEditorMode(false);
|
2025-12-03 22:15:22 +08:00
|
|
|
|
|
2025-12-13 19:44:08 +08:00
|
|
|
|
// Set up asset path resolver for rendering system
|
|
|
|
|
|
// 为渲染系统设置资产路径解析器
|
|
|
|
|
|
this._setupAssetPathResolver();
|
|
|
|
|
|
|
2025-12-16 12:46:14 +08:00
|
|
|
|
// Initialize scene manager
|
|
|
|
|
|
// 初始化场景管理器
|
|
|
|
|
|
this._initializeSceneManager();
|
|
|
|
|
|
|
2025-12-03 22:15:22 +08:00
|
|
|
|
this._initialized = true;
|
|
|
|
|
|
console.log('[Runtime] Initialized');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-16 12:46:14 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Initialize the runtime scene manager
|
|
|
|
|
|
* 初始化运行时场景管理器
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _initializeSceneManager(): void {
|
|
|
|
|
|
if (!this._runtime) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Create scene manager with scene loader
|
|
|
|
|
|
// 使用场景加载器创建场景管理器
|
|
|
|
|
|
this._sceneManager = new RuntimeSceneManager(
|
|
|
|
|
|
(url: string) => this._runtime!.loadSceneFromUrl(url),
|
|
|
|
|
|
'./scenes'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Auto-discover scenes from catalog
|
|
|
|
|
|
// 从目录自动发现场景
|
|
|
|
|
|
// scenes 是运行时扩展字段,不在 IAssetCatalog 接口中
|
|
|
|
|
|
// scenes is a runtime extension field, not in IAssetCatalog interface
|
|
|
|
|
|
const catalog = this._fileSystem?.catalog as { scenes?: Array<{ name: string; path: string }> } | null;
|
|
|
|
|
|
if (catalog?.scenes) {
|
|
|
|
|
|
const scenes = catalog.scenes.map((scene) => ({
|
|
|
|
|
|
name: scene.name,
|
|
|
|
|
|
path: `./scenes/${scene.name}.ecs`
|
|
|
|
|
|
}));
|
|
|
|
|
|
this._sceneManager.registerScenes(scenes);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Register scene manager as a service
|
|
|
|
|
|
// 注册场景管理器为服务
|
|
|
|
|
|
const serviceRegistry = this._runtime.getServiceRegistry();
|
|
|
|
|
|
if (serviceRegistry) {
|
|
|
|
|
|
serviceRegistry.register(RuntimeSceneManagerToken, this._sceneManager);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Also register in Core.services for global access (systems can access it)
|
|
|
|
|
|
// 同时注册到 Core.services 供全局访问(系统可以访问)
|
|
|
|
|
|
// RuntimeSceneManager 实现了 IService 接口(有 dispose 方法)
|
|
|
|
|
|
// RuntimeSceneManager implements IService interface (has dispose method)
|
|
|
|
|
|
const GlobalSceneManagerKey = Symbol.for('@esengine/service:runtimeSceneManager');
|
|
|
|
|
|
if (!Core.services.isRegistered(GlobalSceneManagerKey)) {
|
|
|
|
|
|
Core.services.registerInstance(GlobalSceneManagerKey, this._sceneManager);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[Runtime] Scene manager initialized');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-13 19:44:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Set up asset path resolver for the render system
|
|
|
|
|
|
* 为渲染系统设置资产路径解析器
|
|
|
|
|
|
*
|
|
|
|
|
|
* This enables GUID-to-URL resolution for textures in Web runtime.
|
|
|
|
|
|
* 这使得 Web 运行时能够将 GUID 解析为纹理 URL。
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _setupAssetPathResolver(): void {
|
|
|
|
|
|
const renderSystem = this._runtime?.renderSystem;
|
|
|
|
|
|
const assetManager = this._runtime?.assetManager;
|
|
|
|
|
|
if (!renderSystem || !assetManager) return;
|
|
|
|
|
|
|
|
|
|
|
|
const assetBaseUrl = this._assetBaseUrl;
|
|
|
|
|
|
|
|
|
|
|
|
renderSystem.setAssetPathResolver((guidOrPath: string): string => {
|
|
|
|
|
|
// Skip if already a valid URL
|
|
|
|
|
|
// 如果已经是有效的 URL 则跳过
|
|
|
|
|
|
if (!guidOrPath || guidOrPath.startsWith('http') || guidOrPath.startsWith('data:') || guidOrPath.startsWith('/')) {
|
|
|
|
|
|
return guidOrPath;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check if this is a GUID using the unified validation function
|
|
|
|
|
|
// 使用统一的验证函数检查是否为 GUID
|
|
|
|
|
|
if (isValidGUID(guidOrPath)) {
|
|
|
|
|
|
// Get metadata from runtime's asset manager
|
|
|
|
|
|
// 从运行时资产管理器获取元数据
|
|
|
|
|
|
const metadata = assetManager.getDatabase().getMetadata(guidOrPath);
|
|
|
|
|
|
if (metadata?.path) {
|
|
|
|
|
|
// Construct full URL: baseUrl + relative path
|
|
|
|
|
|
// 构建完整 URL:baseUrl + 相对路径
|
|
|
|
|
|
let relativePath = metadata.path.replace(/\\/g, '/');
|
|
|
|
|
|
|
|
|
|
|
|
// Remove 'assets/' prefix if present to avoid duplication
|
|
|
|
|
|
// 移除 'assets/' 前缀以避免重复(assetBaseUrl 已包含 /assets)
|
|
|
|
|
|
if (relativePath.startsWith('assets/')) {
|
|
|
|
|
|
relativePath = relativePath.substring(7);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return `${assetBaseUrl}/${relativePath}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GUID not found in catalog, return original
|
|
|
|
|
|
// 目录中未找到 GUID,返回原值
|
|
|
|
|
|
console.warn(`[BrowserRuntime] GUID not found in asset catalog: ${guidOrPath}`);
|
|
|
|
|
|
return guidOrPath;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Not a GUID, treat as relative path
|
|
|
|
|
|
// 不是 GUID,视为相对路径
|
|
|
|
|
|
return `${assetBaseUrl}/${guidOrPath}`;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[Runtime] Asset path resolver configured');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-03 22:15:22 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Load a scene from URL
|
|
|
|
|
|
* 从 URL 加载场景
|
2025-12-16 12:46:14 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @param sceneUrl 场景 URL 或名称 | Scene URL or name
|
2025-12-03 22:15:22 +08:00
|
|
|
|
*/
|
|
|
|
|
|
async loadScene(sceneUrl: string): Promise<void> {
|
|
|
|
|
|
if (!this._runtime) {
|
|
|
|
|
|
throw new Error('Runtime not initialized. Call initialize() first.');
|
|
|
|
|
|
}
|
2025-12-16 12:46:14 +08:00
|
|
|
|
|
|
|
|
|
|
// Use scene manager if available for proper tracking
|
|
|
|
|
|
// 如果可用,使用场景管理器进行正确跟踪
|
|
|
|
|
|
if (this._sceneManager) {
|
|
|
|
|
|
await this._sceneManager.loadSceneByPath(sceneUrl);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await this._runtime.loadSceneFromUrl(sceneUrl);
|
|
|
|
|
|
}
|
2025-12-03 22:15:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 18:23:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Load a scene from data object (for single-file mode)
|
|
|
|
|
|
* 从数据对象加载场景(用于单文件模式)
|
|
|
|
|
|
*/
|
|
|
|
|
|
async loadSceneFromData(sceneData: unknown): Promise<void> {
|
|
|
|
|
|
if (!this._runtime) {
|
|
|
|
|
|
throw new Error('Runtime not initialized. Call initialize() first.');
|
|
|
|
|
|
}
|
|
|
|
|
|
await this._runtime.loadSceneFromData(sceneData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-03 22:15:22 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-16 12:46:14 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Get the scene manager
|
|
|
|
|
|
* 获取场景管理器
|
|
|
|
|
|
*
|
|
|
|
|
|
* Use this to load scenes, check available scenes, and listen to scene events.
|
|
|
|
|
|
* 使用它来加载场景、检查可用场景和监听场景事件。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* // Load a scene by name
|
|
|
|
|
|
* await runtime.sceneManager?.loadScene('Level1');
|
|
|
|
|
|
*
|
|
|
|
|
|
* // Get list of available scenes
|
|
|
|
|
|
* const scenes = runtime.sceneManager?.availableScenes;
|
|
|
|
|
|
*
|
|
|
|
|
|
* // Listen to scene load events
|
|
|
|
|
|
* runtime.sceneManager?.onLoadComplete((sceneName) => {
|
|
|
|
|
|
* console.log(`Scene loaded: ${sceneName}`);
|
|
|
|
|
|
* });
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns The scene manager instance, or null if not initialized
|
|
|
|
|
|
*/
|
|
|
|
|
|
get sceneManager(): IRuntimeSceneManager | null {
|
|
|
|
|
|
return this._sceneManager;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-03 22:15:22 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 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
|
|
|
|
|
|
};
|