diff --git a/packages/loader/src/resource-deserialize/resources/parser/HierarchyParser.ts b/packages/loader/src/resource-deserialize/resources/parser/HierarchyParser.ts index 2c6bdaa241..ba6263bc01 100644 --- a/packages/loader/src/resource-deserialize/resources/parser/HierarchyParser.ts +++ b/packages/loader/src/resource-deserialize/resources/parser/HierarchyParser.ts @@ -1,4 +1,4 @@ -import { Entity, Engine, Loader, Scene } from "@galacean/engine-core"; +import { Engine, Entity, Loader, Scene, Transform } from "@galacean/engine-core"; import type { IEntity, IHierarchyFile, IRefEntity, IStrippedEntity } from "../schema"; import { ReflectionParser } from "./ReflectionParser"; import { ParserContext, ParserType } from "./ParserContext"; @@ -220,7 +220,8 @@ export abstract class HierarchyParser { - const entity = new Entity(engine, entityConfig.name); + const transform = entityConfig.transform; + const entity = new Entity(engine, entityConfig.name, transform ? Loader.getClass(transform.class) : Transform); if (!entityConfig.parent) this.context.rootIds.push(entityConfig.id); return Promise.resolve(entity); @@ -288,11 +289,17 @@ export abstract class HierarchyParser { return this._getEntityByConfig(entityConfig).then((entity) => { entity.isActive = entityConfig.isActive ?? true; - const { position, rotation, scale } = entityConfig; - if (position) entity.transform.position.copyFrom(position); - if (rotation) entity.transform.rotation.copyFrom(rotation); - if (scale) entity.transform.scale.copyFrom(scale); + const transform = entity.transform; + const transformConfig = entityConfig.transform; + if (transformConfig) { + this.parsePropsAndMethods(transform, transformConfig); + } else { + const { position, rotation, scale } = entityConfig; + if (position) transform.position.copyFrom(position); + if (rotation) transform.rotation.copyFrom(rotation); + if (scale) transform.scale.copyFrom(scale); + } entity.layer = entityConfig.layer ?? entity.layer; // @ts-ignore this._context.type === ParserType.Prefab && entity._markAsTemplate(this._context.resource); @@ -162,7 +168,8 @@ export class ReflectionParser { }) ); } else { - const entity = new Entity(engine, entityConfig.name); + const transform = entityConfig.transform; + const entity = new Entity(engine, entityConfig.name, transform ? Loader.getClass(transform.class) : Transform); return Promise.resolve(entity); } } diff --git a/packages/loader/src/resource-deserialize/resources/schema/BasicSchema.ts b/packages/loader/src/resource-deserialize/resources/schema/BasicSchema.ts index d33b1fdaa6..2e82a81122 100644 --- a/packages/loader/src/resource-deserialize/resources/schema/BasicSchema.ts +++ b/packages/loader/src/resource-deserialize/resources/schema/BasicSchema.ts @@ -38,6 +38,7 @@ export type IMethodParams = Array | IMethod; export interface IBasicEntity { name?: string; id?: string; + transform?: IComponent; components?: Array; isActive?: boolean; position?: IVector3; diff --git a/packages/ui/src/component/UICanvas.ts b/packages/ui/src/component/UICanvas.ts index f4fb01a4b9..0ee7d6d1c1 100644 --- a/packages/ui/src/component/UICanvas.ts +++ b/packages/ui/src/component/UICanvas.ts @@ -1,4 +1,5 @@ import { + BoolUpdateFlag, Camera, CameraModifyFlags, Component, @@ -91,6 +92,10 @@ export class UICanvas extends Component implements IElement { private _referenceResolutionPerUnit: number = 100; @ignoreClone private _hierarchyVersion: number = -1; + @ignoreClone + private _center: Vector3 = new Vector3(); + @ignoreClone + private _centerDirtyFlag: BoolUpdateFlag; /** * The conversion ratio between reference resolution and unit for UI elements in this canvas. @@ -219,6 +224,7 @@ export class UICanvas extends Component implements IElement { // @ts-ignore this._referenceResolution._onValueChanged = this._onReferenceResolutionChanged; this._rootCanvasListener = this._rootCanvasListener.bind(this); + this._centerDirtyFlag = entity.registerWorldChangeFlag(); } raycast(ray: Ray, out: UIHitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean { @@ -260,7 +266,8 @@ export class UICanvas extends Component implements IElement { const { frameCount } = engine.time; // @ts-ignore const renderElement = (this._renderElement = engine._renderElementPool.get()); - this._updateSortDistance(context.virtualCamera.position); + const virtualCamera = context.virtualCamera; + this._updateSortDistance(virtualCamera.isOrthographic, virtualCamera.position, virtualCamera.forward); renderElement.set(this.sortOrder, this._sortDistance); const { width, height } = engine.canvas; const renderers = this._getRenderers(); @@ -297,7 +304,7 @@ export class UICanvas extends Component implements IElement { /** * @internal */ - _updateSortDistance(cameraPosition: Vector3): void { + _updateSortDistance(isOrthographic: boolean, cameraPosition: Vector3, cameraForward: Vector3): void { switch (this._realRenderMode) { case CanvasRenderMode.ScreenSpaceOverlay: this._sortDistance = 0; @@ -306,7 +313,13 @@ export class UICanvas extends Component implements IElement { this._sortDistance = this._distance; break; case CanvasRenderMode.WorldSpace: - this._sortDistance = Vector3.distance(cameraPosition, this.entity.transform.worldPosition); + const boundsCenter = this._getCenter(); + if (isOrthographic) { + Vector3.subtract(boundsCenter, cameraPosition, boundsCenter); + this._sortDistance = Vector3.dot(boundsCenter, cameraForward); + } else { + this._sortDistance = Vector3.distanceSquared(boundsCenter, cameraPosition); + } break; } } @@ -575,6 +588,18 @@ export class UICanvas extends Component implements IElement { } } + private _getCenter(): Vector3 { + if (this._centerDirtyFlag.flag) { + const center = this._center; + const uiTransform = this.entity.transform; + const { pivot, size } = uiTransform; + center.set((0.5 - pivot.x) * size.x, (0.5 - pivot.y) * size.y, 0); + Vector3.transformCoordinate(center, uiTransform.worldMatrix, center); + this._centerDirtyFlag.flag = false; + } + return this._center; + } + private _getRealRenderMode(): number { if (this._isRootCanvas) { const mode = this._renderMode; diff --git a/packages/ui/src/component/advanced/Text.ts b/packages/ui/src/component/advanced/Text.ts index 3efe77a1e7..63b8e9c19d 100644 --- a/packages/ui/src/component/advanced/Text.ts +++ b/packages/ui/src/component/advanced/Text.ts @@ -228,6 +228,7 @@ export class Text extends UIRenderer implements ITextRenderer { const { engine } = this; // @ts-ignore this.font = engine._textDefaultFont; + this.raycastEnabled = false; // @ts-ignore this.setMaterial(engine._basicResources.textDefaultMaterial); } diff --git a/packages/ui/src/component/index.ts b/packages/ui/src/component/index.ts new file mode 100644 index 0000000000..1f89431265 --- /dev/null +++ b/packages/ui/src/component/index.ts @@ -0,0 +1,11 @@ +export { UICanvas } from "./UICanvas"; +export { UIGroup } from "./UIGroup"; +export { UIRenderer } from "./UIRenderer"; +export { UITransform } from "./UITransform"; +export { Button } from "./advanced/Button"; +export { Image } from "./advanced/Image"; +export { Text } from "./advanced/Text"; +export { ColorTransition } from "./interactive/transition/ColorTransition"; +export { ScaleTransition } from "./interactive/transition/ScaleTransition"; +export { SpriteTransition } from "./interactive/transition/SpriteTransition"; +export { Transition } from "./interactive/transition/Transition"; diff --git a/packages/ui/src/component/interactive/UIInteractive.ts b/packages/ui/src/component/interactive/UIInteractive.ts index d432655ec7..c9a7e7ba95 100644 --- a/packages/ui/src/component/interactive/UIInteractive.ts +++ b/packages/ui/src/component/interactive/UIInteractive.ts @@ -154,7 +154,7 @@ export class UIInteractive extends Script implements IGroupAble { override onDestroy(): void { super.onDestroy(); const transitions = this._transitions; - for (let i = 0, n = transitions.length; i < n; i++) { + for (let i = transitions.length - 1; i >= 0; i--) { transitions[i].destroy(); } } diff --git a/packages/ui/src/component/interactive/transition/ColorTransition.ts b/packages/ui/src/component/interactive/transition/ColorTransition.ts index 4d61082348..84790245d2 100644 --- a/packages/ui/src/component/interactive/transition/ColorTransition.ts +++ b/packages/ui/src/component/interactive/transition/ColorTransition.ts @@ -33,19 +33,31 @@ export class ColorTransition extends Transition { } private _onNormalValueChanged(): void { - this._finalState === InteractiveState.Normal && this._updateValue(); + if (this._finalState === InteractiveState.Normal) { + this._finalValue = this._normal; + this._updateValue(); + } } private _onHoverValueChanged(): void { - this._finalState === InteractiveState.Hover && this._updateValue(); + if (this._finalState === InteractiveState.Hover) { + this._finalValue = this._hover; + this._updateValue(); + } } private _onPressedValueChanged(): void { - this._finalState === InteractiveState.Pressed && this._updateValue(); + if (this._finalState === InteractiveState.Pressed) { + this._finalValue = this._pressed; + this._updateValue(); + } } private _onDisabledValueChanged(): void { - this._finalState === InteractiveState.Disable && this._updateValue(); + if (this._finalState === InteractiveState.Disable) { + this._finalValue = this._disabled; + this._updateValue(); + } } protected _getTargetValueCopy(): Color { diff --git a/packages/ui/src/component/interactive/transition/SpriteTransition.ts b/packages/ui/src/component/interactive/transition/SpriteTransition.ts index 35e9d420ed..4a20c1bc13 100644 --- a/packages/ui/src/component/interactive/transition/SpriteTransition.ts +++ b/packages/ui/src/component/interactive/transition/SpriteTransition.ts @@ -44,6 +44,6 @@ export class SpriteTransition extends Transition { } protected override _applyValue(value: Sprite): void { - this._target.sprite = value || this._normal; + this._target.sprite = value; } } diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 886cf922c7..40ace96d19 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -4,7 +4,9 @@ import { CullMode, Engine, Entity, + Font, IClass, + Loader, Material, PipelineStage, ReflectionParser, @@ -12,20 +14,10 @@ import { Shader, ShaderPass } from "@galacean/engine"; +import * as GUIComponent from "./component"; import uiDefaultFs from "./shader/uiDefault.fs.glsl"; import uiDefaultVs from "./shader/uiDefault.vs.glsl"; - -export { UICanvas } from "./component/UICanvas"; -export { UIGroup } from "./component/UIGroup"; -export { UIRenderer } from "./component/UIRenderer"; -export { UITransform } from "./component/UITransform"; -export { Button } from "./component/advanced/Button"; -export { Image } from "./component/advanced/Image"; -export { Text } from "./component/advanced/Text"; -export { ColorTransition } from "./component/interactive/transition/ColorTransition"; -export { ScaleTransition } from "./component/interactive/transition/ScaleTransition"; -export { SpriteTransition } from "./component/interactive/transition/SpriteTransition"; -export { Transition } from "./component/interactive/transition/Transition"; +export * from "./component"; export { CanvasRenderMode } from "./enums/CanvasRenderMode"; export { ResolutionAdaptationMode } from "./enums/ResolutionAdaptationMode"; export { UIPointerEventEmitter } from "./input/UIPointerEventEmitter"; @@ -110,3 +102,12 @@ ReflectionParser.registerCustomParseComponent("Text", async (instance: any, item } return instance; }); + +/** + * Register GUI components for the editor. + */ +export function registerGUI() { + for (let key in GUIComponent) { + Loader.registerClass(key, GUIComponent[key]); + } +} diff --git a/packages/ui/src/input/UIPointerEventEmitter.ts b/packages/ui/src/input/UIPointerEventEmitter.ts index ca37c0ef07..aa02cd0a6e 100644 --- a/packages/ui/src/input/UIPointerEventEmitter.ts +++ b/packages/ui/src/input/UIPointerEventEmitter.ts @@ -66,11 +66,12 @@ export class UIPointerEventEmitter extends PointerEventEmitter { camera.screenPointToRay(pointer.position, ray); /** Other canvases */ - const cameraPosition = camera.entity.transform.position; + const isOrthographic = camera.isOrthographic; + const { worldPosition: cameraPosition, worldForward: cameraForward } = camera.entity.transform; /** Sort by rendering order */ canvasElements = componentsManager._canvases; for (let k = 0, n = canvasElements.length; k < n; k++) { - canvasElements.get(k)._updateSortDistance(cameraPosition); + canvasElements.get(k)._updateSortDistance(isOrthographic, cameraPosition, cameraForward); } canvasElements.sort((a, b) => a.sortOrder - b.sortOrder || a._sortDistance - b._sortDistance); for (let k = 0, n = canvasElements.length; k < n; k++) { @@ -80,7 +81,7 @@ export class UIPointerEventEmitter extends PointerEventEmitter { /** Post-rendering first detection */ for (let k = 0, n = canvasElements.length; k < n; k++) { const canvas = canvasElements.get(k); - if (canvas.renderCamera !== camera) continue; + if (!canvas._canRender(camera)) continue; if (canvas.raycast(ray, hitResult, farClipPlane)) { this._updateRaycast((hitResult).component, pointer); return;