【Cocos 3.8.0】2D游戏中如何计算两个物体夹角,如何计算HingeJoint2D关节角度,如何将四元数转为z轴angle

Cocos Creator 3.8.0
2D游戏中如何计算两个物体的角度差?
0. 我是一个2D游戏,只存在Z轴的旋转,但是我在代码中找不到任何方法可以直接获取物体的Z轴旋转。

  1. node.angle是个固定值,即便物体旋转了,node.angle的值也一致不变,不知道是不是BUG
  2. worldRotation是个四元数,我可以得到AB两个物体的四元数,我要如何计算出AB两物体在Z轴的角度相差多少?
  3. 使用worldRotation.getEulerAngles将四元数转换为欧拉角,得到的欧拉角X-Y轴偶尔也会有数值,比如(180,180,30),(0,0,30),所以不能转为欧拉角之后再求Z轴夹角。

start() {

    // 我是一个2D游戏,只存在Z轴旋转,所以我只关心Z轴,不关心四元数和欧拉角的X和Y轴

    // 但是我在代码中找不到任何方法可以直接获取物体的Z轴旋转角度

    const a = this.node.getChildByPath('A');

    const b = this.node.getChildByPath('B');

    // angle属性一直是初始值, 不会根据物体的旋转而变化。所以无法用a.angle - b.angle来计算角度

    console.log(a.angle);

    // worldRotation是四元数, 如何根据a和b的四元数计算出a和b的角度差?

    console.log(a.worldRotation);

    console.log(b.worldRotation);

    // 将四元数转换为欧拉角, 经常会出现X-Y轴也有值的情况

    // 比如有时会返回(180, 180, 30)这种情况, 有时会返回(0, 0, 30)这种情况

    // (180, 180, 30) 和 (0, 0, 30) 表示的不是同一个角度!!!

    const aEulerAngles = new Vec3();

    a.worldRotation.getEulerAngles(aEulerAngles);

    const bEulerAngles = new Vec3();

    b.worldRotation.getEulerAngles(bEulerAngles);

}

image

大家知道如何获取HingeJoint2D关节的角度么?
image

哪位高手指点一下,我请你喝两杯星巴克,V给你

测试了一下物体的angle就是 绕z的欧拉角有值的啊

image
image

你的版本是3.8.x么?我试了好几次,都没值

1赞

一、非常感谢!我测试了你的方法确实可行!
二、但是还有一个缺陷,Quat.angle得到的夹角范围是0-180度之间,我想要的范围是0-360或者[0,180]U[-180,0]。
三、因为如果只有0-180度的话,A相对B顺时针转1度与逆时针转1度所得到的结果都是1,但是明显是不一样的。
四、加我的微信吧(tht651762666)我把咖啡券发个你
image
1

    // 计算两个四元数之间的夹角并打印
    function printRotationInfo(color: string, quat1: Quat, quat2: Quat) {
        // 先求出第二个四元数的共轭
        let conj = new Quat();
        Quat.conjugate(conj, quat2);
        // 计算第二个四元数旋转到第一个四元数所需的差值
        let diff = new Quat();
        Quat.multiply(diff, conj, quat1);

        // 通过差值四元数,可以得出旋转方向和角度
        let direction = diff.z < 0 ? '顺时针' : '逆时针';
        console.log(`diff: ${JSON.stringify(diff)}`);
        // 四元数转换为欧拉角
        let diffEuler = new Vec3();
        Quat.toEuler(diffEuler, diff);
        console.log(`diffEuler: ${JSON.stringify(diffEuler)}`);

        // 打印旋转信息
        console.log(`${color} = 绿色 ${direction} 旋转 ${diffEuler.z} 度`);
    }

    // 计算并打印旋转信息
    printRotationInfo('红色', this.spRed.rotation, this.spGreen.rotation);
    printRotationInfo('蓝色', this.spBlue.rotation, this.spGreen.rotation);

image

方向问题试试这样呢?
咖啡就不用了,谢谢你的好意

我描述有误,有值,但是这个值不会变。例如初始值是20度,然后不管物体如何旋转(相对父物体旋转),再次获取angle都是初始值。

好的谢谢!好人一生平安!我也找到了一个方法,通过Quat.getAxisAngle可以得到旋转轴和旋转弧度。

1赞

找到了一个方法,目前来看没有问题,但是我无法证明此方法没有BUG。
用到的API是Quat.getAxisAngle, 这个函数可以获取四元数的旋转轴和旋转弧度。
对于2D游戏来说,旋转轴要么是Z轴正方向,要么是Z轴负方向。
下面是我的代码,大家可以看看 :grinning:

import { _decorator, Component, HingeJoint2D, Input, input, Label, Node, Quat, RigidBody2D, Vec3 } from 'cc';
const { ccclass } = _decorator;

@ccclass('Main')
export class Main extends Component {
    // 物体B上挂一个HingeJoint2D关节, 将A-B两物体首尾相连
    NodeA: Node;
    NodeB: Node;
    Label: Label;
    Joint: HingeJoint2D;

    start() {
        // 找到所有物体
        this.NodeA = this.node.getChildByPath("A");
        this.NodeB = this.node.getChildByPath("B");
        this.Label = this.node.getChildByPath("Label").getComponent(Label);
        this.Joint = this.NodeB.getComponent(HingeJoint2D);
        // 注册鼠标按下事件
        input.on(Input.EventType.MOUSE_DOWN, this.onMouseDown, this);
    }

    update(dt: number): void {
        // 获取关节角度, 并同步到Label上
        const a = get_joint_angle(this.Joint);
        this.Label.string = `${a.toFixed(1)}`;
    }

    // 鼠标按下时, 给B物体一个扭矩, 使得AB物体转起来, 以便观察角度变化
    onMouseDown() {
        const rbB = this.NodeB.getComponent(RigidBody2D);
        rbB.applyTorque(rbB.getMass() * 100, true);
    }
}
// 将四元数转换为角度, 角度范围为[0-360)
export function quat_to_angle(q: Quat): number {
    // Quat.getAxisAngle()可以获取四元数对应的旋转轴和旋转弧度
    // rad为弧度, 范围是[0, 2*PI), 这是确定的, 我看了源代码
    let axis = new Vec3();
    const rad = Quat.getAxisAngle(axis, q);
    // 如果rad为0或者2*PI, 则直接返回0, 因为此时的axis是没有意义的
    if (rad < 0.000001 || 2 * Math.PI - rad < 0.000001) {
        return 0;
    }
    // 2D游戏中, 只有z轴旋转, x和y轴的值应该(也许,大概,差不多)为0
    // *********************这是我通过观察后得到的结论, 源码中的数学公式看不懂, 我无法证明!!!!!!!!!!!
    // 如果我的结论正确的话, 那么2D游戏的旋转轴只有两种情况:(0,0,1)和(0,0,-1)
    if (Math.abs(axis.x) > 0.001 || Math.abs(axis.y) > 0.001) {
        console.error('quat_to_angle error', axis.x, axis.y, axis.z, rad);
        return 0;
    }
    // 将弧度转换为角度
    const a = rad / Math.PI * 180;
    // 如果旋转轴是Z轴的负方向, 则需要转化一下角度
    if (axis.z < 0) {
        return 360 - a;
    }
    return a;
}


// 将角度规范到[0-360)之间
export function normalized_angle_360(a: number): number {
    while (a < 0) {
        a += 360;
    }
    while (a >= 360) {
        a -= 360;
    }
    return a;
}

// 将角度规范到[0-180]U(-180,0)之间
export function normalized_angle_180(a: number): number {
    a = normalized_angle_360(a);
    if (a > 180) {
        a -= 360;
    }
    return a;
}

// 获取关节角度
// three_hundred_and_sixty为true的话, 返回的范围是[0-360)
// three_hundred_and_sixty为false的话, 返回的范围是[0-180]U(-180,0)
export function get_joint_angle(joint: HingeJoint2D, three_hundred_and_sixty: boolean = false): number {
    const a1 = quat_to_angle(joint.node.worldRotation);
    let a = a1;
    if (joint.connectedBody != null) {
        const a2 = quat_to_angle(joint.connectedBody.node.worldRotation);
        a = a1 - a2;
    }
    if (three_hundred_and_sixty) {
        a = normalized_angle_360(a);
    } else {
        a = normalized_angle_180(a);
    }
    return a;
}

image
image
3

2赞