Open4

Flame 学習記録

づだづだ

Flame を学ぶモチベ

  • 既存の Flutter アプリに、簡易的に組み込めるゲームを、という要望があるかも知れない(妄想)ので、そこ叶えれるならやりたい。
    • Flame は Flutter のウィジェットとして実装できるので
  • シンプルにどんな技術か知りたい。
  • いずれゲーム開発に手を出したいので、その走りになれば。

ゴール

テニスのゲーム作りたい。
とりあえずキーボード操作ではなく、アプリのタップ操作前提で(余裕があればキーボードも)。
やり始めないとスケジュール感が見えないが、とりあえず無茶に、12月中にブロック崩し作りたい。
その辺はやりながら変わってくけど、一旦このままで。

想定は、ブロック崩し => エアホッケー => テニス => オンラインゲーム対応
ラグ軽減のための試作とかやりたい

コイントスしたい

づだづだ

1日目(2024/12/8)

https://docs.flame-engine.org/latest/flame/flame.html

公式のチュートリアルを進める

メモ

Flutter と Flame で、クラス名の競合とかありそうなので、①Flutter と Flame の架け橋、②Flame のみの実装 、でファイル分けるのが良さそう

first_game_widget.dart
import 'package:flame/game.dart' as flame_game;
import 'package:flutter/material.dart';

import 'first_game.dart';

class FirstGameWidget extends StatelessWidget {
  const FirstGameWidget({super.key});

  
  Widget build(BuildContext context) {
    return flame_game.GameWidget.controlled(
      gameFactory: () {
        return FirstGame();
      },
    );
  }
}
first_game.dart
import 'package:flame/components.dart';

class FirstGame extends FlameGame {
  FirstGame() : super(children: [_Image()]) {}
}

成果

Material のテーマと繋げる

1. ThemeData をとる FlameGame クラスの作成

abstract class MaterialFlameGame extends FlameGame {
  MaterialFlameGame({
    super.camera,
    super.children,
    required this.themeData,
  });

  final ThemeData themeData;
}

HasAncestor で、子孫から参照できるようにする

InheritedWidget みたいだね

typedef HasMaterialGame = HasAncestor<MaterialFlameGame>;

extension HasMaterialWorldReferenceEx on HasMaterialGame {
  ThemeData get themeData => ancestor.themeData;
  ColorScheme get colorScheme => themeData.colorScheme;
  TextTheme get textTheme => themeData.textTheme;
}

material.dartText と同じような挙動をする Component を実装

流石に DefaultTextStyle を採用するのはちょっとズレてると思い、シンプルに bodyMedium を実装する形

class MaterialTextComponent extends TextComponent with HasMaterialGame {
  MaterialTextComponent({required super.text});

  
  void onMount() {
    super.onMount();
    textRenderer = TextPaint(
      style: textStyle,
    );
  }

  TextStyle get textStyle => themeData.textTheme.bodyMedium!;
}

ダークテーマでも動くようになった。

これが意味あるかはわからん。

縦に並べる宣言的な Component

なんか命令的に並べる方法しかなかったので、縦に並ぶやつを作ってみた。

class _ColumnComponent extends Component {
  _ColumnComponent({required Iterable<PositionComponent> super.children});

  
  void onGameResize(Vector2 size) {
    super.onGameResize(size);

    Component? current;
    for (final child in children) {
      const x = 0.0;

      if (child is! PositionComponent) {
        throw AssertionError(
          '_ColumnComponent の children には、PositionComponent を使用してください',
        );
      }

      if (current is PositionComponent) {
        child.position = Vector2(x, current.y + current.height);
      } else {
        child.position = Vector2(x, 0);
      }

      current = child;
    }
  }
}
class FirstGame extends FlameScaffoldBackgroundGame {
  FirstGame({required super.themeData})
      : super(
          children: [
            _ColumnComponent(
              children: [
                _Image(),
                MaterialTextComponent.title(text: 'Panda'),
                MaterialTextComponent(text: 'Panda じゃないかも'),
                MaterialTextComponent(text: '🍅トマトかも'),
                MaterialTextComponent(text: '🍅トマトかも'),
                MaterialTextComponent(text: '🍅トマトかも'),
              ],
            )
          ],

づだづだ

2日目(2024/12/14)

ジェスチャー、エフェクト、当たり判定らへんをちょっとずつ触っていく。
コイントスやってみたい。

成果

タップで動かすやつ

変数で距離の切り替えできた