本篇文章的内容需要在完成以下内容代码的基础上进行哦!

  • 《开始用Flutter做游戏吧》
  • 《Flutter游戏:万有引力定律》
  • 《Flutter游戏:垃圾里会生蚊子》
  • 《Flutter游戏:蚊子飞来飞去》

加载更多资源

首先下载接下来要用到的游戏资源文件,因为之前已经下载过一部分,所以下面讲一下这次添加了哪些内容。

  • branding/title.png:游戏标题图片,建议7:4大小,即7x4图块
  • ui/start-button.png:开始游戏按钮,建议2:1大小,即6x3图块
  • bg/lose-splash.png:游戏结束图像,建议7:5大小,即7x5图块
  • ui/dialog-credits.png:关于对话框,建议3:2大小,即12x8图块
  • ui/dialog-help.png:帮助对话框,与关于对话框大小相同
  • ui/icon-credits.png:关于图标,建议为1个图块大小的正方形
  • ui/icon-help.png:帮助图标,与关于图标大小相同

了解了这些资源是干什么的以后,开始将以下代码添加到pubspec.yaml文件中的assets部分。

flutter:uses-material-design: trueassets:...- assets/images/bg/lose-splash.png- assets/images/branding/title.png- assets/images/ui/dialog-credits.png- assets/images/ui/dialog-help.png- assets/images/ui/icon-credits.png- assets/images/ui/icon-help.png- assets/images/ui/start-button.png

打开main.dart文件,并将刚才在pubspec.yaml文件中添加的资源传递给Flame.images.loadAll调用的字符串(String)列表中。

  Flame.images.loadAll(<String>[...'bg/lose-splash.png','branding/title.png','ui/dialog-credits.png','ui/dialog-help.png','ui/icon-credits.png','ui/icon-help.png','ui/start-button.png',]);

这样就完成了游戏资源的预加载部分。

准备游戏页面

一个好的游戏应该有一个欢迎页面和一个游戏页面,在长时间的游戏以后,胜利或失败时也要一个给出结果的页面,而且当玩家点击开始按钮时,应该从欢迎页面切换到游戏页面。所以,我们的游戏应该有下面3个页面。

  • 欢迎页面:首次打开游戏时,显示欢迎页面,或者叫主页面,该视图会在中间显示一个游戏标题,然后页面底部还会显示开始按钮。
  • 游戏页面:这是玩家在玩游戏时所看到的页面,这个页面隐藏了游戏标题,并专注于蚊子的随机运动。
  • 失败页面:当玩家失败时,会出现一个飞溅的页面,具体情况是当玩家失败时,屏幕中间会出现一个提示玩家游戏失败的飞溅图像,并带有开始按钮,玩家可以重新开始。

对于上面的3个页面,都显示同一个背景,并且蚊子也是可见的,这会使玩家感觉游戏页面是主页面,而欢迎页面才是真正的主页面,欢迎真正的引导玩家进入游戏,最后游戏结束后的失败页面是玩家失败后,休息一下再重新开始的页面。

为此呢,我们需要记录当前页面,这里可以使用整数执行此操作,并将页面从0到2编号,或者还可以将页面以字符串来记录。

在Dart语言中,有一种枚举(enum)的数据类型,正好可以用来处理这个情况,我们将在查看玩家所处的页面或告诉游戏更改页面时枚举页面。新建一个lib/view.dart文件,并添加下面的代码。

enum View {home,playing,lost,
}

现在需要为游戏添加一个实例变量,它将为我们保存当前页面的值。因此呢,要在使用它之前先导入页面View枚举,然后再添加实例变量。我们将它命名为活动页面(activeView),类型设置为View枚举类型。

打开hit-game.dart文件,并提交下面的代码。

...
import 'package:hello_flame/view.dart';class HitGame extends Game {...View activeView = View.home;

这样我们就准备好处理每个页面了。

实现欢迎页面

在前面已经简单说了欢迎页面,而在代码中,页面只是另一个类似组件的逻辑,可以拥有自己的子组件。它也可以是虚拟的,无论玩家真正看到什么页面,它总是可见的。

如果是欢迎页面的话,我们将在定义页面时使用组件类,就像其他组件一样,我们只需从游戏循环中调用其实例的渲染(render)和更新(update)方法。

首先在lib目录下创建一个views文件夹,并在该目录下创建views/home-view.dart文件,并编写下面的代码。

import 'dart:ui';
import 'package:flame/sprite.dart';
import 'package:hello_flame/hit-game.dart';class HomeView {final HitGame game;Rect titleRect;Sprite titleSprite;HomeView(this.game) {}void render(Canvas c) {}void update(double t) {}
}

上面代码中,先是导入将使用的类和定义的文件,然后我们定义一个名为HomeView的类,它有3个实例变量,其中一个是final,需要在创建这个类的实例时传递并赋值。同时该类有1个构造函数和另外2个方法,分别是游戏循环的渲染(render)和更新(update)方法。

在构造函数中,将初始化titleRecttitleSprite变量,以便它们可以在渲染(render)方法中使用。

  HomeView(this.game) {titleRect = Rect.fromLTWH(game.tileSize,(game.screenSize.height / 2) - (game.tileSize * 4),game.tileSize * 7,game.tileSize * 4,);titleSprite = Sprite('branding/title.png');}

上面的2行代码中,第1行为titleRect变量赋值,第2行为titleSprite变量赋值。其中,titleRect变量的值是一个矩形(Rect)的定义,4行代码对应于工厂构造函数.fromLTWH所需的参数。

因为我们确定要在一个7×4块的矩形内显示标题图片,所以会将game.tileSize * 7game.tileSize * 4传递给最后2个参数,这2个参数对应于矩形的宽度和高度。

对于左侧(left)参数,从9个图块大小的屏幕宽度中,减去标题图像7个图块大小的矩形宽度,可以得到2个额外空间的图块。为了使标题图像居中,这里将这2个额外的图块分布到左右两侧,使图像偏移1个图块。所以,这里就传入了game.tileSize * 1或简单地传递game.tileSize

顶部(top)参数有所不同,因为我们不想让标题图像位于屏幕的中心位置。要计算屏幕的中心位置,只需将屏幕高度除以2即可,从中减去4个图块大小的标题图像高度,就可以得到中心化的适当偏移位置。

目前已经初始化了titleRecttitleSprite,可以开始编写用于显示实际图像的代码了,在渲染(render)方法中,添加下面的代码。

  void render(Canvas c) {titleSprite.renderRect(c, titleRect);}

现在打开hit-game.dart文件,并导入HomeView类文件,然后添加一个HomeView类型的homeView实例变量。接下来,还需要在确定屏幕大小后初始化该实例变量,因此在调用resize之后,将在initialize方法中添加以下代码。

...
import 'package:hello_flame/views/home-view.dart';class HitGame extends Game {...View activeView = View.home;HomeView homeView;...void initialize() async {...homeView = HomeView(this);produceFly();}

最后还要在屏幕上使用渲染HomeView的渲染(render),因此,在游戏类的渲染(render)方法中,在最后调用HomeView实例的渲染(render)方法,使其最后渲染。

因为渲染的顺序与显示顺序相同,因为我们想要的是背景优先,然后是蚊子,最后才是标题图像,这将确保标题图片位于屏幕所有内容之上。

  void render(Canvas canvas) {background.render(canvas);enemy.forEach((Fly fly) => fly.render(canvas));if (activeView == View.home) homeView.render(canvas);}

上面的代码中,先判断活动视图(activeView)当前是否为主视图,如果是,就渲染HomeView实例,如果不是,则渲染(render)方法将跳过此行代码,因此不会呈现HomeView实例。

现在运行游戏,可以看到像下图所展示的效果。

但是呢,当还在游戏页面内时,玩家仍然可以点击并干掉蚊子,不过没关系,这对游戏没有任何影响。接下来,玩家要开始游戏了,所以必须有一个开始按钮。首先,创建另一个组件,命名为StartButton,放在新建的components/start-button.dart文件中。

import 'dart:ui';
import 'package:flame/sprite.dart';
import 'package:hello_flame/hit-game.dart';class StartButton {final HitGame game;Rect rect;Sprite sprite;StartButton(this.game) {}void render(Canvas c) {}void update(double t) {}void onTapDown() {}
}

这个类定义与其他组件类大致相同,不同的是它有一个onTapDown处理程序,我们将在这里编写“开始”游戏的代码。首先在构造函数中初始化rectsprite变量。

  StartButton(this.game) {rect = Rect.fromLTWH(game.tileSize * 1.5,(game.screenSize.height * .75) - (game.tileSize * 1.5),game.tileSize * 6,game.tileSize * 3,);sprite = Sprite('ui/start-button.png');}

上面的代码与HomeView类构造函数中,标题图像的初始化基本相同。不同的是,除了6×3个图块大小外,还有左侧(left)和顶部(top)偏移。

开始按钮的宽度为6个图块,这意味着在屏幕的9个图块宽度上有3个额外的图块,这样的话,每侧都有1.5个图块,所以这里将game.tileSize * 1.5提供给左侧(left)参数。对于顶部(top)参数,这样就可以让按钮的垂直位置恰好位于屏幕高度的四分之三即0.75的位置。

在初始化rectsprite变量之后,需要渲染图像,所以要下面代码添加到render函数中。

  void render(Canvas c) {sprite.renderRect(c, rect);}

接下来需要在游戏类中添加一个StartButton组件的实例,打开hit-game.dart文件,导入依赖,然后将实例变量与其他实例变量一起添加。同时在确定屏幕大小后,使用StartButton类的新实例初始化startButton变量,并在render方法中增加下面代码。

...
import 'package:hello_flame/components/start-button.dart';class HitGame extends Game {...HomeView homeView;StartButton startButton;...void initialize() async {...startButton = StartButton(this);produceFly();}...void render(Canvas canvas) {...if (activeView == View.home || activeView == View.lost) {startButton.render(canvas);}}

上面代码中,我们添加的这4行代码,分别是导入StartButton类、创建StartButton类的实例并将其存储在实例变量中、最后展示StartButton到屏幕上。

现在运行游戏,可以看到开始按钮会在欢迎页面和失败页面上呈现,这样玩家就可以从欢迎页面或失败页面重新开始游戏了。

处理开始按钮

在上一步中,我们让开始按钮显示在屏幕上,但是点击没有反应,不用急,接下来就开始处理开始按钮的响应逻辑。首先,需要确保点击不会穿过物体,例如在点击开始按钮(startButton)时,同一位置的蚊子不应该接收到点击事件。

在游戏类的onTapDown处理程序中创建一个isHandled变量,用于保存当前是否已调用了点击处理程序,在onTapDown处理程序的开始处创建它,并将初始值设置为false。在检查点击是否命中矩形(Rect)组件之前,先检查isHandled是否仍为false值,然后再调用组件的点击处理程序。

  void onTapDown(TapDownDetails d) {bool isHandled = false;if (!isHandled && startButton.rect.contains(d.globalPosition)) {if (activeView == View.home || activeView == View.lost) {startButton.onTapDown();isHandled = true;}}enemy.forEach((Fly fly) {if (fly.flyRect.contains(d.globalPosition)) {fly.onTapDown();}});}

上面代码中,首先对isHandled进行检查,确保尚未处理点击事件,这个检查项与检查点击是否在开始按钮(startButton)的rect属性内是一起的。如果通过了这些条件的检查,再额外检查玩家是否当前处于欢迎页面或失败页面中。

只有满足所有条件,游戏才会调用开始按钮的onTapDown处理程序,变量isHandled也被设置为true,让下面的代码知道已经处理了这次的点击。接下来要做的是,使用isHandled检查把之前的蚊子点击处理程序包装起来。

  void onTapDown(TapDownDetails d) {...if (!isHandled) {enemy.forEach((Fly fly) {if (fly.flyRect.contains(d.globalPosition)) {fly.onTapDown();isHandled = true;}});}}

上面的代码基本上与上面类似,首先包含了对isHandled的检查,这使得代码块只会没有处理开始按钮的情况下运行,其次,如果至少有一个蚊子被点击,则将isHandled变量设置为true

接下来,打开components/start-button.dart文件,开始编写实际处理开始按钮点击的代码。在调用开始按钮的onTapHandler时,需要将游戏的activeView设置为View.playing,所以这里先导入定义View枚举的文件。然后在onTapDown里面,将游戏的activeView设置为所需的值。

...
import 'package:hello_flame/view.dart';class StartButton {...void onTapDown() {game.activeView = View.playing;}
}

现在运行游戏,可以看到点击开始按钮以后,标题图片和开始按钮都不见了,进入了我们之前的游戏页面。

Flutter游戏:启动时的欢迎页相关推荐

  1. Flutter项目启动时黑屏解决办法

    我的flutter项目是带一个动画启动页的,在真机上开启app时,遇到以下现象: 白屏几秒->黑屏几秒->动画启动页. 研究了很多文献,一直想解决这个问题,目前已经得到了安卓上的解决办法. ...

  2. Unity3D 取消发布游戏启动时的设置窗口并窗口化运行

    https://www.jianshu.com/p/542f5cbb02e2 在本文,笔者将教大家如何取消启动时的设置窗口(如下图所示),并将游戏窗口化运行 这个窗口叫分辨率配置窗口 1.如何取消配置 ...

  3. 设置UE游戏启动时为窗口模式,而不是全屏

    设置UE游戏启动时为窗口模式,而不是全屏 在工程config目录下新建文件DefaultGameUserSettings.ini,并输入以下内容 [/Script/Engine.GameUserSet ...

  4. Flutter游戏:简单规则与结束页

    本篇文章的内容需要在完成以下内容代码的基础上进行哦! <开始用Flutter做游戏吧> <Flutter游戏:万有引力定律> <Flutter游戏:垃圾里会生蚊子> ...

  5. mysql修改游戏元宝_页游源码【武斗乾坤】自带安装启动教程+元宝游戏数据修改教程+自由一键游戏启动服务端...

    页游源码[武斗乾坤]自带安装启动教程+元宝游戏数据修改教程+自由一键游戏启动服务端_站长下载 资源说明: 1.本资源为一键启动服务端,只需要安装好所需组件一键启动即可运行. 2.资源默认为单机架设,无 ...

  6. 《uni-app》一个非canvas的飞机对战小游戏-启动页

    这是一个没有套路的前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言-博主看到后会去代替大家踩坑的-接下来的几篇都是uni-app的小实战,有助于我们更好的去学 ...

  7. adobe游戏服务器未响应,[转载]Adobe Photoshop CS 启动时未响应的解决办法

    故障现象 Adobe Photoshop CS 启动时不能及时响应,在任务管理器中会出现2个Photoshop进程,状态全部是"未响应",要过很久才能进入程序界面,进入程序后打开图 ...

  8. linux c 启动程序吗,Linux下C程序启动时的系统调用

    写程序跟踪发现,在Linux i386中,一个程序体完全为空的C语言程序启动时要进行近100个系统调用,如下所示. [ 1]syscall: 11 //execve [ 2]syscall: 45 / ...

  9. flutter ios启动白屏_Flutter技术架构概览

    前言 最近在整理各种技术架构,给自己的列了个TODO list,希望能在几个月的时间内,研究完各种前端技术架构,包括移动端技术架构.今天分享一下自己整理的flutter技术架构.完整的技术架构TODO ...

最新文章

  1. python中try except处理程序异常的三种常用方法
  2. 关于32bit与4Gib的问题
  3. mysql链表_MySql链表语句--博客园老牛大讲堂
  4. 招聘 | 南京柯基数据招聘自然语言处理工程师
  5. DHTMLX 前端框架 建立你的一个应用程序教程(二)--设置布局
  6. 一文读懂 | 进程怎么绑定 CPU
  7. html富文本编辑器插件_vue中使用vuequilleditor富文本编辑器
  8. 苹果发布App“一年之最”:快手短视频广东播放最多 山东原创第一
  9. 【翻译】What the f*ck JavaScript?(JavaScript你怎么这样啊???)
  10. 机器学习算法对比分析
  11. API接口设计的五大公共参数
  12. 以防遗忘001_通过斜率求垂直线段的端点,附Unity(UI image)画线
  13. 在线正则表达式测试器(JavaScript)
  14. Unity --- 角色动画的使用以及按键控制角色运动
  15. 在线会议中人脸面部轮廓图像提取(三)——Dlib库人脸面部轮廓图像特征提取
  16. kindle书摘-围城-相爱勿相伤
  17. Python+Selenium自动化测试——126邮箱自动登录脚本(登录首页是二维码,切入账号密码输入框)
  18. uniapp小程序当前页面刷新
  19. 具有连续调制光栅区域的光波导优化
  20. 10月25日 c语言 读取7个数(1—50)的整数值,每读取一个值,程序打印出该值个数的*

热门文章

  1. Extended Twin Composite Number 思维题
  2. batch入门教程(4)
  3. 【五级流水线CPU】—— 5. 转移指令(14条)
  4. Jupyter Notebook——夏侯南溪常用的快捷键
  5. 树莓派教程 - 1.2 树莓派GPIO库wiringPi 软件PWM
  6. 剑指offer——4.二维数组中的查找
  7. 【Ionic】Ionic/cmd提示 'node' 不是内部或外部命令,也不是可运行的程序
  8. 数据在各层之间的发送过程
  9. UVa12107 (120ms)代码
  10. BZOJ2938:[Poi2000]病毒