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 扩展名
This commit is contained in:
YHH
2025-12-04 22:43:26 +08:00
committed by GitHub
parent 0d9bab910e
commit d7454e3ca4
16 changed files with 393 additions and 40 deletions

View File

@@ -375,7 +375,15 @@ export class Entity {
if (this.scene.referenceTracker) {
this.scene.referenceTracker.registerEntityScene(this.id, this.scene);
}
// 编辑器模式下延迟执行 onAddedToEntity | Defer onAddedToEntity in editor mode
if (this.scene.isEditorMode) {
this.scene.queueDeferredComponentCallback(() => {
component.onAddedToEntity();
});
} else {
component.onAddedToEntity();
}
if (this.scene && this.scene.eventSystem) {
this.scene.eventSystem.emitSync('component:added', {

View File

@@ -78,6 +78,18 @@ export interface IScene {
*/
readonly services: ServiceContainer;
/**
* 编辑器模式标志
*
* 当为 true 时,组件的生命周期回调(如 onAddedToEntity会被延迟
* 直到调用 begin() 开始运行场景时才会触发。
*
* Editor mode flag.
* When true, component lifecycle callbacks (like onAddedToEntity) are deferred
* until begin() is called to start running the scene.
*/
isEditorMode: boolean;
/**
* 获取系统列表
*/
@@ -98,6 +110,15 @@ export interface IScene {
*/
unload(): void;
/**
* 添加延迟的组件生命周期回调
*
* Queue a deferred component lifecycle callback.
*
* @param callback 要延迟执行的回调 | The callback to defer
*/
queueDeferredComponentCallback(callback: () => void): void;
/**
* 开始场景
*/

View File

@@ -117,6 +117,30 @@ export class Scene implements IScene {
*/
private _didSceneBegin: boolean = false;
/**
* 编辑器模式标志
*
* 当为 true 时,组件的生命周期回调(如 onAddedToEntity会被延迟
* 直到调用 begin() 开始运行场景时才会触发。
*
* Editor mode flag.
* When true, component lifecycle callbacks (like onAddedToEntity) are deferred
* until begin() is called to start running the scene.
*/
public isEditorMode: boolean = false;
/**
* 延迟的组件生命周期回调队列
*
* 在编辑器模式下,组件的 onAddedToEntity 回调会被加入此队列,
* 等到 begin() 调用时统一执行。
*
* Deferred component lifecycle callback queue.
* In editor mode, component's onAddedToEntity callbacks are queued here,
* and will be executed when begin() is called.
*/
private _deferredComponentCallbacks: Array<() => void> = [];
/**
* 系统列表缓存
*/
@@ -319,14 +343,47 @@ export class Scene implements IScene {
*/
public unload(): void {}
/**
* 添加延迟的组件生命周期回调
*
* 在编辑器模式下,组件的 onAddedToEntity 回调会通过此方法加入队列。
*
* Queue a deferred component lifecycle callback.
* In editor mode, component's onAddedToEntity callbacks are queued via this method.
*
* @param callback 要延迟执行的回调 | The callback to defer
*/
public queueDeferredComponentCallback(callback: () => void): void {
this._deferredComponentCallbacks.push(callback);
}
/**
* 开始场景,启动实体处理器等
*
* 这个方法会启动场景。它将启动实体处理器等并调用onStart方法。
* 在编辑器模式下,此方法还会执行所有延迟的组件生命周期回调。
*
* This method starts the scene. It will start entity processors and call onStart.
* In editor mode, this method also executes all deferred component lifecycle callbacks.
*/
public begin() {
// 标记场景已开始运行并调用onStart方法
// 标记场景已开始运行
this._didSceneBegin = true;
// 执行所有延迟的组件生命周期回调 | Execute all deferred component lifecycle callbacks
if (this._deferredComponentCallbacks.length > 0) {
for (const callback of this._deferredComponentCallbacks) {
try {
callback();
} catch (error) {
this.logger.error('Error executing deferred component callback:', error);
}
}
// 清空队列 | Clear the queue
this._deferredComponentCallbacks = [];
}
// 调用onStart方法
this.onStart();
}

View File

@@ -598,6 +598,28 @@ export class EngineBridge implements IEngineBridge {
this.getEngine().setShowGizmos(show);
}
/**
* Set editor mode.
* 设置编辑器模式。
*
* When false (runtime mode), editor-only UI like grid, gizmos,
* and axis indicator are automatically hidden.
* 当为 false运行时模式编辑器专用 UI 会自动隐藏。
*/
setEditorMode(isEditor: boolean): void {
if (!this.initialized) return;
this.getEngine().setEditorMode(isEditor);
}
/**
* Get editor mode.
* 获取编辑器模式。
*/
isEditorMode(): boolean {
if (!this.initialized) return true;
return this.getEngine().isEditorMode();
}
// ===== Multi-viewport API =====
// ===== 多视口 API =====

View File

@@ -117,6 +117,11 @@ export class GameEngine {
* The shader ID for referencing this shader | 用于引用此着色器的ID
*/
compileShader(vertex_source: string, fragment_source: string): number;
/**
* Get editor mode.
* 获取编辑器模式。
*/
isEditorMode(): boolean;
/**
* Render sprites as overlay (without clearing screen).
* 渲染精灵作为叠加层(不清除屏幕)。
@@ -156,6 +161,15 @@ export class GameEngine {
* * `r`, `g`, `b`, `a` - Color components (0.0-1.0) | 颜色分量 (0.0-1.0)
*/
setClearColor(r: number, g: number, b: number, a: number): void;
/**
* Set editor mode.
* 设置编辑器模式。
*
* When false (runtime mode), editor-only UI like grid, gizmos,
* and axis indicator are automatically hidden.
* 当为 false运行时模式编辑器专用 UI如网格、gizmos、坐标轴指示器会自动隐藏。
*/
setEditorMode(is_editor: boolean): void;
/**
* Set gizmo visibility.
* 设置辅助工具可见性。
@@ -374,6 +388,7 @@ export interface InitOutput {
readonly gameengine_hasMaterial: (a: number, b: number) => number;
readonly gameengine_hasShader: (a: number, b: number) => number;
readonly gameengine_height: (a: number) => number;
readonly gameengine_isEditorMode: (a: number) => number;
readonly gameengine_isKeyDown: (a: number, b: number, c: number) => number;
readonly gameengine_loadTexture: (a: number, b: number, c: number, d: number) => [number, number];
readonly gameengine_loadTextureByPath: (a: number, b: number, c: number) => [number, number, number];
@@ -389,6 +404,7 @@ export interface InitOutput {
readonly gameengine_setActiveViewport: (a: number, b: number, c: number) => number;
readonly gameengine_setCamera: (a: number, b: number, c: number, d: number, e: number) => void;
readonly gameengine_setClearColor: (a: number, b: number, c: number, d: number, e: number) => void;
readonly gameengine_setEditorMode: (a: number, b: number) => void;
readonly gameengine_setMaterialBlendMode: (a: number, b: number, c: number) => number;
readonly gameengine_setMaterialColor: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => number;
readonly gameengine_setMaterialFloat: (a: number, b: number, c: number, d: number, e: number) => number;

View File

@@ -475,14 +475,28 @@ fn update_tsconfig_file(
// Check for index.d.ts
// 检查是否存在 index.d.ts
let dts_path = module_path.join("index.d.ts");
if dts_path.exists() {
let module_name = format!("@esengine/{}", module_id);
if !dts_path.exists() {
continue;
}
// Read module.json to get the actual package name
// 读取 module.json 获取实际的包名
let module_json_path = module_path.join("module.json");
let module_name = if module_json_path.exists() {
fs::read_to_string(&module_json_path)
.ok()
.and_then(|content| serde_json::from_str::<serde_json::Value>(&content).ok())
.and_then(|json| json.get("name").and_then(|n| n.as_str()).map(|s| s.to_string()))
.unwrap_or_else(|| format!("@esengine/{}", module_id))
} else {
format!("@esengine/{}", module_id)
};
let dts_path_str = format!("{}/{}/index.d.ts", engine_path_normalized, module_id);
paths.insert(module_name, serde_json::json!([dts_path_str]));
module_count += 1;
}
}
}
println!("[update_tsconfig_file] Found {} modules with type definitions", module_count);

View File

@@ -248,14 +248,20 @@ export class ${className} extends Component {
@Property({ type: 'number', label: 'Example Property' })
public exampleProperty: number = 0;
onInitialize(): void {
// 组件初始化时调用
// Called when component is initialized
/**
* 组件添加到实体时调用
* Called when component is added to entity
*/
onAddedToEntity(): void {
console.log('${className} added to entity');
}
onDestroy(): void {
// 组件销毁时调用
// Called when component is destroyed
/**
* 组件从实体移除时调用
* Called when component is removed from entity
*/
onRemovedFromEntity(): void {
console.log('${className} removed from entity');
}
}
`;

View File

@@ -25,8 +25,12 @@ import type { ModuleManifest } from '../services/RuntimeResolver';
*
* This matches the structure of published builds for consistency
* 这与发布构建的结构一致
*
* @param importMap - Import map for module resolution
* @param modules - Module manifests for plugin loading
* @param hasUserRuntime - Whether user-runtime.js exists and should be loaded
*/
function generateRuntimeHtml(importMap: Record<string, string>, modules: ModuleManifest[]): string {
function generateRuntimeHtml(importMap: Record<string, string>, modules: ModuleManifest[], hasUserRuntime: boolean = false): string {
const importMapScript = `<script type="importmap">
${JSON.stringify({ imports: importMap }, null, 2).split('\n').join('\n ')}
</script>`;
@@ -45,6 +49,44 @@ function generateRuntimeHtml(importMap: Record<string, string>, modules: ModuleM
}`
).join('\n');
// Generate user runtime loading code
// 生成用户运行时加载代码
const userRuntimeCode = hasUserRuntime ? `
updateLoading('Loading user scripts...');
try {
// Import ECS framework and set up global for user-runtime.js shim
// 导入 ECS 框架并为 user-runtime.js 设置全局变量
const ecsFramework = await import('@esengine/ecs-framework');
window.__ESENGINE__ = window.__ESENGINE__ || {};
window.__ESENGINE__.ecsFramework = ecsFramework;
// Load user-runtime.js which contains compiled user components
// 加载 user-runtime.js其中包含编译的用户组件
const userRuntimeScript = document.createElement('script');
userRuntimeScript.src = './user-runtime.js?_=' + Date.now();
await new Promise((resolve, reject) => {
userRuntimeScript.onload = resolve;
userRuntimeScript.onerror = reject;
document.head.appendChild(userRuntimeScript);
});
// Register user components to ComponentRegistry
// 将用户组件注册到 ComponentRegistry
if (window.__USER_RUNTIME_EXPORTS__) {
const { ComponentRegistry, Component } = ecsFramework;
const exports = window.__USER_RUNTIME_EXPORTS__;
for (const [name, exported] of Object.entries(exports)) {
if (typeof exported === 'function' && exported.prototype instanceof Component) {
ComponentRegistry.register(exported);
console.log('[Preview] Registered user component:', name);
}
}
}
} catch (e) {
console.warn('[Preview] Failed to load user scripts:', e.message);
}
` : '';
return `<!DOCTYPE html>
<html lang="en">
<head>
@@ -136,7 +178,7 @@ ${importMapScript}
${pluginImportCode}
await runtime.initialize(wasmModule);
${userRuntimeCode}
updateLoading('Loading scene...');
await runtime.loadScene('./scene.json?_=' + Date.now());
@@ -681,9 +723,9 @@ export function Viewport({ locale = 'en', messageHub }: ViewportProps) {
// Save editor camera state
editorCameraRef.current = { x: camera2DOffset.x, y: camera2DOffset.y, zoom: camera2DZoom };
setPlayState('playing');
// Hide grid and gizmos in play mode
EngineService.getInstance().setShowGrid(false);
EngineService.getInstance().setShowGizmos(false);
// Disable editor mode (hides grid, gizmos, axis indicator)
// 禁用编辑器模式隐藏网格、gizmos、坐标轴指示器
EngineService.getInstance().setEditorMode(false);
// Switch to player camera
syncPlayerCamera();
engine.start();
@@ -708,9 +750,9 @@ export function Viewport({ locale = 'en', messageHub }: ViewportProps) {
// Restore editor camera state
setCamera2DOffset({ x: editorCameraRef.current.x, y: editorCameraRef.current.y });
setCamera2DZoom(editorCameraRef.current.zoom);
// Restore grid and gizmos
EngineService.getInstance().setShowGrid(showGrid);
EngineService.getInstance().setShowGizmos(showGizmos);
// Restore editor mode (restores grid, gizmos, axis indicator based on settings)
// 恢复编辑器模式根据设置恢复网格、gizmos、坐标轴指示器
EngineService.getInstance().setEditorMode(true);
// Restore editor default background color
EngineService.getInstance().setClearColor(0.1, 0.1, 0.12, 1.0);
};
@@ -888,8 +930,21 @@ export function Viewport({ locale = 'en', messageHub }: ViewportProps) {
await TauriAPI.writeFileContent(`${runtimeDir}/asset-catalog.json`, JSON.stringify(assetCatalog, null, 2));
console.log(`[Viewport] Asset catalog created with ${Object.keys(catalogEntries).length} entries`);
// Copy user-runtime.js if it exists
// 如果存在用户运行时,复制 user-runtime.js
let hasUserRuntime = false;
if (projectPath) {
const userRuntimePath = `${projectPath}\\.esengine\\compiled\\user-runtime.js`;
const userRuntimeExists = await TauriAPI.pathExists(userRuntimePath);
if (userRuntimeExists) {
await TauriAPI.copyFile(userRuntimePath, `${runtimeDir}\\user-runtime.js`);
console.log('[Viewport] Copied user-runtime.js');
hasUserRuntime = true;
}
}
// Generate HTML with import maps (matching published build structure)
const runtimeHtml = generateRuntimeHtml(importMap, modules);
const runtimeHtml = generateRuntimeHtml(importMap, modules, hasUserRuntime);
await TauriAPI.writeFileContent(`${runtimeDir}/index.html`, runtimeHtml);
// Start local server and open browser
@@ -954,10 +1009,26 @@ export function Viewport({ locale = 'en', messageHub }: ViewportProps) {
}
}
// Write scene data and HTML with import maps
// Write scene data
const sceneDataStr = typeof sceneData === 'string' ? sceneData : new TextDecoder().decode(sceneData);
await TauriAPI.writeFileContent(`${runtimeDir}/scene.json`, sceneDataStr);
await TauriAPI.writeFileContent(`${runtimeDir}/index.html`, generateRuntimeHtml(importMap, modules));
// Copy user-runtime.js if it exists
// 如果存在用户运行时,复制 user-runtime.js
let hasUserRuntime = false;
const currentProject = projectService?.getCurrentProject();
if (currentProject?.path) {
const userRuntimePath = `${currentProject.path}\\.esengine\\compiled\\user-runtime.js`;
const userRuntimeExists = await TauriAPI.pathExists(userRuntimePath);
if (userRuntimeExists) {
await TauriAPI.copyFile(userRuntimePath, `${runtimeDir}\\user-runtime.js`);
console.log('[Viewport] Copied user-runtime.js for device preview');
hasUserRuntime = true;
}
}
// Write HTML with import maps
await TauriAPI.writeFileContent(`${runtimeDir}/index.html`, generateRuntimeHtml(importMap, modules, hasUserRuntime));
// Copy textures referenced in scene
const assetsDir = `${runtimeDir}\\assets`;

View File

@@ -612,6 +612,26 @@ export class EngineService {
return this._runtime?.renderSystem?.getShowGizmos() ?? true;
}
/**
* Set editor mode.
* 设置编辑器模式。
*
* When false (runtime mode), editor-only UI like grid, gizmos,
* and axis indicator are automatically hidden.
* 当为 false运行时模式编辑器专用 UI 会自动隐藏。
*/
setEditorMode(isEditor: boolean): void {
this._runtime?.setEditorMode(isEditor);
}
/**
* Get editor mode.
* 获取编辑器模式。
*/
isEditorMode(): boolean {
return this._runtime?.isEditorMode() ?? true;
}
/**
* Set UI canvas size for boundary display.
*/

View File

@@ -213,6 +213,15 @@ function copyEngineModulesPlugin(): Plugin {
if (fs.existsSync(sourceMapPath)) {
fs.copyFileSync(sourceMapPath, path.join(moduleOutputDir, 'index.js.map'));
}
// Copy type definitions if exists
// 复制类型定义文件(如果存在)
// Handle both .js and .mjs extensions
// 处理 .js 和 .mjs 两种扩展名
const distDir = path.dirname(module.distPath);
const dtsPath = path.join(distDir, 'index.d.ts');
if (fs.existsSync(dtsPath)) {
fs.copyFileSync(dtsPath, path.join(moduleOutputDir, 'index.d.ts'));
}
hasRuntime = true;
// Copy additional included files (e.g., chunks)

View File

@@ -62,6 +62,10 @@ export class SceneManagerService implements IService {
throw new Error('No active scene');
}
// 确保编辑器模式下设置 isEditorMode延迟组件生命周期回调
// Ensure isEditorMode is set in editor to defer component lifecycle callbacks
scene.isEditorMode = true;
// 只移除实体,保留系统(系统由模块管理)
// Only remove entities, preserve systems (systems managed by modules)
scene.entities.removeAllEntities();
@@ -117,6 +121,11 @@ export class SceneManagerService implements IService {
if (!scene) {
throw new Error('No active scene');
}
// 确保编辑器模式下设置 isEditorMode延迟组件生命周期回调
// Ensure isEditorMode is set in editor to defer component lifecycle callbacks
scene.isEditorMode = true;
scene.deserialize(jsonData, {
strategy: 'replace'
});

View File

@@ -7,7 +7,7 @@
*/
import type { IService } from '@esengine/ecs-framework';
import { Injectable, createLogger, PlatformDetector } from '@esengine/ecs-framework';
import { Injectable, createLogger, PlatformDetector, ComponentRegistry as CoreComponentRegistry } from '@esengine/ecs-framework';
import type {
IUserCodeService,
UserScriptInfo,
@@ -333,9 +333,23 @@ export class UserCodeService implements IService, IUserCodeService {
if (this._isComponentClass(exported)) {
logger.debug(`Found component: ${name} | 发现组件: ${name}`);
// Register with ComponentRegistry if provided | 如果提供了 ComponentRegistry 则注册
// ComponentRegistry expects ComponentTypeInfo object, not the class directly
// ComponentRegistry 期望 ComponentTypeInfo 对象,而不是直接传入类
// Register with Core ComponentRegistry for serialization/deserialization
// 注册到核心 ComponentRegistry 用于序列化/反序列化
try {
CoreComponentRegistry.register(exported);
// Debug: verify registration
const registeredType = CoreComponentRegistry.getComponentType(name);
if (registeredType) {
logger.info(`Component ${name} registered to core registry successfully`);
} else {
logger.warn(`Component ${name} registered but not found by name lookup`);
}
} catch (err) {
logger.warn(`Failed to register component ${name} to core registry | 注册组件 ${name} 到核心注册表失败:`, err);
}
// Register with Editor ComponentRegistry for UI display
// 注册到编辑器 ComponentRegistry 用于 UI 显示
if (componentRegistry && typeof componentRegistry.register === 'function') {
try {
componentRegistry.register({
@@ -345,7 +359,7 @@ export class UserCodeService implements IService, IUserCodeService {
description: `User component: ${name}`
});
} catch (err) {
logger.warn(`Failed to register component ${name} | 注册组件 ${name} 失败:`, err);
logger.warn(`Failed to register component ${name} to editor registry | 注册组件 ${name} 到编辑器注册表失败:`, err);
}
}

View File

@@ -77,6 +77,14 @@ pub struct Engine {
/// Whether to show gizmos.
/// 是否显示辅助工具。
show_gizmos: bool,
/// Whether the engine is running in editor mode.
/// 引擎是否在编辑器模式下运行。
///
/// When false (runtime mode), editor-only UI like grid, gizmos,
/// and axis indicator are automatically hidden.
/// 当为 false运行时模式编辑器专用 UI如网格、gizmos、坐标轴指示器会自动隐藏。
is_editor: bool,
}
impl Engine {
@@ -116,6 +124,7 @@ impl Engine {
show_grid: true,
viewport_manager: ViewportManager::new(),
show_gizmos: true,
is_editor: true, // 默认为编辑器模式 | Default to editor mode
})
}
@@ -154,6 +163,7 @@ impl Engine {
show_grid: true,
viewport_manager: ViewportManager::new(),
show_gizmos: true,
is_editor: true, // 默认为编辑器模式 | Default to editor mode
})
}
@@ -212,8 +222,9 @@ impl Engine {
let [r, g, b, a] = self.renderer.get_clear_color();
self.context.clear(r, g, b, a);
// Render grid first (background)
if self.show_grid {
// Render grid first (background) - only in editor mode
// 首先渲染网格(背景)- 仅在编辑器模式下
if self.is_editor && self.show_grid {
self.grid_renderer.render(self.context.gl(), self.renderer.camera());
self.grid_renderer.render_axes(self.context.gl(), self.renderer.camera());
}
@@ -221,8 +232,9 @@ impl Engine {
// Render sprites
self.renderer.render(self.context.gl(), &self.texture_manager)?;
// Render gizmos on top
if self.show_gizmos {
// Render gizmos on top - only in editor mode
// 在顶部渲染 gizmos - 仅在编辑器模式下
if self.is_editor && self.show_gizmos {
self.gizmo_renderer.render(self.context.gl(), self.renderer.camera());
// Render axis indicator in corner
// 在角落渲染坐标轴指示器
@@ -411,6 +423,23 @@ impl Engine {
self.show_gizmos
}
/// Set editor mode.
/// 设置编辑器模式。
///
/// When false (runtime mode), editor-only UI like grid, gizmos,
/// and axis indicator are automatically hidden regardless of their individual settings.
/// 当为 false运行时模式编辑器专用 UI如网格、gizmos、坐标轴指示器
/// 会自动隐藏,无论它们的单独设置如何。
pub fn set_editor_mode(&mut self, is_editor: bool) {
self.is_editor = is_editor;
}
/// Get editor mode.
/// 获取编辑器模式。
pub fn is_editor(&self) -> bool {
self.is_editor
}
/// Set clear color for the active viewport.
/// 设置活动视口的清除颜色。
pub fn set_clear_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
@@ -504,8 +533,9 @@ impl Engine {
renderer_camera.rotation = camera.rotation;
renderer_camera.set_viewport(camera.viewport_width(), camera.viewport_height());
// Render grid if enabled
if show_grid {
// Render grid if enabled - only in editor mode
// 渲染网格(如果启用)- 仅在编辑器模式下
if self.is_editor && show_grid {
self.grid_renderer.render(viewport.gl(), &camera);
self.grid_renderer.render_axes(viewport.gl(), &camera);
}
@@ -513,8 +543,9 @@ impl Engine {
// Render sprites
self.renderer.render(viewport.gl(), &self.texture_manager)?;
// Render gizmos if enabled
if show_gizmos {
// Render gizmos if enabled - only in editor mode
// 渲染 gizmos如果启用- 仅在编辑器模式下
if self.is_editor && show_gizmos {
self.gizmo_renderer.render(viewport.gl(), &camera);
// Render axis indicator in corner
// 在角落渲染坐标轴指示器

View File

@@ -390,6 +390,24 @@ impl GameEngine {
self.engine.set_show_gizmos(show);
}
/// Set editor mode.
/// 设置编辑器模式。
///
/// When false (runtime mode), editor-only UI like grid, gizmos,
/// and axis indicator are automatically hidden.
/// 当为 false运行时模式编辑器专用 UI如网格、gizmos、坐标轴指示器会自动隐藏。
#[wasm_bindgen(js_name = setEditorMode)]
pub fn set_editor_mode(&mut self, is_editor: bool) {
self.engine.set_editor_mode(is_editor);
}
/// Get editor mode.
/// 获取编辑器模式。
#[wasm_bindgen(js_name = isEditorMode)]
pub fn is_editor_mode(&self) -> bool {
self.engine.is_editor()
}
// ===== Multi-viewport API =====
// ===== 多视口 API =====

View File

@@ -138,9 +138,9 @@ export class BrowserRuntime {
this._runtime.assetManager.setReader(this._assetReader);
}
// Browser-specific settings (no editor UI)
this._runtime.setShowGrid(false);
this._runtime.setShowGizmos(false);
// Disable editor mode (hides grid, gizmos, axis indicator)
// 禁用编辑器模式隐藏网格、gizmos、坐标轴指示器
this._runtime.setEditorMode(false);
this._initialized = true;
console.log('[Runtime] Initialized');

View File

@@ -218,6 +218,12 @@ export class GameRuntime {
Core.setScene(this._scene);
}
// 编辑器模式下设置 isEditorMode延迟组件生命周期回调
// Set isEditorMode in editor mode to defer component lifecycle callbacks
if (this._platform.isEditorMode()) {
this._scene.isEditorMode = true;
}
// 6. 添加基础系统
this._scene.addSystem(new HierarchySystem());
this._scene.addSystem(new TransformSystem());
@@ -402,6 +408,12 @@ export class GameRuntime {
this._renderSystem.setPreviewMode(true);
}
// 调用场景 begin() 触发延迟的组件生命周期回调
// Call scene begin() to trigger deferred component lifecycle callbacks
if (this._scene) {
this._scene.begin();
}
// 启用游戏逻辑系统
this._enableGameLogicSystems();
@@ -576,6 +588,31 @@ export class GameRuntime {
}
}
/**
* 设置编辑器模式
* Set editor mode
*
* When false (runtime mode), editor-only UI like grid, gizmos,
* and axis indicator are automatically hidden.
* 当为 false运行时模式编辑器专用 UI 会自动隐藏。
*/
setEditorMode(isEditor: boolean): void {
if (this._bridge) {
this._bridge.setEditorMode(isEditor);
}
}
/**
* 获取编辑器模式
* Get editor mode
*/
isEditorMode(): boolean {
if (this._bridge) {
return this._bridge.isEditorMode();
}
return true;
}
/**
* 设置清除颜色
* Set clear color