本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
Flutter&Flame 游戏开发系列前言:
该系列是 [张风捷特烈] 的 Flame 游戏开发教程。Flutter 作为 全平台 的 原生级 渲染框架,兼具 全端
跨平台和高性能的特点。目前官方对休闲游戏的宣传越来越多,以 Flame 游戏引擎为基础,Flutter 有游戏方向发展的前景。本系列教程旨在让更多的开发者了解 Flutter 游戏开发。
第一季:30 篇短文章,快速了解 Flame 基础。
[已完结]
第二季:从休闲游戏实践,进阶 Flutter&Flame 游戏开发。
两季知识是独立存在的,第二季 不需要 第一季作为基础。本系列教程源码地址在 【toly1994328/toly_game】,系列文章列表可在《文章总集》 或 【github 项目首页】 查看。
Trex 小游戏介绍
Chrome 在断网时,会有一个小恐龙跳跃躲避障碍物的小游戏,也可以在 chrome://dino/
地址访问。这个游戏 麻雀虽小五脏俱全 ,是体验游戏开发很好的切入点。
它包含以下几个要点:
- 角色呈现
- 跳跃移动
- 碰撞检测
- 分数记录
这个小游戏将作为 Flutter&Flame 第二季的先锋。通过对恐龙跳跃小游戏的逐步实现,来初步体验 Flame 开发一个小游戏的基本工作流程。下面开始进入游戏开发的世界吧~
一、地面、云朵和障碍物的呈现
本小结你将收获的技能点: 本节源码见 [lib/trex/01]
[1]. 资源加载 : 运行 Flame 的项目代码,加载图片资源。
[2]. 角色的呈现: 如何通过精灵图将角色展示到场景中。
[3]. 角色的定位:如何控制角色在场景中的位置。
在 Flame 中,场景中的一切都是 Component 对象的组合,为了区分Flutter 中的 Widget (组件),文中一律称之为 构件 (游戏的构成零件) 。比如下图中的小恐龙、云朵、地面、分数、障碍物,都是一个个被加入到游戏主类中的构件:
本小节我们将读取图片资源,展示地面、云朵和障碍物三个静态的角色,了解一下 Component 的基本使用。
1.游戏主类和资源图片加载
Flame 中通过 GameWidget 组件呈现,其中传入一个 FlameGame 的派生类作为游戏的入口。这里游戏中的所有资源通过 精灵图
的方式集合在一起,如下所示:
TrexGame 可以在 onLoad 回调 中异步加载资源;游戏的图片资源可以通过 Flame.images.load
方法加载。复写 backgroundColor 方法,可以修改游戏的背景色(默认是黑色)
1 | dart复制代码---->[lib/trex/01/main.dart]---- |
2. 静态角色的呈现: 云朵
拿云朵来说,它在游戏中的也是以 Component 的身份呈现在场景中的。SpriteComponent
可以展示一个精灵资源,对于 精灵图
来说,我们可以通过顶点坐标 srcPosition 和尺寸 srcSize 来确定某一个精灵,如下示意:
下面定义 CloudComponent
继承自 SpriteComponent,在 onLoad 回调中根据图片资源对象创建 Sprite ,并为 sprite 赋值即可:
tips:
with HasGameReference<TrexGame>
后,类中可以通过 game 得到 TrexGame 对象.
1 | dart复制代码---->[lib/trex/01/heroes/cloud_component.dart]---- |
有人可能会问,我怎么能知道坐标和尺寸的确切数值?
- 精灵图制作时,工具会给出坐标相关的配置信息(
如下图
),可以解析 json 文件得到精灵尺寸和位置。 - 如果你是拿别人的精灵图,且没有配置信息,可以自己用 PhotoShop 量一下。
云朵的构件已经准备完毕,接下来把它 "挂在"
屏幕上。TrexGame#onLoad
方法中通过 add 方法添加 Component 进行展示。构件默认会定位在场景的 左上角
:
1 | dart复制代码---->[lib/trex/01/trex_game.dart]---- |
3. 构件的定位: 地面和障碍物
如下所示,我们先把地面放在场景中。同样定义一个 GroundComponent
的构件,在 onLoad
时设置地面对于的精灵图。默认会在左上角,SpriteComponent 派生类中,可以通过 x,y
决定构件的位置:
onGameResize 回调会 在窗口尺寸变化时
或者构件加载完后
触发,其中的 size 是窗口尺寸。这里想让地面在中间偏下一点,只要将 y
赋值即可:
1 | dart复制代码class GroundComponent extends SpriteComponent with HasGameReference<TrexGame>{ |
接下来把第一个障碍物放到场景的中间,同理创建一个 ObstacleComponent 构件表示障碍物。在 onLoad 回调
中创建 Sprite ; 在 onGameResize 回调
中设置偏移量:
1 | dart复制代码class ObstacleComponent extends SpriteComponent |
通过云朵、地面、障碍物三个图片精灵的展示,大家应该对如何呈现一个图片资源有了清晰地认知。
小思考: 如何在场景中添加多个障碍物和云朵? (稍后介绍)
二、小恐龙的呈现与状态变化
本小结你将收获的技能点: 本节源码见 [lib/trex/02]
[1]. 多状态精灵 : 一个构建中如何拥有多种状态,并支持切换。
[2]. 键盘和手势 : 通过点击事件和键盘回调事件,切换小恐龙的展示状态。
1. 多状态精灵图片的处理
场地已经在界面上了,那么接下来让小恐龙登场吧! 在游戏中,小恐龙有 不同状态
, 使用需要展示不同的图片资源,这里将它的状态通过 PlayerState
表示:
1 | dart复制代码---->[lib/trex/02/heroes/player.dart]---- |
像这种不同状态有不同图片,而且某些状态需要有 序列帧动画 的角色。可以通过 SpriteAnimationGroupComponent
构件进行展示,它支持一个 泛型 T
表示状态。创建 Player 类型如下:
1 | dart复制代码---->[lib/trex/02/heroes/player.dart]---- |
上面的 SpriteComponent 通过 sprite
对象展示静态的精灵图片,这里 SpriteAnimationGroupComponent 有一个映射 animations
对象:
以状态 T 为键,以 SpriteAnimation 为值。我们需要完成对 animations 映射赋值的工作。
其中 SpriteAnimation
就是序列帧图片,用来展示角色,每一帧图片在对应精灵图的一个矩形区域。下面简单封装 loadAnimation
方法来加载:
- 传入角色的尺寸 size 和位置列表 frames,来确定矩形区域。
- stepTime 表示帧动画的间隔时间秒数。
1 | dart复制代码---->[lib/trex/02/heroes/player.dart]---- |
2. 映射关系的初始化和呈现
在 Player 构件的 onLoad 回到中通过 _initAnimations
方法来初始化映射关系:
1 | scss复制代码---->[lib/trex/02/heroes/player.dart]---- |
将不同的 PlayerState
状态,对应为不同的 SpriteAnimation
资源。其中 frames
表示图片的序列帧起点坐标,有多个就表示当前状态具有动画效果:
1 | dart复制代码---->[lib/trex/01/heroes/player.dart]---- |
然后在 TrexGame 中创建 Player 对象,在 onLoad 方法中通过 add 添加构建,此时角色精灵就可以展示出来了。
SpriteAnimationGroupComponent 中同样可以通过 x 和 y
数值设置构件的位置。在 onGameResize
回调中根据窗口尺寸进行设置:
1 | dart复制代码---->[lib/trex/02/heroes/player.dart]---- |
3.键盘所示与 Player 状态切换
SpriteAnimationGroupComponent
中的 current
表示当前的状态,更新该值就可以展示对应状态的图片资源。如下所示,在 toggleState
方法中轮换状态值:
1 | dart复制代码---->[lib/trex/02/heroes/player.dart]---- |
然后只要在合适的时机触发 Player#toggleState
方法即可切换小恐龙的状态。通过混入:
- KeyboardEvents 监听键盘事件。
- TapCallbacks 监听点击手势事件。
下面代码中,监听到按键 a
以及 onTapDown
事件时,触发 player.toggleState()
:
1 | dart复制代码---->[lib/trex/02/heroes/player.dart]---- |
SpriteAnimationGroupComponent
可以通过 debugMode=true
展示调试信息,包括矩形的边界和位置信息。如下所示,切换是否展示信息也就是切换 debugMode 的真假:
这里在 Player 中添加一个 toggleDebugMode 方法,切换 debugMode
值,并且在键盘 D :
1 | dart复制代码---->[lib/trex/02/heroes/player.dart]---- |
三、文字的展示
界面呈现中处理图片之外,最重要的就是文字。Flame 中通过 TextComponent 展示文字,本节就来介绍一下文字的展现方式。
[1]. 使用文字 :通过文本展示小恐龙的状态信息以及提示信息。
[2]. 精灵字体 :通过 SpriteFont 展示分数的图片像素文字。
1.文字信息的展示
虽然现在呈现了小恐龙的状态变化,但是看起来并不是很清晰,如果界面上可以展示一些提示文字,就可以清晰地自动当前案例的作用。比如当前操作的按键作用以及小恐龙的状态信息:
flame 中一切的表现都是 Component
, 为了方便展示维护提示信息,可以像 Player 那样将其视为一个角色加入游戏场景中。 如下所示,定义 HelpText 继承自 PositionComponent
,让其拥有定位能力;在 onLoad 回调中加入两个 TextComponent 分别展示状态和提示文字。并提供 changeState 方法更新状态文字的内容:
1 | dart复制代码class HelpText extends PositionComponent with HasGameReference<TrexGame> { |
然后在 TrexGame 中将 HelpText 像 Player 那样加入到场景中。当点击和按键事件时,通过 changeState 方法修改状态文字即可:
1 | dart复制代码class TrexGame extends FlameGame with KeyboardEvents, TapCallbacks { |
2. 精灵字体 SpriteFont
Flame 中提供了 SpriteFont 方便展示精灵图中的字体。在项目精灵图中,有数字和字母相关的图片作为分数。通过精灵字体,就可以将对应的字符串
映射为 精灵图片列表
展示:
比如下面的 1024 HI 2048
字符串,就可以访问到对应的精灵图片,展示文字:
同样,这里也定义一个 ScoreComponent
负责维护分数角色的展示, 在 onLoad 回调中创建并添加 TextComponent
。通过 SpriteFont
来建立字符集合图片区域的映射 Glyph
对象。这样对应的字符在渲染时就可以找到对应区域的图片精灵,完成展示:
1 | dart复制代码class ScoreComponent extends PositionComponent with HasGameReference<TrexGame> { |
到这里已经人物登场啦,第一集的内容就介绍完毕了。下面整理了一下本集的知识。大家可以自己根据每一项思考一下具体内容:
下一章将继续推进,学习如何让画面动起来。
本文转载自: 掘金