本記事はFlutter #2 Advent Calendar 2020の5日目の記事です
はじめに
2020年はゲームギアミクロ、ミニテトリス、ゲーム&ウォッチなど携帯ゲームがいっぱい発売されました!
というわけでゲームギアミクロと同じくらいのサイズのAndroidスマホ、Jelly Pro向けにゲーム&ウォッチ風アプリを作ってみました。(上の大きく見えるのはPixel4です)
前々から調査していたのですが、12/1〜12/3にドット絵を書いて、12/4の仕事終わりからコードを書いて12/5にできたサンプルコードが以下になります。
ソースコード: https://github.com/keidroid/flame_santa/
衝突判定やスコアなどのゲーム要素は作っていないのですが、プレゼントが上から落ちてきて、左右キーでサンタが動かせるようになっています。
今回FlameというFlutterの2Dゲームエンジンで作ったのですが、Flameの良いところをサンプルアプリのコードの解説しつつ紹介していきます。
ちなみに、Flameは2020年12月5日現在、安定版が0.82.0なのですが11月から1.0.0-rc1がリリースされています。もうすぐ1.0.0の正式版がリリースされるとのことです
https://flame-engine.org/
https://github.com/flame-engine/flame
Flameの良いところ
セットアップがかんたん
パッケージ化されているのでpubspecに1行追加し、 flutter pub get
するのみです。
dependencies:
flutter:
sdk: flutter
flame: ^1.0.0-rc2 //この行をたす
ゲームのための初期設定がかんたん
Flameの初期設定は以下のように行います。
基本的にはFlame.initを呼び出すだけでFlame.utilに集約されています。
Flame.util.setLandscapeを呼び出すと右向き・左向きの横画面に両対応します。
void main() async {
await Flame.init(
fullScreen: true, orientation: DeviceOrientation.landscapeRight);
await Flame.util.setLandscape();
await Flame.util.initialDimensions();
:
Widgetを使うことでドット絵向けのゲーム画面が作りやすい
今回サンプルアプリの画面のドット絵部分は64x64となっています。
スマホ向けゲームでは異なる解像度や画面サイズに対応しないといけないのがつらいですが、Flameではゲーム画面をWidgetとして扱うことで対応できます。
FlutterのSizedBox Widgetを使えば、画面解像度(今回は64x64)を保ったまま画面いっぱいに引き伸ばすことが可能です。
これによってドットがボケることもありません。
SizedBox(width: 64, height: 64, child: game.widget)))),
Unityのような3D系のゲームエンジンだと、初期設定で拡大するとぼやけた2D画像になるものが多いため、ドット絵のゲームにおすすめです。
操作系UIをゲーム画面外に配置できる
ゲーム画面内のタッチイベントでも操作可能ですが、今回はゲーム画面外の画面端にButtonを配置しました。
FlameではGameをWidgetとして扱えるので他Widgetとの連携を簡単に行うことができます。ゲーム自体はドット絵にして、ボタンをリアルにすることなども可能です。
今回のサンプルアプリではGame WidgetとButton Widgetを均等配置することで、サイズが違う端末でも親指で操作できるようになっています。
Pixel4 と Jelly Pro のキャプチャ比較: ボタン位置(灰色)が調整されている
画像の回転・反転がかんたん
FlutterのみでImageクラスを扱おうとすると、行列計算を使用しないと回転・反転ができなかったのですがFlameではComponentクラスでラップされており、回転・反転操作がしやすくなっています。
import 'package:flame/components/component.dart';
Sprite sprite = Sprite('player.png');
final size = Vector2.all(128.0);
var player = SpriteComponent.fromSprite(size, sprite);
// screen coordinates
player.position = ...
player.angle = 30; // 回転はangle指定
player.renderFlipX = true; // 反転はflipで指定
player.render(canvas);
表示形式のサポート
2Dゲームには必須のSpriteSheet形式はもちろん、Flutterで使用できるSVGやRive(元Flare)もGame Widget内に組み込むことができます(未検証)
他にもParticles、Tilemap、Parallaxなど2Dゲームに必要そうな機能が一通りあります。
https://flame-engine.org/docs/components.md
Flutter Webで動作する
Flutter本体の動作が危ういですがFlame自体は動作しました!
以下ページのFirebase Hostingで公開します。
Flutterのビルド後にCSSを追記しました。
CSS側でレンダリングを pixelated
指定すればドット絵がボケずに拡大します。
body {
image-rendering: pixelated;
}
2D物理演算がバンドルされている
少し前までBox2Dをサポートしていたのですが、1.0.0-rc2現在は派生のForge2dをサポートしています。通常のBox2Dと同じ用に扱うことができます。
Forge2d
https://github.com/flame-engine/flame_forge2d
Box2D
https://pub.dev/packages/flame_box2d
派生の2D RPGゲームエンジンがある
FlutterとFlameを使用して、Bonfireという2DのRPGエンジンを作られている方がいます。
https://github.com/RafaelBarbosatec/bonfire
Flutter+Flameを使ってみて気になったところ
- 非力な端末だとFlutterのGestureDetectorでイベントが抜けることがあった
- fpsを落とす機能が見当たらない. (ドット絵ゲームはカクカクにしたい)
Kotlinで書きたい
まとめ
ドット絵のゲームやミニゲームを作成する方にはFlameとてもおすすめです。
今までFlameの0.x.x系を使用していたのですが、1.0.0ではFlutterのデフォルトの機能をより生かして独自クラスを減らした構成になっているように感じました。
以下はFlutterでゲーム制作を模索する過去ログです。Flameを使用することでFlutterでのゲーム作成のしきいが一気に下がったように思います。
第1弾: Flutterを半日くらいやって良さげだと思ったこと
https://qiita.com/keidroid/items/dd3230dba329eb9f82c5
第2弾: Flutterで作ったミニゲームをFlutter for Webに移植した
https://qiita.com/keidroid/items/84691d1c891061298144
第3弾: Flutterの低レイヤー周り - Flutterでゲーム制作はできそうか調査してみた
https://qiita.com/keidroid/items/ac213910980bc125995a