【程式設計-C#】打磚塊遊戲 – 動作

【程式設計-C#】打磚塊遊戲 – 動作

【程式設計-C#】打磚塊遊戲 – 動作

(到上一篇【程式設計】打磚塊遊戲 – 處理物件及畫面美工)

打磚塊遊戲設計資源 (請按下面圖片):

接下來,在這個教學裏,我們要實現底下遊戲動作: 打磚塊專案檔20180522

  1. 球的移動及碰到邊界的處理
  2. 處理球拍的移動 (鍵盤 + 滑鼠)
  3. 球拍擊中球的判斷及處理
  4. 球擊中磚塊的判斷及處理
  5. 遊戲結束條件:1.球掉出下邊界;2.按ESC鍵
  6. 進入到下一關:擊中全部磚塊,所有磚塊消失。(你會怎麼做?)

首先,我們所有的動作皆透過計時器物件來實現,將計時器物件拖拉至程式主畫面,設定interval,依照人類視覺暫留特性,每秒只要高於16個影像,看起來的東西就會覺得是連貫的,電影的FPS為24,遊戲則要更高,至少30以上,所以,我們的計時器的interval  = 1 / 40  * 1000 =  25

一、球的移動及碰到邊界的處理 BrickBreakout – 2 – 1

程式一開始先宣告:

int X_Inc = 5, Y_Inc = 5;
// 水平移動增量 X_Inc (正:往右邊,負:往左邊)
// 垂直移動增量 Y_Inc(正:往下邊,負:往上面)

計時器的Tick事件:

private void timer1_Tick(object sender, EventArgs e)
{
    ball.Left += Y_Inc; //水平
    ball.Top += X_Inc; //垂直

    //邊界碰撞
    if (ball.Left < 0 || ball.Left > this.Bounds.Width) Y_Inc = -Y_Inc; //左右邊界
    if (ball.Top > this.Bounds.Height || ball.Top < 0) X_Inc = -X_Inc; //上下邊界
}

二、處理球拍的移動 (鍵盤 + 滑鼠)

在timer1的Tick事件中加入:

private void timer1_Tick(object sender, EventArgs e)
{
    //球的移動處理
    ball.Left += Y_Inc;
    ball.Top += X_Inc;

    //球的邊界碰撞
    if (ball.Left < 0 || ball.Left > this.Bounds.Width) Y_Inc = -Y_Inc; //左右邊界
    if (ball.Top > this.Bounds.Height || ball.Top < 0) X_Inc = -X_Inc; //上下邊界

    //球拍的移動
    if (Cursor.Position.X >= (this.Bounds.Width - racket.Width))//如果游標在右邊界處 
        racket.Left = this.Bounds.Width - racket.Width;//減去 球拍的水平位置,否則的話,球拍會在視窗 
    else racket.Left = Cursor.Position.X; //球拍的水平位置 = 游標的水平位置

}

除了使用滑鼠移動游標來移動球拍,我們另外抓取按鍵事件來移動球拍,作法是在Form表單中的KeyDown事件抓取左右按鍵:

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Left) Cursor.Position = new Point(Cursor.Position.X  - 20, Cursor.Position.Y);
    if (e.KeyCode == Keys.Right) Cursor.Position = new Point(Cursor.Position.X + 20, Cursor.Position.Y);
}

三、球拍擊中球的判斷及處理 BrickBreakout – 2-2

判斷球拍是否擊中球,並且在確認擊中之後,球的垂直移動方向由往下變往上。

private void timer1_Tick(object sender, EventArgs e)
{
    //球的邊界碰撞
    if (ball.Left <= 0 || ball.Left >= (this.Bounds.Width - ball.Width)) X_Inc = -X_Inc; //左右邊界
    if (ball.Top >= (this.Bounds.Height - ball.Height) || ball.Top <= 0) Y_Inc = -Y_Inc; //上下邊界

    //球拍是否擊中球:
    if (ball.Left >= racket.Left && ball.Left <= (racket.Left + racket.Width) && ball.Top >= (racket.Top - ball.Height)) Y_Inc = -Y_Inc;

    //球拍的移動
    if (Cursor.Position.X >= (this.Bounds.Width - racket.Width))//如果游標在右邊界處 
        racket.Left = this.Bounds.Width - racket.Width;//減去 球拍的水平位置,否則的話,球拍會在視窗 
    else racket.Left = Cursor.Position.X; //球拍的水平位置 = 游標的水平位置

    //球移動
    ball.Left += X_Inc;
    ball.Top += Y_Inc;
}

四、球擊中磚塊的判斷及處理 BrickBreakout – 2-3

球擊中磚塊的判斷跟球拍是否擊中球作法類似,擊中確認後,我們只需要將該磚塊的Visible設為false,使磚塊看不見就好了!(簡單吧?!)

程式在timer1的Tick事件中加了這個部份的處理:

//一一測試每顆磚塊是否被球擊中
for (int i = 0; i < bricks.GetLength(0); i++)
{
    for (int j = 0; j < bricks.GetLength(1); j++)
    {
        if (bricks[i, j].Visible == true) // 若磚塊是可見的話…,表示磚塊尚未被擊中
        {
            if (ball.Left >= bricks[i, j].Left && ball.Left <= (bricks[i, j].Left + bricks[i, j].Width) && ball.Top <= (bricks[i, j].Top + bricks[i, j].Height))
            {
                Y_Inc = -Y_Inc;
                bricks[i, j].Visible = false; //判定擊中後,球的垂直移動方向改變,並將磚塊的Visible屬性設false,使其看不見
                goto HitBrickExit; //一旦擊中,就不用測試其他的磚塊…,跳離這個測試以節省時間
            }
        }
    }
}
HitBrickExit:

另外,再加上球掉出下邊界外的處理:

//球掉到下邊界外,也就是球拍沒擊中球…,遊戲結束
if (ball.Top >= (this.Bounds.Height - ball.Height))
{
    timer1.Enabled = false; //中止計時器1
    lblGameOver.Visible = true; //顯示出遊戲結束訊息
}

再加上幾個按鍵的處理:

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Left) Cursor.Position = new Point(Cursor.Position.X - 20, Cursor.Position.Y);
    if (e.KeyCode == Keys.Right) Cursor.Position = new Point(Cursor.Position.X + 20, Cursor.Position.Y);
    if (e.KeyCode == Keys.Escape) this.Close(); //按Esc離開遊戲
    if (e.KeyCode == Keys.F1) //重新開始遊戲
    {
        ball.Left = (this.Bounds.Width - ball.Width) / 2; //球位置,置於視窗中心
        ball.Top = (this.Bounds.Height - ball.Height) / 2;
        timer1.Enabled = true; //重新啟動計時器1
        lblGameOver.Visible = false; //遊戲訊息隱藏
        place_bricsk(); //重新排列所有的磚塊
    }
}

遊戲錄影:

音效的處理請參考下一篇:【程式設計】打磚塊遊戲 – 播放背景音樂與音效 (同時)

問題、意見、遊戲改進提議等歡迎在文章下面的迴響交流。謝謝。

【程式設計-C#】打磚塊遊戲 – 同時播放背景音樂與音效

【程式設計-C#】打磚塊遊戲 – 同時播放背景音樂與音效

【程式設計-C#】打磚塊遊戲 – 同時播放背景音樂與音效

 

設計遊戲所需資源關鍵字:電動遊戲 – Video game, 打磚塊 – Arkanoid, 音效 – sound, 背景 – background, 圖片- picture, 角色 – character, 魔王 – boss

背景音樂下載

打磚塊遊戲設計資源 :

在一個遊戲中,會有背景音樂,然後,遇到一些事件要產生特定的音效,用傳統的方式播放音樂/音效,會只聽到一個聲音…

為了要”同時”播放背景音樂與音效,我們要用二種不同的播放器,否則,當播放一個音效時,背景音樂就會停止:

  1. .Windows內建的WindowsMediaPlayer
  2. Net本身的Windows.Media.SoundPlayer類別

要使用Windows內建的WindowsMediaPlayer,需先加入C:\Windows\System32\wmp.dll到參考中,操作方式如下:

請先把聲音檔加入專案中,並設置”一律複製”:

我們將播放撞擊聲寫成一個方法,方便我們反覆的叫用這個方法來播放適當的聲音:

private void playBom() //播放撞擊音樂方法
{
    var player1 = new WMPLib.WindowsMediaPlayer();
    player1.URL = "collision.wav"; //撞擊聲,我們的聲音檔像圖片一樣加入專案中。
}

然後,在需要播放撞擊聲時,進行上述方法的呼叫:擊中球拍與磚塊

//球拍是否擊中球:
if (ball.Left >= racket.Left && ball.Left <= (racket.Left + racket.Width) && ball.Top >= (racket.Top - ball.Height))
{
    Y_Inc = -Y_Inc;
    playBom();
}

//一一測試每顆磚塊是否被球擊中
for (int i = bricks.GetLength(0) - 1; i >=0 ; i--)
{
    for (int j = bricks.GetLength(1) - 1; j >= 0 ; j--)
    {
        if (bricks[i, j].Visible == true) // 若磚塊是可見的話…,表示磚塊尚未被擊中
        {
            if (ball.Left >= bricks[i, j].Left && ball.Left <= (bricks[i, j].Left + bricks[i, j].Width) && ball.Top <= (bricks[i, j].Top + bricks[i, j].Height))
            {
                Y_Inc = -Y_Inc;
                playBom();
                bricks[i, j].Visible = false; //判定擊中後,球的垂直移動方向改變,並將磚塊的Visible屬性設false,使其看不見
                goto HitBrickExit; //一旦擊中,就不用測試其他的磚塊…,跳離這個測試以節省時間
            }
        }
    }
}
HitBrickExit:

背景音樂以C#的Windows.Media.SoundPlayer類別來播放(放在Form1方法的最後段)

//播放背景音樂  
System.Media.SoundPlayer player = new System.Media.SoundPlayer("POL-cooking-mania-short.wav");
player.PlayLooping();

專案檔下載:BrickBreakout – 3

音樂與音效資源: