LoginSignup
30
21

More than 1 year has passed since last update.

FlutterでRPGゲームを作る【Part 1/5 - マップ生成】

Last updated at Posted at 2022-10-24

Banner.gif

はじめに

作成するゲームについて

Screen Shot 2022-10-24 at 16.09.53.png

  • 見下ろし型のRPGゲームです
  • タッチパネルとキーボードの入力に対応しています
  • いくつかのイベントがあります
    • NPCとの会話する
    • アイテムを拾う・使う
    • スイッチを押して扉を開く(的なイベント)
  • 戦闘はありません (Bonfireは戦闘アクションの実装機能も充実しています)

Bonfireについて

  • Flutterで2DRPGゲーム作るためのフレームワークです
  • 画面ロジックの実装から、状態管理までBonfireだけで完結させることができます
  • FlameというFlutter用のゲームエンジンに依存しています
  • Bonfire 公式HP ← 解説やコードサンプル、Demoゲームがあります
  • Flutterドキュメントページ
  • Github

開発環境

OS:       macOS 12.6
Flutter:  3.3.5
Bonfire:  2.10.10
Tiled:    1.9.2
Xcode:    14.0.1
テスト環境: Chrome, iOS Simulator

今回のパートの大まかな流れ

  1. Flutterプロジェクトとパッケージ、アセットの準備
  2. マップ用タイルセットの作成 (Tiled利用)
  3. マップの作成 (Tiled利用)
  4. WidgetとUIを実装して動作確認

1. Flutterプロジェクトとパッケージ、アセットの準備

1.1 プロジェクト作成

$ flutter create --platforms=web,ios simple_bonfire
  • ターミナルから、プロジェクトを作成するディレクトリでflutter create
  • プラットフォームにiOSとWebを指定
  • プロジェクト名simple_bonfireはお好みで変更可能

1.2 bonfireパッケージの追加

  • プロジェクトディレクトリでflutter pub add
$ flutter pub add bonfire
  • pubspec.yamlファイルにbonfireが追加されているのを確認
pubspec.yaml
dependencies:
  flutter:
    sdk: flutter  
  cupertino_icons: ^1.0.2
  bonfire: ^2.10.10

1.3 アセットの準備

pubspec.yaml
flutter:
  uses-material-design: true
  assets:
    - assets/images/
    - assets/images/maps/

2. マップ用タイルセットの作成

  • 元のpngファイルをタイルセットとして利用するための設定jsonファイルを作成します
  • このjsonファイルを作成するためのツールがTiledです

2.1 Tiledプロジェクトの作成

  • こちらよりTiledをダウンロードしてください
  • Tiledを起動して、New Project...を選択しsimple_bonfire/assets/images/maps下にプロジェクトを作成
    simple_bonfire.tiled-projectsimple_bonfire.tiled-sessionの2つのファイルが生成されます
    Screen Shot 2022-10-22 at 23.51.19.png

2.2 アセットpngファイルの登録と分割

  • 新しいタイルセットを選択し、ポップアップのフィールドにそれぞれ値を入力
    Screen Shot 2022-10-22 at 23.53.46.png
    • 名前:タイルセットにわかりやすい名前をつける
    • 種類:均等にタイルが… (デフォルト)
    • 画像:先にダウンロードしたアセットpngファイルを選択
    • タイルの幅、高さ:アセットに合わせて16pxに変更
  • ファイル名をつけて保存を選択し.tsjで保存した後、.jsonに変更する
    (jsonに変更するのはいろいろと便利のため)
assets/maps/franuka_halloween_tileset.json
{
    "columns": 32,
    "image": "franuka_halloween_tileset.png",
    "imageheight": 512,
    "imagewidth": 512,
    "margin": 0,
    "name": "franuka_halloween_tileset",
    "spacing": 0,
    "tilecount": 1024,
    "tiledversion": "1.9.2",
    "tileheight": 16,
    "tilewidth": 16,
    "type": "tileset",
    "version": "1.9"
}
  • Tiled上で、画像が正しくタイル上に分割されているのを確認する
    Screen Shot 2022-10-23 at 0.07.31.png

3. マップの作成

  • まず、作成したタイルセットを使ってマップ生成用jsonファイルを生成します
  • Tiledでファイル > 新規 > 新しいマップの順に選択
  • ポップアップのフィールドにそれぞれ値を入力
    Screen Shot 2022-10-23 at 0.19.42.png
    • 種類:四角型タイル (デフォルト)
    • 出力形式:CSV (デフォルト)
    • 描画順序:左から右、上から下 (デフォルト)
    • マップの大きさ:任意の値を入力
    • タイルの大きさ:アセットに合わせて幅、高さともに16px
  • マップ編集画面が表示されたら、まず地面のレイヤーを作成
    • 右側のタイルを選択して、真ん中のカラムのセルに配置していきます
      (Tiledの詳しい操作は割愛しますが、画像の状態で完成です)
      Screen Shot 2022-10-23 at 0.27.31.png
  • 壁やオブジェクト用のレイヤーを作成
    • 右上のレイヤー枠で右クリック > タイルレイヤーを選択 (オブジェクトレイヤーではありません)
    • タイルレイヤー2が生成されたら、タイルレイヤー1の上に移動
    • 柵やオブジェクトを自由に配置
    • タイルレイヤー1を非表示 (目のアイコンをクリック) して画像の様な表示になったら完成
      (確認後に非表示を解除してください)
      Screen Shot 2022-10-23 at 0.37.50.png
  • ファイル > 保存を選択し.tmj形式で保存後、拡張子は.jsonに変更
assets/maps/halloween_map_01.json
{
   "compressionlevel": -1,
   "height": 16,
   "infinite": false,
   "layers": [
      {
         "data":[514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514,
            "...とても長いので中略..."
            514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514],
         "height":16,
         "id":1,
         "name":"\u30bf\u30a4\u30eb\u30ec\u30a4\u30e4\u30fc1",
         "opacity":1,
         "type":"tilelayer",
         "visible":true,
         "width":20,
         "x":0,
         "y":0
      }, 
      {
         "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 13, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 15, 0,
            0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0,
            "...とても長いので中略...",
            0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0,
            0, 77, 18, 18, 18, 18, 18, 19, 0, 0, 0, 0, 17, 18, 18, 18, 18, 18, 79, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
         "height": 16,
         "id": 2,
         "name": "\u30bf\u30a4\u30eb\u30ec\u30a4\u30e4\u30fc2",
         "opacity": 1,
         "type": "tilelayer",
         "visible": true,
         "width": 20,
         "x": 0,
         "y": 0
      }
   ],
   "nextlayerid": 3,
   "nextobjectid": 1,
   "orientation": "orthogonal",
   "renderorder": "right-down",
   "tiledversion": "1.9.2",
   "tileheight": 16,
   "tilesets": [
      {
         "firstgid": 1,
         "source": "franuka_halloween_tileset.json"
      }
   ],
   "tilewidth": 16,
   "type": "map",
   "version": "1.9",
   "width": 20
}

4 WidgetとUIを実装して動作確認

  • 作成したマップ生成用jsonファイルを使い、FlutterのWidgetを作成します

4.1 マップWidgetの作成

  • Statefull WidgetのbuildBonfireWidgetreturnします
  • BonfireWidget内で以下の3つを設定しています
    • マップ用jsonファイルの読み込み
    • 画面上にジョイスティックやボタンと、キーボードの入力の有効化
    • 遷移直後の読み込み画面
  • 細かな説明はコメントに留めます
lib/maps/halloween_map_01.dart
import 'package:flutter/material.dart';
import 'package:bonfire/bonfire.dart';
import 'package:flutter/services.dart';

class HalloweenMap01 extends StatefulWidget {
  const HalloweenMap01({Key? key}) : super(key: key);
  @override
  State<HalloweenMap01> createState() => _HalloweenMap01State();
}

class _HalloweenMap01State extends State<HalloweenMap01> {
  final tileSize = 48.0; // タイルのサイズ定義

  @override
  Widget build(BuildContext context) {
    // ゲーム画面Widget
    return BonfireWidget(
      // マップ用jsonファイル読み込み
      map: WorldMapByTiled(
        'maps/halloween_map_01.json',
        forceTileSize: Vector2(tileSize, tileSize),
      ),
      // 入力インターフェースの設定
      joystick: Joystick(
        // 画面上のジョイスティック追加
        directional: JoystickDirectional(
          color: Colors.white,
        ),
        actions: [
          // 画面上のアクションボタン追加
          JoystickAction(
            color: Colors.white,
            actionId: 1,
            margin: const EdgeInsets.all(65),
          ),
        ],
        // キーボード用入力の設定
        keyboardConfig: KeyboardConfig(
          keyboardDirectionalType: KeyboardDirectionalType.wasdAndArrows, // キーボードの矢印とWASDを有効化
          acceptedKeys: [LogicalKeyboardKey.space], // キーボードのスペースバーを有効化
        ),
      ),
      // ロード中の画面の設定
      progress: Container(
        width: double.maxFinite,
        height: double.maxFinite,
        color: Colors.black,
      ),
    );
  }
}

4.2 マップWidgetの実装

  • いよいよ作成したマップを表示してゲームを動かします
  • MaterialApphomeに作成したマップWidgetを入れます
lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:bonfire/bonfire.dart';
import 'package:simple_bonfire/maps/halloween_map_01.dart';

void main() async {
  // iOSでは横画面にする
  WidgetsFlutterBinding.ensureInitialized();
  if (!kIsWeb) {
    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 const MaterialApp(
      title: 'Simple Bonfire',
      debugShowCheckedModeBanner: false,
      home: HalloweenMap01(),
    );
  }
}

4.3 起動して表示と動作の確認

  • 今回はWebとiOS版で動作確認をしています
  • キャラクターは実装していませんが、矢印キーやWASD、ジョイスティックでカメラが動くのが確認できます
    Screen Shot 2022-10-23 at 11.48.09.png
    Simulator Screen Shot - iPhone 14 Pro Max - 2022-10-23 at 11.49.37.png

おわりに

アセットやマップの準備には外部のツールも使用し一手間かかりましたが、Flutterの実装部分はとてもシンプルだったと思います。
次回はプレイヤーを実装し、当たり判定などゲームとして必要な要素をさらに追加していきます。
いいね、質問やコメントお待ちしております!

Part 2:プレイヤーと当たり判定の実装へと続く

30
21
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
21