news 2026/4/16 13:49:00

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/4/16 12:43:02

k8s 安装headlamp

dashboard因为贡献值过少&#xff0c;已经推荐使用headlamp https://github.com/kubernetes-sigs/headlamp 文档&#xff1a;https://headlamp.dev/docs/latest/installation/desktop/linux-installation/#appimage [adminlocalhost root]$ kubectl describe po my-headlamp…

作者头像 李华
网站建设 2026/4/16 12:42:03

电商九大核心模块业务实体与生命周期全解

本文是《复杂电商业务梳理实战指南》的补充篇&#xff0c;我们将深入解析除商品模块外的八大核心业务模块&#xff0c;详细定义每个模块的业务实体、生命周期和关键状态节点。这是构建企业级数据仓库的核心元数据基础。 为什么需要清晰的业务实体定义&#xff1f; 在数据仓库设…

作者头像 李华
网站建设 2026/4/16 12:42:50

机器学习三大流派:监督、无监督与强化学习

拆解机器学习的三大支柱&#xff1a;监督学习、无监督学习与强化学习 1. 前言&#xff1a;机器是如何“学会”的&#xff1f; 在人工智能的浪潮中&#xff0c;我们经常听到“模型训练”这个词。但机器毕竟不是人类&#xff0c;没有大脑神经元&#xff0c;它是如何从一堆冰冷的…

作者头像 李华
网站建设 2026/4/16 12:44:37

PCM接口:原理、典型应用与软件功能实现详解

目录 一、PCM 接口核心原理 1. PCM 技术的本质&#xff1a;模拟信号数字化三步骤 2. PCM 接口的硬件架构与信号定义 关键时序参数 3. PCM 接口的主流协议格式 二、PCM 接口典型应用案例 1. 音频采集与播放系统&#xff08;最典型应用&#xff09; 应用场景 工作流程 …

作者头像 李华
网站建设 2026/4/14 15:43:59

导师严选2026 9款一键生成论文工具测评:专科生毕业论文全攻略

导师严选2026 9款一键生成论文工具测评&#xff1a;专科生毕业论文全攻略 2026年专科生论文写作工具测评&#xff1a;如何高效选对“好帮手” 随着高校教育的不断深化&#xff0c;专科生在毕业论文撰写过程中面临的挑战也日益增多。从选题构思到文献检索、从内容组织到格式排版…

作者头像 李华