* refactor: 编辑器/运行时架构拆分与构建系统升级 * feat(core): 层级系统重构与UI变换矩阵修复 * refactor: 移除 ecs-components 聚合包并修复跨包组件查找问题 * fix(physics): 修复跨包组件类引用问题 * feat: 统一运行时架构与浏览器运行支持 * feat(asset): 实现浏览器运行时资产加载系统 * fix: 修复文档、CodeQL安全问题和CI类型检查错误 * fix: 修复文档、CodeQL安全问题和CI类型检查错误 * fix: 修复文档、CodeQL安全问题、CI类型检查和测试错误 * test: 补齐核心模块测试用例,修复CI构建配置 * fix: 修复测试用例中的类型错误和断言问题 * fix: 修复 turbo build:npm 任务的依赖顺序问题 * fix: 修复 CI 构建错误并优化构建性能
244 lines
6.6 KiB
TypeScript
244 lines
6.6 KiB
TypeScript
import { Component, ECSComponent, Serializable, Serialize, Property } from '@esengine/ecs-framework';
|
||
import type { AssetReference } from '@esengine/asset-system';
|
||
|
||
/**
|
||
* 精灵组件 - 管理2D图像渲染
|
||
* Sprite component - manages 2D image rendering
|
||
*/
|
||
@ECSComponent('Sprite')
|
||
@Serializable({ version: 2, typeId: 'Sprite' })
|
||
export class SpriteComponent extends Component {
|
||
/** 纹理路径或资源ID | Texture path or asset ID */
|
||
@Serialize()
|
||
@Property({ type: 'asset', label: 'Texture', assetType: 'texture' })
|
||
public texture: string = '';
|
||
|
||
/**
|
||
* 资产GUID(新的资产系统)
|
||
* Asset GUID for new asset system
|
||
*/
|
||
@Serialize()
|
||
public assetGuid?: string;
|
||
|
||
/**
|
||
* 纹理ID(运行时使用)
|
||
* Texture ID for runtime rendering
|
||
*/
|
||
public textureId: number = 0;
|
||
|
||
/**
|
||
* 资产引用(运行时,不序列化)
|
||
* Asset reference (runtime only, not serialized)
|
||
*/
|
||
private _assetReference?: AssetReference<HTMLImageElement>;
|
||
|
||
/**
|
||
* 精灵宽度(像素)
|
||
* Sprite width in pixels
|
||
*/
|
||
@Serialize()
|
||
@Property({
|
||
type: 'number',
|
||
label: 'Width',
|
||
min: 0,
|
||
actions: [{
|
||
id: 'nativeSize',
|
||
label: 'Native',
|
||
tooltip: 'Set to texture native size',
|
||
icon: 'Maximize2'
|
||
}]
|
||
})
|
||
public width: number = 64;
|
||
|
||
/**
|
||
* 精灵高度(像素)
|
||
* Sprite height in pixels
|
||
*/
|
||
@Serialize()
|
||
@Property({
|
||
type: 'number',
|
||
label: 'Height',
|
||
min: 0,
|
||
actions: [{
|
||
id: 'nativeSize',
|
||
label: 'Native',
|
||
tooltip: 'Set to texture native size',
|
||
icon: 'Maximize2'
|
||
}]
|
||
})
|
||
public height: number = 64;
|
||
|
||
/**
|
||
* UV坐标 [u0, v0, u1, v1]
|
||
* UV coordinates [u0, v0, u1, v1]
|
||
* 默认为完整纹理 [0, 0, 1, 1]
|
||
* Default is full texture [0, 0, 1, 1]
|
||
*/
|
||
@Serialize()
|
||
public uv: [number, number, number, number] = [0, 0, 1, 1];
|
||
|
||
/** 颜色(十六进制)| Color (hex string) */
|
||
@Serialize()
|
||
@Property({ type: 'color', label: 'Color' })
|
||
public color: string = '#ffffff';
|
||
|
||
/** 透明度 (0-1) | Alpha (0-1) */
|
||
@Serialize()
|
||
@Property({ type: 'number', label: 'Alpha', min: 0, max: 1, step: 0.01 })
|
||
public alpha: number = 1;
|
||
|
||
/**
|
||
* 原点X (0-1, 0.5为中心)
|
||
* Origin point X (0-1, where 0.5 is center)
|
||
*/
|
||
@Serialize()
|
||
@Property({ type: 'number', label: 'Origin X', min: 0, max: 1, step: 0.01 })
|
||
public originX: number = 0.5;
|
||
|
||
/**
|
||
* 原点Y (0-1, 0.5为中心)
|
||
* Origin point Y (0-1, where 0.5 is center)
|
||
*/
|
||
@Serialize()
|
||
@Property({ type: 'number', label: 'Origin Y', min: 0, max: 1, step: 0.01 })
|
||
public originY: number = 0.5;
|
||
|
||
/**
|
||
* 精灵是否可见
|
||
* Whether sprite is visible
|
||
*/
|
||
@Serialize()
|
||
@Property({ type: 'boolean', label: 'Visible' })
|
||
public visible: boolean = true;
|
||
|
||
/** 是否水平翻转 | Flip sprite horizontally */
|
||
@Serialize()
|
||
@Property({ type: 'boolean', label: 'Flip X' })
|
||
public flipX: boolean = false;
|
||
|
||
/** 是否垂直翻转 | Flip sprite vertically */
|
||
@Serialize()
|
||
@Property({ type: 'boolean', label: 'Flip Y' })
|
||
public flipY: boolean = false;
|
||
|
||
/**
|
||
* 渲染层级/顺序(越高越在上面)
|
||
* Render layer/order (higher = rendered on top)
|
||
*/
|
||
@Serialize()
|
||
@Property({ type: 'integer', label: 'Sorting Order' })
|
||
public sortingOrder: number = 0;
|
||
|
||
/** 锚点X (0-1) - 别名为originX | Anchor X (0-1) - alias for originX */
|
||
get anchorX(): number {
|
||
return this.originX;
|
||
}
|
||
set anchorX(value: number) {
|
||
this.originX = value;
|
||
}
|
||
|
||
/** 锚点Y (0-1) - 别名为originY | Anchor Y (0-1) - alias for originY */
|
||
get anchorY(): number {
|
||
return this.originY;
|
||
}
|
||
set anchorY(value: number) {
|
||
this.originY = value;
|
||
}
|
||
|
||
constructor(texture: string = '') {
|
||
super();
|
||
this.texture = texture;
|
||
}
|
||
|
||
/**
|
||
* 从精灵图集区域设置UV
|
||
* Set UV from a sprite atlas region
|
||
*
|
||
* @param x - 区域X(像素)| Region X in pixels
|
||
* @param y - 区域Y(像素)| Region Y in pixels
|
||
* @param w - 区域宽度(像素)| Region width in pixels
|
||
* @param h - 区域高度(像素)| Region height in pixels
|
||
* @param atlasWidth - 图集总宽度 | Atlas total width
|
||
* @param atlasHeight - 图集总高度 | Atlas total height
|
||
*/
|
||
setAtlasRegion(
|
||
x: number,
|
||
y: number,
|
||
w: number,
|
||
h: number,
|
||
atlasWidth: number,
|
||
atlasHeight: number
|
||
): void {
|
||
this.uv = [
|
||
x / atlasWidth,
|
||
y / atlasHeight,
|
||
(x + w) / atlasWidth,
|
||
(y + h) / atlasHeight
|
||
];
|
||
this.width = w;
|
||
this.height = h;
|
||
}
|
||
|
||
/**
|
||
* 设置资产引用
|
||
* Set asset reference
|
||
*/
|
||
setAssetReference(reference: AssetReference<HTMLImageElement>): void {
|
||
// 释放旧引用 / Release old reference
|
||
if (this._assetReference) {
|
||
this._assetReference.release();
|
||
}
|
||
this._assetReference = reference;
|
||
if (reference) {
|
||
this.assetGuid = reference.guid;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取资产引用
|
||
* Get asset reference
|
||
*/
|
||
getAssetReference(): AssetReference<HTMLImageElement> | undefined {
|
||
return this._assetReference;
|
||
}
|
||
|
||
/**
|
||
* 异步加载纹理
|
||
* Load texture asynchronously
|
||
*/
|
||
async loadTextureAsync(): Promise<void> {
|
||
if (this._assetReference) {
|
||
try {
|
||
const textureAsset = await this._assetReference.loadAsync();
|
||
// 如果纹理资产有 textureId 属性,使用它
|
||
// If texture asset has textureId property, use it
|
||
if (textureAsset && typeof textureAsset === 'object' && 'textureId' in textureAsset) {
|
||
this.textureId = (textureAsset as any).textureId;
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to load texture:', error);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取有效的纹理源
|
||
* Get effective texture source
|
||
*/
|
||
getTextureSource(): string {
|
||
return this.assetGuid || this.texture;
|
||
}
|
||
|
||
/**
|
||
* 组件销毁时调用
|
||
* Called when component is destroyed
|
||
*/
|
||
onDestroy(): void {
|
||
// 释放资产引用 / Release asset reference
|
||
if (this._assetReference) {
|
||
this._assetReference.release();
|
||
this._assetReference = undefined;
|
||
}
|
||
}
|
||
}
|