使用Flutter开发俄罗斯方块小游戏

news2024/12/28 20:20:48

一、本篇文章主要是来讲解下俄罗斯方块游戏的开发思路(当然可能不是最好的思路),博客文章顶部有代码(仅供参考)

二、效果图

视频效果图地址

三、UI页面思路拆解

  • 游戏的主界面两部分组成,上面为15*10的格子用来放置方块,下面为操作按钮和显示当前分数(也就是消失了多少行方块)
  • 每个方块的大小计算:根据当前屏幕的宽度、显示的列数、方块之间的间隙
 Size _calcRectSize(double screenWidth, int count) {
    double remainderWidth = screenWidth - ((count - 1) * widget.gap);
    return Size.square(remainderWidth / count);
  }

计算出每个方块的大小,也就可以计算出格子所占的高度了,接下来通过CustomPaint进行绘制游戏背景即可,如下

class GameBgWidget extends StatelessWidget {
  ///省略部分代码...
  
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size.fromHeight(height),
      painter: _GameBgPainter(parent: this, size: rectSize),
    );
  }
}
  • 同理,绘制游戏过程中显示的UI也应当是和背景一模一样的大小,最后将这两个上下层叠 就达到了方块在格子中移动的效果,整体UI布局如下:
class GameWidget extends StatefulWidget {
  final int colCount;
  final int rowCount;
  final double gap;

  const GameWidget({
    Key? key,
    required this.colCount,
    required this.rowCount,
    required this.gap,
  }) : super(key: key);

  
  State<StatefulWidget> createState() => _GameWidgetState();
}

class _GameWidgetState extends BaseState<GameWidget> {

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        LayoutBuilder(
          builder: (_, constrains) {
            //计算方块的大小
            final size = _calcRectSize(constrains.maxWidth, widget.colCount);
            //计算所占的高度
            final height = _calcCanvasHeight(size);
            return Stack(
              children: [
              	//游戏背景组件
                GameBgWidget(
                  colCount: widget.colCount,
                  rowCount: widget.rowCount,
                  gap: widget.gap,
                  rectSize: size,
                  height: height,
                ),
                //游戏进行中的数据组件
                GameDataWidget(
                  colCount: widget.colCount,
                  rowCount: widget.rowCount,
                  gap: widget.gap,
                  rectSize: size,
                  height: height,
                  scoreCallback: (line) {
                  },
                ),
              ],
            );
          },
        ),
        Expanded(
          child: Container(
            color: Colors.blueGrey,
            ///省略操作按钮代码...
            ),
        ),
      ],
    );
  }

四、接下来就是重点了游戏逻辑的开发思路,一个怎样的方法会比较好处理数据,下面将为大家说说我的思路

1、 这里可以将整个游戏界面(格子)看成一个15*10的二维数组,当某一个格子内有方块的时候,那么对应的二维数组位置就不为空,如下表示:

  • 当假设开始加入一个“O”型方块的时候,就会变成如下这样

还有一点:这里为什么二维数组里面装的是Color、null而不是0、1呢,原因就是每个方块的颜色会随机生成,同时当方块消失的时候上面的方块要进行下移,所以就需要知道每个格子需要绘制什么颜色的方块

2、 游戏中会产生的所有方块类型如下:

可以讲如上七种方块大致形象称为"O,Z,S,T,J,L,I"类型

  • 那现在的重点就是又该如何表示这些方块了?其实和上面同理也可以使用一个二维数组来进行表示,当某个位置没有方块则为0有则为1如下:
    • “O” 2*2
    • “Z” 2*3
    • “S” 2*3
    • “T” 2*3
    • “J” 3*2
    • “L” 3*2
    • “I” 4*1

3、现在就可以抽象出一个方块的模板来了

abstract class BaseBlock {
  List<Color> allColors = [
    Colors.amber,
    Colors.lightBlue,
    Colors.red,
    Colors.blue,
    Colors.pink,
    Colors.lightGreen,
    Colors.purpleAccent,
  ];

  ///方块数据
  List<List<int>> block;

  ///方向
  BlocDirection direction;

  ///颜色
  late Color color;
  
  BaseBlock({
    required this.block,
    this.direction = BlocDirection.top,
  }) {
    color = allColors[Random().nextInt(allColors.length)];
  }

  ///宽度
  int get width => block.first.length;

  ///高度
  int get height => block.length;

  ///旋转
  void rotate() {
    int nextIndex = (direction.index + 1) % BlocDirection.values.length;
    direction = BlocDirection.values[nextIndex];
  }
}
  • 有了模板实现起来就很快了,举几个例子
    • "O"型方块
    class OBlock extends BaseBlock {
      OBlock()
          : super(block: [
              [1, 1],
              [1, 1]
            ]);
    }
    
    • "T"型方块
    class TBlock extends BaseBlock {
      TBlock()
          : super(block: [
              [1, 1, 1],
              [0, 1, 0]
            ]);
    
      
      void rotate() {
        var currDirection = direction;
        super.rotate();
        if (currDirection == BlocDirection.top) {
          block = [
            [0, 1],
            [1, 1],
            [0, 1]
          ];
        } else if (currDirection == BlocDirection.right) {
          block = [
            [0, 1, 0],
            [1, 1, 1]
          ];
        } else if (currDirection == BlocDirection.bottom) {
          block = [
            [1, 0],
            [1, 1],
            [1, 0]
          ];
        } else {
          block = [
            [1, 1, 1],
            [0, 1, 0]
          ];
        }
      }
    }
    

    因为每一个方块可以进行无限制旋转,所以T型方块重写rotate函数进行实现,也就是将旋转后将新block数据进行更新即可。

五、剩下的就是对方块进行向下移动,左移、右移动同时需要进行判断是否可以向左、向右、向下移动

  • 对于生成的方块我们可以使用(x,y)来进行标记位置,向下移动y+1,向左移动x-1,向右移动x+1,同时对边界进行处理,也要判断要移动的位置是否已经有方块了
  • 最后对于某一行是否填满可以进行消除的判断就更简单些了,需要判断每一行是否都不为null,然后对应的行删除,然后在 在0下标插入一行,这样就完成了消除同时保证了二维数组的正确性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/713369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Proxyman 替换js

在真机排查问题时&#xff0c;js不能格式化&#xff0c;导致没法看问题出在那一行&#xff0c;此时可以用这个方法替换js。 方法&#xff1a; 安装proxyman后&#xff0c;以iOS设备为例&#xff0c;菜单-证书-在iOS上安装证书 电脑、真机连接同一个网络&#xff0c;配置代理&…

SparkCore

Spark是一种快速、通用、可扩展的大数据分析引擎 Spark的特点 Speed&#xff1a;快速高效 性能比Hadoop MapReduce快100倍。即便是不将数据cache到内存中&#xff0c;其速度也是MapReduce10 倍以上。 Ease of Use&#xff1a;简洁易用 Spark支持 Java、Scala、Python和R等…

ctfshow—红包4

0x00 前言 CTF 加解密合集&#xff1a;CTF 加解密合集 0x01 题目 from secrets import randbelow from nationalsecret import p, r, k, flagg 2 y pow(g, k, p)def gogogo():print("Another chance:")t int(input(t ))c randbelow(p)print("Here is my…

关于anaconda的python虚拟环境

1.查看anaconda的虚拟环境 在cmd中输入 conda env list //查看conda中的虚拟环境和 activate 环境名称 //激活环境 pip list //查看该环境的包 python // 可以查看该环境python的版本 deactivate //退出环境2.使用anaconda创建新的…

Java Web HTMLCSS(2)23.6.30

2&#xff0c;CSS 2.1 概述 CSS 是一门语言&#xff0c;用于控制网页表现。我们之前介绍过W3C标准。W3C标准规定了网页是由以下组成&#xff1a; 结构&#xff1a;HTML表现&#xff1a;CSS行为&#xff1a;JavaScript CSS也有一个专业的名字&#xff1a;Cascading Style Sh…

如何在 macOS 上同时使用 Flutter2 和 Flutter3 进行 ios 开发

如何在 macOS 上同时使用 Flutter2 和 Flutter3 进行 ios 开发 前言 猫哥主打系统环境是: macos flutter 3.7.12 ruby 3.2.2 cocoapods 1.12.1 xcode 14.3.1 这套配置运行最新的项目没问题&#xff0c;但是最近需要维护 flutter 2.10.5 这种老项目&#xff0c;虽然用了 fvm 进…

武汉理工大学第四届ACM校赛(部分补题与写题)

开裂 目录 k-雇佣农民 题目描述 输入描述: 输出描述: 输入 输出 备注: 小e的苹果树 不降序列 k-雇佣农民 题目描述 Ly很喜欢星际争霸二这款游戏&#xff0c;但是他现在玩不到了。所以Ly现在只能做一个关于农民的题消磨时光。 开始时Ly没有任何农民&#xff0c;第i天白…

三、QPushButton的使用,信号和槽

QT从入门到实战学习笔记 一、QPushButton的创建二、中文要设置成UTF-8格式才不会乱码三、对象树1、验证被释放掉 四、QT窗口坐标系九、信号和槽---点击按钮关闭窗口1、查询signal信号的定义&#xff08;帮助文档&#xff09;2、搜索QWidget查找槽函数&#xff08;slot是槽的意思…

华为freebuds 5无线充电充不上电怎么办?

相信很多人都会遇到跟我一样的问题&#xff0c;华为FreeBuds 5无线充电充不进电是怎么回事&#xff1f;为此我专门整理了以下的经验&#xff0c;相信对大家有所帮助。 1. 充电时要把耳机盒保护套拆下来&#xff0c;耳机盒与充电底座之间不要有东西挡着。这样耳机盒充电时可以更…

jenkins的环境搭建

jenkins 环境 安装 我之前使用war安装、安装比较简单、就是jenkins的 对应的插件不能下载下来、后来发现是版本的问题、使用docker-compose 安装、jenkins安装 插件很容易安装下来 1、安装jdk 解压jdk 配置环境变量 #set java environment JAVA_HOME/usr/local/jdk1.8.0_281…

基于单片机和GSM短信模块的家庭防盗火灾安全报警系统

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;627短信 获取完整论文报告&#xff08;含无水印图片和代码&#xff09; 本系统主要由单片机和GSM短信模块组成&#xff0c;借助最可靠、最成熟的GSM移动网络&#xff0c;以最直观的中文短消息或电话形式&#xff0c;直接把…

392.04亿元?台积电公布下一代工艺发展路线图,2纳米2025 年投产

台积电近日在日本举办了一次研讨会&#xff0c;详细介绍了N3E工艺节点的最新进展和引人注目的性能提升。此外&#xff0c;台积电还公布了令人期待的下一代N2工艺的发展路线图。 台积电副总裁Kevin Zhang透露&#xff0c;公司正以迅猛速度发展&#xff0c;预计2022年的投资金额将…

360测试开发技术面试题目

目录 一、java方面 二、Linux方面 三、数据库方面 四、性能测试方面 五、HTTP协议方面 六、其他 总结&#xff1a; 最近面试了360测试开发的职位&#xff0c;将面试题整理出来分享~ 一、java方面 1、java重载和重写的区别 重载overloading 多个方法、相同的名字&#x…

CF578A(直线方程 + 数学) 1700

CF578A(直线方程 数学) 1700 有一条折线 &#xff0c; 这个折线经过这样一组点&#xff1a; (0,0) - (x,x) - (2x,0) - (3x,x) - (4x,0) - ....现给出折线上一点 &#xff0c; 求 x 的最小值 思路&#xff1a;我们不妨用解方程的思想 &#xff0c;先写出折线的方程&#xf…

AIGC-stable-diffusion系列1- stable-diffusion-webui

安装方法1&#xff0c;源码安装 参考 repo参考地址&#xff1a;https://github.com/AUTOMATIC1111/stable-diffusion-webui python下载地址&#xff1a;https://www.python.org/downloads/release/python-3106/ git下载地址&#xff1a;https://git-scm.com/download/win 官…

逻辑回归模型

目录 引言 逻辑回归的理论基础 逻辑回归的实践 实战案例&#xff1a;银行营销预测 超越逻辑回归 引言 我们在上一篇文章中讨论了线性回归模型&#xff0c;探讨了如何利用它来解决连续变量预测的问题。今天&#xff0c;我们将转向一种新的模型——逻辑回归&#xff0c;它用…

TIDB v7.1 reource control资源管控特性体验贴

作者&#xff1a; bert 原文来源&#xff1a; https://tidb.net/blog/60c87e38 TIDB v7.1 reource control资源管控特性体验贴 1. 使用场景&#xff1a; 定义&#xff1a;TIDB的资源管控 (Resource Control) &#xff0c;使用资源管控特性&#xff0c;将用户绑定到某个资源…

摇骰子设计与实现(uni-app微信小程序)

文章目录 摇骰子设计与实现准备工作实现步骤以及思路第一步&#xff1a;实现准备状态第二步&#xff1a;实现晃动中状态第三步&#xff1a;等待开起状态第四步&#xff1a;开启后状态部分优化 总代码 摇骰子设计与实现 手机摇一摇可以摇骰子&#xff0c;上划可查看结果&#x…

桥梁健康监测:时刻感知桥梁“脉搏”

随着交通量的不断增加&#xff0c;桥梁作为一种重要的交通基础设施&#xff0c;其安全性和可靠性面临着日益严峻的挑战。桥梁健康监测是保障桥梁安全和预防桥梁事故的重要手段&#xff0c;本文将介绍桥梁健康监测的意义、技术手段和应用案例。 一、桥梁健康监测的意义 保障交通…

解决React18+ts项目导入模块的声明报错

路径配置 项目路径别名的配置 ts对指向src的目录提示是不支持的 所以需要手动配置符号指向 在vite.config.ts import path from path export default defineConfig({plugins:[react()],resolve:{alias:{"":path.resolve(__dirname, ./src)}} })但这时path模块引入会…