このページではFlmaeを使って、キーボード操作でキャラクターを左右に移動させる方法を紹介します。
ゲーム画面を作成
まずは何もない空のゲーム画面を作成します。
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
void main() {
  final game = SampleGame();
  runApp(
      GameWidget(game: game)
  );
}
class SampleGame extends FlameGame {
}

キャラクターの配置
プロジェクトにassets/imagesフォルダを作成し、そこに画像を設置します。
今回はいらすとやからサッカーボールの画像をお借りしました。
ファイル名は何でも良いですが、今回は「ball」としておきました。

画像の読み込みができるようにpubspec.yamlにも以下のように追記しておきます。
  assets:
    - assets/images/画像ができたら、Flameで背景画像を設定する方法で紹介したやり方でサッカーボールを画面に追加します。
import 'package:flame/components.dart'; // 追加
import 'package:flame/flame.dart'; // 追加
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
void main() {
  final game = SampleGame();
  runApp(
      GameWidget(game: game)
  );
}
class SampleGame extends FlameGame {
  // 追加 
  @override
  Future<void> onLoad() async {
    add(Ball());
  }
}
// 追加
class Ball extends SpriteComponent with HasGameRef<SampleGame>{
  @override
  Future<void> onLoad() async {
    // 画像の読み込み
    final ball = await Flame.images.load("ball.png");
    // ボールのサイズ
    size = Vector2(100, 100);
    // 画面のサイズを取得
    final screenHeight = gameRef.size.y;
    // ボールの配置位置を左下に配置(Y座標:画面の高さ - ボールのサイズの高さ - 20)
    position = Vector2(50, screenHeight - size.y - 20);
    sprite = Sprite(ball);
  }
}
実行すると画面の左下にサッカーボールが配置されます。

サッカーボールを移動させる
キーボード操作を可能にするには、次のようにmixinを追加します。
SampleGameクラスにHasKeyboardHandlerComponentsを追加
import 'package:flame/events.dart';
class SampleGame extends FlameGame with HasKeyboardHandlerComponents {
・・・
}BallクラスにKeyboardHandlerを追加
class Ball extends SpriteComponent with HasGameRef<SampleGame>, KeyboardHandler{
・・・
}これによりボールに対してキーボードの操作を検知できるようになります。
Ballクラスにボールを移動させる処理を書いていきましょう。
import 'package:flutter/services.dart'; // 追加
class Ball extends SpriteComponent with HasGameRef<SampleGame>, KeyboardHandler{
  // 追加
  int horizontalDirection = 0;  // 水平方向の向き:左か右か
  final Vector2 velocity = Vector2.zero();
  final double moveSpeed = 200;
  @override
  Future<void> onLoad() async {
    // 画像の読み込み
    final ball = await Flame.images.load("ball.png");
    // ボールのサイズ
    size = Vector2(100, 100);
    // 画面のサイズを取得
    final screenHeight = gameRef.size.y;
    // ボールの配置位置を左下に配置(Y座標:画面の高さ - ボールのサイズの高さ - 20)
    position = Vector2(50, screenHeight - size.y - 20);
    sprite = Sprite(ball);
  }
  // キーボードの操作イベント
  @override
  bool onKeyEvent(RawKeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
    // 水平方向の向きをリセット
    horizontalDirection = 0;
    // Aか左を押したら、左方向(-1)をセット
    horizontalDirection += (keysPressed.contains(LogicalKeyboardKey.keyA) ||
        keysPressed.contains(LogicalKeyboardKey.arrowLeft))
        ? -1
        : 0;
    // Dか→を押したら、右方向(1)をセット
    horizontalDirection += (keysPressed.contains(LogicalKeyboardKey.keyD) ||
        keysPressed.contains(LogicalKeyboardKey.arrowRight))
        ? 1
        : 0;
    return true;
  }
  @override
  void update(double dt) {
    // ボールの速度を更新
    velocity.x = horizontalDirection * moveSpeed;
    // ボールの位置を更新
    position += velocity * dt;
    super.update(dt);
  }
}onKeyEventでキーボードを操作したときの処理を記述します。
水平方向の左右の向きを表す変数horizontalDirectionを用紙し、左(またはA)を押した場合-1を、右(またはD)を押した場合1をセットし、進行方向を決定します。
updateでは進行方向にボールを移動させています。
この状態、実行すると以下のようになります。
移動中にサッカーボールを回転させる
キーボードでボールの移動ができるようになりましたが、少し不自然なのでボールの回転も追加しておきましょう。
import 'dart:math'; // 追加
class Ball extends SpriteComponent with HasGameRef<SampleGame>, KeyboardHandler{
  int horizontalDirection = 0;  // 水平方向の向き:左か右か
  final Vector2 velocity = Vector2.zero();
  final double moveSpeed = 200;
  // 追加
  double rotationSpeed = 5.0;  // 回転スピード
  double rotationAngle = 0.0;  // 回転角度
  @override
  Future<void> onLoad() async {
    // 画像の読み込み
    final ball = await Flame.images.load("ball.png");
    // ボールのサイズ
    size = Vector2(100, 100);
    // 画面のサイズを取得
    final screenHeight = gameRef.size.y;
    // ボールの配置位置を左下に配置(Y座標:画面の高さ - ボールのサイズの高さ - 20)
    position = Vector2(50, screenHeight - size.y - 20);
    // 追加:ボールの中心をアンカーとする(anchor:コンポーネントの位置や操作の基準点)
    anchor = Anchor.center;
    sprite = Sprite(ball);
  }
  // キーボードの操作イベント
  @override
  bool onKeyEvent(RawKeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
    // 水平方向の向きをリセット
    horizontalDirection = 0;
    // Aか左を押したら、左方向(-1)をセット
    horizontalDirection += (keysPressed.contains(LogicalKeyboardKey.keyA) ||
        keysPressed.contains(LogicalKeyboardKey.arrowLeft))
        ? -1
        : 0;
    // Dか→を押したら、右方向(1)をセット
    horizontalDirection += (keysPressed.contains(LogicalKeyboardKey.keyD) ||
        keysPressed.contains(LogicalKeyboardKey.arrowRight))
        ? 1
        : 0;
    return true;
  }
  @override
  void update(double dt) {
    // ボールの速度を更新
    velocity.x = horizontalDirection * moveSpeed;
    // ボールの位置を更新
    position += velocity * dt;
    // 追加:ボールが左右に移動するときに回転させる
    if (horizontalDirection != 0) {
      rotationAngle += rotationSpeed * horizontalDirection * dt;
      // 回転角度が2π(360度)を超えた場合、 0度に戻す
      if (rotationAngle > 2 * pi) {
        rotationAngle -= 2 * pi;
      } else if (rotationAngle < -2 * pi) {
        // 回転角度が-2π(-360度)を超えた場合,0度に戻す
        rotationAngle += 2 * pi;
      }
      // ボールの角度をセット
      angle = rotationAngle;
    }
    
    super.update(dt);
  }
}
updateで右に移動中は右回転に、左に移動中は左回転にしています。
anchor = Anchor.centerでボールの中心をアンカーにしておかないと、変な動きになるので注意してください。
実行すると以下のようになります。
全体のコード
import 'dart:math'; 
import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:flame/flame.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
  final game = SampleGame();
  runApp(
      GameWidget(game: game)
  );
}
class SampleGame extends FlameGame with HasKeyboardHandlerComponents {
  @override
  Future<void> onLoad() async {
    add(Ball());
  }
}
class Ball extends SpriteComponent with HasGameRef<SampleGame>, KeyboardHandler{
  int horizontalDirection = 0;  // 水平方向の向き:左か右か
  final Vector2 velocity = Vector2.zero();
  final double moveSpeed = 200;
  double rotationSpeed = 5.0;  // 回転スピード
  double rotationAngle = 0.0;  // 回転角度
  @override
  Future<void> onLoad() async {
    // 画像の読み込み
    final ball = await Flame.images.load("ball.png");
    // ボールのサイズ
    size = Vector2(100, 100);
    // 画面のサイズを取得
    final screenHeight = gameRef.size.y;
    // ボールの配置位置を左下に配置(Y座標:画面の高さ - ボールのサイズの高さ - 20)
    position = Vector2(50, screenHeight - size.y - 20);
    // ボールの中心をアンカーとする(anchor:コンポーネントの位置や操作の基準点)
    anchor = Anchor.center;
    sprite = Sprite(ball);
  }
  // キーボードの操作イベント
  @override
  bool onKeyEvent(RawKeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
    // 水平方向の向きをリセット
    horizontalDirection = 0;
    // Aか左を押したら、左方向(-1)をセット
    horizontalDirection += (keysPressed.contains(LogicalKeyboardKey.keyA) ||
        keysPressed.contains(LogicalKeyboardKey.arrowLeft))
        ? -1
        : 0;
    // Dか→を押したら、右方向(1)をセット
    horizontalDirection += (keysPressed.contains(LogicalKeyboardKey.keyD) ||
        keysPressed.contains(LogicalKeyboardKey.arrowRight))
        ? 1
        : 0;
    return true;
  }
  @override
  void update(double dt) {
    // ボールの速度を更新
    velocity.x = horizontalDirection * moveSpeed;
    // ボールの位置を更新
    position += velocity * dt;
    // ボールが左右に移動するときに回転させる
    if (horizontalDirection != 0) {
      rotationAngle += rotationSpeed * horizontalDirection * dt;
      // 回転角度が2π(360度)を超えた場合、 0度に戻す
      if (rotationAngle > 2 * pi) {
        rotationAngle -= 2 * pi;
      } else if (rotationAngle < -2 * pi) {
        // 回転角度が-2π(-360度)を超えた場合,0度に戻す
        rotationAngle += 2 * pi;
      }
      // ボールの角度をセット
      angle = rotationAngle;
    }
    super.update(dt);
  }
}


Comment