四元数(Quaternion)是一种复数扩展,用于表示在三维空间中的旋转。它由一个实部和三个虚部构成,可以用四个实数来表示:,其中 是基本单位向量,满足以下关系:
通过四元数表达旋转时,我们通常将四元数的实部设为 ,而将虚部作为一个三维向量 表示旋转轴,并将其长度设为旋转角度 。这样就形成了一个单位四元数:。
四元数的好处是能够快速地进行旋转的叠加运算,比如顺序执行两个旋转,可以将它们对应的四元数相乘得到新的旋转对应的四元数。同时,四元数不会存在万向锁问题,因此在计算机图形学领域广泛使用。
如题,假如我们要实现地球,月球的自转以及月球的公转,具体来说,需要两个变量保存地球和月球的旋转四元数。每帧更新时,先将这两个四元数分别绕着自己的 Y 轴旋转一定角度,然后再更新月球的位置,这个位置是参照地球的位置计算得出的。将月球坐标系中的 Z 轴旋转对应的角度转换到地球坐标系下,就可以得到相对于地球的偏移。
具体代码RotateAround.ts如下:
import { _decorator, Component, Node, Quat, Vec3, v3, quat } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('RotateAround')
export class RotateAround extends Component {
// 地球节点
@property({ type: Node })
earth: Node = null;
// 月球节点
@property({ type: Node })
moon: Node = null;
// 地球自转速度
private earthRotationSpeed: number = 1;
// 月球自转速度
private moonRotationSpeed: number = 2;
// 存储地球和月球节点自身旋转的四元数
private earthRotation: Quat = quat();
private moonRotation: Quat = quat();
/** 地球节点位置 */
private earthPos: Vec3 = v3();
// start 函数会在脚本组件第一次被添加到节点上时被调用
start () {
// 复制地球和月球节点的旋转四元数
this.earthRotation = this.earth.rotation.clone();
this.moonRotation = this.moon.rotation.clone();
}
// update 函数会在每一帧被调用
update (deltaTime: number) {
this.updateEarthRotation(deltaTime);
this.updateMoonRotation(deltaTime);
this.updateMoonOrbit(deltaTime);
}
// 更新地球自身的旋转
updateEarthRotation (deltaTime: number) {
Quat.rotateY(this.earthRotation, this.earthRotation, this.earthRotationSpeed * deltaTime);
this.earth.rotation = this.earthRotation;
}
// 更新月球自身的旋转
updateMoonRotation (deltaTime: number) {
Quat.rotateY(this.moonRotation, this.moonRotation, this.moonRotationSpeed * deltaTime);
this.moon.rotation = this.moonRotation;
}
// 更新月球绕地球轨道的位置
updateMoonOrbit (deltaTime: number) {
// 获取地球当前的世界坐标位置,并存储到 earthPos 中
this.earth.getWorldPosition(this.earthPos);
// 指向地球节点的向量
let dir = new Vec3(0, 0, -1);
// 将 dir 向量绕着 moonRotation 所表示的轴进行旋转
Vec3.transformQuat(dir, dir, this.moonRotation);
// 计算出月球在轨道上应该的位置
Vec3.scaleAndAdd(this.earthPos, this.earthPos, dir, 3);
// 设置月球在场景中的位置为计算后的新位置
this.moon.setWorldPosition(this.earthPos);
}
}
将此文件添加到场景中的scriptNode节点上,并将bigBall和smallBall节点分别拉到右侧中Earth和Moon选项中,
点击运行后,效果如下:
由于没有地球和月球模型,这里简单用足球模型表示。
到这里就简单实现用四元数实现了地球的公转和月球的自转