MENU

【Flutter x Flame】キーボードでキャラクターを左右に移動させる

記事内に商品プロモーションが含まれる場合があります

このページでは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);
  }
}
Share

Comment

コメントする

目次