Flutterでゲームを作ろう!
こんにちは、Flutter開発者の皆さん!今回は、Flutterを使って本格的なPongゲームを作成する方法を詳しく解説します。スマートフォン向けに最適化され、右上にスコアを表示する機能も実装します。さあ、一緩に作っていきましょう!
1. プロジェクトのセットアップ
まずは、新しいFlutterプロジェクトを作成し、必要なパッケージをインストールします。
flutter create pong_game
cd pong_game
flutter pub add flame
2. main.dartファイルの作成
lib/main.dart
ファイルを以下のように編集します。
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'pong_game.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: GameWidget(game: PongGame()),
),
),
);
}
3. pong_game.dartファイルの作成
lib/pong_game.dart
ファイルを作成し、以下のコードを追加します。
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
class PongGame extends FlameGame with TapDetector, HasCollisionDetection {
late Ball ball;
late Paddle playerPaddle;
late Paddle aiPaddle;
late TextComponent playerScoreText;
late TextComponent aiScoreText;
int playerScore = 0;
int aiScore = 0;
@override
Future<void> onLoad() async {
await super.onLoad();
ball = Ball();
playerPaddle = Paddle(isPlayer: true);
aiPaddle = Paddle(isPlayer: false);
playerScoreText = TextComponent(
text: 'Player: $playerScore',
position: Vector2(size.x - 100, 20),
textRenderer: TextPaint(style: const TextStyle(color: Colors.white, fontSize: 16)),
);
aiScoreText = TextComponent(
text: 'AI: $aiScore',
position: Vector2(size.x - 100, 40),
textRenderer: TextPaint(style: const TextStyle(color: Colors.white, fontSize: 16)),
);
add(ball);
add(playerPaddle);
add(aiPaddle);
add(playerScoreText);
add(aiScoreText);
}
@override
void update(double dt) {
super.update(dt);
aiPaddle.moveTowards(ball.y);
checkCollisions();
}
void checkCollisions() {
if (ball.collidesWithPaddle(playerPaddle) || ball.collidesWithPaddle(aiPaddle)) {
ball.bounceOffPaddle();
}
if (ball.x <= 0) {
aiScore++;
aiScoreText.text = 'AI: $aiScore';
resetBall();
} else if (ball.x >= size.x) {
playerScore++;
playerScoreText.text = 'Player: $playerScore';
resetBall();
}
}
void resetBall() {
ball.position = size / 2;
ball.velocity = Vector2(ball.speed, 0);
if (math.Random().nextBool()) {
ball.velocity.x *= -1;
}
}
@override
void onTapDown(TapDownInfo info) {
playerPaddle.moveTo(info.eventPosition.game.y);
}
}
class Ball extends PositionComponent with CollisionCallbacks {
static const double radius = 10;
double speed = 300;
late Vector2 velocity;
Ball() : super(size: Vector2.all(radius * 2)) {
anchor = Anchor.center;
}
@override
void onLoad() {
super.onLoad();
position = gameRef.size / 2;
velocity = Vector2(speed, 0);
if (math.Random().nextBool()) {
velocity.x *= -1;
}
}
@override
void update(double dt) {
super.update(dt);
position += velocity * dt;
if (y <= radius || y >= gameRef.size.y - radius) {
velocity.y *= -1;
}
}
bool collidesWithPaddle(Paddle paddle) {
return position.x >= paddle.x &&
position.x <= paddle.x + paddle.width &&
position.y >= paddle.y &&
position.y <= paddle.y + paddle.height;
}
void bounceOffPaddle() {
velocity.x *= -1.1; // Increase speed slightly
velocity.y = (position.y - (gameRef.size.y / 2)) * 2;
velocity = velocity.normalized() * speed;
}
@override
void render(Canvas canvas) {
super.render(canvas);
canvas.drawCircle(Offset(radius, radius), radius, Paint()..color = Colors.white);
}
}
class Paddle extends PositionComponent {
static const double width = 10;
static const double height = 100;
final bool isPlayer;
Paddle({required this.isPlayer}) : super(size: Vector2(width, height)) {
anchor = Anchor.center;
}
@override
void onLoad() {
super.onLoad();
y = gameRef.size.y / 2;
x = isPlayer ? 30 : gameRef.size.x - 30;
}
void moveTo(double yPosition) {
y = yPosition.clamp(height / 2, gameRef.size.y - height / 2);
}
void moveTowards(double targetY) {
const aiSpeed = 3;
if ((targetY - y).abs() > aiSpeed) {
y += (targetY > y ? aiSpeed : -aiSpeed);
}
}
@override
void render(Canvas canvas) {
super.render(canvas);
canvas.drawRect(
Rect.fromLTWH(0, 0, width, height),
Paint()..color = Colors.white,
);
}
}
4. コードの解説
PongGameクラス
-
onLoad()
: ゲームの初期化を行います。ボール、パドル、スコアテキストを作成し、画面に追加します。 -
update()
: 毎フレーム呼び出され、AIパドルの動きやコリジョンチェックを行います。 -
checkCollisions()
: ボールとパドルの衝突判定、得点の更新を行います。 -
resetBall()
: ボールの位置と速度をリセットします。 -
onTapDown()
: 画面タップ時にプレイヤーのパドルを移動させます。
Ballクラス
- ボールの動きや衝突判定のロジックを実装しています。
-
update()
: ボールの位置を更新し、画面端での反射を処理します。 -
collidesWithPaddle()
: パドルとの衝突判定を行います。 -
bounceOffPaddle()
: パドルに当たった時の挙動を定義します。
Paddleクラス
- プレイヤーとAIのパドルを表現します。
-
moveTo()
: プレイヤーのパドル移動に使用します。 -
moveTowards()
: AIパドルの移動に使用します。
5. ゲームの実行
プロジェクトのルートディレクトリで以下のコマンドを実行します。
flutter run
これで、スマートフォン上でPongゲームが動作し、右上にスコアが表示されるはずです!
まとめ
今回は、Flutterを使って本格的なPongゲームを作成しました。Flameエンジンを活用することで、ゲームの基本的な構造を簡単に実装できました。さらに、スコア表示機能を追加することで、よりゲーム性の高いアプリケーションになりました。
このコードをベースに、さらなる機能追加や改良を行ってみてください。例えば、難易度設定、サウンド効果、パーティクル効果などを追加すると、より魅力的なゲームになるでしょう。
Flutterでのゲーム開発は非常に楽しく、多くの可能性を秘めています。ぜひ、自分なりのアイデアを取り入れて、オリジナルのゲームを作ってみてください!
それでは、楽しいFlutter開発を!