【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
前言
第一季完结,谢谢支持 ~
1. Flame 官方案例
在 github
仓库中 flame/examples
中是官方的案例,对于入门而言是很有参考意义的。 但它也不是非常惊艳,作为一个游戏引擎的官方案例来说,还是太过简陋。
其中介绍了很多基本模块,每个模块中的案例都能很方便地找到对应的源码,这一点还是很值得肯定的。
本文我们将基于如下的 Joystick
案例,介绍一下操纵杆的使用,以及角色的移动。移动是最基础的游戏交互,还是先介绍为好。
本文的效果如下,通过左下角的操纵杆,来移动角色:本文源码于 【lib/02】
2. 操纵杆的使用
操纵杆的原理非常简单,如下以大圆中心为原点建立坐标系,正方向分别向 右
和 下
。通过小圆心的坐标就可以确定偏移量以及旋转角度。这里主要使用偏移量来修改角色的 position
位置。
同样,操纵杆本身也是 Component
构建。如下,在 TolyGame
的 onLoad
中构造 JoystickComponent
对象,通过 add
方法加入到游戏中。主要这里的 TolyGame
需要混入 HasDraggables
,才能支持操纵杆拖拽。
---->[02/game.dart]----
class TolyGame extends FlameGame with HasDraggables {
late final JoystickComponent joystick;
@override
Future<void> onLoad() async {
final knobPaint = BasicPalette.blue.withAlpha(200).paint();
final backgroundPaint = BasicPalette.blue.withAlpha(100).paint();
joystick = JoystickComponent(
knob: CircleComponent(radius: 25, paint: knobPaint),
background: CircleComponent(radius: 60, paint: backgroundPaint),
margin: const EdgeInsets.only(left: 40, bottom: 40),
);
add(joystick);
}
现在操纵杆已经加入到了 游戏场景
之中,接下来把角色加入进来。方式也很简单,创建 HeroComponent
对象,再添加到场景中即可。代码如下:
---->[02/game.dart]----
late final HeroComponent player;
---->[onLoad 方法]----
player = HeroComponent();
add(player);
这就说明,游戏中的各种角色,都是 Component
构件,添加到游戏场景之中,后添加的在上层。游戏的核心是维护各个对象数据间的关系。
3. 角色的移动
在上一篇中,我们介绍了 PositionComponent
一族的构件中有 position
属性,来定位角色位置。也就是说,只要根据操纵杆的偏移量,对 position
属性进行修改即可。另外说一下,在一个 Component
中添加 RectangleHitbox
,就可以有如上的紫色信息框,便于查看角色所占区域即位置。
class HeroComponent extends SpriteAnimationComponent with HasGameRef {
HeroComponent() : super(size: Vector2(50,37), anchor: Anchor.center);
@override
Future<void> onLoad() async {
List<Sprite> sprites = [];
for(int i=0;i<=8;i++){
sprites.add(await gameRef.loadSprite('adventurer/adventurer-bow-0$i.png'));
}
animation = SpriteAnimation.spriteList(sprites, stepTime: 0.15);
position = gameRef.size / 2;
add(RectangleHitbox()..debugMode = true);
}
double speed = 200.0; // Pixels/ 秒
void move(Vector2 ds){
position.add(ds);
}
}
这里定义一个 move
方法,接受 Vector2
位移量,类中定义了一个 speed
,用于控制移动速度,值越大就表示每秒运动的位移越长。
4. 世界的刷新
我们日常生活中有钟表计时,可以明确时间的概念,现实中时间是不断进行的,永不停息。在游戏开发中也是类似,默认情况下世界处于不断刷新渲染之中,每次的刷新渲染成为一帧。如果每秒渲染 60
次,那就说明游戏每秒可达 60
帧,也就是常说的 60fps
。不过游戏中的时间是可以暂停的。
另外,在 Component
类中定义了 update
方法,可以覆写它来监听每次刷新的事件。前面我们知道 FlameGame
本身也是 Component
,所以在子类 TolyGame
中可以覆写 update
来监听帧的更新。通过打印日志可以看出来,会不断触发,其中 dt
回调表示两帧之间的时间差。而且每帧之间约等于 0.01666 秒
,也就是 16.6 ms
,即每秒可刷新 60
次。
---->[02/game.dart/TolyGame]----
@override
void update(double dt) {
super.update(dt);
print(dt);
}
使用,只要在 update
回调中,执行 player
的 move
方法即可修改角色位置。其中 joystick.relativeDelta
是偏移量和外圆半径的比值,也就是指移动的百分比。根据物理学公式,可以计算出偏移位移
ds = v * t
其中速度是一个二维的向量,是速度值和 joystick.relativeDelta
向量结合获得的。从而达到操纵杆百分比越大,速度越快的效果。
@override
void update(double dt) {
super.update(dt);
if (!joystick.delta.isZero()) {
Vector2 ds = joystick.relativeDelta * player.speed * dt;
player.move(ds);
}
}
另外可以通过 joystick.delta.screenAngle()
获取操纵杆的旋转角度,也就是可以对角色进行旋转操作,如下所示:
在 PositionComponent
中除了 Vector2
类型的 position
进行定位;还有double
类型的 angle
用于控制旋转角度;以及 Vector2
类型的 scale
控制缩放。
---->[HeroComponent#rotateTo]----
void rotateTo(double deg){
angle = deg;
}
在 joystick
偏移了非零时,获取角度为 player
设置旋转角度即可。另外,如果操纵杆偏移量为 0
,恢复原位。
---->[TolyGame#update]----
// 角色旋转
if (!joystick.delta.isZero()) {
player.rotateTo(joystick.delta.screenAngle());
}else{
player.rotateTo(0);
}
5. 小结
本文主要简单认识了一下 JoystickComponent
操纵杆构件,并基于此实现了对角色的移动和旋转操作。也简单认识了一下世界的刷新的触发,这里简单瞄一下源码,其实刷新的触发和 Flutter
原生的 Animation
动画刷新是类似的,都是基于 Ticker
来触发。
\
转载自:https://juejin.cn/post/7102192145380950053