Feature/render pipeline (#232)
* refactor(engine): 重构2D渲染管线坐标系统 * feat(engine): 完善2D渲染管线和编辑器视口功能 * feat(editor): 实现Viewport变换工具系统 * feat(editor): 优化Inspector渲染性能并修复Gizmo变换工具显示 * feat(editor): 实现Run on Device移动预览功能 * feat(editor): 添加组件属性控制和依赖关系系统 * feat(editor): 实现动画预览功能和优化SpriteAnimator编辑器 * feat(editor): 修复SpriteAnimator动画预览功能并迁移CI到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(ci): 迁移项目到pnpm并修复CI构建问题 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 移除 network 相关包 * chore: 移除 network 相关包
This commit is contained in:
@@ -255,7 +255,7 @@ export class SoAStorage<T extends Component> {
|
||||
for (const key of uint8ClampedFields) decoratedFields.set(key, 'uint8clamped');
|
||||
|
||||
// 只遍历实例自身的属性(不包括原型链),跳过 id
|
||||
const instanceKeys = Object.keys(instance).filter(key => key !== 'id');
|
||||
const instanceKeys = Object.keys(instance).filter((key) => key !== 'id');
|
||||
|
||||
for (const key of instanceKeys) {
|
||||
const value = (instance as Record<string, unknown>)[key];
|
||||
@@ -264,31 +264,31 @@ export class SoAStorage<T extends Component> {
|
||||
// 跳过函数(通常不会出现在 Object.keys 中,但以防万一)
|
||||
if (type === 'function') continue;
|
||||
|
||||
// 检查装饰器类型
|
||||
const decoratorType = decoratedFields.get(key);
|
||||
const effectiveType = decoratorType ? 'number' : type;
|
||||
this.fieldTypes.set(key, effectiveType);
|
||||
// 检查装饰器类型
|
||||
const decoratorType = decoratedFields.get(key);
|
||||
const effectiveType = decoratorType ? 'number' : type;
|
||||
this.fieldTypes.set(key, effectiveType);
|
||||
|
||||
if (decoratorType) {
|
||||
// 有装饰器标记的数字字段
|
||||
const ArrayConstructor = SoATypeRegistry.getConstructor(decoratorType as TypedArrayTypeName);
|
||||
this.fields.set(key, new ArrayConstructor(this._capacity));
|
||||
} else if (type === 'number') {
|
||||
// 无装饰器的数字字段,默认使用 Float32Array
|
||||
this.fields.set(key, new Float32Array(this._capacity));
|
||||
} else if (type === 'boolean') {
|
||||
// 布尔值使用 Uint8Array 存储为 0/1
|
||||
this.fields.set(key, new Uint8Array(this._capacity));
|
||||
} else if (type === 'string') {
|
||||
// 字符串专门处理
|
||||
this.stringFields.set(key, new Array(this._capacity));
|
||||
} else if (type === 'object' && value !== null) {
|
||||
// 处理集合类型
|
||||
if (this.serializeMapFields.has(key) || this.serializeSetFields.has(key) || this.serializeArrayFields.has(key)) {
|
||||
// 序列化存储
|
||||
this.serializedFields.set(key, new Array(this._capacity));
|
||||
}
|
||||
// 其他对象类型会在updateComponentAtIndex中作为复杂对象处理
|
||||
if (decoratorType) {
|
||||
// 有装饰器标记的数字字段
|
||||
const ArrayConstructor = SoATypeRegistry.getConstructor(decoratorType as TypedArrayTypeName);
|
||||
this.fields.set(key, new ArrayConstructor(this._capacity));
|
||||
} else if (type === 'number') {
|
||||
// 无装饰器的数字字段,默认使用 Float32Array
|
||||
this.fields.set(key, new Float32Array(this._capacity));
|
||||
} else if (type === 'boolean') {
|
||||
// 布尔值使用 Uint8Array 存储为 0/1
|
||||
this.fields.set(key, new Uint8Array(this._capacity));
|
||||
} else if (type === 'string') {
|
||||
// 字符串专门处理
|
||||
this.stringFields.set(key, new Array(this._capacity));
|
||||
} else if (type === 'object' && value !== null) {
|
||||
// 处理集合类型
|
||||
if (this.serializeMapFields.has(key) || this.serializeSetFields.has(key) || this.serializeArrayFields.has(key)) {
|
||||
// 序列化存储
|
||||
this.serializedFields.set(key, new Array(this._capacity));
|
||||
}
|
||||
// 其他对象类型会在updateComponentAtIndex中作为复杂对象处理
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,7 +515,7 @@ export class SoAStorage<T extends Component> {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
// entityId 是只读的
|
||||
writable: propStr !== 'entityId',
|
||||
writable: propStr !== 'entityId'
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
|
||||
94
packages/core/src/ECS/Decorators/PropertyDecorator.ts
Normal file
94
packages/core/src/ECS/Decorators/PropertyDecorator.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
export type PropertyType = 'number' | 'integer' | 'string' | 'boolean' | 'color' | 'vector2' | 'vector3' | 'enum' | 'asset' | 'animationClips';
|
||||
|
||||
/**
|
||||
* Action button configuration for property fields
|
||||
* 属性字段的操作按钮配置
|
||||
*/
|
||||
export interface PropertyAction {
|
||||
/** Action identifier | 操作标识符 */
|
||||
id: string;
|
||||
/** Button label | 按钮标签 */
|
||||
label: string;
|
||||
/** Button tooltip | 按钮提示 */
|
||||
tooltip?: string;
|
||||
/** Icon name from Lucide | Lucide图标名称 */
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制关系声明
|
||||
* Control relationship declaration
|
||||
*/
|
||||
export interface PropertyControl {
|
||||
/** 被控制的组件名称 | Target component name */
|
||||
component: string;
|
||||
/** 被控制的属性名称 | Target property name */
|
||||
property: string;
|
||||
}
|
||||
|
||||
export interface PropertyOptions {
|
||||
/** 属性类型 */
|
||||
type: PropertyType;
|
||||
/** 显示标签 */
|
||||
label?: string;
|
||||
/** 最小值 (number/integer) */
|
||||
min?: number;
|
||||
/** 最大值 (number/integer) */
|
||||
max?: number;
|
||||
/** 步进值 (number/integer) */
|
||||
step?: number;
|
||||
/** 枚举选项 (enum) */
|
||||
options?: Array<{ label: string; value: any }>;
|
||||
/** 是否只读 */
|
||||
readOnly?: boolean;
|
||||
/** 资源文件扩展名 (asset) */
|
||||
fileExtension?: string;
|
||||
/** Action buttons for this property | 属性的操作按钮 */
|
||||
actions?: PropertyAction[];
|
||||
/** 此属性控制的其他组件属性 | Properties this field controls */
|
||||
controls?: PropertyControl[];
|
||||
}
|
||||
|
||||
export const PROPERTY_METADATA = Symbol('property:metadata');
|
||||
|
||||
/**
|
||||
* 属性装饰器 - 声明组件属性的编辑器元数据
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @ECSComponent('Transform')
|
||||
* export class TransformComponent extends Component {
|
||||
* @Property({ type: 'vector3', label: 'Position' })
|
||||
* public position: Vector3 = { x: 0, y: 0, z: 0 };
|
||||
*
|
||||
* @Property({ type: 'number', label: 'Speed', min: 0, max: 100 })
|
||||
* public speed: number = 10;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function Property(options: PropertyOptions): PropertyDecorator {
|
||||
return (target: object, propertyKey: string | symbol) => {
|
||||
const constructor = target.constructor;
|
||||
const existingMetadata = Reflect.getMetadata(PROPERTY_METADATA, constructor) || {};
|
||||
|
||||
existingMetadata[propertyKey as string] = options;
|
||||
|
||||
Reflect.defineMetadata(PROPERTY_METADATA, existingMetadata, constructor);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类的所有属性元数据
|
||||
*/
|
||||
export function getPropertyMetadata(target: Function): Record<string, PropertyOptions> | undefined {
|
||||
return Reflect.getMetadata(PROPERTY_METADATA, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件类是否有属性元数据
|
||||
*/
|
||||
export function hasPropertyMetadata(target: Function): boolean {
|
||||
return Reflect.hasMetadata(PROPERTY_METADATA, target);
|
||||
}
|
||||
@@ -7,16 +7,30 @@ import { ComponentType } from '../../Types';
|
||||
*/
|
||||
export const COMPONENT_TYPE_NAME = Symbol('ComponentTypeName');
|
||||
|
||||
/**
|
||||
* 存储组件依赖的Symbol键
|
||||
*/
|
||||
export const COMPONENT_DEPENDENCIES = Symbol('ComponentDependencies');
|
||||
|
||||
/**
|
||||
* 存储系统类型名称的Symbol键
|
||||
*/
|
||||
export const SYSTEM_TYPE_NAME = Symbol('SystemTypeName');
|
||||
|
||||
/**
|
||||
* 组件装饰器配置选项
|
||||
*/
|
||||
export interface ComponentOptions {
|
||||
/** 依赖的其他组件名称列表 */
|
||||
requires?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件类型装饰器
|
||||
* 用于为组件类指定固定的类型名称,避免在代码混淆后失效
|
||||
*
|
||||
* @param typeName 组件类型名称
|
||||
* @param options 组件配置选项
|
||||
* @example
|
||||
* ```typescript
|
||||
* @ECSComponent('Position')
|
||||
@@ -24,9 +38,15 @@ export const SYSTEM_TYPE_NAME = Symbol('SystemTypeName');
|
||||
* x: number = 0;
|
||||
* y: number = 0;
|
||||
* }
|
||||
*
|
||||
* // 带依赖声明
|
||||
* @ECSComponent('SpriteAnimator', { requires: ['Sprite'] })
|
||||
* class SpriteAnimatorComponent extends Component {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function ECSComponent(typeName: string) {
|
||||
export function ECSComponent(typeName: string, options?: ComponentOptions) {
|
||||
return function <T extends new (...args: any[]) => Component>(target: T): T {
|
||||
if (!typeName || typeof typeName !== 'string') {
|
||||
throw new Error('ECSComponent装饰器必须提供有效的类型名称');
|
||||
@@ -35,10 +55,22 @@ export function ECSComponent(typeName: string) {
|
||||
// 在构造函数上存储类型名称
|
||||
(target as any)[COMPONENT_TYPE_NAME] = typeName;
|
||||
|
||||
// 存储依赖关系
|
||||
if (options?.requires) {
|
||||
(target as any)[COMPONENT_DEPENDENCIES] = options.requires;
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件的依赖列表
|
||||
*/
|
||||
export function getComponentDependencies(componentType: ComponentType): string[] | undefined {
|
||||
return (componentType as any)[COMPONENT_DEPENDENCIES];
|
||||
}
|
||||
|
||||
/**
|
||||
* System元数据配置
|
||||
*/
|
||||
|
||||
@@ -6,11 +6,13 @@ export {
|
||||
getComponentInstanceTypeName,
|
||||
getSystemInstanceTypeName,
|
||||
getSystemMetadata,
|
||||
getComponentDependencies,
|
||||
COMPONENT_TYPE_NAME,
|
||||
COMPONENT_DEPENDENCIES,
|
||||
SYSTEM_TYPE_NAME
|
||||
} from './TypeDecorators';
|
||||
|
||||
export type { SystemMetadata } from './TypeDecorators';
|
||||
export type { SystemMetadata, ComponentOptions } from './TypeDecorators';
|
||||
|
||||
export {
|
||||
EntityRef,
|
||||
@@ -20,3 +22,12 @@ export {
|
||||
} from './EntityRefDecorator';
|
||||
|
||||
export type { EntityRefMetadata } from './EntityRefDecorator';
|
||||
|
||||
export {
|
||||
Property,
|
||||
getPropertyMetadata,
|
||||
hasPropertyMetadata,
|
||||
PROPERTY_METADATA
|
||||
} from './PropertyDecorator';
|
||||
|
||||
export type { PropertyOptions, PropertyType, PropertyControl } from './PropertyDecorator';
|
||||
|
||||
@@ -495,8 +495,6 @@ export interface ISceneDebugData {
|
||||
sceneEntityCount: number;
|
||||
/** 场景系统数 */
|
||||
sceneSystemCount: number;
|
||||
/** 场景内存使用量 */
|
||||
sceneMemory: number;
|
||||
/** 场景启动时间 */
|
||||
sceneUptime: number;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ export class SceneDataCollector {
|
||||
sceneRunTime: 0,
|
||||
sceneEntityCount: 0,
|
||||
sceneSystemCount: 0,
|
||||
sceneMemory: 0,
|
||||
sceneUptime: 0
|
||||
};
|
||||
}
|
||||
@@ -36,7 +35,6 @@ export class SceneDataCollector {
|
||||
sceneRunTime: runTime,
|
||||
sceneEntityCount: entityList?.buffer?.length || 0,
|
||||
sceneSystemCount: entityProcessors?.processors?.length || 0,
|
||||
sceneMemory: 0, // TODO: 计算实际场景内存
|
||||
sceneUptime: runTime
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user