今回バーを動かしてボールを跳ね返すところまでをやります。
ボールをつくる
ブロック崩しではボールをつくり動かす必要があります。まずはボールを表示させましょう。ボールを表示させるためにはサイズと位置を決める必要があります。ボールの形状は円、直径は10ピクセルとします。また位置はフィールド変数で管理します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public partial class Form1 : Form { const int BALL_WIDTH = 10; const int BALL_HEIGHT = 10; // ボールの初期位置 int ballPosX = 20; int ballPosY = 200; void DrawBall(Graphics g) { SolidBrush brush = new SolidBrush(Color.LightBlue); Rectangle rect = new Rectangle(new Point(ballPosX, ballPosY), new Size(BALL_WIDTH, BALL_HEIGHT)); g.FillEllipse(brush, rect); } protected override void OnPaint(PaintEventArgs e) { DrawBall(e.Graphics); DrawBar(e.Graphics); DrawBlocks(e.Graphics); DrawField(e.Graphics); base.OnPaint(e); } } |
ボールを動かすためのメソッドも必要です。タイマーをつかってTickイベントを発生させて、そのなかでボールを移動させます。
ボールはある方向に向かって移動しています。そこで移動方向を管理するフィールド変数を用意します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public partial class Form1 : Form { int ballDirectX = 2; int ballDirectY = 5; public Form1() { InitializeComponent(); BackColor = Color.Black; this.DoubleBuffered = true; timer.Interval = 30; timer.Tick += Timer_Tick; timer.Start(); GameStart(); } private void Timer_Tick(object sender, EventArgs e) { MoveBall(ballDirectX, ballDirectY); Invalidate(); } void MoveBall(int x, int y) { ballPosX += x; ballPosY += y; } void GameStart() { } } |
壁に当たったボールがはねかえる
これでボールは斜め下方向に向かって移動します。しかしこれだけではボールは跳ね返ることなく、そのまま画面の下に消えてしまいます。跳ね返すメソッドが必要です。
ではボールが跳ね返るときとはどのような場合でしょうか。
壁に当たったとき
バーにあたったとき
ブロックに当たったとき
この3つが考えられます。
横の壁に当たったときはX方向の向きを変える
上の壁にあたったときはY方向の向きを変える
ただ符号を反転するだけでよいのでしょうか?
左の壁にあたったときは進行方向のX成分が負のときは正にする
右の壁にあたったときは進行方向のX成分が正のときは負にする
このようにしないと最初からフォールドの外にあった場合、フォールドの外だから向きを変える、移動、移動先もフォールドの外、だから向きを変えて・・・となるとフィールド内に復帰することができなくなってしまいます。
おそらくこれが正しいのではないかと思われます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
public partial class Form1 : Form { private void Timer_Tick(object sender, EventArgs e) { // ボールの移動方向を適切な方向に修正する SetBallDirect(); // その方向にボールが移動したように描画させる MoveBall(ballDirectX, ballDirectY); Invalidate(); } void SetBallDirect() { SetBallDirectIfHitWall(); } bool SetBallDirectIfHitWall() { Rectangle ballRect = GetBallRectangle(); Rectangle fieldRect = GetFieldRectangle(); // ボールが右から外へ出てしまった if(fieldRect.Right < ballRect.Right) { if(ballDirectX > 0) ballDirectX *= -1; return true; } // ボールが左から外へ出てしまった if(ballRect.Left < fieldRect.Left) { if(ballDirectX < 0) ballDirectX *= -1; return true; } // ボールが上へ出てしまった if(ballRect.Top < fieldRect.Top) { if(ballDirectY < 0) ballDirectY *= -1; return true; } // ボールが下へ出てしまった if(fieldRect.Bottom < ballRect.Bottom) { if(ballDirectY > 0) ballDirectY *= -1; return true; } return false; } } |
上記コード内で使用されているGetBallRectangleメソッドとGetFieldRectangleメソッドはボールとフィールドの矩形を返すメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 |
public partial class Form1 : Form { Rectangle GetBallRectangle() { return new Rectangle(new Point(ballPosX, ballPosY), new Size(BALL_WIDTH, BALL_HEIGHT)); } Rectangle GetFieldRectangle() { return new Rectangle(new Point(FIELD_POSITION_X , FIELD_POSITION_Y), new Size(FIELD_WIDTH, FIELD_HEIGHT)); } } |
バーに当たったボールがはねかえる
つぎにバーに当たったときボールを跳ね返す動作を実装します。これもSetBallDirectメソッドに少し付け加えればよさそうです。
バーの矩形とボールの矩形を取得して、それぞれの位置関係から当たっているかどうかを判定しています。当たった場合よりも当たっていない場合を考えたほうが早いかもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public partial class Form1 : Form { void SetBallDirect() { SetBallDirectIfHitWall(); SetBallDirectIfHitBar(); } bool SetBallDirectIfHitBar() { if(IsBarHit()) { ballDirectY *= -1; return true; } return false; } bool IsBarHit() { Rectangle barRect = GetBarRectangle(); Rectangle ballRect = GetBallRectangle(); // 当たっていない場合を考えたほうが早いかも if(barRect.Top > ballRect.Bottom) return false; if(barRect.Left > ballRect.Right) return false; if(barRect.Right < ballRect.Left) return false; if(barRect.Bottom < ballRect.Top) return false; return true; } Rectangle GetBarRectangle() { return new Rectangle(new Point(barPosX, barPosY), new Size(BAR_WIDTH, BAR_HEIGHT)); } } |
バーを移動させる
それからバーを移動させる処理も必要です。
キーが押されたときにバーを移動させればよいのですが、それではキーが押されたときにちょっとだけ移動して、しばらく押し続けると連続して移動するという不自然な動きになります。
そこで右に移動するのか左に移動するのか動かないのかをフィールド変数に格納して、キーを押したら右または左に移動、キーから手を離したら動かなくなるという仕様にします。
そしてタイマーイベントを捕捉してbarDirectXが0でなければ移動させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public partial class Form1 : Form { int barDirectX = 0; protected override void OnKeyDown(KeyEventArgs e) { if(e.KeyCode == Keys.Left) barDirectX = -10; if(e.KeyCode == Keys.Right) barDirectX = 10; base.OnKeyDown(e); } protected override void OnKeyUp(KeyEventArgs e) { if(e.KeyCode == Keys.Left) barDirectX = 0; if(e.KeyCode == Keys.Right) barDirectX = 0; base.OnKeyUp(e); } } |
これは実際に移動させるためのメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public partial class Form1 : Form { // 描画される場所を移動する void MoveBar(int x) { if(barPosX + x < FIELD_POSITION_X) return; if(FIELD_POSITION_X + FIELD_WIDTH < barPosX + x + BAR_WIDTH && x > 0) return; barPosX += x; } private void Timer_Tick(object sender, EventArgs e) { MoveBall(ballDirectX, ballDirectY); MoveBar(barDirectX); Invalidate(); } // 実際にその場所に描画する void DrawBar(Graphics g) { Point barPos = new Point(barPosX, barPosY); Size size = new Size(BAR_WIDTH, BAR_HEIGHT); SolidBrush brush = new SolidBrush(Color.Blue); Rectangle rect = new Rectangle(barPos, size); g.FillRectangle(brush, rect); } protected override void OnPaint(PaintEventArgs e) { DrawBall(e.Graphics); DrawBar(e.Graphics); DrawBlocks(e.Graphics); DrawField(e.Graphics); base.OnPaint(e); } } |
失礼いたします。キーを押してもバーが移動しません。
宣言したMoveBarメソッドはどこで使用するのでしょうか
返事が遅れて申し訳ありません。
Timer_Tickメソッド内です。記事も修正しました。