Files
esengine/packages/sprite/src/SpriteComponent.ts
YHH b42a7b4e43 Feature/editor optimization (#251)
* 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 构建错误并优化构建性能
2025-12-01 22:28:51 +08:00

244 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}