【基于Flutter&Flame 的飞机大战开发笔记】搭建项目及创建一架战机

前言

最近在学习基于Flutter的游戏引擎Flame,并尝试自己编写一个飞机大战。本文将记录工程搭建和玩家战机创建的过程。强烈推荐在阅读文章前,优先食用以下内容,这将会对你有一定的Flame知识基础。当然,Flutter的基础也是必要的。

Flutter&Flame 游戏专栏 - 张风捷特烈的专栏

flame-engine/flame: A Flutter based game engine

环境及资源搭建

新建项目后,在pubspec.yaml中添加Flame的核心依赖。ps:这里笔者使用的是当前最新的1.2.0版本。

environment:
  sdk: ">=2.17.5 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flame: ^1.2.0

还有就是常规的Flutter静态资源配置了。ps:这些资源不全是在本文中使用,图片资源都是在网上找到的,是微信飞机大战的资源仅为学习所用image.png

创建战机

游戏环境

void main() {
  runApp(GameWidget(game: Game()));
}

class Game extends FlameGame with HasDraggables {
  @override
  Future<void> onLoad() async {}
}

在Flutter的main函数中,runApp传入一个GameWidget,实际是一个StatefulWidgetGame继承自FlameGame,它是Flame定义的一个ComponentFlame依赖于Component进行展开,类比于Flutter依赖于Widget。后续在游戏里的对象都会是在FlameGame对象中添加Component实现

// game_widget.dart
class GameWidget<T extends Game> extends StatefulWidget {
  final T? game;
  ...

onLoadComponent中的生命周期之一,学习过Android的话可以和Activity的onCreate进行类比。

玩家战机

class Player extends SpriteAnimationComponent {
  Player({required Vector2 initPosition, required Vector2 size})
      : super(position: initPosition, size: size);

  @override
  Future<void> onLoad() async {
    List<Sprite> sprites = [];
    for (int i = 1; i <= 2; i++) {
      sprites.add(await Sprite.load('player/me$i.png'));
    }
    final spriteAnimation = SpriteAnimation.spriteList(sprites, stepTime: 0.15);
    animation = spriteAnimation;

    add(RectangleHitbox()..debugMode = true);
  }

创建类Player

  • 继承自SpriteAnimationComponent,这是一个可以加载动画的Component,也可以理解为序列帧。这里战机共两帧,设置切换间隔为0.15s。ps:默认是循环播放的,这里没有特殊需求循环即可。
// sprite_animation.dart
factory SpriteAnimation.spriteList(
  List<Sprite> sprites, {
  required double stepTime,
  bool loop = true,
}) {
  return SpriteAnimation(
    sprites.map((sprite) => SpriteAnimationFrame(sprite, stepTime)).toList(),
    loop: loop,
  );
}
  • 构造方法需要传入initPositionsize,即初始位置和大小。两者都为Vector2。ps:由于SpriteAnimationComponent继承自PositionComponent,所以会有anchor锚点的概念,默认是左上角的,锚点的变更会影响position的值,以及后续在布局上的参考位置
  • 添加一个RectangleHitbox,可以观察战机当前的位置。

控制战机

在手机上操作战机一般会使用拖拽,或者是拖动的交互。可以在Player中加入HasGameRefDraggable的混入。前者是用于获取最上层FlameGame对象,即我们自定义的Game对象(ps:加入的Component也是一个树状结构);后者用于实现单个对象的拖拽效果

class Player extends SpriteAnimationComponent with HasGameRef, Draggable {
  Player({required Vector2 initPosition, required Vector2 size})
      : super(position: initPosition, size: size);

  @override
  Future<void> onLoad() async {
    // 。。。
  }
  
  
  @override
  bool onDragUpdate(DragUpdateInfo info) {
    final willToPosition = position + info.delta.global;
    double x = willToPosition.x;
    double y = willToPosition.y;

    if (x < 0) {
      x = 0;
    } else if (x > gameRef.size.x - size.x) {
      x = gameRef.size.x - size.x;
    }
    if (y < 0) {
      y = 0;
    } else if (y > gameRef.size.y - size.y) {
      y = gameRef.size.y - size.y;
    }

    position = Vector2(x, y);
    return true;
  }

onDragUpdate方法会返回一个DragUpdateInfo对象,这里取info.delta.global,即为相对于当前位置的移动差值。正常情况下只需要叠加到position即可。考虑到边界问题,所以需要获取Game对象的大小进行计算,再更新position。

ps:Draggable还有onDragStartonDragEnd等方法可实现,机制上类似原生的触摸事件机制

Component最外层也就是Game需要添加HasDraggables的混入,这样事件才能往下传递

class Game extends FlameGame with HasDraggables {

实现效果

最后来看看实现效果,这里还加了一个视差组件作为背景。 VID_20220705172021_.gif

最后

本文记录了基本的Flame环境搭建以及飞机大战中的玩家战机创建。后续会记录有关战机子弹的逻辑。

猜你喜欢

转载自juejin.im/post/7116839599799795743