手势触摸旋转视角/模型 | 社区征文

01

效果演示

Cocos Creator 版本:3.4.1

1摄像机围绕目标旋转并限制垂直方向角度

gif 中演示了随着鼠标滑动,摄像机围绕目标做相应的位移及旋转,水平方向可以360旋转,垂直方向限定在一定角度内,类似于《哈利波特魔法觉醒》中寝室的视角旋转

2 旋转目标节点

gif 中演示了随着鼠标滑动,目标节点做相应的旋转,可用于360度无死角观察目标

3 第一人称视角变换

gif 中演示了随着鼠标滑动,摄像机做相应的旋转,类似于编辑器中鼠标右键旋转视角,或者常见游戏中第一人称视角变换

02

实现方法

1 摄像机围绕目标旋转并限制垂直方向角度

随着手指滑动,摄像机在以目标节点为圆心,以摄像机和目标节点的距离为半径形成的球体表面做相应的位移,并时刻朝向目标节点

首先通过 event.getDelta() 获取当前触摸滑动的偏移值

let delta = event.getDelta();

delta:触点距离上一次事件移动的距离对象

将 delta 分解:

deltaX:水平方向的触摸偏移值

deltaY:垂直方向的触摸偏移值

当手指滑动时

水平方向的滑动偏移对应摄像机绕 Y 轴旋转的弧线(AOB,蓝色弧线),向右为正,向左为负

垂直方向的滑动偏移对应摄像机绕 X 轴旋转的弧线(COD,红色弧线),向下为正,向上为负

将滑动偏移分解后,需要求解的只是摄像机在水平和垂直方向移动一定弧度后的位置及角度

已知:

旋转向量:摄像机到目标节点的向量

旋转轴:水平方向是 Y 轴,即 v3(0, 1, 0),垂直方向是 X 轴的反向,即 v3(-1, 0, 0)

旋转弧度:滑动偏移在水平和垂直方向的分量

根据滑动偏移值计算旋转弧度(速度):

let delta = event.getDelta();
let speed = 0.002;
let horizontal = delta.x * speed;
let vertical = delta.y * speed;

根据旋转轴和旋转角度计算四元数:

let quat = new Quat();
Quat.fromAxisAngle(quat, axis, angle);

根据旋转向量及四元数计算旋转后的坐标:

绕任意轴旋转/绕任意点旋转/平滑旋转/自定义环形体、胶囊体/面向目标位置 中有详细的介绍

let position = v3();
Vec3.subtract(position, node.worldPosition, point);
Vec3.transformQuat(position, position, quat);
Vec3.add(position, point, position);

根据旋转后的坐标及目标节点坐标计算四元数:

let dir = v3();
Vec3.subtract(dir, position, this.nodeTarget.worldPosition);
let rotation = new Quat();
Quat.fromViewUp(rotation, dir.normalize(), Vec3.UP);

根据四元数计算欧拉角:

这里需要对旋转角度做限制,以使场景表现更加合理

涉及到具体的角度时,使用欧拉角更直观

let euler = v3();
rotation.getEulerAngles(euler);
if (euler.x < -40 || euler.x > -10) {
    return;
}

设置节点位置及旋转

node.setWorldPosition(position);
node.setWorldRotation(rotation);

完整代码:

aroundTarget(event: EventTouch) {
    let delta = event.getDelta();
    let speed = 0.002;
    let horizontal = delta.x * speed;
    let vertical = delta.y * speed;
    // 计算水平方向的偏移量
    this.rotateAround(this.nodeCamera, this.nodeTarget.worldPosition, v3(0, 1, 0), horizontal);
    // 计算垂直方向的偏移量
    this.rotateAround(this.nodeCamera, this.nodeTarget.worldPosition, v3(-1, 0, 0), vertical);
}
rotateAround(node: Node, point: Vec3, axis: Vec3, angle: number) {
    // 根据旋转轴和旋转角度构建四元数
    let quat = new Quat();
    Quat.fromAxisAngle(quat, axis, angle);

    // 计算旋转后的位置
    let position = v3();
    Vec3.subtract(position, node.worldPosition, point);
    Vec3.transformQuat(position, position, quat);
    Vec3.add(position, point, position);

    // 根据旋转后的位置计算四元数
    let dir = v3();
    Vec3.subtract(dir, position, this.nodeTarget.worldPosition);
    let rotation = new Quat();
    Quat.fromViewUp(rotation, dir.normalize(), Vec3.UP);

    // 根据四元数计算欧拉角 判断是否在需要的范围内
    let euler = v3();
    rotation.getEulerAngles(euler);
    if (euler.x < -40 || euler.x > -10) {
        return;
    }

    // 设置位置及旋转
    node.setWorldPosition(position);
    node.setWorldRotation(rotation);
}

2 旋转目标节点

随着手指滑动,目标节点以滑动偏移向量的垂线为轴做相应的旋转

已知:

旋转四元数:目标节点的 rotation

旋转轴:触摸偏移向量的垂线,即 (-delta.y, delta.x, 0)

旋转弧度:触摸偏移距离,距离越大,旋转角度越大

完整代码:

rotateTarget(event: EventTouch) {
    let delta = event.getDelta();

    // 计算旋转轴
    let axis = v3(-delta.y, delta.x, 0).normalize();

    // 计算旋转速度
    let rad = delta.length() * 0.02;

    // 将节点的旋转绕
    let quat = new Quat();
    Quat.rotateAround(quat, this.nodeTarget.worldRotation, axis, rad);
    this.nodeTarget.setWorldRotation(quat);
}

3 第一人称视角变换

随着手指滑动,摄像机位置保持不变,只做相应的旋转

相比第一种场景,这里就简单很多,将触摸偏移分解为水平和垂直方向后,直接根据旋转轴和旋转弧度计算四元数

let rotateAround = function (node: Node, axis: Vec3, angle: number) {
    let quat = new Quat();

    // 绕世界空间下指定轴旋转四元数
    Quat.rotateAround(quat, node.worldRotation, axis, angle);

    // 设置旋转
    node.setWorldRotation(quat);
}

完整 demo 公众号回复:
触摸旋转

更多笔记

请扫码关注

12赞

:kissing_heart:又跟大佬学了亿点点

还好我早就关注了

image

学到了,鸭哥yyds

鸦哥,666

学到了,鸭哥DDDD

DDDDdddddd.

膜拜大佬~

鸦哥居然试图教会我 :nerd_face:

用这个方式实现之后左右拖动到某个角度的时候 上下就很难拖动了 是啥原因呀?

大佬太强啦,,