Feature/tilemap editor (#237)
* feat: 添加 Tilemap 编辑器插件和组件生命周期支持 * feat(editor-core): 添加声明式插件注册 API * feat(editor-core): 改进tiledmap结构合并tileset进tiledmapeditor * feat: 添加 editor-runtime SDK 和插件系统改进 * fix(ci): 修复SceneResourceManager里变量未使用问题
This commit is contained in:
201
packages/editor-core/src/Gizmos/GizmoRegistry.ts
Normal file
201
packages/editor-core/src/Gizmos/GizmoRegistry.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Gizmo Registry
|
||||
* Gizmo 注册表
|
||||
*
|
||||
* Manages gizmo providers for different component types.
|
||||
* Uses registry pattern instead of prototype modification for cleaner architecture.
|
||||
* 管理不同组件类型的 gizmo 提供者。
|
||||
* 使用注册表模式替代原型修改,实现更清晰的架构。
|
||||
*/
|
||||
|
||||
import type { Component, ComponentType, Entity } from '@esengine/ecs-framework';
|
||||
import type { IGizmoProvider, IGizmoRenderData } from './IGizmoProvider';
|
||||
|
||||
/**
|
||||
* Gizmo provider function type
|
||||
* Gizmo 提供者函数类型
|
||||
*
|
||||
* A function that generates gizmo data for a specific component instance.
|
||||
* 为特定组件实例生成 gizmo 数据的函数。
|
||||
*/
|
||||
export type GizmoProviderFn<T extends Component = Component> = (
|
||||
component: T,
|
||||
entity: Entity,
|
||||
isSelected: boolean
|
||||
) => IGizmoRenderData[];
|
||||
|
||||
/**
|
||||
* Gizmo Registry Service
|
||||
* Gizmo 注册表服务
|
||||
*
|
||||
* Centralized registry for component gizmo providers.
|
||||
* Allows plugins to register gizmo rendering for any component type
|
||||
* without modifying the component class itself.
|
||||
*
|
||||
* 组件 gizmo 提供者的中心化注册表。
|
||||
* 允许插件为任何组件类型注册 gizmo 渲染,
|
||||
* 而无需修改组件类本身。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Register a gizmo provider for SpriteComponent
|
||||
* GizmoRegistry.register(SpriteComponent, (sprite, entity, isSelected) => {
|
||||
* const transform = entity.getComponent(TransformComponent);
|
||||
* return [{
|
||||
* type: 'rect',
|
||||
* x: transform.position.x,
|
||||
* y: transform.position.y,
|
||||
* width: sprite.width,
|
||||
* height: sprite.height,
|
||||
* // ...
|
||||
* }];
|
||||
* });
|
||||
*
|
||||
* // Get gizmo data for a component
|
||||
* const gizmos = GizmoRegistry.getGizmoData(spriteComponent, entity, true);
|
||||
* ```
|
||||
*/
|
||||
export class GizmoRegistry {
|
||||
private static providers = new Map<ComponentType, GizmoProviderFn>();
|
||||
|
||||
/**
|
||||
* Register a gizmo provider for a component type.
|
||||
* 为组件类型注册 gizmo 提供者。
|
||||
*
|
||||
* @param componentType - The component class to register for
|
||||
* @param provider - Function that generates gizmo data
|
||||
*/
|
||||
static register<T extends Component>(
|
||||
componentType: ComponentType<T>,
|
||||
provider: GizmoProviderFn<T>
|
||||
): void {
|
||||
this.providers.set(componentType, provider as GizmoProviderFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a gizmo provider for a component type.
|
||||
* 取消注册组件类型的 gizmo 提供者。
|
||||
*
|
||||
* @param componentType - The component class to unregister
|
||||
*/
|
||||
static unregister(componentType: ComponentType): void {
|
||||
this.providers.delete(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a component type has a registered gizmo provider.
|
||||
* 检查组件类型是否有注册的 gizmo 提供者。
|
||||
*
|
||||
* @param componentType - The component class to check
|
||||
*/
|
||||
static hasProvider(componentType: ComponentType): boolean {
|
||||
return this.providers.has(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gizmo provider for a component type.
|
||||
* 获取组件类型的 gizmo 提供者。
|
||||
*
|
||||
* @param componentType - The component class
|
||||
* @returns The provider function or undefined
|
||||
*/
|
||||
static getProvider(componentType: ComponentType): GizmoProviderFn | undefined {
|
||||
return this.providers.get(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get gizmo data for a component instance.
|
||||
* 获取组件实例的 gizmo 数据。
|
||||
*
|
||||
* @param component - The component instance
|
||||
* @param entity - The entity owning the component
|
||||
* @param isSelected - Whether the entity is selected
|
||||
* @returns Array of gizmo render data, or empty array if no provider
|
||||
*/
|
||||
static getGizmoData(
|
||||
component: Component,
|
||||
entity: Entity,
|
||||
isSelected: boolean
|
||||
): IGizmoRenderData[] {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
const provider = this.providers.get(componentType);
|
||||
|
||||
if (provider) {
|
||||
try {
|
||||
return provider(component, entity, isSelected);
|
||||
} catch (e) {
|
||||
// Silently ignore errors from gizmo providers
|
||||
// 静默忽略 gizmo 提供者的错误
|
||||
console.warn(`[GizmoRegistry] Error in gizmo provider for ${componentType.name}:`, e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all gizmo data for an entity (from all components with providers).
|
||||
* 获取实体的所有 gizmo 数据(来自所有有提供者的组件)。
|
||||
*
|
||||
* @param entity - The entity to get gizmos for
|
||||
* @param isSelected - Whether the entity is selected
|
||||
* @returns Array of all gizmo render data
|
||||
*/
|
||||
static getAllGizmoDataForEntity(entity: Entity, isSelected: boolean): IGizmoRenderData[] {
|
||||
const allGizmos: IGizmoRenderData[] = [];
|
||||
|
||||
for (const component of entity.components) {
|
||||
const gizmos = this.getGizmoData(component, entity, isSelected);
|
||||
allGizmos.push(...gizmos);
|
||||
}
|
||||
|
||||
return allGizmos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an entity has any components with gizmo providers.
|
||||
* 检查实体是否有任何带有 gizmo 提供者的组件。
|
||||
*
|
||||
* @param entity - The entity to check
|
||||
*/
|
||||
static hasAnyGizmoProvider(entity: Entity): boolean {
|
||||
for (const component of entity.components) {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
if (this.providers.has(componentType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered component types.
|
||||
* 获取所有已注册的组件类型。
|
||||
*/
|
||||
static getRegisteredTypes(): ComponentType[] {
|
||||
return Array.from(this.providers.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all registered providers.
|
||||
* 清除所有已注册的提供者。
|
||||
*/
|
||||
static clear(): void {
|
||||
this.providers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter to make GizmoRegistry work with the IGizmoProvider interface.
|
||||
* 使 GizmoRegistry 与 IGizmoProvider 接口兼容的适配器。
|
||||
*
|
||||
* This allows components to optionally implement IGizmoProvider directly,
|
||||
* while also supporting the registry pattern.
|
||||
* 这允许组件可选地直接实现 IGizmoProvider,
|
||||
* 同时也支持注册表模式。
|
||||
*/
|
||||
export function isGizmoProviderRegistered(component: Component): boolean {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
return GizmoRegistry.hasProvider(componentType);
|
||||
}
|
||||
182
packages/editor-core/src/Gizmos/IGizmoProvider.ts
Normal file
182
packages/editor-core/src/Gizmos/IGizmoProvider.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* Gizmo Provider Interface
|
||||
* Gizmo 提供者接口
|
||||
*
|
||||
* Allows components to define custom gizmo rendering in the editor.
|
||||
* Uses the Rust WebGL renderer for high-performance gizmo display.
|
||||
* 允许组件定义编辑器中的自定义 gizmo 渲染。
|
||||
* 使用 Rust WebGL 渲染器实现高性能 gizmo 显示。
|
||||
*/
|
||||
|
||||
import type { Entity } from '@esengine/ecs-framework';
|
||||
|
||||
/**
|
||||
* Gizmo type enumeration
|
||||
* Gizmo 类型枚举
|
||||
*/
|
||||
export type GizmoType = 'rect' | 'circle' | 'line' | 'grid';
|
||||
|
||||
/**
|
||||
* Color in RGBA format (0-1 range)
|
||||
* RGBA 格式颜色(0-1 范围)
|
||||
*/
|
||||
export interface GizmoColor {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rectangle gizmo data (rendered via Rust WebGL)
|
||||
* 矩形 gizmo 数据(通过 Rust WebGL 渲染)
|
||||
*/
|
||||
export interface IRectGizmoData {
|
||||
type: 'rect';
|
||||
/** Center X position in world space | 世界空间中心 X 位置 */
|
||||
x: number;
|
||||
/** Center Y position in world space | 世界空间中心 Y 位置 */
|
||||
y: number;
|
||||
/** Width in world units | 世界单位宽度 */
|
||||
width: number;
|
||||
/** Height in world units | 世界单位高度 */
|
||||
height: number;
|
||||
/** Rotation in radians | 旋转角度(弧度) */
|
||||
rotation: number;
|
||||
/** Origin X (0-1, default 0.5 for center) | 原点 X(0-1,默认 0.5 居中) */
|
||||
originX: number;
|
||||
/** Origin Y (0-1, default 0.5 for center) | 原点 Y(0-1,默认 0.5 居中) */
|
||||
originY: number;
|
||||
/** Color | 颜色 */
|
||||
color: GizmoColor;
|
||||
/** Show transform handles (move/rotate/scale based on mode) | 显示变换手柄 */
|
||||
showHandles: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Circle gizmo data
|
||||
* 圆形 gizmo 数据
|
||||
*/
|
||||
export interface ICircleGizmoData {
|
||||
type: 'circle';
|
||||
/** Center X position | 中心 X 位置 */
|
||||
x: number;
|
||||
/** Center Y position | 中心 Y 位置 */
|
||||
y: number;
|
||||
/** Radius | 半径 */
|
||||
radius: number;
|
||||
/** Color | 颜色 */
|
||||
color: GizmoColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Line gizmo data
|
||||
* 线条 gizmo 数据
|
||||
*/
|
||||
export interface ILineGizmoData {
|
||||
type: 'line';
|
||||
/** Line points | 线段点 */
|
||||
points: Array<{ x: number; y: number }>;
|
||||
/** Color | 颜色 */
|
||||
color: GizmoColor;
|
||||
/** Whether to close the path | 是否闭合路径 */
|
||||
closed: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grid gizmo data
|
||||
* 网格 gizmo 数据
|
||||
*/
|
||||
export interface IGridGizmoData {
|
||||
type: 'grid';
|
||||
/** Top-left X position | 左上角 X 位置 */
|
||||
x: number;
|
||||
/** Top-left Y position | 左上角 Y 位置 */
|
||||
y: number;
|
||||
/** Total width | 总宽度 */
|
||||
width: number;
|
||||
/** Total height | 总高度 */
|
||||
height: number;
|
||||
/** Number of columns | 列数 */
|
||||
cols: number;
|
||||
/** Number of rows | 行数 */
|
||||
rows: number;
|
||||
/** Color | 颜色 */
|
||||
color: GizmoColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Union type for all gizmo data
|
||||
* 所有 gizmo 数据的联合类型
|
||||
*/
|
||||
export type IGizmoRenderData = IRectGizmoData | ICircleGizmoData | ILineGizmoData | IGridGizmoData;
|
||||
|
||||
/**
|
||||
* Gizmo Provider Interface
|
||||
* Gizmo 提供者接口
|
||||
*
|
||||
* Components can implement this interface to provide custom gizmo rendering.
|
||||
* The returned data will be rendered by the Rust WebGL engine.
|
||||
* 组件可以实现此接口以提供自定义 gizmo 渲染。
|
||||
* 返回的数据将由 Rust WebGL 引擎渲染。
|
||||
*/
|
||||
export interface IGizmoProvider {
|
||||
/**
|
||||
* Get gizmo render data for this component
|
||||
* 获取此组件的 gizmo 渲染数据
|
||||
*
|
||||
* @param entity The entity owning this component | 拥有此组件的实体
|
||||
* @param isSelected Whether the entity is selected | 实体是否被选中
|
||||
* @returns Array of gizmo render data | Gizmo 渲染数据数组
|
||||
*/
|
||||
getGizmoData(entity: Entity, isSelected: boolean): IGizmoRenderData[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a component implements IGizmoProvider
|
||||
* 检查组件是否实现了 IGizmoProvider
|
||||
*/
|
||||
export function hasGizmoProvider(component: unknown): component is IGizmoProvider {
|
||||
return component !== null &&
|
||||
typeof component === 'object' &&
|
||||
'getGizmoData' in component &&
|
||||
typeof (component as Record<string, unknown>).getGizmoData === 'function';
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create a gizmo color from hex string
|
||||
* 从十六进制字符串创建 gizmo 颜色的辅助函数
|
||||
*/
|
||||
export function hexToGizmoColor(hex: string, alpha: number = 1): GizmoColor {
|
||||
let r = 0, g = 1, b = 0;
|
||||
if (hex.startsWith('#')) {
|
||||
const hexValue = hex.slice(1);
|
||||
if (hexValue.length === 3) {
|
||||
r = parseInt(hexValue[0] + hexValue[0], 16) / 255;
|
||||
g = parseInt(hexValue[1] + hexValue[1], 16) / 255;
|
||||
b = parseInt(hexValue[2] + hexValue[2], 16) / 255;
|
||||
} else if (hexValue.length === 6) {
|
||||
r = parseInt(hexValue.slice(0, 2), 16) / 255;
|
||||
g = parseInt(hexValue.slice(2, 4), 16) / 255;
|
||||
b = parseInt(hexValue.slice(4, 6), 16) / 255;
|
||||
}
|
||||
}
|
||||
return { r, g, b, a: alpha };
|
||||
}
|
||||
|
||||
/**
|
||||
* Predefined gizmo colors
|
||||
* 预定义的 gizmo 颜色
|
||||
*/
|
||||
export const GizmoColors = {
|
||||
/** Green for selected entities | 选中实体的绿色 */
|
||||
selected: { r: 0, g: 1, b: 0.5, a: 1 } as GizmoColor,
|
||||
/** Semi-transparent green for unselected | 未选中实体的半透明绿色 */
|
||||
unselected: { r: 0, g: 1, b: 0.5, a: 0.4 } as GizmoColor,
|
||||
/** White for camera frustum | 相机视锥体的白色 */
|
||||
camera: { r: 1, g: 1, b: 1, a: 0.8 } as GizmoColor,
|
||||
/** Cyan for colliders | 碰撞体的青色 */
|
||||
collider: { r: 0, g: 1, b: 1, a: 0.6 } as GizmoColor,
|
||||
/** Yellow for grid | 网格的黄色 */
|
||||
grid: { r: 1, g: 1, b: 0, a: 0.3 } as GizmoColor,
|
||||
};
|
||||
12
packages/editor-core/src/Gizmos/index.ts
Normal file
12
packages/editor-core/src/Gizmos/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Gizmo System
|
||||
* Gizmo 系统
|
||||
*
|
||||
* Provides interfaces for custom gizmo rendering in the editor.
|
||||
* Gizmos are rendered by the Rust WebGL engine for optimal performance.
|
||||
* 为编辑器中的自定义 gizmo 渲染提供接口。
|
||||
* Gizmo 由 Rust WebGL 引擎渲染以获得最佳性能。
|
||||
*/
|
||||
|
||||
export * from './IGizmoProvider';
|
||||
export * from './GizmoRegistry';
|
||||
Reference in New Issue
Block a user