一, 场景设计

Cocos Creator 3.x之旋转基础_Quat

二,整体场景效果

Cocos Creator 3.x之旋转基础_旋转角度_02

三,QuatView.ts代码

import {_decorator, Component, EventTouch, Input, input, Node, Quat, v3, Vec3, Vec2} from "cc";

const {ccclass, property} = _decorator;

@ccclass("QuatView")
export class QuatView extends Component {
    @property({type: Node, tooltip: "目标cube"})
    private cube: Node = null;
    @property({type: Node, tooltip: "玩家"})
    private player: Node = null;

    private isSlerp: boolean;
    private rotationQuat: Quat;
    private tempQuat: Quat;
    private timerMs: number;
    private durationMs: number;
    private tempV3: Vec3;

    protected onLoad(): void {
        this.isSlerp = false;
        this.rotationQuat = new Quat();
        this.tempQuat = new Quat();
        this.tempV3 = new Vec3();
    }

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

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

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

    private onTouchHandler(e: EventTouch): void {
        switch (e.type) {
            case Input.EventType.TOUCH_END:
                // this.turningToTarget(true);
                break;
            case Input.EventType.TOUCH_MOVE:
                // this.touchRotation(e);
                this.touchAroundRotation(e);
                break;
        }
    }

    /**
     * 转向目标
     */
    private turningToTarget(isSlerp: boolean): void {
        let targetPosition: Vec3 = this.cube.worldPosition
            .subtract(this.player.worldPosition)
            .normalize();//看向的方向 player -> cube
        Quat.fromViewUp(this.rotationQuat, targetPosition, Vec3.UP);//获取转向rotationQuat
        if (Quat.equals(this.rotationQuat, this.player.worldRotation)) {
            this.isSlerp = false;
            console.log(`%c 已经旋转到目标值,不需要再次旋转了`, "color:#F00");
        } else {
            if (!isSlerp) {
                this.player.setWorldRotation(this.rotationQuat);
            } else {
                this.durationMs = 2000;
                this.timerMs = 0;
            }
            this.isSlerp = isSlerp;
        }
    }

    /**
     * 触摸自旋转
     */
    private touchRotation(e: EventTouch): void {
        const delta: Vec2 = e.getDelta();
        const axis: Vec3 = v3(-delta.y, delta.x, 0); //旋转轴,根据相似三角形求出
        const rad: number = delta.length() * 1e-2; //旋转角度
        this.tempQuat = this.player.getRotation(); //当前的四元数
        Quat.rotateAround(this.rotationQuat, this.tempQuat, axis.normalize(), rad); //当面的四元数绕旋转轴旋转
        this.player.setRotation(this.rotationQuat);
    }


    private touchAroundRotation(e: EventTouch): void {
        const delta: Vec2 = e.getDelta();
        // 绕轴转
        // 这里选取轴朝上
        const axis2: Vec3 = Vec3.UP;//旋转轴: Y
        const rad2: number = 1e-2 * delta.x; //旋转角度
        // 计算坐标
        const point: Vec3 = this.cube.worldPosition; //旋转点
        const pointNow: Vec3 = this.player.worldPosition; // 当前点的位置
        // 算出坐标点的旋转四元数
        Quat.fromAxisAngle(this.tempQuat, axis2, rad2);//在zx轴上的旋转量
        // 计算旋转点和现有点的向量
        Vec3.subtract(this.tempV3, pointNow, point);//pointNow - point , cube -> player
        // 计算旋转后的向量
        Vec3.transformQuat(this.tempV3, this.tempV3, this.tempQuat);
        // 计算旋转后的点
        Vec3.add(this.tempV3, point, this.tempV3);//point + this.tempV3
        this.player.setWorldPosition(this.tempV3);

        // 计算朝向
        // 这么旋转会按原始的朝向一起旋转
        const quatNow: Quat = this.player.worldRotation;
        Quat.rotateAround(this.tempQuat, quatNow, axis2, rad2);
        Quat.normalize(this.tempQuat, this.tempQuat);
        this.player.setWorldRotation(this.tempQuat);
    }

    //做一个动画的缓动
    protected update(dt: number): void {
        if (!this.isSlerp) return;
        this.timerMs += dt * 1000;
        const ratio: number = this.timerMs / this.durationMs;
        if (ratio < 1) {
            //先快后慢, 使用球面插值,不然在旋转的时候会出现变形
            this.tempQuat.set(this.player.worldRotation).slerp(this.rotationQuat, ratio);
        } else {
            this.tempQuat = this.rotationQuat;
            this.isSlerp = false;
        }
        this.player.setWorldRotation(this.tempQuat);
    }
}

脚本字段赋值:

Cocos Creator 3.x之旋转基础_四元数_03

 四,效果

1, 转向目标 turningToTarget

Cocos Creator 3.x之旋转基础_四元数_04

    2, 围绕转动

Cocos Creator 3.x之旋转基础_Rotation_05