その他

Flutter flame を試してみる

Flutter

flameとは

https://pub.dev/packages/flame

Flutter で2Dゲームを実現しようというこのパッケージ、気になっていたんですよね。

「Flutter で2Dゲームを作ろうなんて正気じゃないね。東京から各駅停車を乗り継いで大阪まで行くようなものだよ」なんて思っていました。というか、今も思っています、用途が違うという意味でね。新幹線(Unity)使えばいいじゃない?

界隈でどれくらい市民権を得ているかは知りませんが、今日はそんなチャレンジングな flame で遊んでみようかと思います。

「当たり判定?何それ美味しいの?」的なビジネスアプリ脳な私がもし、手軽にゲームを実装できるのであれば、それは大したアーキテクチャと言えるかもしれません。

とりあえず、キャラクターを設置して動かしてみることにしましょう。

大阪…は無理だから横浜、いや品川あたりまで行ってみようか。

Hello, World!

なにはともあれ、Hello, World!

/// ホーム画面
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: GameWidget(
          game: Sample(),
        ),
      ),
    );
  }
}

ホーム画面のウィジェットがこちら。なるほど、GameWidget なるウィジェットでゲームのインスタンスをツリーに組み込むんですね。

/// サンプルゲーム
class Sample extends FlameGame {
  @override
  Future<void> onLoad() async {
    // 文字列を配置する
    await add(
      PositionComponent(
        children: [TextComponent(text: 'Hello, World!', anchor: Anchor.center)],
        position: Vector2(size.x * 0.5, size.y * 0.5),
        anchor: Anchor.center,
      ),
    );
    super.onLoad();
  }
}

で、ゲームの実装がこちら。ここからはウィジェットから離れて flame の世界になります。

Flutter がウィジェットで構成されるように、flame はコンポーネントで構成されており、宣言的に組み立てていけば良さそうです。位置を実装するコンポーネントにテキストコンポーネントを持たせてゲームコンポーネントに置きます。

スプライトアニメーションを作ってみよう

こちらのスプライトシートを使ってキャラクターアニメーションを実現してみましょう。

from: GAME ART 2D
/// キャラクター
class Knight extends SpriteAnimationGroupComponent with HasGameRef<Sample> {
  // サイズの定義
  final _knightSize = Vector2(587.0, 707.0);

  @override
  Future<void> onLoad() async {
    // スプライトシートを定義する
    final spriteSheet = SpriteSheet(
      image: await gameRef.images.load('knight.png'),
      srcSize: _knightSize,
    );

    // スプライトシートから作成したアニメーションを設定する
    animations = {
      KnightState.walk: spriteSheet.createAnimation(row: 0, to: 10, stepTime: 0.1),
    };

    // その他のプロパティを設定する
    current = KnightState.walk;
    size = Vector2(_knightSize.x / 2, _knightSize.y / 2);
    anchor = Anchor.center;
  }
}

/// キャラクターの状態
enum KnightState {walk, attack}

上下2種類のアクションを表現するため、複数のスプライトアニメーションを持たせることができる SpriteAnimationGroupComponent を継承してキャラクターのコンポーネントクラスを作成しました。上記のテキストコンポーネントと差し替えることで表示させます。

リソースの読み込みはゲームインスタンスから行う必要がありますが HasGameRef mixin を実装することで参照を持たせることができるみたい、便利!でも、使い方には注意ですね。

タップアクションをつけてみよう

もう一つのアニメーションは、タップイベント時に発火してみましょう。スプライトシートの定義にもう一行追加します。

アニメーションの切り替えは、current プロパティを変更するだけのようです。攻撃アニメーションが終了したら歩行アニメーションに戻すために少し工夫が必要そうですね。ここでは、アニメーションに実装されているティッカーのイベントメソッドを利用して実現してみました。

/// キャラクター
class Knight extends SpriteAnimationGroupComponent with HasGameRef<Sample> {
  ...

  @override
  Future<void> onLoad() async {
    ...

    // スプライトシートから作成したアニメーションを設定する
    animations = {
      KnightState.walk: spriteSheet.createAnimation(row: 0, to: 10, stepTime: 0.1),
      KnightState.attack: spriteSheet.createAnimation(row: 1, to: 10, stepTime: 0.05, loop: false),
    };

    ...

    // 攻撃アニメーション終了イベントを設定する
    animationTickers?[KnightState.attack]?.onComplete = attackEnd;
    super.onLoad();
  }

  /// 攻撃開始
  void attackStart() {
    current = KnightState.attack;
  }

  /// 攻撃終了
  void attackEnd() async {
    await Future.delayed(const Duration(milliseconds: 100));
    animationTickers?[KnightState.attack]?.reset();
    current = KnightState.walk;
  }
}

ゲームコンポーネントにタップイベントを実装して攻撃アクションを呼び出してみよう。

/// サンプルゲーム
class Sample extends FlameGame with TapCallbacks {
  ...

  @override
  void onTapDown(TapDownEvent event) {
    // 攻撃する
    knight.attackStart();
    super.onTapDown(event);
  }
}

まとめ

詳細は省きますが、ParallaxComponent を使ってシームレスな背景を流して完成したのがコチラ。

いやぁ、アニメーションはすんなり作れました。でもおそらく、大変なのはこれから先の物理計算などの実装になるのでしょうね。

箱根の山の手前まで、小田原あたりなら flame で手軽に実装するのもアリだなぁ。そんな感想を抱きましたとさ。

背景)著作者:upklyak/出典:Freepik

サンプルコードはこちらから。

author img for YoshiiHiroki

YoshiiHiroki

Yoshii

前の記事へ

次の記事へ

一覧へ戻る
PAGE TOP