[Development Notes for Aircraft Wars Based on Flutter&Flame] Building a Project and Creating a Fighter

foreword

Recently I am learning a Flutter-based game engine Flameand trying to write a plane battle myself. This article will record the process of project construction and player fighter creation. It is strongly recommended to give priority to the following content before reading the article, which will give you a certain Flameknowledge base . Of course, the foundation of Flutter is also necessary.

Flutter&Flame Game Column - Zhang Fengjie's column

flame-engine/flame: A Flutter based game engine

Environment and resource construction

After creating a new project, pubspec.yamladd the core dependencies of Flame in . ps: Here the author is using the latest version 1.2.0.

environment:
  sdk: ">=2.17.5 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flame: ^1.2.0

There is also the regular Flutter static resource configuration. ps: These resources are not all used in this article. The picture resources are all found on the Internet. The resources of WeChat Airplane Wars are only for learning .image.png

Create a fighter

game environment

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

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

In Flutter's mainfunction, runAppone is passed in GameWidget, which is actually one StatefulWidget. GameInherited from FlameGame, which is the one defined by Flame Component. Flame relies on Componentunwinding, analogously to how Flutter reliesWidget . Subsequent objects in the game will be implemented in the FlameGameobjectComponent .

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

onLoadIt is Componentone of the life cycles in Activity. If you have learned Android, you can compare it with Activity's onCreate.

player fighter

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);
  }

Create class 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: DraggableThere are onDragStartother onDragEndmethods that can be implemented, and the mechanism is similar to the native touch event mechanism .

The Componentoutermost layer is HasDraggablesthe mixin that Game needs to add so that events can be passed down .

class Game extends FlameGame with HasDraggables {

achieve effect

Finally, let's take a look at the implementation effect. Here, a parallax component is added as a background.VID_20220705172021_.gif

at last

This article documents the basic Flame environment construction and the creation of player fighters in Aircraft Wars. The logic of fighter bullets will be recorded later.

Guess you like

Origin juejin.im/post/7116839599799795743