一,场景设计

1,场景

Cocos Creator 3.x之坐标转换_3D

2, 结构

Cocos Creator 3.x之坐标转换_坐标转换_02

3, 在CoordinateConversionView节点上挂载CoordinateConversionView.ts脚本。

二, 代码

import {Camera, Component, EventTouch, find, Input, input, Mat4, Node, UITransform, v3, Vec2, Vec3} from 'cc';

/**
 * 坐标转换
 */
export class CoordinateConversionView extends Component {
    private canvasNode: Node = null;

    private camera3D: Camera = null;
    private cameraUI: Camera = null;

    private cube: Node = null;
    private cubeA: Node = null;
    private cubeB: Node = null;

    private button: Node = null;
    private test1: Node = null;
    private label1: Node = null;
    private test2: Node = null;

    protected start(): void {
        this.cubeA = this.node.getChildByName("CubeA");
        this.cubeB = this.node.getChildByName("CubeB");
        this.canvasNode = this.node.getChildByName("Canvas");
        this.cameraUI = find("Canvas/Camera", this.node).getComponent(Camera);
        this.button = find("Canvas/Button", this.node);
        this.test1 = find("Canvas/Test1", this.node);
        this.label1 = this.test1.getChildByName("Label1");
        this.test2 = find("Canvas/Test2", this.node);
    }

    public init(camera3D: Camera, cube: Node): void {
        this.camera3D = camera3D;
        this.cube = cube;
    }

    protected onEnable(): void {
        this.listener(true);
    }

    protected onDisable(): void {
        this.listener(false);
    }

    private listener(isAdd: boolean): void {
        if (isAdd) {
            input.on(Input.EventType.TOUCH_START, this.onTouchHandler, this);
        } else {
            input.on(Input.EventType.TOUCH_START, this.onTouchHandler, this);
        }
    }

    private onTouchHandler(event: EventTouch): void {
        //屏幕坐标的获取: 2D当中代表UI坐标,3D中代表屏幕坐标
        let screenPos: Vec2 = event.getLocation();
        //UI最表获取
        let uiPos: Vec2 = event.getUILocation();

        console.log(`%c screen x:${screenPos.x} ,y:${screenPos.y}`, "color:#F00");
        console.log(`%c ui x: ${uiPos.x} , y: ${uiPos.y}`, "color:#F00");

        // this.screen2Ui(screenPos, uiPos);
        // this.ui2Screen(screenPos, uiPos);
        // this.screenTo3D(screenPos, uiPos);
        // this.threeD2Screen2UI(screenPos, uiPos);
        // this.threeD2UI(screenPos, uiPos);
        // this.ui2ui(screenPos, uiPos);
        this.threeD(screenPos, uiPos);
    }

    /**
     * <屏幕 => UI>: 点击屏幕让UI上的btnNode节点设置到点击位置上
     */
    private screen2Ui(screenPos: Vec2, uiPos: Vec2): void {
        //方法1: 坐标转换(不会出错)
        let uiPoint: Vec3 = this.cameraUI.screenToWorld(v3(screenPos.x, screenPos.y, 0));
        this.button.setWorldPosition(uiPoint);
        //方法2: 直接使用UI坐标(若:parent的坐标不是(0,0)就会出错)
        // this.btnNode.setWorldPosition(v3(uiPos.x, uiPos.y, 0));
    }

    /**
     * UI坐标到屏幕坐标
     */
    private ui2Screen(screenPos: Vec2, uiPos: Vec2): void {
        let screenPoint: Vec3 = this.cameraUI.worldToScreen(v3(uiPos.x, uiPos.y, 0));
        console.log(
            `%c ui2Screen x: ${screenPoint.x}-${screenPos.x} , y: ${screenPoint.y}-${screenPos.y}`,
            "color:#0F0"
        );
    }

    //设置cube(3D)到屏幕位置
    private screenTo3D(screenPos: Vec2, uiPos: Vec2): void {
        let cZ: number = this.camera3D.node.worldPosition.z / 1000;
        let worldPoint: Vec3 = this.camera3D.screenToWorld(v3(screenPos.x, screenPos.y, cZ));
        this.cube.setWorldPosition(worldPoint);
    }

    /**
     * 3D -> Screen -> UI
     * 将button放在cube的上面
     */
    private threeD2Screen2UI(screenPos: Vec2, uiPos: Vec2): void {
        let screenPoint: Vec3 = this.camera3D.worldToScreen(this.cube.worldPosition);
        let uiPoint: Vec3 = this.cameraUI.screenToWorld(screenPoint);
        this.button.setWorldPosition(uiPoint);
    }

    //将button放在cube的上面
    private threeD2UI(screenPos: Vec2, uiPos: Vec2): void {
        let out: Vec3 = new Vec3();
        this.camera3D.convertToUINode(this.cube.worldPosition, this.canvasNode, out);
        this.button.parent = this.canvasNode;
        this.button.position = out;
    }

    //UI内部坐标转换
    private ui2ui(screenPos: Vec2, uiPos: Vec2): void {
        let pos: Vec3 = new Vec3();
        let test2UITransform: UITransform = this.test2.getComponent(UITransform);
        // test2UITransform.convertToNodeSpaceAR(this.label1.worldPosition, pos);//这种也行
        pos = test2UITransform.convertToNodeSpaceAR(this.label1.worldPosition);
        this.label1.parent = this.test2;
        this.label1.position = pos;
    }

    //将cubeB放在cubeA下
    private threeD(screenPos: Vec2, uiPos: Vec2): void {
        let tempPos: Vec3 = new Vec3();
        let tempMat4: Mat4 = new Mat4();
        Mat4.invert(tempMat4, this.cubeA.getWorldMatrix()); //得到A的逆矩阵
        Vec3.transformMat4(tempPos, this.cubeB.worldPosition, tempMat4); //得到B在A中的坐标
        this.cubeB.parent = this.cubeA;
        this.cubeB.position = tempPos;
    }


    protected onDestroy(): void {
        this.camera3D = null;
        this.cubeA = null;
        this.cubeB = null;
        this.cameraUI = null;
        this.button = null;
        this.test1 = null;
        this.label1 = null;
        this.test2 = null;
        this.cube = null;
        this.canvasNode = null;
    }
}

三, 结果

1,点击屏幕让UI上的btnNode节点设置到点击位置上

/**
     * <屏幕 => UI>: 点击屏幕让UI上的btnNode节点设置到点击位置上
     */
    private screen2Ui(screenPos: Vec2, uiPos: Vec2): void {
        //方法1: 坐标转换(不会出错)
        let uiPoint: Vec3 = this.cameraUI.screenToWorld(v3(screenPos.x, screenPos.y, 0));
        this.button.setWorldPosition(uiPoint);
        //方法2: 直接使用UI坐标(若:parent的坐标不是(0,0)就会出错)
        // this.btnNode.setWorldPosition(v3(uiPos.x, uiPos.y, 0));
    }

Cocos Creator 3.x之坐标转换_3D_03

2,设置cube(3D)到屏幕位置

//设置cube(3D)到屏幕位置
    private screenTo3D(screenPos: Vec2, uiPos: Vec2): void {
        let cZ: number = this.camera3D.node.worldPosition.z / 1000;
        let worldPoint: Vec3 = this.camera3D.screenToWorld(v3(screenPos.x, screenPos.y, cZ));
        this.cube.setWorldPosition(worldPoint);
    }

Cocos Creator 3.x之坐标转换_3.x_04

3, 将button放在cube的上面

/**
     * 3D -> Screen -> UI
     * 将button放在cube的上面
     */
    private threeD2Screen2UI(screenPos: Vec2, uiPos: Vec2): void {
        let screenPoint: Vec3 = this.camera3D.worldToScreen(this.cube.worldPosition);
        let uiPoint: Vec3 = this.cameraUI.screenToWorld(screenPoint);
        this.button.setWorldPosition(uiPoint);
    }

Cocos Creator 3.x之坐标转换_3D_05

4, 将button放在cube的上面

//将button放在cube的上面
    private threeD2UI(screenPos: Vec2, uiPos: Vec2): void {
        let out: Vec3 = new Vec3();
        this.camera3D.convertToUINode(this.cube.worldPosition, this.canvasNode, out);
        this.button.parent = this.canvasNode;
        this.button.position = out;
    }

Cocos Creator 3.x之坐标转换_坐标转换_06

5, UI内部坐标转换

//UI内部坐标转换
    private ui2ui(screenPos: Vec2, uiPos: Vec2): void {
        let pos: Vec3 = new Vec3();
        let test2UITransform: UITransform = this.test2.getComponent(UITransform);
        // test2UITransform.convertToNodeSpaceAR(this.label1.worldPosition, pos);//这种也行
        pos = test2UITransform.convertToNodeSpaceAR(this.label1.worldPosition);
        this.label1.parent = this.test2;
        this.label1.position = pos;
    }

Cocos Creator 3.x之坐标转换_坐标转换_07

6, 将cubeB放在cubeA下

//将cubeB放在cubeA下
    private threeD(screenPos: Vec2, uiPos: Vec2): void {
        let tempPos: Vec3 = new Vec3();
        let tempMat4: Mat4 = new Mat4();
        Mat4.invert(tempMat4, this.cubeA.getWorldMatrix()); //得到A的逆矩阵
        Vec3.transformMat4(tempPos, this.cubeB.worldPosition, tempMat4); //得到B在A中的坐标
        this.cubeB.parent = this.cubeA;
        this.cubeB.position = tempPos;
    }

Cocos Creator 3.x之坐标转换_cocos_08