【qml-12】Quick3D实现机器人鼠标拖拽转换视角(无限角度)与滚轮缩放

背景:

还是这个机器人示教器项目,上一篇说到使用WasdController实现的拖拽视角,并不是很完美。此篇记录的是:

可以使用鼠标拖拽,实现视角的无限切换,无论物体现在什么姿态,只管任意拖拽鼠标,直到想要的姿态为止。

可以使用鼠标滚轮缩放物体大小。

可以一键重置。

WasdController:

之所以说这种方式不好,是基于我最初的需求而言。如果想拖拽鼠标转换视角,那它就有bug。

因为WasdController的功能,实际是控制目标节点的相对姿态,假设目标节点是个有生命虫子,它原本的姿态是跟屏幕前的你一样,面朝屏幕深处,背朝屏幕(也就是你),上下左右跟屏幕前的你一样。

所以想象一下,如果是个3d游戏,极品飞车那种室外视角,屏幕里那个车的姿态。使用WasdController时,在屏幕上拖拽上下左右,车头就会随动。

(图片源于网络,如有侵权请告知)

再想象如果让WasdController控制相机,用鼠标在屏幕上拖动上下左右时,很像极品飞车的车内视角,或者cs这类第一视角的游戏画面效果。

(图片源于网络,如有侵权请告知)

(图片源于网络,如有侵权请告知)

而且WasdController还可以支持键盘操作。所以实际上如果你用quick做个3d游戏,真的可以。

只是对于我最初的需求而言不太合适。所以就有了下面新的方法。

在这之前还是要强调一句,非常推荐先做demo,把可能的各种数值做成可以在界面调整的,范围大一些。方便调试。想象一下,只旋转镜头(camera)的话,等于自己扭头,很可能看不见目标物体,所以要调整参数,才知道具体什么效果。

MouseArea:

其实最早想到的就是用这个了,只不过刚好看到WasdController,就想着尽量简单去实现,无奈它不能胜任,所以下面看如何用MouseArea实现。

先回顾一下我曾做过的一个简单版的预览调试界面:

左边滑块按照从上往下说。

1、横向:实际是调整的机器人节点的z轴欧拉旋转,也就是说,无论总体场景的姿态如何,只要左右拖动,机器人即自己左右转。

2、纵向:实际是机器人父级节点的x轴欧拉旋转。

3、camera xyz:很少用,也就一开始用一下看看效果。镜头上下远近动,物体就动(上下左右正好相反)。

4、上条中,本次使用的就是camera的z,控制远近,也就是大小(近大远小)。

5、后面就是机器人关节控制,本次不说了。

最上面的复位:让1、2归零,以及下文实现的机器人相关参数归零。

下面以代码为例说明。

RobotArea.qml:

只取部分节选。

javascript 复制代码
    //定义属性,用于外界输入参数
    property double _dAngleHorizontal: 0
    property double _dAngleVertical: 0

    //让外界知道鼠标拖拽的幅度
    readonly property double _dX_AngleDraged: _ma._dX_AngleDraged
    readonly property double _dY_AngleDraged: _ma._dY_AngleDraged

    //让外界调用,用于重置姿态
    function f_ResetPosture()
    {
        _ma._rX_prev = 0;
        _ma._rY_prev = 0;
        _ma._dX_draged = 0;
        _ma._dY_draged = 0;
        _ma._dX_AngleDraged = 0;
        _ma._dY_AngleDraged = 0;
    }

    //让外界知道滚轮变化数值,实际对应相机z轴数值,也就是远近
    property double _dScale: 0

    //之前的WasdController不用了
    // WasdController {
    //     controlledObject: scene
    //     xSpeed: -0.1
    //     ySpeed: -0.1
    // }

    MouseArea {
        id: _ma
        anchors.fill: parent
        property real _rX_prev: 0
        property real _rY_prev: 0
        property double _dX_draged: 0
        property double _dY_draged: 0
        property double _dX_AngleDraged: 0
        property double _dY_AngleDraged: 0
        onPressed: {
            _rX_prev = mouseX;
            _rY_prev = mouseY;
        }
        onPositionChanged: {
            let dDraged360 = 360 * 8000 / 720;

            _dX_draged += mouseX - _rX_prev;
            if (_dX_draged > dDraged360) { _dX_draged = 0; }
            if (_dX_draged < -dDraged360) { _dX_draged = 0; }
            _dY_draged += mouseY - _rY_prev;
            if (_dY_draged > dDraged360) { _dY_draged = 0; }
            if (_dY_draged < -dDraged360) { _dY_draged = 0; }

            _dX_AngleDraged = _dX_draged * 720 / 8000;
            _dY_AngleDraged = _dY_draged * 720 / 8000;
        }

        onWheel: (wheel) => {
                     if (wheel.angleDelta.y > 0)
                     {
                         root._dScale++;
                         if (root._dScale > 20) { root._dScale = 20; }
                     }
                     if (wheel.angleDelta.y < 0)
                     {
                         root._dScale--;
                         if (root._dScale < -20) { root._dScale = -20; }
                     }
        }
    }

我认为代码应该见名知意了。我把MouseArea放在RobotArea里面,算是封装,外界调用时更简洁。逻辑上划分清楚。

数据类型:real和double通用就得了,没影响。

drag值:大于360或小于-360时让它归零,就实现无限翻转了。

滚轮wheel值:往前滚动是120,往后是-120,这个qt设计的不好,计数还得自己搞。这个数对应外界camera的z轴值,我的项目具体情况是,z轴取值正负20,同样要自己试过才知道多大值合适。

main_simple.qml:

最外层的界面。将来集成到项目时,也是参考此界面的代码,可以把RobotArea当做组件用。

javascript 复制代码
    RowLayout {
        anchors.fill: parent
        ColumnLayout {
            Layout.fillHeight: true

            //复位按钮
            Button {
                text: "Reset Posture"
                onClicked: {
                    sldr_horizontal.value = 0;
                    sldr_vertical.value = 0;
                    _robot.f_ResetPosture();
                    _robot._dScale = 0;
                }
            }

            //横向姿态
            RowLayout {
                Label { text: "horizontal" }
                Slider {
                    id:sldr_horizontal
                    from: -360
                    to: 360
                    value: 0
                }
            }
            Label { text: sldr_horizontal.value }

            //纵向姿态
            RowLayout {
                Label { text: "vertical" }
                Slider {
                    id:sldr_vertical
                    from: -360
                    to: 360
                    value: 0
                }
            }
            Label { text: sldr_vertical.value }

            ...

            //远近、大小
            RowLayout {
                Label { text: "camera z" }
                Slider {
                    id:sldr_cmr_z
                    from: -20
                    to: 20
                    value: 0
                }
            }
            Label { text: sldr_cmr_z.value }

            ...
        }

        RobotArea {
            id: _robot
            Layout.fillHeight: true
            Layout.fillWidth: true

            //横纵姿态
            _dAngleHorizontal: sldr_horizontal.value
            _dAngleVertical: sldr_vertical.value

            //远近大小
            _dCameraZ: sldr_cmr_z.value

            ...

            //拖拽、滚轮
            on_DX_AngleDragedChanged: sldr_horizontal.value = _dX_AngleDraged
            on_DY_AngleDragedChanged: sldr_vertical.value = _dY_AngleDraged
            on_DScaleChanged: sldr_cmr_z.value = -_dScale
        }
    }

以上种种,有些貌似能用属性绑定很简洁,但不行,有些会发生循环绑定。如果用过TabBar和SwipeView组合,一定知道官方代码有

onCurrentIndexChanged: tabBar.setCurrentIndex(currentIndex)

setCurrentIndex函数就是设置当前索引,但不触发indexChanged信号,很显然就是有些绑定不能让它陷入死循环,要手动控制。这就是这类函数的意义。

效果:

完成上述这些,就可以愉快玩耍了。

左右拖拽:机器人左右转。

上下拖拽:机器人前后翻跟头。

滚轮上下:远缩小,近放大。

任意时候任意角度,可以随便看细节了。

如果不爽,点一下姿态重置按钮,一切从头开始。

本文完。

相关推荐
cdming6 小时前
微软Win11双AI功能来袭:“AI管家”+聊天机器人重构桌面交互体验
人工智能·microsoft·机器人
Lenz's law7 小时前
智元灵犀X1-本体通讯架构分析
机器人
万俟淋曦7 小时前
【论文速递】2025年第30周(Jul-20-26)(Robotics/Embodied AI/LLM)
人工智能·深度学习·ai·机器人·论文·robotics·具身智能
Larry_Yanan11 小时前
QML学习笔记(四十三)QML与C++交互:上下文属性暴露
c++·笔记·qt·学习·ui·交互
郝学胜-神的一滴12 小时前
Three.js光照技术详解:为3D场景注入灵魂
开发语言·前端·javascript·3d·web3·webgl
武子康14 小时前
AI-调查研究-106-具身智能 机器人学习数据采集工具和手段:传感器、API、遥操作、仿真与真人示教全流程
人工智能·深度学习·机器学习·ai·系统架构·机器人·具身智能
jerryinwuhan14 小时前
机器人模拟器(python)
开发语言·python·机器人
列兵阿甘14 小时前
知微传感Dkam系列3D相机SDK例程篇:Python获取内外参
python·数码相机·3d
jerryinwuhan15 小时前
利用舵机实现机器人行走
人工智能·机器人
武子康15 小时前
AI-调查研究-107-具身智能 强化学习与机器人训练数据格式解析:从状态-动作对到多模态轨迹标准
人工智能·深度学习·机器学习·ai·系统架构·机器人·具身智能