9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

FlutterでRPGゲームを作る【Part 3/5 - マップ移動】

Last updated at Posted at 2022-10-25

Banner.gif

はじめに

開発環境

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

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

  1. マップを作成しセンサーを配置する
  2. マップ移動のメソッドを作成する
  3. マップ移動を実装する

1. マップを作成しセンサーを配置する

1.1 2つ目のマップを作成する

  • マップ移動を実装する前に、まず2つ目のマップを作成します。
    マップWidgetはマップ生成用のjsonファイルを元に作られます。
    GUIツールTiledを使ったマップ生成用jsonファイルの作成方法はPart 1Part 2でも簡単に解説していますので、そちらをご覧ください。
    今回もこのアセットで画像の様なマップを用意し、名前はsimple_bonfire/assets/maps/halloween_map_02.jsonとしました。
    無料アセットを利用されたいかたはこちらなどがおすすめです
    Screen Shot 2022-10-24 at 20.06.33.png
  • 今回は柵と木や家を重ねて描画したり大きな木を隣接して配置するために、レイヤーをたくさん重ねています。

1.2 センサーを配置する

マップの移動はセンサーオブジェクトとプレイヤーの接触をトリガーにします。

  • オブジェクトレイヤーの追加
    センサーを配置するために、マップにオブジェクトレイヤーを追加します。
    レイヤー > 新規 > オブジェクトレイヤーの順で選択すると、画像の様にタイルレイヤーとは異なるアイコンのレイヤーが右のカラムに追加されます。
    68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323730303630322f38346265613536392d613564342d383434612d653836352d3562616363666562336331342e706e67.png

  • センサーを配置

    • 追加されたオブジェクトレイヤーをリストから選択した状態で、メニューの四角形を追加を選択する。
    • マップの出口付近をドラッグしてセンサーを配置する。
  • センサーのプロパティの編集

    • メニューのオブジェクトを選択を選択後、中央のマップ上に配置したセンサーを選択
    • 名前にbottomExitSensor, ClassにexitSensorを指定
      値は任意ですが、わかりやすく統一感のあるものがよいです。
    • 必要であれば位置やサイズを微調整して、保存
      68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323730303630322f30363231366565362d353935322d313531362d353137352d3435393765353433313263612e706e67.png
  • 生成されたjsonファイルのlayersの配列に"type": "objectgroup"の要素が追加されているのを確認してください。

simple_bonfire/assets/maps/halloween_map_02.json
{
   "compressionlevel": -1,
   "height": 16,
   "infinite": false,
   "layers": [
      {"tilelayerが": "ここに並びます"},
      {
         "draworder": "topdown",
         "id": 6,
         "name": "Object Layer 1",
         "objects": [
            {
               "class": "exitSensor",
               "height": 8,
               "id": 1,
               "name": "bottomExitSensor",
               "rotation": 0,
               "visible": true,
               "width": 64,
               "x": 128,
               "y": 216
            }
         ],
         "opacity": 1,
         "type": "objectgroup",
         "visible": true,
         "x": 0,
         "y": 0
      }
   ],
   "以下": "略"
}

1.3 マップWidgetを作成する

ほぼPart 1で作成したhalloween_map_01.dartのコピペなので、ここには変更箇所のみ記載します。

  • 読み込むjsonのパス
  • プレイヤーの初期位置を調整し、初期方向を上に
  • cameraConfig()zoom: 1.25を追加
simple_bonfire/lib/maps/halloween_map_02.dart
import 'package:flutter/material.dart';
import 'package:bonfire/bonfire.dart';
import 'package:flutter/services.dart';
import 'package:simple_bonfire/player/player_bearded_dude.dart';
import 'package:simple_bonfire/player/player_sprite.dart';

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

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

  @override
  Widget build(BuildContext context) {
    // 画面
    return BonfireWidget(
      // showCollisionArea: true,
      // マップ用jsonファイル読み込み
      map: WorldMapByTiled(
        'maps/halloween_map_02.json',
        forceTileSize: Vector2(tileSize, tileSize),
      ),
      // プレイヤーキャラクター
      player: PlayerBeardedDude(
        Vector2(tileSize * 9.5, tileSize * 12),
        spriteSheet: PlayerSpriteSheet.all,
        initDirection: Direction.up,
      ),
      // カメラ設定
      cameraConfig: CameraConfig(
        zoom: 1.25, // 小さめのマップなので少し拡大してみる
        moveOnlyMapArea: true,
        sizeMovementWindow: Vector2.zero(),
        smoothCameraEnabled: true,
        smoothCameraSpeed: 10,
      ),
      // 入力インターフェースの設定
      joystick: Joystick(
        // 画面上のジョイスティック追加
        directional: JoystickDirectional(
          color: Colors.white,
        ),
        actions: [
          // 画面上のアクションボタン追加
          JoystickAction(
            color: Colors.white,
            actionId: 1,
            margin: const EdgeInsets.all(65),
          ),
        ],
        // キーボード用入力の設定
        keyboardConfig: KeyboardConfig(
          keyboardDirectionalType: KeyboardDirectionalType.wasdAndArrows,
          acceptedKeys: [LogicalKeyboardKey.space],
        ),
      ),
      // ロード中の画面の設定
      progress: Container(
        width: double.maxFinite,
        height: double.maxFinite,
        color: Colors.black,
      ),
    );
  }
}

1.4 もう一つのマップを調整する

Part 1で作ったマップのプレイヤーの初期位置を入り口の近くに変更します。(今回はセンサーのすぐ上あたりに変更)

simple_bonfire/lib/maps/halloween_map_01.dart
// ...省略...

class _HalloweenMap01State extends State<HalloweenMap01> {
  final tileSize = 48.0;

  @override
  Widget build(BuildContext context) {
    // 画面
    return BonfireWidget(
      // ...省略...

      // プレイヤーキャラクター
      player: PlayerBeardedDude(
        Vector2(tileSize * 9.5, tileSize * 12.5), // 初期位置変更
        spriteSheet: PlayerSpriteSheet.all,
        initDirection: Direction.up, // 初期向き変更
      ),

      // ...省略...
    );
  }
}

2. マップ移動のメソッドを作成する

Flutterでよく使われるNavigator.push()pop()による画面遷移はゲームのマップ移動には不向きです。
pushAndRemoveUntil()を使い、より適した、そしてゲームらしいエフェクトの画面遷移を実装します。

2.1 遷移メソッドの作成

コードはBonfire公式サンプルのひとつ、MultiBiomeGameから拝借しました。

simple_bonfire/lib/utilities/extentions.dart
import 'package:flutter/material.dart';

extension BuildContextExtensions on BuildContext {
  Future goTo(Widget page) {
    return Navigator.pushAndRemoveUntil(
      this,
      PageRouteBuilder(
        pageBuilder: (context, animation, secondaryAnimation) => page,
        transitionsBuilder: (context, animation, secondaryAnimation, child) {
          return Container(
            color: Colors.black,
            child: FadeTransition(
              opacity: animation,
              child: child,
            ),
          );
        },
      ),
      (Route<dynamic> route) => false,
    );
  }
}

2.2 マップ移動センサーデコレーションを作成

Bonfireでは、プレイヤーとマップ以外のゲーム内の要素は正式にはDecorationと呼びます。
ここでは遷移を実行するDecorationを作成します。
プレイヤーと接触をトリガーにメソッドを実行できるSensorを継承しています。
引数でposition, sizeと、移動先のマップWidgetを受け取ります。

simple_bonfire/lib/utilities/exit_map_sensor.dart
import 'package:flutter/material.dart';
import 'package:bonfire/bonfire.dart';
import 'package:simple_bonfire/utilities/extentions.dart';

class ExitMapSensor extends GameDecoration with Sensor {
  ExitMapSensor({required Vector2 position, required Vector2 size, required this.nextMap})
      : super(position: position, size: size);
  // 移動先のマップ
  Widget nextMap;
  // 連続実行防止用
  bool hasContact = false;

  @override
  void onContact(component) {
    if (!hasContact && component is Player) {
      hasContact = true;
      context.goTo(nextMap);
    }
  }
}

3. マップ移動を実装する

作成したExitMapSensorデコレーションをオブジェクトとしてゲーム内に生成します。

3.1 マップにセンサーを追加

  • BonfireWidget内のmap: WorldMapByTiledobjectsBuilderを追加
  • objectsBuilderExitMapSensorデコレーションを渡す
    オブジェクト群はmap型で渡します。keyはTiledで作成した (jsonファイルに記載されている) オブジェクトの名前を、valueに作成したExitMapSensorを渡します。
    Tiledで設定した (jsonファイルに記載されている) オブジェクトの座標やサイズなどの値はpropertiesの各プロパティから参照できます。
simple_bonfire/lib/maps/halloween_map_02.dart
import 'package:simple_bonfire/utilities/exit_map_sensor.dart';
// ...省略...

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

  @override
  Widget build(BuildContext context) {
    // 画面
    return BonfireWidget(
      showCollisionArea: true, // センサー確認するためtrueにする
      // マップ用jsonファイル読み込み
      map: WorldMapByTiled(
        'maps/halloween_map_02.json',
        forceTileSize: Vector2(tileSize, tileSize),
        objectsBuilder: {
          // デコレーションを追加
          'bottomExitSensor': (properties) => ExitMapSensor(
                position: properties.position, // jsonから取得
                size: properties.size,         // jsonから取得
                nextMap: const HalloweenMap01(), // 移動先のマップ
              ),
        },
      ),

      // ...中略...
    );
  }
}

3.2 ゲーム内で確認

BonfireWidget内にshowCollisionArea: trueを追加してオブジェクトを可視化したら、ゲーム画面を確認します。
Screen Shot 2022-10-25 at 1.08.14.png
想定通りの位置にセンサーがあり、接触で実際にマップ移動することを確かめてください。
Screen Shot 2022-10-25 at 1.08.35.png

正しく実装できているのが確認できたら、移動先のマップにも同様にセンサーを実装しましょう。
同じ作業ですので詳しい説明は省きます。

  • オブジェクトレイヤーを追加しセンサーを配置して…
    Screen Shot 2022-10-24 at 23.52.54.png

  • objectsBuilderにセンサーを追加して…

simple_bonfire/lib/maps/halloween_map_01.dart
import 'package:simple_bonfire/utilities/exit_map_sensor.dart';
// ...省略...

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

  @override
  Widget build(BuildContext context) {
    // 画面
    return BonfireWidget(
      showCollisionArea: true, // センサー確認するためtrueにする
      // マップ用jsonファイル読み込み
      map: WorldMapByTiled(
        'maps/halloween_map_01.json',
        forceTileSize: Vector2(tileSize, tileSize),
        objectsBuilder: {
          // デコレーションを追加
          'bottomExitSensor': (properties) => ExitMapSensor(
                position: properties.position, // jsonから取得
                size: properties.size,         // jsonから取得
                nextMap: const HalloweenMap02(), // 移動先のマップ
              ),
        },
      ),

      // ...中略...
    );
  }
}
  • ゲーム内で表示と動作を確認して完了です。
    Screen Shot 2022-10-25 at 11.12.16.png

おわりに

今回はセンサーをマップ移動に利用しました。
次回Part 4では、落ちてるアイテムを拾ったりスイッチを踏むなどのイベントを引き続きセンサーを使って実装していきます。
いいねとコメント、質問などお待ちしております!

Part 4はこちら

9
4
0

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
9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?