Feature/runtime cdn and plugin loader (#240)
* feat(ui): 完善 UI 布局系统和编辑器可视化工具 * refactor: 移除 ModuleRegistry,统一使用 PluginManager 插件系统 * fix: 修复 CodeQL 警告并提升测试覆盖率 * refactor: 分离运行时入口点,解决 runtime bundle 包含 React 的问题 * fix(ci): 添加 editor-core 和 editor-runtime 到 CI 依赖构建步骤 * docs: 完善 ServiceContainer 文档,新增 Symbol.for 模式和 @InjectProperty 说明 * fix(ci): 修复 type-check 失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): behavior-tree 构建添加 @tauri-apps 外部依赖 * fix(ci): behavior-tree 添加 @tauri-apps/plugin-fs 类型依赖 * fix(ci): platform-web 添加缺失的 behavior-tree 依赖 * fix(lint): 移除正则表达式中不必要的转义字符
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Editor Appearance Plugin
|
||||
* 编辑器外观插件
|
||||
*/
|
||||
|
||||
import type { ServiceContainer } from '@esengine/ecs-framework';
|
||||
import { createLogger } from '@esengine/ecs-framework';
|
||||
import type { IPluginLoader, IEditorModuleLoader, PluginDescriptor } from '@esengine/editor-core';
|
||||
import { SettingsRegistry } from '@esengine/editor-core';
|
||||
import { SettingsService } from '../../services/SettingsService';
|
||||
|
||||
const logger = createLogger('EditorAppearancePlugin');
|
||||
|
||||
/**
|
||||
* Editor Appearance 编辑器模块
|
||||
*/
|
||||
class EditorAppearanceEditorModule implements IEditorModuleLoader {
|
||||
async install(services: ServiceContainer): Promise<void> {
|
||||
const settingsRegistry = services.resolve(SettingsRegistry);
|
||||
|
||||
settingsRegistry.registerCategory({
|
||||
id: 'appearance',
|
||||
title: '外观',
|
||||
description: '配置编辑器的外观设置',
|
||||
sections: [
|
||||
{
|
||||
id: 'font',
|
||||
title: '字体设置',
|
||||
description: '配置编辑器字体样式',
|
||||
settings: [
|
||||
{
|
||||
key: 'editor.fontSize',
|
||||
label: '字体大小 (px)',
|
||||
type: 'range',
|
||||
defaultValue: 13,
|
||||
description: '编辑器界面的字体大小',
|
||||
min: 11,
|
||||
max: 18,
|
||||
step: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'inspector',
|
||||
title: '检视器设置',
|
||||
description: '配置属性检视器显示',
|
||||
settings: [
|
||||
{
|
||||
key: 'inspector.decimalPlaces',
|
||||
label: '数字小数位数',
|
||||
type: 'number',
|
||||
defaultValue: 4,
|
||||
description: '数字类型属性显示的小数位数,设置为 -1 表示不限制',
|
||||
min: -1,
|
||||
max: 10,
|
||||
step: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this.applyFontSettings();
|
||||
this.setupSettingsListener();
|
||||
|
||||
logger.info('Installed');
|
||||
}
|
||||
|
||||
async uninstall(): Promise<void> {
|
||||
logger.info('Uninstalled');
|
||||
}
|
||||
|
||||
async onEditorReady(): Promise<void> {
|
||||
logger.info('Editor is ready');
|
||||
}
|
||||
|
||||
private applyFontSettings(): void {
|
||||
const settings = SettingsService.getInstance();
|
||||
const baseFontSize = settings.get<number>('editor.fontSize', 13);
|
||||
|
||||
logger.info(`Applying font size: ${baseFontSize}px`);
|
||||
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--font-size-xs', `${baseFontSize - 2}px`);
|
||||
root.style.setProperty('--font-size-sm', `${baseFontSize - 1}px`);
|
||||
root.style.setProperty('--font-size-base', `${baseFontSize}px`);
|
||||
root.style.setProperty('--font-size-md', `${baseFontSize + 1}px`);
|
||||
root.style.setProperty('--font-size-lg', `${baseFontSize + 3}px`);
|
||||
root.style.setProperty('--font-size-xl', `${baseFontSize + 5}px`);
|
||||
}
|
||||
|
||||
private setupSettingsListener(): void {
|
||||
window.addEventListener('settings:changed', ((event: CustomEvent) => {
|
||||
const changedSettings = event.detail;
|
||||
logger.info('Settings changed event received', changedSettings);
|
||||
|
||||
if ('editor.fontSize' in changedSettings) {
|
||||
logger.info('Font size changed, applying...');
|
||||
this.applyFontSettings();
|
||||
}
|
||||
}) as EventListener);
|
||||
}
|
||||
}
|
||||
|
||||
const descriptor: PluginDescriptor = {
|
||||
id: '@esengine/editor-appearance',
|
||||
name: 'Editor Appearance',
|
||||
version: '1.0.0',
|
||||
description: 'Configure editor appearance settings',
|
||||
category: 'tools',
|
||||
icon: 'Palette',
|
||||
enabledByDefault: true,
|
||||
canContainContent: false,
|
||||
isEnginePlugin: true,
|
||||
isCore: true,
|
||||
modules: [
|
||||
{
|
||||
name: 'EditorAppearanceEditor',
|
||||
type: 'editor',
|
||||
loadingPhase: 'earliest'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const EditorAppearancePlugin: IPluginLoader = {
|
||||
descriptor,
|
||||
editorModule: new EditorAppearanceEditorModule()
|
||||
};
|
||||
50
packages/editor-app/src/plugins/builtin/GizmoPlugin.ts
Normal file
50
packages/editor-app/src/plugins/builtin/GizmoPlugin.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Gizmo Plugin
|
||||
* Gizmo 插件
|
||||
*/
|
||||
|
||||
import type { ServiceContainer } from '@esengine/ecs-framework';
|
||||
import type { IPluginLoader, IEditorModuleLoader, PluginDescriptor, GizmoProviderRegistration } from '@esengine/editor-core';
|
||||
import { registerSpriteGizmo } from '../../gizmos';
|
||||
|
||||
/**
|
||||
* Gizmo 编辑器模块
|
||||
*/
|
||||
class GizmoEditorModule implements IEditorModuleLoader {
|
||||
async install(_services: ServiceContainer): Promise<void> {
|
||||
registerSpriteGizmo();
|
||||
}
|
||||
|
||||
async uninstall(): Promise<void> {
|
||||
// Uninstalled
|
||||
}
|
||||
|
||||
getGizmoProviders(): GizmoProviderRegistration[] {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
const descriptor: PluginDescriptor = {
|
||||
id: '@esengine/gizmo',
|
||||
name: 'Gizmo System',
|
||||
version: '1.0.0',
|
||||
description: 'Provides gizmo support for editor components',
|
||||
category: 'tools',
|
||||
icon: 'Move',
|
||||
enabledByDefault: true,
|
||||
canContainContent: false,
|
||||
isEnginePlugin: true,
|
||||
isCore: true,
|
||||
modules: [
|
||||
{
|
||||
name: 'GizmoEditor',
|
||||
type: 'editor',
|
||||
loadingPhase: 'preDefault'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const GizmoPlugin: IPluginLoader = {
|
||||
descriptor,
|
||||
editorModule: new GizmoEditorModule()
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Plugin Config Plugin
|
||||
* 插件配置插件
|
||||
*/
|
||||
|
||||
import type { ServiceContainer } from '@esengine/ecs-framework';
|
||||
import { createLogger } from '@esengine/ecs-framework';
|
||||
import type { IPluginLoader, IEditorModuleLoader, PluginDescriptor } from '@esengine/editor-core';
|
||||
import { SettingsRegistry } from '@esengine/editor-core';
|
||||
|
||||
const logger = createLogger('PluginConfigPlugin');
|
||||
|
||||
/**
|
||||
* Plugin Config 编辑器模块
|
||||
*/
|
||||
class PluginConfigEditorModule implements IEditorModuleLoader {
|
||||
async install(services: ServiceContainer): Promise<void> {
|
||||
const settingsRegistry = services.resolve(SettingsRegistry);
|
||||
|
||||
settingsRegistry.registerCategory({
|
||||
id: 'plugins',
|
||||
title: '插件',
|
||||
description: '管理项目使用的插件',
|
||||
sections: [
|
||||
{
|
||||
id: 'engine-plugins',
|
||||
title: '插件管理',
|
||||
description: '启用或禁用项目需要的插件。禁用不需要的插件可以减少打包体积。',
|
||||
settings: [
|
||||
{
|
||||
key: 'project.enabledPlugins',
|
||||
label: '',
|
||||
type: 'pluginList',
|
||||
defaultValue: [],
|
||||
description: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
logger.info('Installed');
|
||||
}
|
||||
|
||||
async uninstall(): Promise<void> {
|
||||
logger.info('Uninstalled');
|
||||
}
|
||||
|
||||
async onEditorReady(): Promise<void> {
|
||||
logger.info('Editor is ready');
|
||||
}
|
||||
}
|
||||
|
||||
const descriptor: PluginDescriptor = {
|
||||
id: '@esengine/plugin-config',
|
||||
name: 'Plugin Config',
|
||||
version: '1.0.0',
|
||||
description: 'Configure engine plugins',
|
||||
category: 'tools',
|
||||
icon: 'Package',
|
||||
enabledByDefault: true,
|
||||
canContainContent: false,
|
||||
isEnginePlugin: true,
|
||||
isCore: true,
|
||||
modules: [
|
||||
{
|
||||
name: 'PluginConfigEditor',
|
||||
type: 'editor',
|
||||
loadingPhase: 'postDefault'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const PluginConfigPlugin: IPluginLoader = {
|
||||
descriptor,
|
||||
editorModule: new PluginConfigEditorModule()
|
||||
};
|
||||
155
packages/editor-app/src/plugins/builtin/ProfilerPlugin.tsx
Normal file
155
packages/editor-app/src/plugins/builtin/ProfilerPlugin.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Profiler Plugin
|
||||
* 性能分析器插件
|
||||
*/
|
||||
|
||||
import type { ServiceContainer } from '@esengine/ecs-framework';
|
||||
import type {
|
||||
IPluginLoader,
|
||||
IEditorModuleLoader,
|
||||
PluginDescriptor,
|
||||
PanelDescriptor,
|
||||
MenuItemDescriptor
|
||||
} from '@esengine/editor-core';
|
||||
import { MessageHub, SettingsRegistry, PanelPosition } from '@esengine/editor-core';
|
||||
import { ProfilerDockPanel } from '../../components/ProfilerDockPanel';
|
||||
import { ProfilerService } from '../../services/ProfilerService';
|
||||
|
||||
/**
|
||||
* Profiler 编辑器模块
|
||||
*/
|
||||
class ProfilerEditorModule implements IEditorModuleLoader {
|
||||
private messageHub: MessageHub | null = null;
|
||||
private profilerService: ProfilerService | null = null;
|
||||
|
||||
async install(services: ServiceContainer): Promise<void> {
|
||||
this.messageHub = services.resolve(MessageHub);
|
||||
|
||||
const settingsRegistry = services.resolve(SettingsRegistry);
|
||||
settingsRegistry.registerCategory({
|
||||
id: 'profiler',
|
||||
title: '性能分析器',
|
||||
description: '配置性能分析器的行为和显示选项',
|
||||
sections: [
|
||||
{
|
||||
id: 'connection',
|
||||
title: '连接设置',
|
||||
description: '配置WebSocket服务器连接参数',
|
||||
settings: [
|
||||
{
|
||||
key: 'profiler.port',
|
||||
label: '监听端口',
|
||||
type: 'number',
|
||||
defaultValue: 8080,
|
||||
description: '性能分析器WebSocket服务器监听的端口号',
|
||||
placeholder: '8080',
|
||||
min: 1024,
|
||||
max: 65535,
|
||||
validator: {
|
||||
validate: (value: number) => value >= 1024 && value <= 65535,
|
||||
errorMessage: '端口号必须在1024到65535之间'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'profiler.autoStart',
|
||||
label: '自动启动服务器',
|
||||
type: 'boolean',
|
||||
defaultValue: true,
|
||||
description: '编辑器启动时自动启动性能分析器服务器'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'display',
|
||||
title: '显示设置',
|
||||
description: '配置性能数据的显示选项',
|
||||
settings: [
|
||||
{
|
||||
key: 'profiler.refreshInterval',
|
||||
label: '刷新间隔 (毫秒)',
|
||||
type: 'range',
|
||||
defaultValue: 100,
|
||||
description: '性能数据刷新的时间间隔',
|
||||
min: 50,
|
||||
max: 1000,
|
||||
step: 50
|
||||
},
|
||||
{
|
||||
key: 'profiler.maxDataPoints',
|
||||
label: '最大数据点数',
|
||||
type: 'number',
|
||||
defaultValue: 100,
|
||||
description: '图表中保留的最大历史数据点数量',
|
||||
min: 10,
|
||||
max: 500
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this.profilerService = new ProfilerService();
|
||||
(window as any).__PROFILER_SERVICE__ = this.profilerService;
|
||||
}
|
||||
|
||||
async uninstall(): Promise<void> {
|
||||
if (this.profilerService) {
|
||||
this.profilerService.destroy();
|
||||
this.profilerService = null;
|
||||
}
|
||||
delete (window as any).__PROFILER_SERVICE__;
|
||||
}
|
||||
|
||||
getPanels(): PanelDescriptor[] {
|
||||
return [
|
||||
{
|
||||
id: 'profiler-monitor',
|
||||
title: 'Performance Monitor',
|
||||
position: PanelPosition.Center,
|
||||
closable: false,
|
||||
component: ProfilerDockPanel,
|
||||
order: 200
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
getMenuItems(): MenuItemDescriptor[] {
|
||||
return [
|
||||
{
|
||||
id: 'window.profiler',
|
||||
label: 'Profiler',
|
||||
parentId: 'window',
|
||||
execute: () => {
|
||||
this.messageHub?.publish('ui:openWindow', { windowId: 'profiler' });
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async onEditorReady(): Promise<void> {}
|
||||
}
|
||||
|
||||
const descriptor: PluginDescriptor = {
|
||||
id: '@esengine/profiler',
|
||||
name: 'Performance Profiler',
|
||||
version: '1.0.0',
|
||||
description: 'Real-time performance monitoring for ECS systems',
|
||||
category: 'tools',
|
||||
icon: 'BarChart3',
|
||||
enabledByDefault: true,
|
||||
canContainContent: false,
|
||||
isEnginePlugin: true,
|
||||
modules: [
|
||||
{
|
||||
name: 'ProfilerEditor',
|
||||
type: 'editor',
|
||||
loadingPhase: 'postDefault',
|
||||
panels: ['profiler-monitor']
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const ProfilerPlugin: IPluginLoader = {
|
||||
descriptor,
|
||||
editorModule: new ProfilerEditorModule()
|
||||
};
|
||||
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* Project Settings Plugin
|
||||
* 项目设置插件
|
||||
*
|
||||
* Registers project-level settings like UI design resolution.
|
||||
* 注册项目级别的设置,如 UI 设计分辨率。
|
||||
*/
|
||||
|
||||
import type { ServiceContainer } from '@esengine/ecs-framework';
|
||||
import { createLogger, Core } from '@esengine/ecs-framework';
|
||||
import type { IPluginLoader, IEditorModuleLoader, PluginDescriptor } from '@esengine/editor-core';
|
||||
import { SettingsRegistry, ProjectService } from '@esengine/editor-core';
|
||||
import EngineService from '../../services/EngineService';
|
||||
|
||||
const logger = createLogger('ProjectSettingsPlugin');
|
||||
|
||||
/**
|
||||
* Common UI design resolution presets
|
||||
* 常见的 UI 设计分辨率预设
|
||||
*/
|
||||
export const UI_RESOLUTION_PRESETS = [
|
||||
{ label: '1920 x 1080 (Full HD)', value: { width: 1920, height: 1080 } },
|
||||
{ label: '1280 x 720 (HD)', value: { width: 1280, height: 720 } },
|
||||
{ label: '1366 x 768 (HD+)', value: { width: 1366, height: 768 } },
|
||||
{ label: '2560 x 1440 (2K)', value: { width: 2560, height: 1440 } },
|
||||
{ label: '3840 x 2160 (4K)', value: { width: 3840, height: 2160 } },
|
||||
{ label: '750 x 1334 (iPhone 6/7/8)', value: { width: 750, height: 1334 } },
|
||||
{ label: '1125 x 2436 (iPhone X/11/12)', value: { width: 1125, height: 2436 } },
|
||||
{ label: '1080 x 1920 (Mobile Portrait)', value: { width: 1080, height: 1920 } },
|
||||
{ label: '800 x 600 (SVGA)', value: { width: 800, height: 600 } },
|
||||
{ label: '1024 x 768 (XGA)', value: { width: 1024, height: 768 } },
|
||||
];
|
||||
|
||||
/**
|
||||
* Project Settings 编辑器模块
|
||||
*/
|
||||
class ProjectSettingsEditorModule implements IEditorModuleLoader {
|
||||
private settingsListener: ((event: Event) => void) | null = null;
|
||||
|
||||
async install(services: ServiceContainer): Promise<void> {
|
||||
const settingsRegistry = services.resolve(SettingsRegistry);
|
||||
|
||||
// Setup listener for UI design resolution changes
|
||||
this.setupSettingsListener();
|
||||
|
||||
settingsRegistry.registerCategory({
|
||||
id: 'project',
|
||||
title: '项目',
|
||||
description: '项目级别的配置',
|
||||
sections: [
|
||||
{
|
||||
id: 'ui-settings',
|
||||
title: 'UI 设置',
|
||||
description: '配置 UI 系统的基础参数',
|
||||
settings: [
|
||||
{
|
||||
key: 'project.uiDesignResolution.width',
|
||||
label: '设计宽度',
|
||||
type: 'number',
|
||||
defaultValue: 1920,
|
||||
description: 'UI 画布的设计宽度(像素)',
|
||||
min: 320,
|
||||
max: 7680,
|
||||
step: 1
|
||||
},
|
||||
{
|
||||
key: 'project.uiDesignResolution.height',
|
||||
label: '设计高度',
|
||||
type: 'number',
|
||||
defaultValue: 1080,
|
||||
description: 'UI 画布的设计高度(像素)',
|
||||
min: 240,
|
||||
max: 4320,
|
||||
step: 1
|
||||
},
|
||||
{
|
||||
key: 'project.uiDesignResolution.preset',
|
||||
label: '分辨率预设',
|
||||
type: 'select',
|
||||
defaultValue: '1920x1080',
|
||||
description: '选择常见的分辨率预设',
|
||||
options: UI_RESOLUTION_PRESETS.map(p => ({
|
||||
label: p.label,
|
||||
value: `${p.value.width}x${p.value.height}`
|
||||
}))
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
logger.info('Installed');
|
||||
}
|
||||
|
||||
async uninstall(): Promise<void> {
|
||||
// Remove settings listener
|
||||
if (this.settingsListener) {
|
||||
window.removeEventListener('settings:changed', this.settingsListener);
|
||||
this.settingsListener = null;
|
||||
}
|
||||
logger.info('Uninstalled');
|
||||
}
|
||||
|
||||
async onEditorReady(): Promise<void> {
|
||||
logger.info('Editor is ready');
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup listener for settings changes
|
||||
* 设置设置变化监听器
|
||||
*/
|
||||
private setupSettingsListener(): void {
|
||||
this.settingsListener = ((event: CustomEvent) => {
|
||||
const changedSettings = event.detail;
|
||||
|
||||
// Check if UI design resolution changed
|
||||
if ('project.uiDesignResolution.width' in changedSettings ||
|
||||
'project.uiDesignResolution.height' in changedSettings ||
|
||||
'project.uiDesignResolution.preset' in changedSettings) {
|
||||
|
||||
logger.info('UI design resolution changed, applying...');
|
||||
this.applyUIDesignResolution();
|
||||
}
|
||||
}) as EventListener;
|
||||
|
||||
window.addEventListener('settings:changed', this.settingsListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply UI design resolution from ProjectService
|
||||
* 从 ProjectService 应用 UI 设计分辨率
|
||||
*/
|
||||
private applyUIDesignResolution(): void {
|
||||
const projectService = Core.services.tryResolve<ProjectService>(ProjectService);
|
||||
if (!projectService) {
|
||||
logger.warn('ProjectService not available');
|
||||
return;
|
||||
}
|
||||
|
||||
const resolution = projectService.getUIDesignResolution();
|
||||
const engineService = EngineService.getInstance();
|
||||
|
||||
if (engineService.isInitialized()) {
|
||||
engineService.setUICanvasSize(resolution.width, resolution.height);
|
||||
logger.info(`Applied UI design resolution: ${resolution.width}x${resolution.height}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const descriptor: PluginDescriptor = {
|
||||
id: '@esengine/project-settings',
|
||||
name: 'Project Settings',
|
||||
version: '1.0.0',
|
||||
description: 'Configure project-level settings',
|
||||
category: 'tools',
|
||||
icon: 'Settings',
|
||||
enabledByDefault: true,
|
||||
canContainContent: false,
|
||||
isEnginePlugin: true,
|
||||
isCore: true,
|
||||
modules: [
|
||||
{
|
||||
name: 'ProjectSettingsEditor',
|
||||
type: 'editor',
|
||||
loadingPhase: 'postDefault'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const ProjectSettingsPlugin: IPluginLoader = {
|
||||
descriptor,
|
||||
editorModule: new ProjectSettingsEditorModule()
|
||||
};
|
||||
199
packages/editor-app/src/plugins/builtin/SceneInspectorPlugin.ts
Normal file
199
packages/editor-app/src/plugins/builtin/SceneInspectorPlugin.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Scene Inspector Plugin
|
||||
* 场景检视器插件
|
||||
*/
|
||||
|
||||
import { Core, Entity } from '@esengine/ecs-framework';
|
||||
import type { ServiceContainer } from '@esengine/ecs-framework';
|
||||
import type {
|
||||
IPluginLoader,
|
||||
IEditorModuleLoader,
|
||||
PluginDescriptor,
|
||||
PanelDescriptor,
|
||||
MenuItemDescriptor,
|
||||
ToolbarItemDescriptor,
|
||||
EntityCreationTemplate
|
||||
} from '@esengine/editor-core';
|
||||
import { PanelPosition, EntityStoreService, MessageHub } from '@esengine/editor-core';
|
||||
import { TransformComponent, SpriteComponent, SpriteAnimatorComponent, CameraComponent } from '@esengine/ecs-components';
|
||||
|
||||
/**
|
||||
* Scene Inspector 编辑器模块
|
||||
*/
|
||||
class SceneInspectorEditorModule implements IEditorModuleLoader {
|
||||
async install(_services: ServiceContainer): Promise<void> {
|
||||
// Installed
|
||||
}
|
||||
|
||||
async uninstall(): Promise<void> {
|
||||
// Uninstalled
|
||||
}
|
||||
|
||||
getPanels(): PanelDescriptor[] {
|
||||
return [
|
||||
{
|
||||
id: 'panel-scene-hierarchy',
|
||||
title: 'Scene Hierarchy',
|
||||
position: PanelPosition.Left,
|
||||
defaultSize: 250,
|
||||
resizable: true,
|
||||
closable: false,
|
||||
icon: 'List',
|
||||
order: 10
|
||||
},
|
||||
{
|
||||
id: 'panel-entity-inspector',
|
||||
title: 'Entity Inspector',
|
||||
position: PanelPosition.Right,
|
||||
defaultSize: 300,
|
||||
resizable: true,
|
||||
closable: false,
|
||||
icon: 'Search',
|
||||
order: 10
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
getMenuItems(): MenuItemDescriptor[] {
|
||||
return [
|
||||
{
|
||||
id: 'view-scene-inspector',
|
||||
label: 'Scene Inspector',
|
||||
parentId: 'view',
|
||||
shortcut: 'Ctrl+Shift+I'
|
||||
},
|
||||
{
|
||||
id: 'scene-create-entity',
|
||||
label: 'Create Entity',
|
||||
parentId: 'scene',
|
||||
shortcut: 'Ctrl+N'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
getToolbarItems(): ToolbarItemDescriptor[] {
|
||||
return [
|
||||
{
|
||||
id: 'toolbar-create-entity',
|
||||
label: 'New Entity',
|
||||
icon: 'Plus',
|
||||
tooltip: 'Create new entity',
|
||||
execute: () => {}
|
||||
},
|
||||
{
|
||||
id: 'toolbar-delete-entity',
|
||||
label: 'Delete Entity',
|
||||
icon: 'Trash2',
|
||||
tooltip: 'Delete selected entity',
|
||||
execute: () => {}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
getEntityCreationTemplates(): EntityCreationTemplate[] {
|
||||
return [
|
||||
// Sprite
|
||||
{
|
||||
id: 'create-sprite-entity',
|
||||
label: 'Sprite',
|
||||
icon: 'Image',
|
||||
category: 'rendering',
|
||||
order: 10,
|
||||
create: (): number => {
|
||||
return this.createEntity('Sprite', (entity) => {
|
||||
entity.addComponent(new TransformComponent());
|
||||
entity.addComponent(new SpriteComponent());
|
||||
});
|
||||
}
|
||||
},
|
||||
// Animated Sprite
|
||||
{
|
||||
id: 'create-animated-sprite-entity',
|
||||
label: '动画 Sprite',
|
||||
icon: 'Film',
|
||||
category: 'rendering',
|
||||
order: 11,
|
||||
create: (): number => {
|
||||
return this.createEntity('AnimatedSprite', (entity) => {
|
||||
entity.addComponent(new TransformComponent());
|
||||
entity.addComponent(new SpriteAnimatorComponent());
|
||||
});
|
||||
}
|
||||
},
|
||||
// Camera
|
||||
{
|
||||
id: 'create-camera-entity',
|
||||
label: '相机',
|
||||
icon: 'Camera',
|
||||
category: 'rendering',
|
||||
order: 12,
|
||||
create: (): number => {
|
||||
return this.createEntity('Camera', (entity) => {
|
||||
entity.addComponent(new TransformComponent());
|
||||
entity.addComponent(new CameraComponent());
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
private createEntity(baseName: string, setupFn: (entity: Entity) => void): number {
|
||||
const scene = Core.scene;
|
||||
if (!scene) {
|
||||
throw new Error('Scene not available');
|
||||
}
|
||||
|
||||
const entityStore = Core.services.resolve(EntityStoreService);
|
||||
const messageHub = Core.services.resolve(MessageHub);
|
||||
|
||||
if (!entityStore || !messageHub) {
|
||||
throw new Error('EntityStoreService or MessageHub not available');
|
||||
}
|
||||
|
||||
// 计数已有同类实体
|
||||
const count = entityStore.getAllEntities()
|
||||
.filter((e: Entity) => e.name.startsWith(`${baseName} `)).length;
|
||||
const entityName = `${baseName} ${count + 1}`;
|
||||
|
||||
const entity = scene.createEntity(entityName);
|
||||
setupFn(entity);
|
||||
|
||||
entityStore.addEntity(entity);
|
||||
messageHub.publish('entity:added', { entity });
|
||||
messageHub.publish('scene:modified', {});
|
||||
entityStore.selectEntity(entity);
|
||||
|
||||
return entity.id;
|
||||
}
|
||||
|
||||
async onEditorReady(): Promise<void> {}
|
||||
async onProjectOpen(_projectPath: string): Promise<void> {}
|
||||
async onProjectClose(): Promise<void> {}
|
||||
}
|
||||
|
||||
const descriptor: PluginDescriptor = {
|
||||
id: '@esengine/scene-inspector',
|
||||
name: 'Scene Inspector',
|
||||
version: '1.0.0',
|
||||
description: 'Scene hierarchy and entity inspector',
|
||||
category: 'tools',
|
||||
icon: 'Search',
|
||||
enabledByDefault: true,
|
||||
canContainContent: false,
|
||||
isEnginePlugin: true,
|
||||
isCore: true,
|
||||
modules: [
|
||||
{
|
||||
name: 'SceneInspectorEditor',
|
||||
type: 'editor',
|
||||
loadingPhase: 'default',
|
||||
panels: ['panel-scene-hierarchy', 'panel-entity-inspector'],
|
||||
inspectors: ['EntityInspector']
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const SceneInspectorPlugin: IPluginLoader = {
|
||||
descriptor,
|
||||
editorModule: new SceneInspectorEditorModule()
|
||||
};
|
||||
11
packages/editor-app/src/plugins/builtin/index.ts
Normal file
11
packages/editor-app/src/plugins/builtin/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 内置插件
|
||||
* Built-in plugins
|
||||
*/
|
||||
|
||||
export { GizmoPlugin } from './GizmoPlugin';
|
||||
export { SceneInspectorPlugin } from './SceneInspectorPlugin';
|
||||
export { ProfilerPlugin } from './ProfilerPlugin';
|
||||
export { EditorAppearancePlugin } from './EditorAppearancePlugin';
|
||||
export { PluginConfigPlugin } from './PluginConfigPlugin';
|
||||
export { ProjectSettingsPlugin } from './ProjectSettingsPlugin';
|
||||
Reference in New Issue
Block a user