Flutterとbonfire v3.xでゲームを作る ①

# はじめに
今更ですがM1 mac book airを買いました。未開封品16GBで飛びつきました。(M3まで出てましたが予算がなかったもので。。。でもメモリは16GB欲しかったんです)
せっかくなので去年挫折したFlutter+bonfireで作るスマホゲーム製作に再挑戦したいと思います

ちなみに私がゲームを作る時に参考にしたサイトはこちら
「<a href="https://qiita.com/KAZMA_WED/items/ed43e1c7bbaec397c486">FlutterでRPGゲームを作る【Part 1/5 - マップ生成】</a>」
- ※現在と仕様が違うので注意

## 注意事項
<a href="https://pub.dev/packages/bonfire">bonfire</a>は<a href="https://bonfire-engine.github.io/#/">2.x系と3.x系</a>でかなり仕様が異なります。最新の情報を見つけるまで結構苦労しました。
またクラス名の変更などもしょっちゅうありますので（なんでクラス名かえるん・・・？）、なにかエラーがあったら<a href="https://github.com/RafaelBarbosatec/bonfire/releases/tag/v2.6.1">常に最新のchange log</a>を追うことは重要です。
この記事は2024年7月時点のものです。今後も同じような仕様変更が考えられるため、十分に注意してください

## 使用するソフト
- Android Studio + Flutter
- <a href="https://thorbjorn.itch.io/tiled">Tiled</a> (MAP作成)

## ゲームで使用する素材
- 基本的に素材は購入 or 無料のものを使用していきます(<a href="https://itch.io/">itch.ioとか</a>)
    - 利用規約はしっかり目を通しておきましょう。気合を入れて作ったゲームで配信前/配信後にいちゃもんがつくのは非常に面倒です
    - 英文であっても、今ならdeeplやchatGPTに投げればしっかりと翻訳しくれます。しっかりと理解してDLしましょう
- 私がテストで作った素材を末尾に置いておきます（著作権フリー）。MAP制作までならこのデータで進められます

## 構築時点の環境
```
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.22.2, on macOS 14.5 23F79 darwin-arm64, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.1)
[✓] VS Code (version 1.91.1)
[✓] Connected device (3 available)
[✓] Network resources
```

# 構築
## 1. プロジェクトを作成
- プロジェクトを作成します。今回の対象はandroidとiOSにしました。ちなみにbonfire自体はどのOSでも対応しているようです
![スクリーンショット 2024-07-16 21.54.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3737064/a22840a1-758a-0b71-62b4-f709ab9e8104.png)

## 2. gitを作っておこう
- ちゃんとgitを作っておきましょう
- 4.で作るassetsディレクトリを含めるかはよくよく検討してください。mapなどを作成するたびに変更が入ってしまうので、.gitignoreで含めないのもアリかと思います

## 3. bonfireを導入
- ターミナルから以下のコマンドを入力してbonfireを導入します。`pubspec.yaml`にバージョン指定で記載してもOKです
``` 
flutter pub add bonfire 
```

## 4. assetsフォルダを作る
- 以下の階層にassetsフォルダとimages/map/theLandOfBeginningsフォルダを作ります
  - bonfireの内部でassets/images/から呼び出しているクラスがあるため、imagesフォルダは必須で作りましょう
- ついでに最初の画面を格納する`theLandOfBeginnings`フォルダも作っておきます
```
my_flutter_app(ここは各自のプロジェクトフォルダ名)/
├── android/
├── ios/
├── lib/
│   └── map/theLandOfBeginnings/
├── test/
├── assets/
│   └── images/map/theLandOfBeginnings/
├── pubspec.yaml
└── ...
```

## 5. ソースコードを書いていく
まずは最低限の、MAPを表示させるところまで作っていきます。
- main.dart
```dart
import 'package:bonfire/bonfire.dart';
import 'package:flutter/material.dart';
import 'package:my_flutter_app(ここは各自のプロジェクトフォルダ名)/map/theLandOfBeginnings/theLandOfBeginnings1.dart';

void main() async{
  WidgetsFlutterBinding.ensureInitialized();
  // ゲーム画面　横
  await Flame.device.setLandscape();
  await Flame.device.fullScreen();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const TheLandOfBeginnings1(),
    );
  }
}
```

- lib/map/theLandOfBeginnings/theLandOfBeginnings1.dart
```dart
import 'package:flutter/material.dart';
import 'package:bonfire/bonfire.dart';

class TheLandOfBeginnings1 extends StatefulWidget {
  const TheLandOfBeginnings1({super.key});

  @override
  State<TheLandOfBeginnings1> createState() => _TheLandOfBeginnings1State();
}

class _TheLandOfBeginnings1State extends State<TheLandOfBeginnings1> {
  @override
  Widget build(BuildContext context) {
    return BonfireWidget(
      map: WorldMapByTiled(
        WorldMapReader.fromAsset("map/theLandOfBeginnings/tutorial.json"),
        forceTileSize: Vector2(16, 16),
      ),
    );
  }
}
```

- pubspec.yaml抜粋
```yaml
  assets:
    - assets/images/map/theLandOfBeginnings/
```

### 解説
- main.dart
    - main()では画面をフルスクリーンにすることと横で使うことを宣言しています
    - home画面には`TheLandOfBeginnings1`というクラスを設定しています

- theLandOfBeginnings1.dart
    - buildにはBonfireWidget()を呼び出します
    - mapはrequiredなので必須です 
    - ここでは`assets/images/map/theLandOfBeginnings/tutorial.json`を呼び出しています（後ほど作ります）
- pubspec.yaml
    - assetsの指定方法は、呼び出したいファイルが存在するフォルダまでを指定する必要があります

この状態でBUILDするとエラーになりますが、**MAPさえ準備できれば**描写される状態です。
というわけで以下の画像とJSONファイルをDLし、3つとも`assets/images/map/theLandOfBeginnings/`フォルダに格納してください（私が作ったものなので著作権などはフリーです）

![grassland.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3737064/9739a6d1-b054-b5f2-6f5d-a049a9ccfb62.png)

以下の2つのJSONデータも保存します(共有googleドライブを開きます)
<a href="https://drive.google.com/file/d/1dqt6pBDQXhoxF1pZSEqsq99bTagIRZ0W/view?usp=drive_link">grassland.json</a>
<a href="https://drive.google.com/file/d/1ivNtOVuaZAZtjuK8GEvi-Oxi8gj4B_BR/view?usp=drive_link">tutorial.json</a>

これでビルドが通るとこうなります
![スクリーンショット 2024-07-19 12.38.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3737064/ef5425b4-dc47-a210-e7a0-ccbaa0cb09ab.png)

### WorldMapByTiledでエラーが起きた場合
もしかしたら少し古いFlutterバージョンを使っている方の場合、mapで呼び出すクラス名が違うことによるエラーが発生するかもしれません。

その場合は以下の`TiledReader`という呼び出し方法を確認してください。

``` dart
return BonfireWidget（
    map: WorldMapByTiled(
        TiledReader.asset("map/theLandOfBeginnings/tutorial.json.json"),
      ),
）
```

## おわりに
今回の解説はここまでとなります。
Tiledを利用したMAP制作については次回 or 冒頭でも紹介した「<a href="https://qiita.com/KAZMA_WED/items/ed43e1c7bbaec397c486">FlutterでRPGゲームを作る【Part 1/5 - マップ生成】</a>」が参考になりますので、どんどん進めたい方はそちらを見に行ってもいいでしょう

ちなみに私は当初↑の情報をみてゲームを作っていたのですが、MAP作成とプレイヤー導入くらいまではなんとかできたのですが、当たり判定やらの処理で仕様が違いすぎてすごく苦労しました。

次回はTiledでのMAP生成とプレイヤーの導入周りを説明できればと思います。

では良きFlutter Lifeを！