前言
这是一套 张风捷特烈 出品的 Flutter&Flame
系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列文章一览:
- 【Flutter&Flame 游戏 - 壹】开启新世界的大门
- 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
- 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
- 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
- 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
- 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
- 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
- 【Flutter&Flame游戏 - 捌】装弹完毕 | 角色武器发射
- 【Flutter&Flame游戏 - 玖】探索构件 | Component 是什么
- 【Flutter&Flame游戏 - 拾】探索构件 | Component 生命周期回调
- 【Flutter&Flame游戏 - 拾壹】探索构件 | Component 使用细节
- 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
- 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
- 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
- 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
- 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
- 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
- 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
- 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
- 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
- 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
- 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
未完待续 ~
1. 键盘事件
Flutter
作为跨平台的开发框架,本身有键盘的监听行为。Flame
中的键盘事件也只是对 Flutter
原生的一层封装而已,还是非常好理解的。这里我们先来实现如下的效果:按 Y
键时,让角色以自身中心沿 y 轴
反转; 按 X
键时,让角色以自身中心沿 x 轴
反转:代码在 【03/01】
首先介绍一下 Flame
对键盘事件的封装,可以看出是以 mixin
的方式提供回调实现的。注意一点,因为只是 on Game
,示意只有 Game
一族的类才可以混入。
前面知道 FlameGame
中混入了 Game
,所以是 Game
一族。这里 TolyGame
就可以混入 KeyboardEvents
。然后通过覆写 onKeyEvent
方法,来监听键盘事件。其中 RawKeyEvent
有两种类型:RawKeyDownEvent
表示按键按下;RawKeyUpEvent
表示按键抬起,代码如下:
---->[03/01]----
class TolyGame extends FlameGame with KeyboardEvents{
late final HeroComponent player;
@override
Future<void> onLoad() async {
player = HeroComponent();
add(player);
}
@override
KeyEventResult onKeyEvent(
RawKeyEvent event,
Set<LogicalKeyboardKey> keysPressed,
) {
final isKeyDown = event is RawKeyDownEvent;
if (event.logicalKey == LogicalKeyboardKey.keyY&&isKeyDown) {
//TODO 反转Y
}
if (event.logicalKey == LogicalKeyboardKey.keyX&&isKeyDown) {
//TODO 反转X
}
return super.onKeyEvent(event, keysPressed);
}
}
2. 角色的镜像反转
上一篇介绍过角色的 移动
和 旋转
,这里来看一下通过 缩放
来实现沿轴的 镜像反转
。其实思路很简单,对于点来说,沿 Y 轴镜像是保持 y
坐标不变,x
坐标取相反数。scale
的本质就是对坐标在横纵分量上的乘积,所以 scale(-1,1)
表示的是将 x
坐标。
如下,在 HeroComponent
构建中添加 flip
方法,默认沿 y
轴镜像反转:
---->[03/01/HeroComponent]----
void flip({
bool x = false,
bool y = true,
}) {
scale = Vector2(scale.x * (y ? -1 : 1), scale.y * (x ? -1 : 1));
}
接下来,只要在按键监听中,触发 flip
方法即可。这样就实现了通过按键,控制对角色进行镜像反转的效果,代码如下:
---->[03/01/TolyGame$onKeyEvent]----
if (event.logicalKey == LogicalKeyboardKey.keyY && isKeyDown) {
player.flip(y: true);
}
if (event.logicalKey == LogicalKeyboardKey.keyX && isKeyDown) {
player.flip(x: true);
}
同理,结合上一章的内容,我们也可以通过键盘按键来控制角色的移动,如下所示,通过 上下左右
或 WSAD
键进行移动:代码在 【03/02】
代码如下,其中 step
表示按一下的偏移量:
final double step = 10;
if (event.logicalKey == LogicalKeyboardKey.keyY && isKeyDown) {
player.flip(y: true);
}
if (event.logicalKey == LogicalKeyboardKey.keyX && isKeyDown) {
player.flip(x: true);
}
if ((event.logicalKey == LogicalKeyboardKey.arrowUp
|| event.logicalKey == LogicalKeyboardKey.keyW)
&& isKeyDown) {
player.move(Vector2(0, -step));
}
if ((event.logicalKey == LogicalKeyboardKey.arrowDown
|| event.logicalKey == LogicalKeyboardKey.keyS)
&& isKeyDown) {
player.move(Vector2(0, step));
}
if ((event.logicalKey == LogicalKeyboardKey.arrowLeft
|| event.logicalKey == LogicalKeyboardKey.keyA)
&& isKeyDown) {
player.move(Vector2(-step, 0));
}
if ((event.logicalKey == LogicalKeyboardKey.arrowRight
|| event.logicalKey == LogicalKeyboardKey.keyD)
&& isKeyDown) {
player.move(Vector2(step, 0));
}
通过这两个小例子就简单认识了在 Flame
中如何监听键盘事件,下面来看一下手势事件,比如点击、拖拽、长按等。
3. 手势检测 - 点击事件
同样,Flame
中的手势检测也是基于 Flutter
的一层封装,通过 mixin
实现监听功能。另外,注意一点,这也也都是 on Game
,也就是说只有 Game
一族的类才能使用这些手势检测器。
这些手势检测器和 Flutter
中的含义基本一致,就不一一赘述了。使用方式都是混入后,通过覆写方法进行监听,这里主要对 点击 TapDetector
和 拖拽 PanDetector
进行介绍。
如下的小例子中,每次点击屏幕时,角色会顺时针旋转 90°
,而且按下后会显示角色的边界信息,抬手后会消失。代码在 【03/03】
实现也非常简单,将 TolyGame
混入 TapDetector
,然后覆写相关方法进行即可。其中旋转通过 _counter
进行计数,每次点击时加一,然后旋转到 _counter * pi / 2
角度即可。角色的边框信息,通过添加 RectangleHitbox
即可,当其 debugMode
为 true
,就会显示,代码处理如下:
class TolyGame extends FlameGame with TapDetector {
late final HeroComponent player;
late final RectangleHitbox box;
@override
Future<void> onLoad() async {
player = HeroComponent();
box = RectangleHitbox()..debugMode = false;
player.add(box);
add(player);
}
final double step = 10;
double _counter = 0;
@override
void onTap() {
_counter ;
player.rotateTo(_counter * pi / 2);
}
@override
void onTapCancel() {
box.debugMode = false;
}
@override
void onTapDown(TapDownInfo info) {
box.debugMode = true;
}
@override
void onTapUp(TapUpInfo info) {
box.debugMode = false;
}
}
4.手势检测 - 拖拽事件
其实上一章中介绍的操作杆,本质上就是基于拖拽事件实现的,只不过限定拖拽区域而言。如下是通过 PanDetector
实现的移动,在 onPanUpdate
回调中可以监听到鼠标的移位量: 【03/04】
代码如下,通过 onPanUpdate
回调的 DragUpdateInfo
对象中的 info.delta.global
可以得到相对于全局的鼠标偏移量:
class TolyGame extends FlameGame with PanDetector {
// 略同...
@override
void onPanDown(DragDownInfo info) {
box.debugMode = true;
}
@override
void onPanStart(DragStartInfo info) {}
@override
void onPanUpdate(DragUpdateInfo info) {
player.move(info.delta.global);
}
@override
void onPanEnd(DragEndInfo info) {
box.debugMode = false;
}
@override
void onPanCancel() {
box.debugMode = false;
}
}
5. Component 的手势与键盘监听
前面说过,上面的监听都是只能被混入到 Game
一族中,也就是说 Component
构件不能混入,更像是一个全局的手势、事件检测。那么如果只想对某个 Component
进行监听,又该怎么办呢?Flame
源码中在 components/mixin
中提供了 Component
专属的键盘、手势检测混入类。
如下是一个小案例,当鼠标移入角色区域时,边框信息呈绿色,按下时边框变红,且角色顺时针旋转 90°
;鼠标移出区域或抬起时,边框信息取消。这里使用了 Tappable
和 Hoverable
两个 mixin
,代码详见: 【03/05】
处理方式和前面基本一致,这里就不赘述了。另外说明一点,如果某个 Component
混入了 Tappable
,那么最外层的 TolyGame
就要混入 HasTappables
;同理:
事件类型 | Component 混入 | XXXGame 需混入 |
---|---|---|
单击 | Tappable | HasTappables |
悬浮 | Hoverable | HasHoverables |
键盘 | KeyboardHandler | HasKeyboardHandlerComponents |
拖拽 | Draggable | HasDraggables |
class TolyGame extends FlameGame with HasTappables,HasHoverables {
// 略...
}
class HeroComponent extends SpriteAnimationComponent with HasGameRef, Tappable,Hoverable {
// 略...
}
到这里,基本的键盘事件和手势操作就已经介绍完了,这些和 Flutter
原生的事件基本一致。这里来简单瞄一眼单击事件 onTap
的触发,可以看出本质上还是 GestureDetector
在 onTap
中触发 game.onTap
方法的。所以这里的手势和键盘事件也不是什么新知识。
主要需要注意的是:Flame
中对事件检测封装了两套 mix
:一套是基于 Game
的,用于全局的事件检测。另一套是基于 Component
的,用于某个构件角色的事件检测。这里只是简单介绍一下事件的使用,在后面还会经常使用。那本文就到这里,明天见 ~
@张风捷特烈 2022.05.28 未允禁转
我的 公众号: 编程之王
我的 掘金主页
: 张风捷特烈我的 B站主页
: 张风捷特烈我的 github 主页
: toly1994328