WinForms ブロック崩し拡張チュートリアル
目次
~ブロック配置・スコア管理・ゲームオーバー処理を実装しよう~
この資料では、C# の WinForms アプリケーションを用いて、以下の機能を持つブロック崩しゲームの基本形を実装します。
- パドル操作: 左右キーによるパドルの移動
- ボールの動きと反射: 壁やパドルとの衝突処理
- ブロックの配置: 画面上部に複数のブロックを配置し、衝突判定を実施
- スコア管理: ブロック破壊時にスコアを加算
- ゲームオーバー処理: ボールが画面下部に到達した場合、ゲーム終了
必要なステップ
サンプルコード
以下のコードは、各機能を実装したサンプルです。
必要に応じて、ブロックの数やサイズ、スコアの加算値などを変更して、オリジナルのゲームに拡張することが可能です。
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
public partial class Form1 : Form
{
// パドル関連
private bool isLeftPressed = false;
private bool isRightPressed = false;
private float paddleX;
private const float PaddleWidth = 100.0f;
private const float PaddleHeight = 20.0f;
private const float PaddleSpeed = 5.0f;
// ボール関連
private float ballX;
private float ballY;
private float ballVelocityX = 3.0f;
private float ballVelocityY = -3.0f;
private const float BallRadius = 10.0f;
// ブロック関連
private List<RectangleF> blocks;
private const int BlockRows = 5;
private const int BlockColumns = 10;
private const float BlockWidth = 70;
private const float BlockHeight = 20;
private const float BlockPadding = 5;
private const float BlockTopOffset = 30;
// ゲーム状態とスコア
private int score = 0;
private bool isGameOver = false;
private const int TimerInterval = 16; // 約60FPS
private Timer gameTimer;
public Form1()
{
InitializeComponent();
InitializeGame();
}
// ゲーム初期化処理
private void InitializeGame()
{
// ダブルバッファリングで描画のちらつきを防止
this.DoubleBuffered = true;
this.ClientSize = new Size(800, 600);
this.Text = "Breakout Game";
// キーイベントの登録
this.KeyDown += Form1_KeyDown;
this.KeyUp += Form1_KeyUp;
// タイマーの設定(約60FPS)
gameTimer = new Timer { Interval = TimerInterval };
gameTimer.Tick += Timer_Tick;
gameTimer.Start();
// パドルの初期位置(画面下部中央)
paddleX = (this.ClientSize.Width - PaddleWidth) / 2;
// ボールの初期位置(画面中央)
ballX = this.ClientSize.Width / 2;
ballY = this.ClientSize.Height / 2;
// ブロックの初期配置
InitializeBlocks();
}
// ブロック配置の初期化処理
private void InitializeBlocks()
{
blocks = new List<RectangleF>();
// ブロック全体の横幅を計算し、中央配置の開始X座標を決定
float totalWidth = BlockColumns * BlockWidth + (BlockColumns - 1) * BlockPadding;
float startX = (this.ClientSize.Width - totalWidth) / 2;
for (int row = 0; row < BlockRows; row++)
{
for (int col = 0; col < BlockColumns; col++)
{
float x = startX + col * (BlockWidth + BlockPadding);
float y = BlockTopOffset + row * (BlockHeight + BlockPadding);
blocks.Add(new RectangleF(x, y, BlockWidth, BlockHeight));
}
}
}
// キー入力の状態管理
private void SetKeyState(Keys key, bool isPressed)
{
switch (key)
{
case Keys.Left:
isLeftPressed = isPressed;
break;
case Keys.Right:
isRightPressed = isPressed;
break;
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
SetKeyState(e.KeyCode, true);
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
SetKeyState(e.KeyCode, false);
}
// タイマー処理:パドルとボールの状態更新および衝突判定
private void Timer_Tick(object sender, EventArgs e)
{
if (isGameOver)
{
gameTimer.Stop();
return;
}
UpdatePaddlePosition();
UpdateBallPosition();
CheckBallCollision();
Invalidate(); // 再描画要求
}
// パドルの位置更新処理
private void UpdatePaddlePosition()
{
if (isLeftPressed)
paddleX -= PaddleSpeed;
if (isRightPressed)
paddleX += PaddleSpeed;
// パドルが画面外に出ないよう制限
paddleX = Math.Max(0, Math.Min(paddleX, this.ClientSize.Width - PaddleWidth));
}
// ボールの位置更新処理
private void UpdateBallPosition()
{
ballX += ballVelocityX;
ballY += ballVelocityY;
}
// 衝突判定処理(壁、パドル、ブロック)
private void CheckBallCollision()
{
// 壁との衝突判定
if (ballX - BallRadius < 0)
{
ballX = BallRadius;
ballVelocityX = -ballVelocityX;
}
else if (ballX + BallRadius > this.ClientSize.Width)
{
ballX = this.ClientSize.Width - BallRadius;
ballVelocityX = -ballVelocityX;
}
if (ballY - BallRadius < 0)
{
ballY = BallRadius;
ballVelocityY = -ballVelocityY;
}
else if (ballY - BallRadius > this.ClientSize.Height)
{
// ボールが下端に到達したらゲームオーバー
isGameOver = true;
return;
}
// パドルとの衝突判定
RectangleF paddleRect = new RectangleF(paddleX, this.ClientSize.Height - PaddleHeight - 30, PaddleWidth, PaddleHeight);
RectangleF ballRect = new RectangleF(ballX - BallRadius, ballY - BallRadius, BallRadius * 2, BallRadius * 2);
if (paddleRect.IntersectsWith(ballRect) && ballVelocityY > 0)
{
ballY = paddleRect.Top - BallRadius;
ballVelocityY = -Math.Abs(ballVelocityY);
// 衝突位置に応じた水平速度の調整
float hitPos = (ballX - paddleRect.Left) / paddleRect.Width;
ballVelocityX = (hitPos - 0.5f) * 10;
}
// ブロックとの衝突判定
for (int i = 0; i < blocks.Count; i++)
{
if (blocks[i].IntersectsWith(ballRect))
{
// ブロック破壊:リストから該当ブロックを削除
blocks.RemoveAt(i);
i--; // 削除後のインデックス調整
// ボールを反射(ここでは単純にY方向の速度を反転)
ballVelocityY = -ballVelocityY;
// スコア加算(例:1ブロックにつき10点)
score += 10;
// 1フレーム内で複数ブロックとの衝突が起こらないようにループを抜ける
break;
}
}
}
// 描画処理
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.Clear(Color.White);
// パドル描画
g.FillRectangle(Brushes.Blue, paddleX, this.ClientSize.Height - PaddleHeight - 30, PaddleWidth, PaddleHeight);
// ボール描画
g.FillEllipse(Brushes.Red, ballX - BallRadius, ballY - BallRadius, BallRadius * 2, BallRadius * 2);
// ブロック描画
foreach (RectangleF block in blocks)
{
g.FillRectangle(Brushes.Green, block);
g.DrawRectangle(Pens.Black, block.X, block.Y, block.Width, block.Height);
}
// スコア描画
using (Font font = new Font("Arial", 14))
{
g.DrawString("Score: " + score, font, Brushes.Black, 10, 10);
}
// ゲームオーバー時の表示
if (isGameOver)
{
string gameOverText = "Game Over";
using (Font font = new Font("Arial", 24))
{
SizeF textSize = g.MeasureString(gameOverText, font);
g.DrawString(gameOverText, font, Brushes.Red, (this.ClientSize.Width - textSize.Width) / 2, (this.ClientSize.Height - textSize.Height) / 2);
}
}
}
}
各パートの詳細解説
1. ゲーム初期化
- フォーム設定とダブルバッファリング: 描画のちらつきを防ぎ、ウィンドウサイズやタイトルを設定します。
- キーイベントとタイマー: パドル操作のために KeyDown/KeyUp を登録し、約60FPSで状態更新を行うタイマーを開始します。
- パドル・ボール・ブロックの初期配置: パドルは画面下部中央、ボールは画面中央に配置。ブロックは上部に行列状に並べています。
2. パドル操作とボールの動き
- パドル移動: 左右キーの状態に応じてパドルのX座標を更新し、画面外に出ないように制限します。
- ボール移動: 毎フレーム、ボールの位置を速度に従って更新します。
3. 衝突判定とゲーム状態管理
- 壁との衝突: ボールが画面端に到達した場合、反射するように速度を反転。
- パドル衝突: パドルとの接触時、ボールの位置を補正し、反射と衝突位置に応じた左右への加速を実施。
- ブロック衝突: ボールがブロックに接触した場合、該当ブロックを削除し、スコアを加算。
- ゲームオーバー: ボールが画面下部に到達した場合、ゲーム終了状態にしタイマーを停止します。
4. 描画処理
- 各オブジェクトの描画: パドル、ボール、ブロックをそれぞれ描画し、スコアを画面上部に表示。
- ゲームオーバー表示: ゲーム終了時は、中央に「Game Over」のメッセージを表示します。
まとめ
この拡張チュートリアルでは、WinForms を用いてパドルとボールの動作に加え、ブロック配置、スコア管理、ゲームオーバー処理を実装しました。
このサンプルを基に、さらにブロックのパターン変更やライフ管理、レベルアップ、効果音やアニメーションの追加など、さまざまな拡張が可能です。初心者の方もぜひ自分なりの工夫を加えて、オリジナルのブロック崩しゲームを作成してみてください。
ディスカッション
コメント一覧
まだ、コメントがありません