资讯动态

QML 按键事件篇:方向控制

发布时间:2026/9/10 23:40:38 来源:尧图企业网站定制
这一篇做一个简易的方向控制游戏一个方块在网格场上用 WASD 或方向键控制它移动可以按住实现连续移动、支持斜向移动按 R 键瞬间回到中心。这样就把 Keys 按键事件、时钟节拍、坐标运算串起来做出真正有交互手感的东西。多数人接触键盘控制第一反应是用上一篇的 KeyNavigation但它只适用于跳到另一个固定元素想让人物连续走动就需要另一套思路自己记按键状态再用定时器持续更新位置。这篇讲的就是后者。演示代码import QtQuick import QtQuick.Layouts FadeInAnimation { ColumnLayout { anchors.fill: parent anchors.margins: 20 spacing: 15 TitleSeparator { title: 方向键控制 description: 使用 Keys.onPressed 处理 WASD 与方向键按住移动、松开停止同时按下多键时支持斜向移动。 } // 游戏场地 Rectangle { id: field Layout.fillWidth: true Layout.fillHeight: true radius: 10 color: #FAFAFA border.color: #ddd border.width: 1 clip: true focus: true // 默认获取焦点 activeFocusOnTab: true // 按键状态字典 property var pressedKeys: ({ up: false, down: false, left: false, right: false }) // 玩家角色 Rectangle { id: player width: 32 height: 32 radius: 6 color: #1296FF x: field.width / 2 - width / 2 y: field.height / 2 - height / 2 border.color: #0A6FCC border.width: 2 Behavior on x { NumberAnimation { duration: 60 } } Behavior on y { NumberAnimation { duration: 60 } } } // 状态键提示 Column { anchors.top: parent.top anchors.left: parent.left anchors.margins: 8 spacing: 4 Text { text: field.activeFocus ? 焦点已就绪 : 点击场地获取焦点 font.pointSize: 11 color: field.activeFocus ? #21be2b : #999 font.bold: true } Text { text: 坐标( Math.round(player.x) , Math.round(player.y) ) font.pointSize: 10 color: #666 } } Keys.onPressed: (event) { var handled true switch (event.key) { case Qt.Key_W: case Qt.Key_Up: field.pressedKeys.up true; break case Qt.Key_S: case Qt.Key_Down: field.pressedKeys.down true; break case Qt.Key_A: case Qt.Key_Left: field.pressedKeys.left true; break case Qt.Key_D: case Qt.Key_Right: field.pressedKeys.right true; break case Qt.Key_R: // 回到初始位置先停掉移动定时器避免其把回位动画读出的中间值写回 moveTimer.stop() player.x field.width / 2 - player.width / 2 player.y field.height / 2 - player.height / 2 event.accepted true return // 提前返回不启动移动定时器 default: handled false } if (handled) { event.accepted true field.pressedKeys field.pressedKeys // 强制 binding 刷新状态指示器 moveTimer.start() } } Keys.onReleased: (event) { var handled true switch (event.key) { case Qt.Key_W: case Qt.Key_Up: field.pressedKeys.up false; break case Qt.Key_S: case Qt.Key_Down: field.pressedKeys.down false; break case Qt.Key_A: case Qt.Key_Left: field.pressedKeys.left false; break case Qt.Key_D: case Qt.Key_Right: field.pressedKeys.right false; break default: handled false } if (handled) { event.accepted true field.pressedKeys field.pressedKeys if (!field.pressedKeys.up !field.pressedKeys.down !field.pressedKeys.left !field.pressedKeys.right) { moveTimer.stop() // 所有方向都松开停止移动 } } } // 移动定时器节流刷新位置支持斜向 Timer { id: moveTimer interval: 16 // ~60 FPS repeat: true onTriggered: { var speed 4 var newX player.x var newY player.y if (field.pressedKeys.left) newX - speed if (field.pressedKeys.right) newX speed if (field.pressedKeys.up) newY - speed if (field.pressedKeys.down) newY speed // 边界检测 newX Math.max(0, Math.min(field.width - player.width, newX)) newY Math.max(0, Math.min(field.height - player.height, newY)) player.x newX player.y newY } } MouseArea { anchors.fill: parent onClicked: field.forceActiveFocus() } } // 提示 Row { Layout.alignment: Qt.AlignHCenter spacing: 16 Text { text: WASD / 方向键移动; font.pointSize: 11; color: #666 } Text { text: R回到中心; font.pointSize: 11; color: #666 } } } }注源码里还有一层Canvas画的网格背景和底部一组按键状态指示器这里为聚焦核心的按键逻辑做了省略不影响Keys的使用。关键逻辑解析这一套的核心是四件套按键记录、状态存储、定时器驱动、坐标更新。下面拆开讲。接收焦点是前提场地Rectangle写了focus: true默认拿焦点和activeFocusOnTab: true能被 Tab 到。有了焦点Keys.onPressed/onReleased才收得到事件。旁边还有一句点击场地获取焦点的提示点了之后forceActiveFocus()补拿焦点。缺了这一步按什么键都没反应——这是所有键盘应用的第一道门槛。用字典记录按键状态核心数据结构是一个字典pressedKeys记录上下左右四个键当前是否按着property var pressedKeys: ({ up: false, down: false, left: false, right: false })onPressed里按 W 或上箭头就把up置 trueonReleased里反过来置 false。所以这个字典时刻反映此刻正按着哪些方向键。它不属于方块而属于场地——这样所有按键状态集中在父级好管理。WASD 与方向键等价的写法switch里把两套键写到同一个casecase Qt.Key_W: case Qt.Key_Up: field.pressedKeys.up true break多个case共用一段代码W 和上箭头做一样的事。这是一套控制逻辑双键位支持的常见手法游戏里很常见。为什么不能只在 onPressed 里移动按一下键方块不就应该动一步吗那按住不放呢onPressed只在按下那个瞬间触发一次如果只在里面改位置就会按一下动一下没有连续移动的感觉。所以要实现按住持续走就得把每个按键的开/关记下来再让一个持续的机制定时器去驱动移动。定时器负责连续运动Timer每 16 毫秒触发一次约每秒 60 帧也就是一帧看一次pressedKeys左键按着就往 x 减 4右键按着则加 4上下同理。因为每帧都重新读一次字典所以同时按两个方向x 和 y 一起变就实现了斜向移动——这是KeyNavigation做不到的。位移与边界速度常量speed 4每帧移动 4 像素。边界用Math.max(0, Math.min(边界, 目标值))做夹取把 x 限制在 0 到场地宽 - 方块宽之间这样方块不会跑出场地。按下和松开时都会给pressedKeys重新赋值用来强制刷新那些绑定它的状态指示器原 demo 里底部那排变色的按键灯。R 键回中的细节按 R 时先moveTimer.stop()停掉定时器再把方块坐标设回中心。这里有个隐蔽的坑方块坐标加了Behavior动画如果定时器还在跑动画过渡的中间值可能被下一次定时器写回打断回位过程。所以先停定时器保证回位动画一口气走完。松手停走onReleased里做了收尾把所有方向都置 false 之后检查是否四个方向全松下——如果是moveTimer.stop()。如此按住走、全松停体验干净利落。运行验证Qt Creator 打开示例的CMakeLists.txt按CtrlR运行点击场地获得焦点按住 W或上箭头看方块持续向上走松开即停同时按住 W 和 D看方块沿右上方向斜向移动验证多键支持把方块移到边缘看它被挡住不会越界按 R 看它顺滑回到场地中心。小结把按键字典 定时器驱动这套抽出去就够给贪吃蛇、飞机大战写移动逻辑。速度可做成变量按 Shift 加速、按 Ctrl 减速手感更丰富。定时器改驱动rotation或scale就能做出旋转、缩放类持续交互。已验证环境Qt 版本Qt 6.11.1操作系统Windows 11项目地址QML 示例合集V2.0 - qml_keys_focus

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价