Flutter x Flame でブロック崩しゲームを作ってみた!
Flutter と Flutterのゲームエンジンである Flame で、
ブロック崩しゲームを作ってみました。
ソースコードは以下です。
本記事では
- Flameの特徴
- 作成にあたり気をつけたこと(依存関係等)
- 感想
を記載します。
最後にこのブロック崩しアプリを手元で作ることのできるチュートリアル記事を書きましたので、
そちらを紹介します。
ぜひ読んでみてください!
Flame の特徴
Flameの特徴として、以下について話していきます。
- Flameの概要
- ゲームの表示のさせ方
- ゲームの構成
- 機能追加の仕方
Flame の概要
Flameはゲームのための独創的な解決策を提供する、 Flutter のゲームエンジン です。
Flameを利用することにより、ゲームを作成するのに必要なコードの簡素化が行えます。
Flutterでは、こちらのパッケージをインストールするだけでFlameを利用することができます。
公式ドキュメントも用意されており、
かなり力の入っているパッケージとなっています。
ゲームの表示のさせ方
Flameでのゲーム作成の開始はとにかく簡単です。
Flameに用意されているGameWidget
を用いて、
作成したゲームを読み込むだけです。
void main() {
final game = BlockBreaker();
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
child: GameWidget(
game: game,
),
),
),
);
}
(ここでBlockBreaker
が作成したゲームを管理するクラス)
たったこれだけで、ゲームが画面表示されます。
めっちゃ簡単です。
ゲームの構成
Flameで作成するゲームの構成要素は大きく分けて2種類に分かれます。
-
FlameGame
拡張クラス [1] - 各種
Component
拡張クラス
FlameGame拡張クラス
class BlockBreaker extends FlameGame
with HasCollisionDetection, HasDraggableComponents, HasTappableComponents {
// ...
}
FlameGame
拡張クラスはいわばゲームの土台です。
ここで後述の各Component
を配置(add
)することで、ゲームを構築します。
また、FlameGame
拡張クラスが前述のGameWidget
にインスタンスを渡すクラスとなります。
各種Component 拡張クラス
class Ball extends CircleComponent with CollisionCallbacks {
// ...
}
Flameには様々なComponent
と呼ばれるゲーム構成要素が存在します。
これらを継承したクラスを作成することで、ゲームの様々な登場人物(物体)を作成することができます。
上のBall
クラスの例で言うと、CircleComponent
、
円の形のComponent
となり、
半径や色を設定することで円の物体をゲームに登場させることができます。
他にも、
class Block extends RectangleComponent with CollisionCallbacks {
// ...
}
といった、Rectangle
(長方形)のComponent
だったり、
class MyTextButton extends TextBoxComponent with TapCallbacks {
// ...
}
といったTextBox
のComponent
だったりが存在します。
再掲になりますが、
これらをFlameGame
拡張クラスに配置(add
)することでゲームを構築するわけです。
機能追加の仕方
いざゲームを作ろう、と思って悩むのが、以下だと思います。
「物体のドラッグってどう実装したらいいんだろう?」
「物体同士の衝突ってどう実装したらいいんだろう」
これをFlameは簡単に解決します。
Flameにはドラッグ、タップ、衝突検知など様々な機能を持ったクラスが用意されており、
これらをComponent
拡張クラスにmixin
するだけで、
そのクラスでmixin
したクラスの機能を使うことができます。
class Ball extends CircleComponent with CollisionCallbacks {
// ...
}
例えば上記Ball
クラスであれば、CollisionCallbacks
をmixin
しているので、
衝突時に発火するメソッドを使えるようになります。
これらを利用することで、物体がぶつかった時に反射させたり、壊したりが実装できるわけです。
(やってみると、めっちゃ簡単です。)
ざっくりとした解説になりますが、以上がFlameの特徴となります。
作成にあたり気をつけたこと(依存関係等)
コードを書く上でアプリ作成にあたり気をつけたことを解説します。
依存関係の意識
HasGameRef<T>
というクラスがあります。
これをmixin
することで、Component
拡張クラス(以下Component
クラス)から、
自身が配置されているFlameGame
拡張クラス(以下Game
クラス)を参照できるようになります。
これにより、Component
クラスからゲーム画面のサイズを取得できるので、
Component
クラスの配置がとても楽に書くことができます。
最初はこれを使用してブロック崩しゲームを作成していたのですが、
作成したコードをみて気になったのがクラスごとの依存関係です。
HasGameRef<T>
のT
にはGame
クラスを指定します。
そのため、Component
クラスはGame
クラスに依存し、
Game
クラスはComponent
クラスを配置するためComponent
クラスに依存する、
相互参照の関係になります。
感覚的な話になりますが、
これに対しあまり良くないコードだな、と思ってしまいました。
他でゲームを作成しようとした際に、クラスを使い回すことができない、
というのが後付の理由です。
特にComponent
クラスがGame
クラスに依存するのが違和感がありました。
例えばBall
のようなどのゲームでも使いまわせそうな物体が、
Game
に依存しているがために使い回せない、というのが違和感でした。
そこで、現在のコードでは、HasGameRef<T>
の使用を廃止し、
以下の図のような依存関係に修正しています。
これにより使いまわしの効くコードとすることができました。
役割の意識
上記修正にあたり意識したのが、各クラスの役割です。
Game
クラスの役割を、
「ゲームに関する設定や処理、物体の配置を管理するもの」とし、
Component
クラスの役割を、
「Component
に関する機能や処理を管理するもの」として役割分担をしました。
たとえば物体の位置についてです。
これはGame
クラスが役割を持つべきだと考えました。
各Component
にはゲームの盤面の知識がないためです。
そのため、以下のコードのように、Game
クラスにて、位置(position
)の設定を行うようにしました。
class BlockBreaker extends FlameGame
with HasCollisionDetection, HasDraggableComponents, HasTappableComponents {
// ...
Future<void> onLoad() async {
final paddle = Paddle(draggingPaddle: draggingPaddle);
final paddleSize = paddle.size;
paddle
..position.x = size.x / 2 - paddleSize.x / 2
..position.y = size.y - paddleSize.y - kPaddleStartY;
await addMyTextButton('Start!');
await addAll(
[
ScreenHitbox(),
paddle,
],
);
await resetBlocks();
}
// ...
}
他にも、「Ball
が『何回落ちたか』の知識を持っているのおかしいよな」等を考え、
各Componentが持つメソッドは、そのComponent
に関わるものだけになるようコードの修正を行いました。
以上が作成に当たり気をつけたこととなります。
感想
Flameでゲームを作成してみた感想ですが、めっちゃ楽しかったです。
第1版は7時間くらいぶっ続けで作りましたが、楽しんで集中して作成できました。
開発体験も良かったと思います。
座標に対して位置を指定して配置する、というのがFlameの位置指定の仕方で、
好き嫌いが分かれる部分かと思います。
ただ、これは自分は得意な配置の仕方でした。
方眼紙に絵を描いているような感覚でゲームの物体を配置することができたと思います。
物体の反射のロジックだったり、個数変更に強いブロックの位置の計算式だったりを考えるのも楽しかったです。
以下のような図で計算式を考えていました。
あと結構ニッチな分野だと思うのですが、GitHub Copilotが活躍してくれたのも
印象的でした。
今回作成したブロック崩しのロジックだったり実装だったりを利用すれば色々なゲームが作れそうだな、
と頭の中で考えています。
作成したら記事で紹介しようと思っているので、ぜひ、お楽しみにしていてください。
Flutter x Flame のチュートリアル記事
読むことで今回紹介したブロック崩しが作れるチュートリアル記事を作成しました。
ぜひこちらも併せて読んでみてください。
最後に
今回作成したアプリのリポジトリが良いな、と思ったらスターをいただけると嬉しいです。
Flutter大学 というFlutterエンジニアに特化した学習コミュニティに参加しています。
現時点で300人以上が参加する、Flutterを学ぶ人、使っている人の集まるコミュニティです。
以下の招待リンクから参加することができます。
また、週刊Flutter大学というブログの記事作成をしています。
前の週にあったFlutter界隈のニュースなどをまとめたり、アプリ作成の役に立つ知識の記事を書いたりしています。
Flutter情報のキャッチアップに役立つこと間違いなしですので、
ぜひこちらも確認してみてください!
一緒にFlutterを楽しみましょう!
-
正確には
FlameGame
もComponent
の拡張クラスです。
ただ、役割としては分けたほうがわかりやすいため、分けています。 ↩︎
Discussion