news 2026/6/4 2:00:55

Flutter实战:从零实现俄罗斯方块(三)交互控制与事件处理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flutter实战:从零实现俄罗斯方块(三)交互控制与事件处理

Flutter实战:从零实现俄罗斯方块(三)交互控制与事件处理

文章目录

  • Flutter实战:从零实现俄罗斯方块(三)交互控制与事件处理
    • 摘要
    • 前言
    • 一、键盘事件监听
      • 1.1 RawKeyboardListener的基本用法
      • 1.2 如何映射按键到游戏操作?
      • 1.3 WASD键位支持
    • 二、触摸按钮控制
      • 2.1 按钮布局设计
      • 2.2 按钮交互反馈
      • 2.3 长按连续操作
    • 三、游戏状态管理
      • 3.1 状态机设计
      • 3.2 暂停和继续功能
      • 3.3 游戏结束处理
    • 四、焦点管理
      • 4.1 FocusNode的作用
      • 4.2 自动获取焦点
      • 4.3 点击重新获取焦点
    • 五、事件处理常见问题
      • 问题1:按键没有响应
      • 问题2:按键触发了多次
      • 问题3:触摸按钮太小
    • 六、本文小结
    • 参考资料
    • 社区支持

摘要

这是我用Flutter开发俄罗斯方块游戏的第三篇文章,主要讲解游戏的交互控制实现。我会分享如何使用RawKeyboardListener监听键盘事件、如何设计触摸控制按钮、如何管理游戏状态(暂停、结束),以及FocusNode焦点管理的技巧。通过这篇文章,你可以了解到Flutter事件处理的完整流程。

关键词:Flutter、事件处理、RawKeyboardListener、FocusNode、游戏控制、OpenHarmony

前言

在前两篇文章中,我已经实现了俄罗斯方块游戏的数据结构和绘制功能。但游戏还需要玩家能够控制方块才行!

这篇文章我主要解决三个问题:

  1. 如何让键盘控制方块移动和旋转?
  2. 如何添加屏幕按钮方便触摸屏操作?
  3. 如何处理暂停、游戏结束等状态?

系列说明:这是Flutter俄罗斯方块游戏开发系列教程的第3篇也是最后一篇。


一、键盘事件监听

1.1 RawKeyboardListener的基本用法

Flutter提供了RawKeyboardListener Widget来监听键盘事件:

RawKeyboardListener(focusNode:_focusNode,onKey:_handleKeyEvent,autofocus:true,child:Scaffold(// 游戏界面...),)

三个关键参数

  • focusNode:焦点控制器(必须有)
  • onKey:按键回调函数
  • autofocus:是否自动获取焦点

1.2 如何映射按键到游戏操作?

我在_handleKeyEvent函数中处理按键:

void_handleKeyEvent(RawKeyEventevent){// 只处理按键按下事件if(eventis!RawKeyDownEvent)return;if(event.logicalKey==LogicalKeyboardKey.arrowLeft){_game.moveLeft();}elseif(event.logicalKey==LogicalKeyboardKey.arrowRight){_game.moveRight();}elseif(event.logicalKey==LogicalKeyboardKey.arrowDown){_game.moveDown();}elseif(event.logicalKey==LogicalKeyboardKey.arrowUp||event.logicalKey==LogicalKeyboardKey.space){_game.rotate();}elseif(event.logicalKey==LogicalKeyboardKey.keyP){_game.togglePause();}}

按键映射表

按键LogicalKeyboardKey功能
arrowLeft左移一格
arrowRight右移一格
arrowDown加速下落
arrowUp旋转方块
Spacespace旋转方块
PkeyP暂停/继续

1.3 WASD键位支持

很多游戏玩家习惯用WASD控制,我也加上:

void_handleKeyEvent(RawKeyEventevent){if(eventis!RawKeyDownEvent)return;if(event.logicalKey==LogicalKeyboardKey.arrowLeft||event.logicalKey==LogicalKeyboardKey.keyA){_game.moveLeft();}elseif(event.logicalKey==LogicalKeyboardKey.arrowRight||event.logicalKey==LogicalKeyboardKey.keyD){_game.moveRight();}elseif(event.logicalKey==LogicalKeyboardKey.arrowDown||event.logicalKey==LogicalKeyboardKey.keyS){_game.moveDown();}elseif(event.logicalKey==LogicalKeyboardKey.arrowUp||event.logicalKey==LogicalKeyboardKey.keyW||event.logicalKey==LogicalKeyboardKey.space){_game.rotate();}elseif(event.logicalKey==LogicalKeyboardKey.keyP){_game.togglePause();}}

二、触摸按钮控制

2.1 按钮布局设计

对于触摸屏设备,我设计了方向键布局:

Widget_buildControls(double buttonSize){returnContainer(padding:constEdgeInsets.all(10),decoration:BoxDecoration(color:Colors.grey[850],borderRadius:BorderRadius.circular(8),border:Border.all(color:Colors.cyan[300]!,width:2),),child:Column(children:[// 第一行:↑键Row(mainAxisAlignment:MainAxisAlignment.center,children:[_buildControlButton('↑',buttonSize,()=>_game.rotate()),],),constSizedBox(height:5),// 第二行:← ↓ →键Row(mainAxisAlignment:MainAxisAlignment.center,children:[_buildControlButton('←',buttonSize,()=>_game.moveLeft()),constSizedBox(width:5),_buildControlButton('↓',buttonSize,()=>_game.moveDown()),constSizedBox(width:5),_buildControlButton('→',buttonSize,()=>_game.moveRight()),],),constSizedBox(height:10),// 第三行:暂停键_buildControlButton('⏸',buttonSize*3+10,()=>_game.togglePause()),],),);}

布局效果

2.2 按钮交互反馈

Widget_buildControlButton(Stringlabel,double size,VoidCallbackonPressed){returnSizedBox(width:size,height:size,child:ElevatedButton(onPressed:onPressed,style:ElevatedButton.styleFrom(backgroundColor:Colors.cyan[700],foregroundColor:Colors.white,shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(8),),elevation:4,// 阴影效果),child:FittedBox(child:Text(label,style:constTextStyle(fontSize:20,fontWeight:FontWeight.bold,),),),),);}

2.3 长按连续操作

为了方便操作,我实现了长按连续移动:

Widget_buildContinuousButton({requiredStringlabel,required double size,requiredVoidCallbackonAction,}){returnGestureDetector(onLongPressStart:(_){_repeatTimer=Timer.periodic(constDuration(milliseconds:50),(timer)=>onAction(),);},onLongPressEnd:(_){_repeatTimer?.cancel();},onTap:onAction,child:Container(width:size,height:size,decoration:BoxDecoration(color:Colors.cyan[700],borderRadius:BorderRadius.circular(8),),child:Center(child:Text(label,style:constTextStyle(fontSize:20)),),),);}

三、游戏状态管理

3.1 状态机设计

我用简单的状态变量管理游戏:

classTetrisGame{bool paused=false;bool gameOver=false;boolgetisPlaying=>!paused&&!gameOver;}

状态转换

初始化 → playing → paused → playing ↓ gameOver

3.2 暂停和继续功能

voidtogglePause(){paused=!paused;if(paused){_timer?.cancel();}else{start();}updateCallback();}

暂停UI

if(_game.paused&&!_game.gameOver)Container(color:Colors.black.withValues(alpha:0.8),child:Center(child:Text('PAUSED',style:TextStyle(fontSize:32,color:Colors.yellow[400],),),),)

3.3 游戏结束处理

void_spawnPiece(){_currentPiece=_nextPiece;_nextPiece=_getRandomPiece();_currentX=(cols-_currentPiece![0].length)~/2;_currentY=0;// 检查是否立即碰撞if(_checkCollision(_currentX,_currentY,_currentPiece!)){gameOver=true;_timer?.cancel();}}



四、焦点管理

4.1 FocusNode的作用

RawKeyboardListener需要焦点才能接收键盘事件:

class_GamePageStateextendsState<GamePage>{finalFocusNode_focusNode=FocusNode();@overridevoiddispose(){_focusNode.dispose();// 记得释放资源super.dispose();}}

4.2 自动获取焦点

RawKeyboardListener(focusNode:_focusNode,onKey:_handleKeyEvent,autofocus:true,// 自动获取焦点child:Scaffold(...),)

4.3 点击重新获取焦点

GestureDetector(onTap:(){_focusNode.requestFocus();// 点击时重新获取焦点},child:RawKeyboardListener(focusNode:_focusNode,onKey:_handleKeyEvent,child:Scaffold(...),),)

五、事件处理常见问题

问题1:按键没有响应

原因:焦点丢失
解决:使用autofocus或点击重新获取焦点

问题2:按键触发了多次

原因:没有区分KeyDown和KeyUp
解决:只处理KeyDownEvent

if(eventis!RawKeyDownEvent)return;

问题3:触摸按钮太小

解决:根据屏幕大小动态调整

finalbuttonSize=isSmallScreen?50.0:60.0;

六、本文小结

这篇文章我讲解了游戏的交互控制实现:

  1. 键盘控制:RawKeyboardListener监听键盘事件
  2. 触摸控制:屏幕按钮布局和交互
  3. 状态管理:暂停、继续、游戏结束
  4. 焦点管理:FocusNode的正确使用

现在游戏已经完全可以玩了!

系列说明:这是Flutter俄罗斯方块游戏开发系列教程的第3篇,已经全部完结


参考资料

  1. Flutter事件处理官方文档
  2. RawKeyboardListener类API
  3. FocusNode类API
  4. 开源鸿蒙跨平台社区

社区支持

欢迎加入开源鸿蒙跨平台社区:

  • 社区论坛:开源鸿蒙跨平台开发者社区
  • 技术交流:参与讨论,分享经验

如果本文对你有帮助,欢迎点赞、收藏、评论!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/29 14:00:17

如何快速上手Python编程?

新手学 Python 最容易陷入 “只学不用” 的误区&#xff0c;快速上手的关键是&#xff1a;先掌握最小核心语法 → 立刻做小项目 → 边做边补知识点&#xff0c;而不是按 “语法书顺序” 从头啃。 第一步&#xff1a;掌握 “最小够用” 的核心语法&#xff08;1-2 天搞定&#…

作者头像 李华
网站建设 2026/5/13 13:26:05

JavaScript 入门

一、先搞定运行环境&#xff08;零配置&#xff0c;新手友好&#xff09;JavaScript 最棒的一点是无需安装任何软件&#xff0c;浏览器就是你的 “编程实验室”&#xff0c;两步就能开始写代码&#xff1a;打开任意浏览器&#xff08;Chrome/Edge/Firefox&#xff09;&#xff…

作者头像 李华
网站建设 2026/5/31 1:18:02

【课程设计/毕业设计】基于SpringBoot的生活用品销售系统的设计与实现基于springboot的日用品销售系统的设计与实现【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/24 8:57:48

JavaScript 和 Python 哪个更适合初学者?

JavaScript vs Python 核心对比&#xff08;新手视角&#xff09;我从学习门槛、反馈感、核心用途、避坑点四个新手最关心的维度&#xff0c;帮你做直观对比&#xff1a;对比维度PythonJavaScript语法友好度&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;…

作者头像 李华
网站建设 2026/5/23 15:46:55

【课程设计/毕业设计】基于springboot的无人机销售系统的设计与实现【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/30 11:27:55

Java计算机毕设之基于springboot的无人机销售系统的设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华