From e609141facfc1c25f862b1dcb3a802dc67bdca70 Mon Sep 17 00:00:00 2001 From: AZhan Date: Fri, 7 Mar 2025 16:15:44 +0800 Subject: [PATCH] Fix UICanvas adaptation issues (#2580) * fix: the UICanvas.renderMode is CanvasRenderMode.ScreenSpaceCamera bug * fix: the issue with incorrect cache value of UICanvas._getCenter * fix: the issue that UICanvas did not refresh in time when setting the camera multiple times --- packages/ui/src/component/UICanvas.ts | 51 ++++++++++++++++++++------- tests/src/ui/UICanvas.test.ts | 43 ++++++++++++++++++++++ 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/packages/ui/src/component/UICanvas.ts b/packages/ui/src/component/UICanvas.ts index 44e9f4e55..45dc1109d 100644 --- a/packages/ui/src/component/UICanvas.ts +++ b/packages/ui/src/component/UICanvas.ts @@ -7,6 +7,7 @@ import { DisorderedArray, Entity, EntityModifyFlags, + Logger, MathUtil, Matrix, Ray, @@ -153,9 +154,22 @@ export class UICanvas extends Component implements IElement { set renderCamera(value: Camera) { const preCamera = this._renderCamera; if (preCamera !== value) { + this._isSameOrChildEntity(value.entity) && + Logger.warn( + "Camera entity matching or nested within the canvas entity disables canvas auto-adaptation in ScreenSpaceCamera mode." + ); this._renderCamera = value; this._updateCameraObserver(); - this._setRealRenderMode(this._getRealRenderMode()); + const preRenderMode = this._realRenderMode; + const curRenderMode = this._getRealRenderMode(); + if (preRenderMode === curRenderMode) { + if (curRenderMode === CanvasRenderMode.ScreenSpaceCamera) { + this._adapterPoseInScreenSpace(); + this._adapterSizeInScreenSpace(); + } + } else { + this._setRealRenderMode(curRenderMode); + } } } @@ -318,8 +332,9 @@ export class UICanvas extends Component implements IElement { case CanvasRenderMode.WorldSpace: const boundsCenter = this._getCenter(); if (isOrthographic) { - Vector3.subtract(boundsCenter, cameraPosition, boundsCenter); - this._sortDistance = Vector3.dot(boundsCenter, cameraForward); + const distance = UICanvas._tempVec3; + Vector3.subtract(boundsCenter, cameraPosition, distance); + this._sortDistance = Vector3.dot(distance, cameraForward); } else { this._sortDistance = Vector3.distanceSquared(boundsCenter, cameraPosition); } @@ -382,15 +397,18 @@ export class UICanvas extends Component implements IElement { const transform = this.entity.transform; const realRenderMode = this._realRenderMode; if (realRenderMode === CanvasRenderMode.ScreenSpaceCamera) { - const { transform: cameraTransform } = this._renderCamera.entity; - const { worldPosition: cameraWorldPosition, worldForward: cameraWorldForward } = cameraTransform; - const distance = this._distance; - transform.setWorldPosition( - cameraWorldPosition.x + cameraWorldForward.x * distance, - cameraWorldPosition.y + cameraWorldForward.y * distance, - cameraWorldPosition.z + cameraWorldForward.z * distance - ); - transform.worldRotationQuaternion.copyFrom(cameraTransform.worldRotationQuaternion); + const cameraEntity = this._renderCamera.entity; + if (!this._isSameOrChildEntity(cameraEntity)) { + const { transform: cameraTransform } = cameraEntity; + const { worldPosition: cameraWorldPosition, worldForward: cameraWorldForward } = cameraTransform; + const distance = this._distance; + transform.setWorldPosition( + cameraWorldPosition.x + cameraWorldForward.x * distance, + cameraWorldPosition.y + cameraWorldForward.y * distance, + cameraWorldPosition.z + cameraWorldForward.z * distance + ); + transform.worldRotationQuaternion.copyFrom(cameraTransform.worldRotationQuaternion); + } } else { const { canvas } = this.engine; transform.setWorldPosition(canvas.width * 0.5, canvas.height * 0.5, 0); @@ -646,6 +664,15 @@ export class UICanvas extends Component implements IElement { } } } + + private _isSameOrChildEntity(cameraEntity: Entity): boolean { + const canvasEntity = this.entity; + while (cameraEntity) { + if (cameraEntity === canvasEntity) return true; + cameraEntity = cameraEntity.parent; + } + return false; + } } /** diff --git a/tests/src/ui/UICanvas.test.ts b/tests/src/ui/UICanvas.test.ts index 292a0fe25..618fd6a40 100644 --- a/tests/src/ui/UICanvas.test.ts +++ b/tests/src/ui/UICanvas.test.ts @@ -95,6 +95,49 @@ describe("UICanvas", async () => { expect(rootCanvas._isRootCanvas).to.eq(true); }); + // Pose + it("Pose Fit", () => { + const canvasTransform = canvasEntity.transform; + const canvasPosition = canvasTransform.position; + rootCanvas.referenceResolution = new Vector2(800, 600); + rootCanvas.renderMode = CanvasRenderMode.ScreenSpaceCamera; + rootCanvas.distance = 10; + canvasPosition.set(0, 0, 0); + expect(canvasPosition.x).to.eq(0); + expect(canvasPosition.y).to.eq(0); + expect(canvasPosition.z).to.eq(0); + + // Same entity + const cameraSame = canvasEntity.addComponent(Camera); + rootCanvas.renderCamera = cameraSame; + expect(canvasPosition.x).to.eq(0); + expect(canvasPosition.y).to.eq(0); + expect(canvasPosition.z).to.eq(0); + rootCanvas.distance = 100; + expect(canvasPosition.x).to.eq(0); + expect(canvasPosition.y).to.eq(0); + expect(canvasPosition.z).to.eq(0); + + // Not same entity or child entity + rootCanvas.renderCamera = camera; + expect(canvasPosition.x).to.eq(0); + expect(canvasPosition.y).to.eq(0); + expect(canvasPosition.z).to.eq(-100); + rootCanvas.distance = 10; + expect(canvasPosition.x).to.eq(0); + expect(canvasPosition.y).to.eq(0); + expect(canvasPosition.z).to.eq(-10); + + // Child entity + const cameraEntityChild = canvasEntity.createChild("cameraChild"); + const cameraChild = cameraEntityChild.addComponent(Camera); + cameraEntityChild.transform.setPosition(2, 2, 2); + rootCanvas.renderCamera = cameraChild; + expect(canvasPosition.x).to.eq(0); + expect(canvasPosition.y).to.eq(0); + expect(canvasPosition.z).to.eq(-10); + }); + // Size it("Size Fit", () => { rootCanvas.referenceResolution = new Vector2(800, 600);