类似血条跟随(为啥有偏移)

代码如下

import { _decorator, Camera, Component, Node, UITransform, Vec3 } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('test')
export class test extends Component {
    @property(Node)
    targetNode: Node = null;  // 3D 物体

    @property(Camera)
    mainCamera: Camera = null; // 3D 摄像机

    @property(Node)
    uiNode: Node = null; // 2D 跟随的 Sprite (UI)

    @property(Node)
    canvasNode: Node = null; // UI 的 Canvas 节点

    private screenPos = new Vec3();
    private uiPos = new Vec3();

    update() {
        if (!this.targetNode || !this.mainCamera || !this.uiNode || !this.canvasNode) {
            console.error("缺少 targetNode, mainCamera, uiNode 或 canvasNode");
            return;
        }

        // **1. 获取 3D 物体的世界坐标**
        let worldPosition = this.targetNode.worldPosition;
        // **2. 转换为屏幕坐标**
        this.mainCamera.worldToScreen(worldPosition, this.screenPos);
        // **3. 将屏幕坐标转换为 Canvas 的 UI 坐标**
        let canvasUI = this.canvasNode.getComponent(UITransform);
        if (!canvasUI) {
            console.error("Canvas 缺少 UITransform 组件");
            return;
        }

        // 屏幕坐标 -> Canvas 内的 UI 坐标
        canvasUI.convertToNodeSpaceAR(new Vec3(this.screenPos.x, this.screenPos.y, 0), this.uiPos);

        // **4. 更新 UI 节点的位置**
        this.uiNode.setPosition(this.uiPos);
    }
}

多移动几个地方,总是差一些

层级是这样

mainCamera 会移动吗

用3DUI试试?直接把血条挂到3D物体上
RenderRoot2D 组件 | Cocos Creator

1赞

放到lateUpdate,透视相机会这样的,正交会靠近边缘有视觉上偏移和fov有关
也可以自己写(gif 20fps 凑乎看)

Kapture 2025-02-11 at 11.15.48

    public persWorldtoOrthoWorld(
        persCam: renderer.scene.Camera,
        orthoCam: renderer.scene.Camera,
        out: Vec3,
        worldPos: Vec3
      ): Vec3 {
        // 1. 使用透视相机的 viewProj 矩阵转到标准化坐标系 [-1, 1]
        const matViewProj = persCam.matViewProj;
        Vec3.transformMat4(out, worldPos, matViewProj);

        // 2. 同时转换 x 和 y,合并了中间的 0.5 和 2 的因子
        out.x = (out.x + 1) * persCam.width / orthoCam.width - 1;
        out.y = (out.y + 1) * persCam.height / orthoCam.height - 1;
        // 3. 使用正交相机的逆 viewProj 矩阵,回到正交相机的世界坐标
        const matViewProjInv = orthoCam.matViewProjInv;
        Vec3.transformMat4(out, out, matViewProjInv);
        return out;
      }
    protected lateUpdate(dt: number): void {
        const pos = this.cubeHead.worldPosition;
        this.persWorldtoOrthoWorld(this.perspectiveCamera.camera,this.orthographicCamera.camera,_temp_vec3_1, pos)
        this.sp2d_Test.setWorldPosition(_temp_vec3_1)
    }

// 假设 out 在此时已经是通过透视相机的 viewProj 矩阵转换后的坐标,属于标准化设备坐标 (NDC),范围为 [-1, 1]。
// 这里我们需要将这个 [-1, 1] 范围的坐标映射到屏幕空间,再根据透视相机和正交相机视口宽度(或高度)的比例进行调整,
// 最后再还原成 [-1, 1] 的范围,以便使用正交相机的逆矩阵转换回对应的世界坐标。
// 具体步骤分解如下:
//
// 1. (out.x + 1) 将原始 [-1,1] 范围的坐标平移至 [0,2](同理 out.y + 1)。
//    这一步相当于先将负数部分(-1)平移到 0,使得整个区间正数化,便于后续的比例计算;
//
// 2. 乘以 persCam.width / orthoCam.width
//    此处的 persCam.width 是透视相机视口的逻辑宽度,而 orthoCam.width 是正交相机的宽度,
//    两者之比用于调整由于不同相机视口尺寸差异导致的缩放误差。原理上可以理解为:
//      - 先将 [0,2] 映射到对应的透视屏幕宽度,再根据正交相机的宽度,转换成正交相机下的归一化范围;
//
// 3. 最后再减去 1,将上面得到的 [0,2] 范围重新平移回 [-1,1]。
//    这一步恢复了标准化设备坐标系的最终分布,便于后续用正交相机矩阵进行世界坐标转换。
//
// 融合了先将 NDC 坐标转为 [0,2]、再调整比例(乘以 persCam.width/orthoCam.width)、
// 最后平移回 [-1,1] 的操作,相当于将透视相机的标准化坐标转换到正交相机的标准化坐标,简化了中间处理中 0.5 和 2 的因子。

同时调整2D物体的Anchor Point
Kapture 2025-02-11 at 11.26.34

2赞

太6了,还可以这样

哦,又学到了

不会移动,我主要是测下这个api怎么用的

不过我是测下那个api怎么用,我项目里有一个3d物体的点,我要转换成2d的屏幕坐标的点和鼠标坐标进行计算,死活不对。写了这个测试的demo发现就是有偏差 :rofl:。不造是我api用法有问题还是其他的什么用法错误。还是得用矩阵算么 :rofl:

worldToScreen 这个api有问题吧,看其他帖子也提了,没人说明白这个


解决了,根据这个文章解决。https://mp.weixin.qq.com/s/_EOYcNB0KgSi-GjuIexk3g
换一个api就解决了 convertToUINode
完整代码如下:


@ccclass('test')
export class test extends Component {
    @property(Node)
    targetNode: Node = null;  // 3D 物体

    @property(Camera)
    mainCamera: Camera = null; // 3D 摄像机

    @property(Node)
    uiNode: Node = null; // 2D 跟随的 Sprite (UI)

    @property(Node)
    canvasNode: Node = null; // UI 的 Canvas 节点

    private screenPos = new Vec3();
    private uiPos = new Vec3();


    protected start(): void {
        this.schedule(this.test, 3);
    }

    test() {
        if (!this.targetNode || !this.mainCamera || !this.uiNode || !this.canvasNode) {
            console.error("缺少 targetNode, mainCamera, uiNode 或 canvasNode");
            return;
        }

        let worldPosition = this.targetNode.worldPosition;
        this.mainCamera.convertToUINode(worldPosition, this.uiNode,this.screenPos );
        this.uiNode.setPosition(this.screenPos);

    }
}