feat: 添加跨平台运行时、资产系统和UI适配功能 (#256)
* feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
This commit is contained in:
129
packages/platform-web/src/BrowserAssetReader.ts
Normal file
129
packages/platform-web/src/BrowserAssetReader.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Browser Asset Reader
|
||||
* 浏览器资产读取器
|
||||
*
|
||||
* Implements IAssetReader interface for browser environment.
|
||||
* Uses fetch API to load assets from web server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Browser Asset Reader for loading assets via fetch API
|
||||
* 通过 fetch API 加载资产的浏览器读取器
|
||||
*/
|
||||
export class BrowserAssetReader {
|
||||
private _baseUrl: string;
|
||||
private _audioContext: AudioContext | null = null;
|
||||
|
||||
constructor(baseUrl: string = '/assets') {
|
||||
this._baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve asset path to URL
|
||||
* 将资产路径解析为 URL
|
||||
*/
|
||||
private _resolveUrl(absolutePath: string): string {
|
||||
// Handle absolute Windows paths (e.g., F:\TowerECS\assets\...)
|
||||
if (/^[A-Za-z]:[\\/]/.test(absolutePath)) {
|
||||
const normalized = absolutePath.replace(/\\/g, '/');
|
||||
const assetsIndex = normalized.toLowerCase().indexOf('/assets/');
|
||||
if (assetsIndex >= 0) {
|
||||
return `${this._baseUrl}${normalized.substring(assetsIndex + 7)}`;
|
||||
}
|
||||
const filename = normalized.split('/').pop();
|
||||
return `${this._baseUrl}/${filename}`;
|
||||
}
|
||||
|
||||
// Handle relative paths
|
||||
if (absolutePath.startsWith('./') || absolutePath.startsWith('../')) {
|
||||
return absolutePath;
|
||||
}
|
||||
if (absolutePath.startsWith('/assets/')) {
|
||||
return absolutePath;
|
||||
}
|
||||
if (absolutePath.startsWith('assets/')) {
|
||||
return `/${absolutePath}`;
|
||||
}
|
||||
return `${this._baseUrl}/${absolutePath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read text file
|
||||
* 读取文本文件
|
||||
*/
|
||||
async readText(absolutePath: string): Promise<string> {
|
||||
const url = this._resolveUrl(absolutePath);
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load text: ${url} (${response.status})`);
|
||||
}
|
||||
return response.text();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read binary file
|
||||
* 读取二进制文件
|
||||
*/
|
||||
async readBinary(absolutePath: string): Promise<ArrayBuffer> {
|
||||
const url = this._resolveUrl(absolutePath);
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load binary: ${url} (${response.status})`);
|
||||
}
|
||||
return response.arrayBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load image
|
||||
* 加载图片
|
||||
*/
|
||||
async loadImage(absolutePath: string): Promise<HTMLImageElement> {
|
||||
const url = this._resolveUrl(absolutePath);
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = () => reject(new Error(`Failed to load image: ${url}`));
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load audio
|
||||
* 加载音频
|
||||
*/
|
||||
async loadAudio(absolutePath: string): Promise<AudioBuffer> {
|
||||
if (!this._audioContext) {
|
||||
this._audioContext = new AudioContext();
|
||||
}
|
||||
const url = this._resolveUrl(absolutePath);
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load audio: ${url} (${response.status})`);
|
||||
}
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return this._audioContext.decodeAudioData(arrayBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file exists
|
||||
* 检查文件是否存在
|
||||
*/
|
||||
async exists(absolutePath: string): Promise<boolean> {
|
||||
const url = this._resolveUrl(absolutePath);
|
||||
try {
|
||||
const response = await fetch(url, { method: 'HEAD' });
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get base URL
|
||||
* 获取基础 URL
|
||||
*/
|
||||
get baseUrl(): string {
|
||||
return this._baseUrl;
|
||||
}
|
||||
}
|
||||
239
packages/platform-web/src/BrowserRuntime.ts
Normal file
239
packages/platform-web/src/BrowserRuntime.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
// Browser-specific settings (no editor UI)
|
||||
this._runtime.setShowGrid(false);
|
||||
this._runtime.setShowGizmos(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
|
||||
};
|
||||
@@ -1,29 +1,69 @@
|
||||
/**
|
||||
* @esengine/platform-web
|
||||
*
|
||||
* Web/H5 平台适配器 - 仅包含平台差异代码
|
||||
* 通用运行时逻辑在 @esengine/runtime-core
|
||||
* Web/H5 Platform Adapter
|
||||
* Web/H5 平台适配器
|
||||
*
|
||||
* Provides browser-specific implementations:
|
||||
* - BrowserRuntime: Main entry point for game builds
|
||||
* - BrowserAssetReader: Asset loading via fetch API
|
||||
* - Web subsystems: Canvas, Input, Storage, WASM
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
// Web 平台子系统
|
||||
// ============================================
|
||||
// Runtime (main entry for game builds)
|
||||
// ============================================
|
||||
export {
|
||||
BrowserRuntime,
|
||||
create,
|
||||
type RuntimeConfig
|
||||
} from './BrowserRuntime';
|
||||
|
||||
// Default export for convenient usage in game builds
|
||||
export { default } from './BrowserRuntime';
|
||||
|
||||
// Asset reader
|
||||
export { BrowserAssetReader } from './BrowserAssetReader';
|
||||
|
||||
// ============================================
|
||||
// Web Platform Subsystems
|
||||
// ============================================
|
||||
export { WebCanvasSubsystem } from './subsystems/WebCanvasSubsystem';
|
||||
export { WebInputSubsystem } from './subsystems/WebInputSubsystem';
|
||||
export { WebStorageSubsystem } from './subsystems/WebStorageSubsystem';
|
||||
export { WebWASMSubsystem } from './subsystems/WebWASMSubsystem';
|
||||
|
||||
// Web 特定系统
|
||||
// ============================================
|
||||
// Web-specific Systems
|
||||
// ============================================
|
||||
export { Canvas2DRenderSystem } from './systems/Canvas2DRenderSystem';
|
||||
|
||||
// ============================================
|
||||
// Platform Detection Utilities
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Check if running in web browser
|
||||
* 检查是否在浏览器中运行
|
||||
*/
|
||||
export function isWebPlatform(): boolean {
|
||||
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get canvas element by ID
|
||||
* 根据 ID 获取 canvas 元素
|
||||
*/
|
||||
export function getWebCanvas(canvasId: string): HTMLCanvasElement | null {
|
||||
return document.getElementById(canvasId) as HTMLCanvasElement | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new canvas element
|
||||
* 创建新的 canvas 元素
|
||||
*/
|
||||
export function createWebCanvas(width: number, height: number): HTMLCanvasElement {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
/**
|
||||
* Browser Runtime Entry Point
|
||||
* 浏览器运行时入口
|
||||
*
|
||||
* 使用统一的 GameRuntime 架构,静态导入所有插件
|
||||
* Uses the unified GameRuntime architecture with static plugin imports
|
||||
*/
|
||||
|
||||
import { Core } from '@esengine/ecs-framework';
|
||||
import {
|
||||
GameRuntime,
|
||||
createGameRuntime,
|
||||
BrowserPlatformAdapter,
|
||||
runtimePluginManager,
|
||||
BrowserFileSystemService
|
||||
} from '@esengine/runtime-core';
|
||||
|
||||
// 静态导入所有运行时插件(与编辑器保持一致)
|
||||
// Static import all runtime plugins (consistent with editor)
|
||||
import { EnginePlugin } from '@esengine/engine-core';
|
||||
import { CameraPlugin } from '@esengine/camera';
|
||||
import { SpritePlugin } from '@esengine/sprite';
|
||||
import { AudioPlugin } from '@esengine/audio';
|
||||
import { UIPlugin } from '@esengine/ui';
|
||||
import { TilemapPlugin } from '@esengine/tilemap';
|
||||
import { BehaviorTreePlugin } from '@esengine/behavior-tree';
|
||||
// 使用 runtime 子路径导入,包含 WASM 依赖
|
||||
import { PhysicsPlugin } from '@esengine/physics-rapier2d/runtime';
|
||||
|
||||
// 预注册所有插件(在 GameRuntime 初始化前)
|
||||
// Pre-register all plugins (before GameRuntime initialization)
|
||||
const ALL_PLUGINS = [
|
||||
EnginePlugin,
|
||||
CameraPlugin,
|
||||
SpritePlugin,
|
||||
AudioPlugin,
|
||||
UIPlugin,
|
||||
TilemapPlugin,
|
||||
BehaviorTreePlugin,
|
||||
PhysicsPlugin,
|
||||
];
|
||||
|
||||
// 注册并启用所有插件(浏览器运行时默认启用所有功能)
|
||||
for (const plugin of ALL_PLUGINS) {
|
||||
if (plugin) {
|
||||
runtimePluginManager.register(plugin);
|
||||
// 确保所有插件都启用(覆盖 enabledByDefault: false)
|
||||
runtimePluginManager.enable(plugin.descriptor.id);
|
||||
}
|
||||
}
|
||||
|
||||
export interface RuntimeConfig {
|
||||
canvasId: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
/** 项目配置文件 URL / Project config file URL */
|
||||
projectConfigUrl?: string;
|
||||
/** 资产目录文件 URL / Asset catalog file URL */
|
||||
assetCatalogUrl?: string;
|
||||
/** 资产基础 URL / Asset base URL */
|
||||
assetBaseUrl?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑器项目配置文件格式
|
||||
* Editor project config file format (ecs-editor.config.json)
|
||||
*/
|
||||
interface EditorProjectConfig {
|
||||
projectType?: string;
|
||||
plugins?: {
|
||||
enabledPlugins: string[];
|
||||
};
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Browser Runtime Wrapper
|
||||
* 浏览器运行时包装器
|
||||
*/
|
||||
class BrowserRuntime {
|
||||
private _runtime: GameRuntime | null = null;
|
||||
private _canvasId: string;
|
||||
private _configUrl?: string;
|
||||
private _assetCatalogUrl?: string;
|
||||
private _assetBaseUrl?: string;
|
||||
private _fileSystem: BrowserFileSystemService | null = null;
|
||||
|
||||
constructor(config: RuntimeConfig) {
|
||||
this._canvasId = config.canvasId;
|
||||
this._configUrl = config.projectConfigUrl;
|
||||
this._assetCatalogUrl = config.assetCatalogUrl ?? '/asset-catalog.json';
|
||||
this._assetBaseUrl = config.assetBaseUrl ?? '/assets';
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置文件 URL 加载插件配置
|
||||
*/
|
||||
private async _loadConfigFromUrl(): Promise<void> {
|
||||
if (!this._configUrl) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(this._configUrl);
|
||||
if (!response.ok) {
|
||||
console.warn(`[BrowserRuntime] Failed to load config from ${this._configUrl}: ${response.status}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const editorConfig: EditorProjectConfig = await response.json();
|
||||
|
||||
// 如果有插件配置,应用到 runtimePluginManager
|
||||
if (editorConfig.plugins?.enabledPlugins) {
|
||||
runtimePluginManager.loadConfig({ enabledPlugins: editorConfig.plugins.enabledPlugins });
|
||||
console.log('[BrowserRuntime] Loaded plugin config:', editorConfig.plugins.enabledPlugins);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[BrowserRuntime] Error loading config file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async initialize(wasmModule: any): Promise<void> {
|
||||
// 从配置文件加载插件配置(如果指定了 URL)
|
||||
await this._loadConfigFromUrl();
|
||||
|
||||
// 初始化浏览器文件系统服务(用于资产加载)
|
||||
// Initialize browser file system service (for asset loading)
|
||||
this._fileSystem = new BrowserFileSystemService({
|
||||
baseUrl: this._assetBaseUrl,
|
||||
catalogUrl: this._assetCatalogUrl,
|
||||
enableCache: true
|
||||
});
|
||||
await this._fileSystem.initialize();
|
||||
|
||||
// 创建浏览器平台适配器
|
||||
const platform = new BrowserPlatformAdapter({
|
||||
wasmModule: wasmModule
|
||||
});
|
||||
|
||||
// 创建统一运行时
|
||||
// 插件已经预注册了,GameRuntime 会检测到并跳过动态加载
|
||||
this._runtime = createGameRuntime({
|
||||
platform,
|
||||
canvasId: this._canvasId,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
autoStartRenderLoop: false
|
||||
});
|
||||
|
||||
await this._runtime.initialize();
|
||||
|
||||
// 注册文件系统服务到 Core.services(必须在 GameRuntime.initialize 之后,因为 Core 在那时才创建)
|
||||
// Register file system service to Core.services (must be after GameRuntime.initialize as Core is created there)
|
||||
const IFileSystemServiceKey = Symbol.for('IFileSystemService');
|
||||
if (!Core.services.isRegistered(IFileSystemServiceKey)) {
|
||||
Core.services.registerInstance(IFileSystemServiceKey, this._fileSystem);
|
||||
}
|
||||
|
||||
// 设置浏览器特定配置
|
||||
this._runtime.setShowGrid(false);
|
||||
this._runtime.setShowGizmos(false);
|
||||
}
|
||||
|
||||
async loadScene(sceneUrl: string): Promise<void> {
|
||||
if (!this._runtime) {
|
||||
throw new Error('Runtime not initialized');
|
||||
}
|
||||
await this._runtime.loadSceneFromUrl(sceneUrl);
|
||||
}
|
||||
|
||||
start(): void {
|
||||
if (!this._runtime) return;
|
||||
this._runtime.start();
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (!this._runtime) return;
|
||||
this._runtime.stop();
|
||||
}
|
||||
|
||||
handleResize(width: number, height: number): void {
|
||||
if (!this._runtime) return;
|
||||
this._runtime.resize(width, height);
|
||||
}
|
||||
|
||||
get assetManager() {
|
||||
return this._runtime?.assetManager ?? null;
|
||||
}
|
||||
|
||||
get engineIntegration() {
|
||||
return this._runtime?.engineIntegration ?? null;
|
||||
}
|
||||
|
||||
get gameRuntime(): GameRuntime | null {
|
||||
return this._runtime;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
create: (config: RuntimeConfig) => new BrowserRuntime(config),
|
||||
BrowserRuntime,
|
||||
Core,
|
||||
GameRuntime,
|
||||
createGameRuntime,
|
||||
BrowserPlatformAdapter
|
||||
};
|
||||
Reference in New Issue
Block a user