feat: 预制体系统与架构改进 (#303)
* feat(prefab): 实现预制体系统和编辑器 UX 改进 ## 预制体系统 - 新增 PrefabSerializer: 预制体序列化/反序列化 - 新增 PrefabInstanceComponent: 追踪预制体实例来源和修改 - 新增 PrefabService: 预制体核心服务 - 新增 PrefabLoader: 预制体资产加载器 - 新增预制体命令: Create/Instantiate/Apply/Revert/BreakLink ## 预制体编辑模式 - 支持双击 .prefab 文件进入编辑模式 - 预制体编辑模式工具栏 (保存/退出) - 预制体实例指示器和操作菜单 ## 编辑器 UX 改进 - SceneHierarchy 快捷键: F2 重命名, Ctrl+D 复制, ↑↓ 导航 - 支持双击实体名称内联编辑 - 删除实体时显示子节点数量警告 - 右键菜单添加重命名/复制选项及快捷键提示 - 布局持久化和重置功能 ## Bug 修复 - 修复 editor-runtime 组件类重复导致的 TransformComponent 不识别问题 - 修复 .prefab-name 样式覆盖导致预制体工具栏文字不可见 - 修复 Inspector 资源字段高度不正确问题 * feat(editor): 改进编辑器 UX 交互体验 - ContentBrowser: 加载动画 spinner、搜索高亮、改进空状态设计 - SceneHierarchy: 选中项自动滚动到视图、搜索清除按钮 - PropertyInspector: 输入框本地状态管理、Enter/Escape 键处理 - EntityInspector: 组件折叠状态持久化、属性搜索清除按钮 - Viewport: 变换操作实时数值显示 - 国际化: 添加相关文本 (en/zh) * fix(build): 修复 Web 构建资产加载和编辑器 UX 改进 构建系统修复: - 修复 asset-catalog.json 字段名不匹配 (entries vs assets) - 修复 BrowserFileSystemService 支持两种目录格式 - 修复 bundle 策略检测逻辑 (空对象判断) - 修复 module.json 中 assetExtensions 声明和类型推断 行为树修复: - 修复 BehaviorTreeExecutionSystem 使用 loadAsset 替代 loadAssetByPath - 修复 BehaviorTreeAssetType 常量与 module.json 类型名一致 (behavior-tree) 编辑器 UX 改进: - 构建完成对话框添加"打开文件夹"按钮 - 构建完成对话框样式优化 (圆形图标背景、按钮布局) - SceneHierarchy 响应式布局 (窄窗口自动隐藏 Type 列) - SceneHierarchy 隐藏滚动条 错误追踪: - 添加全局错误处理器写入日志文件 (%TEMP%/esengine-editor-crash.log) - 添加 append_to_log Tauri 命令 * feat(render): 修复 UI 渲染和点击特效系统 ## UI 渲染修复 - 修复 GUID 验证 bug,使用统一的 isValidGUID() 函数 - 修复 UI 渲染顺序随机问题,Rust 端使用 IndexMap 替代 HashMap - Web 运行时添加 assetPathResolver 支持 GUID 解析 - UIInteractableComponent.blockEvents 默认值改为 false ## 点击特效系统 - 新增 ClickFxComponent 和 ClickFxSystem - 支持在点击位置播放粒子效果 - 支持多种触发模式和粒子轮换 ## Camera 系统重构 - CameraSystem 从 ecs-engine-bindgen 移至 camera 包 - 新增 CameraManager 统一管理相机 ## 编辑器改进 - 改进属性面板 UI 交互 - 粒子编辑器面板优化 - Transform 命令系统 * feat(render): 实现 Sorting Layer 系统和 Overlay 渲染层 - 新增 SortingLayerManager 管理排序层级 (Background, Default, Foreground, UI, Overlay) - 实现 ISortable 接口,统一 Sprite、UI、Particle 的排序属性 - 修复粒子 Overlay 层被 UI 遮挡问题:添加独立的 Overlay Pass 在 UI 之后渲染 - 更新粒子资产格式:从 sortingOrder 改为 sortingLayer + orderInLayer - 更新粒子编辑器面板支持新的排序属性 - 优化 UI 渲染系统使用新的排序层级 * feat(ci): 集成 SignPath 代码签名服务 - 添加 SignPath 自动签名工作流(Windows) - 配置 release-editor.yml 支持代码签名 - 将构建改为草稿模式,等待签名完成后发布 - 添加证书文件到 .gitignore 防止泄露 * fix(asset): 修复 Web 构建资产路径解析和全局单例移除 ## 资产路径修复 - 修复 Tauri 本地服务器 `/asset?path=...` 路径解析,正确与 root 目录连接 - BrowserPathResolver 支持两种模式: - 'proxy': 使用 /asset?path=... 格式(编辑器 Run in Browser) - 'direct': 使用直接路径 /assets/path.png(独立 Web 构建) - BrowserRuntime 使用 'direct' 模式,无需 Tauri 代理 ## 架构改进 - 移除全局单例 - 移除 globalAssetManager 导出,改用 AssetManagerToken 依赖注入 - 移除 globalPathResolver 导出,改用 PathResolutionService - 移除 globalPathResolutionService 导出 - ParticleUpdateSystem/ClickFxSystem 通过 setAssetManager() 注入依赖 - EngineService 使用 new AssetManager() 替代全局实例 ## 新增服务 - PathResolutionService: 统一路径解析接口 - RuntimeModeService: 运行时模式查询服务 - SerializationContext: EntityRef 序列化上下文 ## 其他改进 - 完善 ServiceToken 注释说明本地定义的意图 - 导出 BrowserPathResolveMode 类型 * fix(build): 添加 world-streaming composite 设置修复类型检查 * fix(build): 移除 world-streaming 引用避免 composite 冲突 * fix(build): 将 const enum 改为 enum 兼容 isolatedModules * fix(build): 添加缺失的 IAssetManager 导入
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"id": "ui",
|
||||
"name": "@esengine/ui",
|
||||
"globalKey": "ui",
|
||||
"displayName": "UI System",
|
||||
"description": "User interface components and layout | 用户界面组件和布局",
|
||||
"version": "1.0.0",
|
||||
@@ -14,6 +15,7 @@
|
||||
"isCore": false,
|
||||
"defaultEnabled": false,
|
||||
"isEngineModule": true,
|
||||
"hasRuntime": true,
|
||||
"canContainContent": true,
|
||||
"platforms": [
|
||||
"web",
|
||||
@@ -22,7 +24,8 @@
|
||||
],
|
||||
"dependencies": [
|
||||
"core",
|
||||
"math"
|
||||
"math",
|
||||
"engine-core"
|
||||
],
|
||||
"exports": {
|
||||
"components": [
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
"type-check": "tsc --noEmit",
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@esengine/asset-system": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@esengine/ecs-framework": "workspace:*",
|
||||
"@esengine/engine-core": "workspace:*",
|
||||
|
||||
@@ -22,7 +22,8 @@ export interface UIBaseConfig {
|
||||
anchor?: AnchorPreset;
|
||||
visible?: boolean;
|
||||
alpha?: number;
|
||||
zIndex?: number;
|
||||
/** 层内排序顺序 | Order within layer */
|
||||
orderInLayer?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +157,7 @@ export class UIBuilder {
|
||||
transform.height = config.height ?? 30;
|
||||
transform.visible = config.visible ?? true;
|
||||
transform.alpha = config.alpha ?? 1;
|
||||
transform.zIndex = config.zIndex ?? 0;
|
||||
transform.orderInLayer = config.orderInLayer ?? 0;
|
||||
|
||||
if (config.anchor) {
|
||||
transform.setAnchorPreset(config.anchor);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { IScene } from '@esengine/ecs-framework';
|
||||
import { ComponentRegistry } from '@esengine/ecs-framework';
|
||||
import type { IRuntimeModule, IPlugin, ModuleManifest, SystemContext } from '@esengine/engine-core';
|
||||
import type { IRuntimeModule, IRuntimePlugin, ModuleManifest, SystemContext } from '@esengine/engine-core';
|
||||
import { EngineBridgeToken } from '@esengine/ecs-engine-bindgen';
|
||||
|
||||
import {
|
||||
@@ -122,7 +122,7 @@ const manifest: ModuleManifest = {
|
||||
editorPackage: '@esengine/ui-editor'
|
||||
};
|
||||
|
||||
export const UIPlugin: IPlugin = {
|
||||
export const UIPlugin: IRuntimePlugin = {
|
||||
manifest,
|
||||
runtimeModule: new UIRuntimeModule()
|
||||
};
|
||||
|
||||
@@ -38,10 +38,16 @@ export class UIInteractableComponent extends Component {
|
||||
/**
|
||||
* 是否阻止事件冒泡
|
||||
* Whether to block event propagation
|
||||
*
|
||||
* 默认为 false,事件会传递给下层元素。
|
||||
* 设置为 true 时,该元素会阻止事件传递。
|
||||
*
|
||||
* Default is false, events propagate to elements below.
|
||||
* Set to true to prevent event propagation.
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'boolean', label: 'Block Events' })
|
||||
public blockEvents: boolean = true;
|
||||
public blockEvents: boolean = false;
|
||||
|
||||
// ===== 状态 State (由 UIInputSystem 更新) =====
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, ECSComponent, Property, Serializable, Serialize } from '@esengine/ecs-framework';
|
||||
import { SortingLayers, type ISortable } from '@esengine/engine-core';
|
||||
|
||||
/**
|
||||
* 锚点预设
|
||||
@@ -25,8 +26,8 @@ export enum AnchorPreset {
|
||||
* Relative positioning system based on parent, supports anchors, pivots, and stretch modes
|
||||
*/
|
||||
@ECSComponent('UITransform')
|
||||
@Serializable({ version: 1, typeId: 'UITransform' })
|
||||
export class UITransformComponent extends Component {
|
||||
@Serializable({ version: 2, typeId: 'UITransform' })
|
||||
export class UITransformComponent extends Component implements ISortable {
|
||||
// ===== 位置 Position =====
|
||||
|
||||
/**
|
||||
@@ -184,12 +185,27 @@ export class UITransformComponent extends Component {
|
||||
public visible: boolean = true;
|
||||
|
||||
/**
|
||||
* 渲染层级,值越大越靠前
|
||||
* Render order, higher values render on top
|
||||
* 排序层
|
||||
* Sorting layer
|
||||
*
|
||||
* UI 默认使用 'UI' 层,在普通精灵之上渲染。
|
||||
* UI defaults to 'UI' layer, rendering above regular sprites.
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Z Index' })
|
||||
public zIndex: number = 0;
|
||||
@Property({
|
||||
type: 'enum',
|
||||
label: 'Sorting Layer',
|
||||
options: ['Background', 'Default', 'Foreground', 'WorldOverlay', 'UI', 'ScreenOverlay', 'Modal']
|
||||
})
|
||||
public sortingLayer: string = SortingLayers.UI;
|
||||
|
||||
/**
|
||||
* 层内顺序,值越大越靠前
|
||||
* Order within layer, higher values render on top
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Order in Layer' })
|
||||
public orderInLayer: number = 0;
|
||||
|
||||
/**
|
||||
* 透明度 (0-1)
|
||||
@@ -232,6 +248,15 @@ export class UITransformComponent extends Component {
|
||||
*/
|
||||
public worldAlpha: number = 1;
|
||||
|
||||
/**
|
||||
* 计算后的世界可见性(考虑父元素可见性)
|
||||
* Computed world visibility (considering parent visibility)
|
||||
*
|
||||
* 如果父元素不可见,子元素也不可见。
|
||||
* If parent is invisible, children are also invisible.
|
||||
*/
|
||||
public worldVisible: boolean = true;
|
||||
|
||||
/**
|
||||
* 计算后的世界旋转(弧度,考虑父元素旋转)
|
||||
* Computed world rotation in radians (considering parent rotation)
|
||||
|
||||
@@ -121,9 +121,9 @@ export {
|
||||
|
||||
// Systems - Core
|
||||
export { UILayoutSystem } from './systems/UILayoutSystem';
|
||||
export { UIInputSystem, MouseButton, type UIInputEvent } from './systems/UIInputSystem';
|
||||
export { UIAnimationSystem, Easing, type EasingFunction, type EasingName } from './systems/UIAnimationSystem';
|
||||
export { UIRenderDataProvider, type IRenderDataProvider, type IUIRenderDataProvider } from './systems/UIRenderDataProvider';
|
||||
export { UIInputSystem, type UIInputEvent } from './systems/UIInputSystem';
|
||||
export { UIAnimationSystem, UIEasing, type EasingFunction, type EasingName } from './systems/UIAnimationSystem';
|
||||
export { UIRenderDataProvider, type IUIRenderDataProvider } from './systems/UIRenderDataProvider';
|
||||
|
||||
// Systems - Render (ECS-compliant render systems)
|
||||
export {
|
||||
|
||||
@@ -13,7 +13,7 @@ export type EasingFunction = (t: number) => number;
|
||||
* 预定义缓动函数
|
||||
* Predefined easing functions
|
||||
*/
|
||||
export const Easing = {
|
||||
export const UIEasing = {
|
||||
linear: (t: number) => t,
|
||||
|
||||
// Quad
|
||||
@@ -126,7 +126,10 @@ export const Easing = {
|
||||
* 缓动函数名称映射
|
||||
* Easing function name mapping
|
||||
*/
|
||||
export type EasingName = keyof typeof Easing;
|
||||
export type EasingName = keyof typeof UIEasing;
|
||||
|
||||
/** @deprecated Use UIEasing instead */
|
||||
export const Easing = UIEasing;
|
||||
|
||||
/**
|
||||
* UI 动画系统
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { EntitySystem, Matcher, Entity, Time, ECSSystem } from '@esengine/ecs-framework';
|
||||
import { sortingLayerManager, MouseButton } from '@esengine/engine-core';
|
||||
import { UITransformComponent } from '../components/UITransformComponent';
|
||||
import { UIInteractableComponent } from '../components/UIInteractableComponent';
|
||||
import { UIButtonComponent } from '../components/widgets/UIButtonComponent';
|
||||
@@ -6,15 +7,9 @@ import { UISliderComponent } from '../components/widgets/UISliderComponent';
|
||||
import { UIScrollViewComponent } from '../components/widgets/UIScrollViewComponent';
|
||||
import type { UILayoutSystem } from './UILayoutSystem';
|
||||
|
||||
/**
|
||||
* 鼠标按钮
|
||||
* Mouse buttons
|
||||
*/
|
||||
export enum MouseButton {
|
||||
Left = 0,
|
||||
Middle = 1,
|
||||
Right = 2
|
||||
}
|
||||
// Re-export MouseButton for backward compatibility
|
||||
// 为向后兼容重新导出 MouseButton
|
||||
export { MouseButton };
|
||||
|
||||
/**
|
||||
* 输入事件数据
|
||||
@@ -210,11 +205,14 @@ export class UIInputSystem extends EntitySystem {
|
||||
|
||||
const dt = Time.deltaTime;
|
||||
|
||||
// 按 zIndex 从高到低排序,确保上层元素优先处理
|
||||
// 按 sortKey 从高到低排序,确保上层元素优先处理
|
||||
// Sort by sortKey from high to low, ensuring top elements are processed first
|
||||
const sorted = [...entities].sort((a, b) => {
|
||||
const ta = a.getComponent(UITransformComponent)!;
|
||||
const tb = b.getComponent(UITransformComponent)!;
|
||||
return tb.zIndex - ta.zIndex;
|
||||
const sortKeyA = sortingLayerManager.getSortKey(ta.sortingLayer, ta.orderInLayer);
|
||||
const sortKeyB = sortingLayerManager.getSortKey(tb.sortingLayer, tb.orderInLayer);
|
||||
return sortKeyB - sortKeyA;
|
||||
});
|
||||
|
||||
let consumed = false;
|
||||
@@ -225,8 +223,9 @@ export class UIInputSystem extends EntitySystem {
|
||||
const transform = entity.getComponent(UITransformComponent)!;
|
||||
const interactable = entity.getComponent(UIInteractableComponent)!;
|
||||
|
||||
// 跳过不可见或禁用的元素
|
||||
if (!transform.visible || !interactable.enabled) {
|
||||
// 跳过不可见或禁用的元素(使用 worldVisible 考虑父级可见性)
|
||||
// Skip invisible or disabled elements (use worldVisible to consider parent visibility)
|
||||
if (!transform.worldVisible || !interactable.enabled) {
|
||||
// 如果之前悬停,触发离开
|
||||
if (interactable.hovered) {
|
||||
this.handleMouseLeave(entity, interactable);
|
||||
|
||||
@@ -111,7 +111,8 @@ export class UILayoutSystem extends EntitySystem {
|
||||
parentWidth: number,
|
||||
parentHeight: number,
|
||||
parentAlpha: number,
|
||||
parentMatrix: Matrix2D
|
||||
parentMatrix: Matrix2D,
|
||||
parentVisible: boolean = true
|
||||
): void {
|
||||
const transform = entity.getComponent(UITransformComponent);
|
||||
if (!transform) return;
|
||||
@@ -194,14 +195,15 @@ export class UILayoutSystem extends EntitySystem {
|
||||
transform.computedHeight = height;
|
||||
transform.worldAlpha = parentAlpha * transform.alpha;
|
||||
|
||||
// 计算世界可见性(父元素不可见则子元素也不可见)
|
||||
// Calculate world visibility (if parent is invisible, children are also invisible)
|
||||
transform.worldVisible = parentVisible && transform.visible;
|
||||
|
||||
// 使用矩阵乘法计算世界变换
|
||||
this.updateWorldMatrix(transform, parentMatrix);
|
||||
|
||||
transform.layoutDirty = false;
|
||||
|
||||
// 如果元素不可见,跳过子元素
|
||||
if (!transform.visible) return;
|
||||
|
||||
// 处理子元素布局
|
||||
const children = this.getUIChildren(entity);
|
||||
if (children.length === 0) return;
|
||||
@@ -224,7 +226,8 @@ export class UILayoutSystem extends EntitySystem {
|
||||
width,
|
||||
height,
|
||||
transform.worldAlpha,
|
||||
transform.localToWorldMatrix
|
||||
transform.localToWorldMatrix,
|
||||
transform.worldVisible
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -361,6 +364,8 @@ export class UILayoutSystem extends EntitySystem {
|
||||
childTransform.computedWidth = size.width;
|
||||
childTransform.computedHeight = childHeight;
|
||||
childTransform.worldAlpha = parentTransform.worldAlpha * childTransform.alpha;
|
||||
// 传播世界可见性 | Propagate world visibility
|
||||
childTransform.worldVisible = parentTransform.worldVisible && childTransform.visible;
|
||||
// 使用矩阵乘法计算世界旋转和缩放
|
||||
this.updateWorldMatrix(childTransform, parentTransform.localToWorldMatrix);
|
||||
childTransform.layoutDirty = false;
|
||||
@@ -459,6 +464,8 @@ export class UILayoutSystem extends EntitySystem {
|
||||
childTransform.computedWidth = childWidth;
|
||||
childTransform.computedHeight = size.height;
|
||||
childTransform.worldAlpha = parentTransform.worldAlpha * childTransform.alpha;
|
||||
// 传播世界可见性 | Propagate world visibility
|
||||
childTransform.worldVisible = parentTransform.worldVisible && childTransform.visible;
|
||||
// 使用矩阵乘法计算世界旋转和缩放
|
||||
this.updateWorldMatrix(childTransform, parentTransform.localToWorldMatrix);
|
||||
childTransform.layoutDirty = false;
|
||||
@@ -515,6 +522,8 @@ export class UILayoutSystem extends EntitySystem {
|
||||
childTransform.computedWidth = cellWidth;
|
||||
childTransform.computedHeight = cellHeight;
|
||||
childTransform.worldAlpha = parentTransform.worldAlpha * childTransform.alpha;
|
||||
// 传播世界可见性 | Propagate world visibility
|
||||
childTransform.worldVisible = parentTransform.worldVisible && childTransform.visible;
|
||||
// 使用矩阵乘法计算世界旋转和缩放
|
||||
this.updateWorldMatrix(childTransform, parentTransform.localToWorldMatrix);
|
||||
childTransform.layoutDirty = false;
|
||||
@@ -575,7 +584,8 @@ export class UILayoutSystem extends EntitySystem {
|
||||
parentTransform.computedWidth,
|
||||
parentTransform.computedHeight,
|
||||
parentTransform.worldAlpha,
|
||||
parentTransform.localToWorldMatrix
|
||||
parentTransform.localToWorldMatrix,
|
||||
parentTransform.worldVisible
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +40,14 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
const collector = getUIRenderCollector();
|
||||
|
||||
for (const entity of entities) {
|
||||
const transform = entity.getComponent(UITransformComponent)!;
|
||||
const button = entity.getComponent(UIButtonComponent)!;
|
||||
const transform = entity.getComponent(UITransformComponent);
|
||||
const button = entity.getComponent(UIButtonComponent);
|
||||
const render = entity.getComponent(UIRenderComponent);
|
||||
|
||||
if (!transform.visible) continue;
|
||||
// 空值检查 | Null check
|
||||
if (!transform || !button) continue;
|
||||
|
||||
if (!transform.worldVisible) continue;
|
||||
|
||||
const x = transform.worldX ?? transform.x;
|
||||
const y = transform.worldY ?? transform.y;
|
||||
@@ -55,7 +58,9 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
const width = (transform.computedWidth ?? transform.width) * scaleX;
|
||||
const height = (transform.computedHeight ?? transform.height) * scaleY;
|
||||
const alpha = transform.worldAlpha ?? transform.alpha;
|
||||
const baseOrder = 100 + transform.zIndex;
|
||||
// 使用排序层和层内顺序 | Use sorting layer and order in layer
|
||||
const sortingLayer = transform.sortingLayer;
|
||||
const orderInLayer = transform.orderInLayer;
|
||||
// 使用 transform 的 pivot 作为旋转/缩放中心
|
||||
const pivotX = transform.pivotX;
|
||||
const pivotY = transform.pivotY;
|
||||
@@ -73,12 +78,13 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
width, height,
|
||||
0xFFFFFF, // White tint for texture
|
||||
alpha,
|
||||
baseOrder,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
{
|
||||
rotation,
|
||||
pivotX,
|
||||
pivotY,
|
||||
texturePath: textureGuid
|
||||
textureGuid
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -94,7 +100,8 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
width, height,
|
||||
button.currentColor,
|
||||
bgAlpha * alpha,
|
||||
baseOrder + (button.useTexture() ? 0.05 : 0),
|
||||
sortingLayer,
|
||||
orderInLayer + (button.useTexture() ? 1 : 0),
|
||||
{
|
||||
rotation,
|
||||
pivotX,
|
||||
@@ -113,7 +120,8 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
render.borderWidth,
|
||||
render.borderColor,
|
||||
render.borderAlpha * alpha,
|
||||
baseOrder + 0.1,
|
||||
sortingLayer,
|
||||
orderInLayer + 2,
|
||||
rotation,
|
||||
pivotX,
|
||||
pivotY
|
||||
@@ -133,7 +141,8 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
borderWidth: number,
|
||||
borderColor: number,
|
||||
alpha: number,
|
||||
sortOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
rotation: number,
|
||||
pivotX: number,
|
||||
pivotY: number
|
||||
@@ -148,7 +157,7 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
(left + right) / 2, top - borderWidth / 2,
|
||||
width, borderWidth,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -156,7 +165,7 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
(left + right) / 2, bottom + borderWidth / 2,
|
||||
width, borderWidth,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -165,7 +174,7 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
left + borderWidth / 2, (top + bottom) / 2,
|
||||
borderWidth, sideBorderHeight,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -173,7 +182,7 @@ export class UIButtonRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
right - borderWidth / 2, (top + bottom) / 2,
|
||||
borderWidth, sideBorderHeight,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,10 +38,13 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
const collector = getUIRenderCollector();
|
||||
|
||||
for (const entity of entities) {
|
||||
const transform = entity.getComponent(UITransformComponent)!;
|
||||
const progressBar = entity.getComponent(UIProgressBarComponent)!;
|
||||
const transform = entity.getComponent(UITransformComponent);
|
||||
const progressBar = entity.getComponent(UIProgressBarComponent);
|
||||
|
||||
if (!transform.visible) continue;
|
||||
// 空值检查 | Null check
|
||||
if (!transform || !progressBar) continue;
|
||||
|
||||
if (!transform.worldVisible) continue;
|
||||
|
||||
const x = transform.worldX ?? transform.x;
|
||||
const y = transform.worldY ?? transform.y;
|
||||
@@ -52,7 +55,9 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
const width = (transform.computedWidth ?? transform.width) * scaleX;
|
||||
const height = (transform.computedHeight ?? transform.height) * scaleY;
|
||||
const alpha = transform.worldAlpha ?? transform.alpha;
|
||||
const baseOrder = 100 + transform.zIndex;
|
||||
// 使用排序层和层内顺序 | Use sorting layer and order in layer
|
||||
const sortingLayer = transform.sortingLayer;
|
||||
const orderInLayer = transform.orderInLayer;
|
||||
// 使用 transform 的 pivot 作为旋转/缩放中心
|
||||
const pivotX = transform.pivotX;
|
||||
const pivotY = transform.pivotY;
|
||||
@@ -67,7 +72,8 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
renderX, renderY, width, height,
|
||||
progressBar.backgroundColor,
|
||||
progressBar.backgroundAlpha * alpha,
|
||||
baseOrder,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
{
|
||||
rotation,
|
||||
pivotX,
|
||||
@@ -84,7 +90,8 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
progressBar.borderWidth,
|
||||
progressBar.borderColor,
|
||||
alpha,
|
||||
baseOrder + 0.2,
|
||||
sortingLayer,
|
||||
orderInLayer + 2,
|
||||
transform,
|
||||
pivotX,
|
||||
pivotY
|
||||
@@ -98,13 +105,13 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
if (progressBar.showSegments) {
|
||||
this.renderSegmentedFill(
|
||||
collector, renderX, renderY, width, height,
|
||||
progress, progressBar, alpha, baseOrder + 0.1, transform,
|
||||
progress, progressBar, alpha, sortingLayer, orderInLayer + 1, transform,
|
||||
pivotX, pivotY
|
||||
);
|
||||
} else {
|
||||
this.renderSolidFill(
|
||||
collector, renderX, renderY, width, height,
|
||||
progress, progressBar, alpha, baseOrder + 0.1, transform,
|
||||
progress, progressBar, alpha, sortingLayer, orderInLayer + 1, transform,
|
||||
pivotX, pivotY
|
||||
);
|
||||
}
|
||||
@@ -125,7 +132,8 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
progress: number,
|
||||
progressBar: UIProgressBarComponent,
|
||||
alpha: number,
|
||||
sortOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
transform: UITransformComponent,
|
||||
pivotX: number,
|
||||
pivotY: number
|
||||
@@ -188,7 +196,8 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
fillX, fillY, fillWidth, fillHeight,
|
||||
fillColor,
|
||||
progressBar.fillAlpha * alpha,
|
||||
sortOrder,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
{
|
||||
rotation,
|
||||
pivotX: 0.5,
|
||||
@@ -210,7 +219,8 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
progress: number,
|
||||
progressBar: UIProgressBarComponent,
|
||||
alpha: number,
|
||||
sortOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
transform: UITransformComponent,
|
||||
pivotX: number,
|
||||
pivotY: number
|
||||
@@ -292,7 +302,8 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
segmentHeight,
|
||||
segmentColor,
|
||||
progressBar.fillAlpha * alpha,
|
||||
sortOrder + i * 0.001,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
{
|
||||
rotation,
|
||||
pivotX: 0.5,
|
||||
@@ -315,7 +326,8 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
borderWidth: number,
|
||||
borderColor: number,
|
||||
alpha: number,
|
||||
sortOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
transform: UITransformComponent,
|
||||
pivotX: number,
|
||||
pivotY: number
|
||||
@@ -332,7 +344,7 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
(left + right) / 2, top - borderWidth / 2,
|
||||
width, borderWidth,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -340,7 +352,7 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
(left + right) / 2, bottom + borderWidth / 2,
|
||||
width, borderWidth,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -349,7 +361,7 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
left + borderWidth / 2, (top + bottom) / 2,
|
||||
borderWidth, sideBorderHeight,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -357,7 +369,7 @@ export class UIProgressBarRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
right - borderWidth / 2, (top + bottom) / 2,
|
||||
borderWidth, sideBorderHeight,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,10 +49,14 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
continue;
|
||||
}
|
||||
|
||||
const transform = entity.getComponent(UITransformComponent)!;
|
||||
const render = entity.getComponent(UIRenderComponent)!;
|
||||
const transform = entity.getComponent(UITransformComponent);
|
||||
const render = entity.getComponent(UIRenderComponent);
|
||||
|
||||
if (!transform.visible) continue;
|
||||
// 空值检查 - 组件可能在反序列化或初始化期间尚未就绪
|
||||
// Null check - component may not be ready during deserialization or initialization
|
||||
if (!transform || !render) continue;
|
||||
|
||||
if (!transform.worldVisible) continue;
|
||||
|
||||
const x = transform.worldX ?? transform.x;
|
||||
const y = transform.worldY ?? transform.y;
|
||||
@@ -64,7 +68,9 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
const alpha = transform.worldAlpha ?? transform.alpha;
|
||||
// 使用世界旋转(考虑父级旋转)
|
||||
const rotation = transform.worldRotation ?? transform.rotation;
|
||||
const baseOrder = 100 + transform.zIndex;
|
||||
// 使用排序层和层内顺序 | Use sorting layer and order in layer
|
||||
const sortingLayer = transform.sortingLayer;
|
||||
const orderInLayer = transform.orderInLayer;
|
||||
// 使用 transform 的 pivot 作为旋转/缩放中心
|
||||
const pivotX = transform.pivotX;
|
||||
const pivotY = transform.pivotY;
|
||||
@@ -85,7 +91,8 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
height + render.shadowBlur * 2,
|
||||
render.shadowColor,
|
||||
render.shadowAlpha * alpha,
|
||||
baseOrder - 0.1,
|
||||
sortingLayer,
|
||||
orderInLayer - 1, // Shadow renders below main content
|
||||
{
|
||||
rotation,
|
||||
pivotX,
|
||||
@@ -97,7 +104,7 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
// Render texture if present
|
||||
// 如果有纹理,渲染纹理
|
||||
if (render.textureGuid) {
|
||||
const texturePath = typeof render.textureGuid === 'string' ? render.textureGuid : undefined;
|
||||
const textureGuid = typeof render.textureGuid === 'string' ? render.textureGuid : undefined;
|
||||
const textureId = typeof render.textureGuid === 'number' ? render.textureGuid : undefined;
|
||||
|
||||
collector.addRect(
|
||||
@@ -105,13 +112,14 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
width, height,
|
||||
render.textureTint,
|
||||
alpha,
|
||||
baseOrder,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
{
|
||||
rotation,
|
||||
pivotX,
|
||||
pivotY,
|
||||
textureId,
|
||||
texturePath,
|
||||
textureGuid,
|
||||
uv: render.textureUV
|
||||
? [render.textureUV.u0, render.textureUV.v0, render.textureUV.u1, render.textureUV.v1]
|
||||
: undefined
|
||||
@@ -126,7 +134,8 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
width, height,
|
||||
render.backgroundColor,
|
||||
render.backgroundAlpha * alpha,
|
||||
baseOrder,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
{
|
||||
rotation,
|
||||
pivotX,
|
||||
@@ -144,7 +153,8 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
render.borderWidth,
|
||||
render.borderColor,
|
||||
render.borderAlpha * alpha,
|
||||
baseOrder + 0.1,
|
||||
sortingLayer,
|
||||
orderInLayer + 1, // Border renders above main content
|
||||
rotation,
|
||||
pivotX,
|
||||
pivotY
|
||||
@@ -164,7 +174,8 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
borderWidth: number,
|
||||
borderColor: number,
|
||||
alpha: number,
|
||||
sortOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
rotation: number,
|
||||
pivotX: number,
|
||||
pivotY: number
|
||||
@@ -181,7 +192,7 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
topBorderCenterX, topBorderCenterY,
|
||||
width, borderWidth,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -190,7 +201,7 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
topBorderCenterX, bottomBorderCenterY,
|
||||
width, borderWidth,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -201,7 +212,7 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
leftBorderCenterX, sideBorderCenterY,
|
||||
borderWidth, sideBorderHeight,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -210,7 +221,7 @@ export class UIRectRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
rightBorderCenterX, sideBorderCenterY,
|
||||
borderWidth, sideBorderHeight,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
* - 预览模式 (previewMode=true): UI 作为屏幕叠加层渲染
|
||||
*/
|
||||
|
||||
import { isValidGUID } from '@esengine/asset-system';
|
||||
import { sortingLayerManager, SortingLayers } from '@esengine/engine-core';
|
||||
|
||||
/**
|
||||
* A single render primitive (rectangle with optional texture)
|
||||
* 单个渲染原语(可选带纹理的矩形)
|
||||
@@ -57,12 +60,14 @@ export interface UIRenderPrimitive {
|
||||
pivotY: number;
|
||||
/** Packed color (0xAABBGGRR) | 打包颜色 */
|
||||
color: number;
|
||||
/** Sort order (lower = rendered first/behind) | 排序顺序 */
|
||||
sortOrder: number;
|
||||
/** 排序层 | Sorting layer */
|
||||
sortingLayer: string;
|
||||
/** 层内排序顺序 | Order within layer */
|
||||
orderInLayer: number;
|
||||
/** Optional texture ID | 可选纹理 ID */
|
||||
textureId?: number;
|
||||
/** Optional texture path | 可选纹理路径 */
|
||||
texturePath?: string;
|
||||
/** Optional texture GUID | 可选纹理 GUID */
|
||||
textureGuid?: string;
|
||||
/** UV coordinates [u0, v0, u1, v1] | UV 坐标 */
|
||||
uv?: [number, number, number, number];
|
||||
}
|
||||
@@ -77,8 +82,12 @@ export interface ProviderRenderData {
|
||||
uvs: Float32Array;
|
||||
colors: Uint32Array;
|
||||
tileCount: number;
|
||||
sortingOrder: number;
|
||||
texturePath?: string;
|
||||
/** 排序层 | Sorting layer */
|
||||
sortingLayer: string;
|
||||
/** 层内排序顺序 | Order within layer */
|
||||
orderInLayer: number;
|
||||
/** 纹理 GUID(如果 textureId 为 0 则使用)| Texture GUID (used if textureId is 0) */
|
||||
textureGuid?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,13 +124,14 @@ export class UIRenderCollector {
|
||||
height: number,
|
||||
color: number,
|
||||
alpha: number,
|
||||
sortOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
options?: {
|
||||
rotation?: number;
|
||||
pivotX?: number;
|
||||
pivotY?: number;
|
||||
textureId?: number;
|
||||
texturePath?: string;
|
||||
textureGuid?: string;
|
||||
uv?: [number, number, number, number];
|
||||
}
|
||||
): void {
|
||||
@@ -141,9 +151,10 @@ export class UIRenderCollector {
|
||||
pivotX: options?.pivotX ?? 0,
|
||||
pivotY: options?.pivotY ?? 0,
|
||||
color: packedColor,
|
||||
sortOrder,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
textureId: options?.textureId,
|
||||
texturePath: options?.texturePath,
|
||||
textureGuid: options?.textureGuid,
|
||||
uv: options?.uv
|
||||
};
|
||||
|
||||
@@ -182,17 +193,23 @@ export class UIRenderCollector {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Sort by sortOrder
|
||||
// 按 sortOrder 排序
|
||||
primitives.sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
// Sort by sortKey (layer order * 10000 + orderInLayer)
|
||||
// 按 sortKey 排序(层顺序 * 10000 + 层内顺序)
|
||||
primitives.sort((a, b) => {
|
||||
const sortKeyA = sortingLayerManager.getSortKey(a.sortingLayer, a.orderInLayer);
|
||||
const sortKeyB = sortingLayerManager.getSortKey(b.sortingLayer, b.orderInLayer);
|
||||
return sortKeyA - sortKeyB;
|
||||
});
|
||||
|
||||
// Group by texture (primitives with same texture can be batched)
|
||||
// 按纹理分组(相同纹理的原语可以批处理)
|
||||
// Group by texture + sortingLayer (primitives with same texture and layer can be batched)
|
||||
// 按纹理 + 排序层分组(相同纹理和层的原语可以批处理)
|
||||
const groups = new Map<string, UIRenderPrimitive[]>();
|
||||
|
||||
for (const prim of primitives) {
|
||||
// Use texture path or 'solid' for solid color rects
|
||||
const key = prim.texturePath ?? (prim.textureId?.toString() ?? 'solid');
|
||||
// Use texture GUID or 'solid' for solid color rects, combined with sorting layer
|
||||
// 使用纹理 GUID 或 'solid' 表示纯色矩形,与排序层组合
|
||||
const textureKey = prim.textureGuid ?? (prim.textureId?.toString() ?? 'solid');
|
||||
const key = `${prim.sortingLayer}:${prim.orderInLayer}:${textureKey}`;
|
||||
let group = groups.get(key);
|
||||
if (!group) {
|
||||
group = [];
|
||||
@@ -212,6 +229,10 @@ export class UIRenderCollector {
|
||||
const uvs = new Float32Array(count * 4);
|
||||
const colors = new Uint32Array(count);
|
||||
|
||||
// Use the first primitive's sorting info (all in group have same layer/order)
|
||||
// 使用第一个原语的排序信息(组内所有原语层/顺序相同)
|
||||
const firstPrim = prims[0];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const p = prims[i];
|
||||
const tOffset = i * 7;
|
||||
@@ -246,28 +267,32 @@ export class UIRenderCollector {
|
||||
colors[i] = p.color;
|
||||
}
|
||||
|
||||
// Use the minimum sortOrder from the group as the batch sortingOrder
|
||||
const minSortOrder = Math.min(...prims.map(p => p.sortOrder));
|
||||
|
||||
const renderData: ProviderRenderData = {
|
||||
transforms,
|
||||
textureIds,
|
||||
uvs,
|
||||
colors,
|
||||
tileCount: count,
|
||||
sortingOrder: minSortOrder
|
||||
sortingLayer: firstPrim.sortingLayer,
|
||||
orderInLayer: firstPrim.orderInLayer
|
||||
};
|
||||
|
||||
// Add texture path if not solid color
|
||||
if (key !== 'solid' && isNaN(parseInt(key))) {
|
||||
renderData.texturePath = key;
|
||||
// Add texture GUID if it's a valid GUID (UUID format)
|
||||
// 如果是有效的 GUID(UUID 格式),则添加纹理 GUID
|
||||
if (firstPrim.textureGuid && isValidGUID(firstPrim.textureGuid)) {
|
||||
renderData.textureGuid = firstPrim.textureGuid;
|
||||
}
|
||||
|
||||
result.push(renderData);
|
||||
}
|
||||
|
||||
// Sort result by sortingOrder
|
||||
result.sort((a, b) => a.sortingOrder - b.sortingOrder);
|
||||
// Sort result by sortKey
|
||||
// 按 sortKey 排序结果
|
||||
result.sort((a, b) => {
|
||||
const sortKeyA = sortingLayerManager.getSortKey(a.sortingLayer, a.orderInLayer);
|
||||
const sortKeyB = sortingLayerManager.getSortKey(b.sortingLayer, b.orderInLayer);
|
||||
return sortKeyA - sortKeyB;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -39,10 +39,13 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
const collector = getUIRenderCollector();
|
||||
|
||||
for (const entity of entities) {
|
||||
const transform = entity.getComponent(UITransformComponent)!;
|
||||
const scrollView = entity.getComponent(UIScrollViewComponent)!;
|
||||
const transform = entity.getComponent(UITransformComponent);
|
||||
const scrollView = entity.getComponent(UIScrollViewComponent);
|
||||
|
||||
if (!transform.visible) continue;
|
||||
// 空值检查 | Null check
|
||||
if (!transform || !scrollView) continue;
|
||||
|
||||
if (!transform.worldVisible) continue;
|
||||
|
||||
const x = transform.worldX ?? transform.x;
|
||||
const y = transform.worldY ?? transform.y;
|
||||
@@ -53,7 +56,9 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
const width = (transform.computedWidth ?? transform.width) * scaleX;
|
||||
const height = (transform.computedHeight ?? transform.height) * scaleY;
|
||||
const alpha = transform.worldAlpha ?? transform.alpha;
|
||||
const baseOrder = 100 + transform.zIndex;
|
||||
// 使用排序层和层内顺序 | Use sorting layer and order in layer
|
||||
const sortingLayer = transform.sortingLayer;
|
||||
const orderInLayer = transform.orderInLayer;
|
||||
// 使用 transform 的 pivot 计算位置
|
||||
const pivotX = transform.pivotX;
|
||||
const pivotY = transform.pivotY;
|
||||
@@ -71,7 +76,7 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
this.renderVerticalScrollbar(
|
||||
collector,
|
||||
baseX, baseY, width, height,
|
||||
scrollView, alpha, baseOrder, rotation
|
||||
scrollView, alpha, sortingLayer, orderInLayer, rotation
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,7 +86,7 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
this.renderHorizontalScrollbar(
|
||||
collector,
|
||||
baseX, baseY, width, height,
|
||||
scrollView, alpha, baseOrder, rotation
|
||||
scrollView, alpha, sortingLayer, orderInLayer, rotation
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -97,7 +102,8 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
viewWidth: number, viewHeight: number,
|
||||
scrollView: UIScrollViewComponent,
|
||||
alpha: number,
|
||||
baseOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
rotation: number
|
||||
): void {
|
||||
const scrollbarWidth = scrollView.scrollbarWidth;
|
||||
@@ -117,7 +123,8 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
scrollbarWidth, trackHeight,
|
||||
scrollView.scrollbarTrackColor,
|
||||
scrollView.scrollbarTrackAlpha * alpha,
|
||||
baseOrder + 0.5,
|
||||
sortingLayer,
|
||||
orderInLayer + 5,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
@@ -140,7 +147,8 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
scrollbarWidth - 2, metrics.size,
|
||||
scrollView.scrollbarColor,
|
||||
handleAlpha * alpha,
|
||||
baseOrder + 0.6,
|
||||
sortingLayer,
|
||||
orderInLayer + 6,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
@@ -155,7 +163,8 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
viewWidth: number, viewHeight: number,
|
||||
scrollView: UIScrollViewComponent,
|
||||
alpha: number,
|
||||
baseOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
rotation: number
|
||||
): void {
|
||||
const scrollbarWidth = scrollView.scrollbarWidth;
|
||||
@@ -175,7 +184,8 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
trackWidth, scrollbarWidth,
|
||||
scrollView.scrollbarTrackColor,
|
||||
scrollView.scrollbarTrackAlpha * alpha,
|
||||
baseOrder + 0.5,
|
||||
sortingLayer,
|
||||
orderInLayer + 5,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
@@ -198,7 +208,8 @@ export class UIScrollViewRenderSystem extends EntitySystem {
|
||||
metrics.size, scrollbarWidth - 2,
|
||||
scrollView.scrollbarColor,
|
||||
handleAlpha * alpha,
|
||||
baseOrder + 0.6,
|
||||
sortingLayer,
|
||||
orderInLayer + 6,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,10 +38,13 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
const collector = getUIRenderCollector();
|
||||
|
||||
for (const entity of entities) {
|
||||
const transform = entity.getComponent(UITransformComponent)!;
|
||||
const slider = entity.getComponent(UISliderComponent)!;
|
||||
const transform = entity.getComponent(UITransformComponent);
|
||||
const slider = entity.getComponent(UISliderComponent);
|
||||
|
||||
if (!transform.visible) continue;
|
||||
// 空值检查 | Null check
|
||||
if (!transform || !slider) continue;
|
||||
|
||||
if (!transform.worldVisible) continue;
|
||||
|
||||
const x = transform.worldX ?? transform.x;
|
||||
const y = transform.worldY ?? transform.y;
|
||||
@@ -52,7 +55,9 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
const width = (transform.computedWidth ?? transform.width) * scaleX;
|
||||
const height = (transform.computedHeight ?? transform.height) * scaleY;
|
||||
const alpha = transform.worldAlpha ?? transform.alpha;
|
||||
const baseOrder = 100 + transform.zIndex;
|
||||
// 使用排序层和层内顺序 | Use sorting layer and order in layer
|
||||
const sortingLayer = transform.sortingLayer;
|
||||
const orderInLayer = transform.orderInLayer;
|
||||
// 使用 transform 的 pivot 计算中心位置
|
||||
const pivotX = transform.pivotX;
|
||||
const pivotY = transform.pivotY;
|
||||
@@ -82,7 +87,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
trackLength, trackThickness,
|
||||
slider.trackColor,
|
||||
slider.trackAlpha * alpha,
|
||||
baseOrder,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
} else {
|
||||
@@ -91,7 +97,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
trackThickness, trackLength,
|
||||
slider.trackColor,
|
||||
slider.trackAlpha * alpha,
|
||||
baseOrder,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
@@ -110,7 +117,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
fillLength, trackThickness,
|
||||
slider.fillColor,
|
||||
slider.fillAlpha * alpha,
|
||||
baseOrder + 0.1,
|
||||
sortingLayer,
|
||||
orderInLayer + 1,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
} else {
|
||||
@@ -121,7 +129,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
trackThickness, fillLength,
|
||||
slider.fillColor,
|
||||
slider.fillAlpha * alpha,
|
||||
baseOrder + 0.1,
|
||||
sortingLayer,
|
||||
orderInLayer + 1,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
@@ -133,7 +142,7 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
this.renderTicks(
|
||||
collector, centerX, centerY,
|
||||
trackLength, trackThickness,
|
||||
slider, alpha, baseOrder + 0.05,
|
||||
slider, alpha, sortingLayer, orderInLayer,
|
||||
isHorizontal, rotation
|
||||
);
|
||||
}
|
||||
@@ -156,7 +165,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
slider.handleWidth, slider.handleHeight,
|
||||
0x000000,
|
||||
0.3 * alpha,
|
||||
baseOrder + 0.15,
|
||||
sortingLayer,
|
||||
orderInLayer + 2,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
@@ -168,7 +178,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
slider.handleWidth, slider.handleHeight,
|
||||
handleColor,
|
||||
alpha,
|
||||
baseOrder + 0.2,
|
||||
sortingLayer,
|
||||
orderInLayer + 3,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -182,7 +193,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
slider.handleBorderWidth,
|
||||
slider.handleBorderColor,
|
||||
alpha,
|
||||
baseOrder + 0.25,
|
||||
sortingLayer,
|
||||
orderInLayer + 4,
|
||||
rotation
|
||||
);
|
||||
}
|
||||
@@ -199,7 +211,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
trackLength: number, trackThickness: number,
|
||||
slider: UISliderComponent,
|
||||
alpha: number,
|
||||
sortOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
isHorizontal: boolean,
|
||||
rotation: number
|
||||
): void {
|
||||
@@ -231,7 +244,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
tickWidth, tickHeight,
|
||||
slider.tickColor,
|
||||
alpha,
|
||||
sortOrder,
|
||||
sortingLayer,
|
||||
orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
@@ -248,7 +262,8 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
borderWidth: number,
|
||||
borderColor: number,
|
||||
alpha: number,
|
||||
sortOrder: number,
|
||||
sortingLayer: string,
|
||||
orderInLayer: number,
|
||||
rotation: number
|
||||
): void {
|
||||
const halfW = width / 2;
|
||||
@@ -259,7 +274,7 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
x, y - halfH + halfB,
|
||||
width, borderWidth,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -267,7 +282,7 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
x, y + halfH - halfB,
|
||||
width, borderWidth,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -275,7 +290,7 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
x - halfW + halfB, y,
|
||||
borderWidth, height - borderWidth * 2,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
|
||||
@@ -283,7 +298,7 @@ export class UISliderRenderSystem extends EntitySystem {
|
||||
collector.addRect(
|
||||
x + halfW - halfB, y,
|
||||
borderWidth, height - borderWidth * 2,
|
||||
borderColor, alpha, sortOrder,
|
||||
borderColor, alpha, sortingLayer, orderInLayer,
|
||||
{ rotation, pivotX: 0.5, pivotY: 0.5 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,10 +94,14 @@ export class UITextRenderSystem extends EntitySystem {
|
||||
const collector = getUIRenderCollector();
|
||||
|
||||
for (const entity of entities) {
|
||||
const transform = entity.getComponent(UITransformComponent)!;
|
||||
const text = entity.getComponent(UITextComponent)!;
|
||||
const transform = entity.getComponent(UITransformComponent);
|
||||
const text = entity.getComponent(UITextComponent);
|
||||
|
||||
if (!transform.visible || !text.text) continue;
|
||||
// 空值检查 - 组件可能在反序列化或初始化期间尚未就绪
|
||||
// Null check - component may not be ready during deserialization or initialization
|
||||
if (!transform || !text) continue;
|
||||
|
||||
if (!transform.worldVisible || !text.text) continue;
|
||||
|
||||
const x = transform.worldX ?? transform.x;
|
||||
const y = transform.worldY ?? transform.y;
|
||||
@@ -108,7 +112,9 @@ export class UITextRenderSystem extends EntitySystem {
|
||||
const width = (transform.computedWidth ?? transform.width) * scaleX;
|
||||
const height = (transform.computedHeight ?? transform.height) * scaleY;
|
||||
const alpha = transform.worldAlpha ?? transform.alpha;
|
||||
const baseOrder = 100 + transform.zIndex;
|
||||
// 使用排序层和层内顺序 | Use sorting layer and order in layer
|
||||
const sortingLayer = transform.sortingLayer;
|
||||
const orderInLayer = transform.orderInLayer;
|
||||
// 使用 transform 的 pivot 作为旋转/缩放中心
|
||||
const pivotX = transform.pivotX;
|
||||
const pivotY = transform.pivotY;
|
||||
@@ -131,7 +137,8 @@ export class UITextRenderSystem extends EntitySystem {
|
||||
width, height,
|
||||
0xFFFFFF, // White tint (color is baked into texture)
|
||||
alpha,
|
||||
baseOrder + 1, // Text renders above background
|
||||
sortingLayer,
|
||||
orderInLayer + 1, // Text renders above background
|
||||
{
|
||||
rotation,
|
||||
pivotX,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* UI module service tokens
|
||||
*/
|
||||
|
||||
import { createServiceToken } from '@esengine/engine-core';
|
||||
import { createServiceToken } from '@esengine/ecs-framework';
|
||||
import type { UILayoutSystem } from './systems/UILayoutSystem';
|
||||
import type { UIInputSystem } from './systems/UIInputSystem';
|
||||
import type { UIRenderDataProvider } from './systems/UIRenderDataProvider';
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2020", "DOM"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"composite": false,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"jsx": "react-jsx",
|
||||
"resolveJsonModule": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
|
||||
Reference in New Issue
Block a user