feat(fairygui): FairyGUI 完整集成 (#314)

* feat(fairygui): FairyGUI ECS 集成核心架构

实现 FairyGUI 的 ECS 原生集成,完全替代旧 UI 系统:

核心类:
- GObject: UI 对象基类,支持变换、可见性、关联、齿轮
- GComponent: 容器组件,管理子对象和控制器
- GRoot: 根容器,管理焦点、弹窗、输入分发
- GGroup: 组容器,支持水平/垂直布局

抽象层:
- DisplayObject: 显示对象基类
- EventDispatcher: 事件分发
- Timer: 计时器
- Stage: 舞台,管理输入和缩放

布局系统:
- Relations: 约束关联管理
- RelationItem: 24 种关联类型

基础设施:
- Controller: 状态控制器
- Transition: 过渡动画
- ScrollPane: 滚动面板
- UIPackage: 包管理
- ByteBuffer: 二进制解析

* refactor(ui): 删除旧 UI 系统,使用 FairyGUI 替代

* feat(fairygui): 实现 UI 控件

- 添加显示类:Image、TextField、Graph
- 添加基础控件:GImage、GTextField、GGraph
- 添加交互控件:GButton、GProgressBar、GSlider
- 更新 IRenderCollector 支持 Graph 渲染
- 扩展 Controller 添加 selectedPageId
- 添加 STATE_CHANGED 事件类型

* feat(fairygui): 现代化架构重构

- 增强 EventDispatcher 支持类型安全、优先级和传播控制
- 添加 PropertyBinding 响应式属性绑定系统
- 添加 ServiceContainer 依赖注入容器
- 添加 UIConfig 全局配置系统
- 添加 UIObjectFactory 对象工厂
- 实现 RenderBridge 渲染桥接层
- 实现 Canvas2DBackend 作为默认渲染后端
- 扩展 IRenderCollector 支持更多图元类型

* feat(fairygui): 九宫格渲染和资源加载修复

- 修复 FGUIUpdateSystem 支持路径和 GUID 两种加载方式
- 修复 GTextInput 同时设置 _displayObject 和 _textField
- 实现九宫格渲染展开为 9 个子图元
- 添加 sourceWidth/sourceHeight 用于九宫格计算
- 添加 DOMTextRenderer 文本渲染层(临时方案)

* fix(fairygui): 修复 GGraph 颜色读取

* feat(fairygui): 虚拟节点 Inspector 和文本渲染支持

* fix(fairygui): 编辑器状态刷新和遗留引用修复

- 修复切换 FGUI 包后组件列表未刷新问题
- 修复切换组件后 viewport 未清理旧内容问题
- 修复虚拟节点在包加载后未刷新问题
- 重构为事件驱动架构,移除轮询机制
- 修复 @esengine/ui 遗留引用,统一使用 @esengine/fairygui

* fix: 移除 tsconfig 中的 @esengine/ui 引用
This commit is contained in:
YHH
2025-12-22 10:52:54 +08:00
committed by GitHub
parent 96b5403d14
commit a1e1189f9d
237 changed files with 30983 additions and 23563 deletions

View File

@@ -0,0 +1,269 @@
/**
* BitmapFont
*
* Bitmap font support for FairyGUI.
* Handles BMFont format from FairyGUI Editor exports.
*
* 位图字体支持
* 处理 FairyGUI 编辑器导出的 BMFont 格式
*/
import type { MSDFFont, IMSDFFontData, IMSDFGlyph } from './MSDFFont';
/**
* FairyGUI bitmap font glyph
* FairyGUI 位图字体字形
*/
export interface IBitmapGlyph {
/** X offset in the glyph | 字形内 X 偏移 */
x: number;
/** Y offset in the glyph | 字形内 Y 偏移 */
y: number;
/** Glyph width | 字形宽度 */
width: number;
/** Glyph height | 字形高度 */
height: number;
/** Horizontal advance | 水平前进量 */
advance: number;
/** Source texture region (if from atlas) | 源纹理区域 */
textureRegion?: {
x: number;
y: number;
width: number;
height: number;
};
/** Texture ID for this glyph | 此字形的纹理 ID */
textureId?: number;
}
/**
* FairyGUI bitmap font data (from UIPackage)
* FairyGUI 位图字体数据(来自 UIPackage
*/
export interface IBitmapFontData {
/** Is TTF (dynamic font) | 是否是 TTF动态字体 */
ttf: boolean;
/** Can be tinted | 可以着色 */
tint: boolean;
/** Font size | 字体大小 */
fontSize: number;
/** Line height | 行高 */
lineHeight: number;
/** Glyphs map (charCode -> glyph) | 字形映射 */
glyphs: Map<number, IBitmapGlyph>;
/** Texture ID for the font atlas | 字体图集纹理 ID */
textureId?: number;
}
/**
* BitmapFont
*
* Adapter for FairyGUI bitmap fonts.
* Can be used for rendering when MSDF fonts are not available.
*
* FairyGUI 位图字体适配器
* 当 MSDF 字体不可用时可用于渲染
*/
export class BitmapFont {
/** Font name | 字体名称 */
public readonly name: string;
/** Texture ID | 纹理 ID */
public textureId: number = 0;
/** Font data | 字体数据 */
private _data: IBitmapFontData;
constructor(name: string, data: IBitmapFontData) {
this.name = name;
this._data = data;
if (data.textureId !== undefined) {
this.textureId = data.textureId;
}
}
/**
* Is this a TTF (dynamic) font
* 是否是 TTF动态字体
*/
public get isTTF(): boolean {
return this._data.ttf;
}
/**
* Can the font be tinted
* 字体是否可以着色
*/
public get canTint(): boolean {
return this._data.tint;
}
/**
* Font size | 字体大小
*/
public get fontSize(): number {
return this._data.fontSize;
}
/**
* Line height | 行高
*/
public get lineHeight(): number {
return this._data.lineHeight;
}
/**
* Get glyph for a character
* 获取字符的字形
*/
public getGlyph(charCode: number): IBitmapGlyph | undefined {
return this._data.glyphs.get(charCode);
}
/**
* Check if font has a glyph
* 检查字体是否有字形
*/
public hasGlyph(charCode: number): boolean {
return this._data.glyphs.has(charCode);
}
/**
* Get all glyphs
* 获取所有字形
*/
public get glyphs(): Map<number, IBitmapGlyph> {
return this._data.glyphs;
}
}
/**
* Bitmap Font Manager
* 位图字体管理器
*/
export class BitmapFontManager {
/** Loaded fonts | 已加载的字体 */
private _fonts: Map<string, BitmapFont> = new Map();
/**
* Register a bitmap font
* 注册位图字体
*/
public registerFont(font: BitmapFont): void {
this._fonts.set(font.name, font);
}
/**
* Get a font by name
* 按名称获取字体
*/
public getFont(name: string): BitmapFont | undefined {
return this._fonts.get(name);
}
/**
* Check if a font is registered
* 检查字体是否已注册
*/
public hasFont(name: string): boolean {
return this._fonts.has(name);
}
/**
* Unload a font
* 卸载字体
*/
public unloadFont(name: string): void {
this._fonts.delete(name);
}
/**
* Clear all fonts
* 清除所有字体
*/
public clear(): void {
this._fonts.clear();
}
/**
* Create from FairyGUI package font data
* 从 FairyGUI 包字体数据创建
*/
public createFromPackageData(name: string, data: IBitmapFontData): BitmapFont {
const font = new BitmapFont(name, data);
this.registerFont(font);
return font;
}
}
/** Global bitmap font manager | 全局位图字体管理器 */
let _bitmapFontManager: BitmapFontManager | null = null;
/**
* Get global bitmap font manager
* 获取全局位图字体管理器
*/
export function getBitmapFontManager(): BitmapFontManager {
if (!_bitmapFontManager) {
_bitmapFontManager = new BitmapFontManager();
}
return _bitmapFontManager;
}
/**
* Convert bitmap font to MSDF-compatible format
* 将位图字体转换为 MSDF 兼容格式
*
* Note: This creates a "fake" MSDF font that uses bitmap rendering.
* The pxRange is set to 0 to disable MSDF processing in the shader.
*
* 注意:这会创建一个使用位图渲染的"伪" MSDF 字体。
* pxRange 设置为 0 以在着色器中禁用 MSDF 处理。
*/
export function convertBitmapToMSDFFormat(
bitmapFont: BitmapFont,
atlasWidth: number,
atlasHeight: number
): IMSDFFontData {
const glyphs: IMSDFGlyph[] = [];
for (const [charCode, glyph] of bitmapFont.glyphs) {
const region = glyph.textureRegion;
if (!region) continue;
glyphs.push({
unicode: charCode,
advance: glyph.advance / bitmapFont.fontSize,
planeBounds: {
left: glyph.x / bitmapFont.fontSize,
bottom: -(glyph.y + glyph.height) / bitmapFont.fontSize,
right: (glyph.x + glyph.width) / bitmapFont.fontSize,
top: -glyph.y / bitmapFont.fontSize
},
atlasBounds: {
left: region.x,
bottom: region.y,
right: region.x + region.width,
top: region.y + region.height
}
});
}
return {
atlas: {
type: 'sdf', // Use simple SDF mode for bitmap
distanceRange: 0, // 0 = disable MSDF processing, use as regular texture
size: bitmapFont.fontSize,
width: atlasWidth,
height: atlasHeight,
yOrigin: 'top'
},
metrics: {
emSize: bitmapFont.fontSize,
lineHeight: bitmapFont.lineHeight / bitmapFont.fontSize,
ascender: 1,
descender: 0
},
glyphs
};
}