終於到了邏輯層,分析一下游戲中的邏輯關係:
小球與磚塊碰撞檢測、小球與擋板碰撞檢測、判斷遊戲結束、積分規則等……
物體碰撞檢測方法很多,其中Rectangle結構中的public bool IntersectsWith(Rectangle rect)函數便是一種方法,此方法確定兩個Rect是否相交!但我在測試過程中發現這方法並不是很準確,兩物體碰撞時並不能立即作出反應,有時會出現"靈魂附體"現象(兩物體會短時間粘貼在一起,並破壞運動規則),比如我在做小球與擋板碰撞檢測時便遇到這種情況,所以在小球與擋板的碰撞檢測只用了傳統的判斷物體相對位置這種方法,如圖:
小球x必須在1----3情形範圍內,且y值在y>=y1-y.Height範圍內,即:
x+小球.Width>=x1 && x<=x1+x1.Width 且 y>=y1-y.Height
當小球情形4時game over!
這時y>=y1!
雖然這樣的檢測方法顯得很"傳統",但檢測的準確性還是蠻高的,如果你們有好的檢測方法不如提供一下思路,儘管這遊戲沒必要多複雜的檢測方法!
有了基本的邏輯關係,下面就要實現它們了,我把所有的邏輯算法、初始化畫面等都封裝在一個Controler控制類下,這樣在窗體類編程時只實例控制類即可,使窗體類與其他對象類的耦合性降低。
控制類:Controler.cs
- public class Controler
- {
- private Bitmap bitmap;
- private Brick brick;
- private Board board;
- private Ball ball;
- //遊戲畫面尺寸
- private int width = 0;
- private int height = 0;
- private bool isGameOver = false;
- public int sorce = 0;
- //構造函數,初始化對象
- public Controler(int w,int h)
- {
- this.width = w;
- this.height = h;
- bitmap = new Bitmap(width, height);
- brick = new Brick();
- board = new Board(width / 2 - 45, height-18, 5);
- ball = new Ball(width / 2 - 45, height - 40, 2,3);
- brick.BrickWall();
- }
- //初始化畫面
- public void InitGame(Graphics g)
- {
- //使用雙緩衝,減少畫面閃爍
- brick.Draw(Graphics.FromImage(bitmap)); //畫磚牆
- board.Draw(Graphics.FromImage(bitmap)); //畫擋板
- ball.Draw(Graphics.FromImage(bitmap)); //畫小球
- g.DrawImage(bitmap, 0, 0);
- g.Dispose();
- }
- //碰撞檢測
- public void Hit()
- {
- //磚塊與小球碰撞
- for (int i = 0; i < brick.Rects.Count; i++)
- {
- if (ball.Rect.IntersectsWith(brick.Rects[i]))
- {
- //刪除磚塊
- brick.Rects.Remove(brick.Rects[i]);
- ball.SpeedX = -ball.SpeedX;
- ball.SpeedY = -ball.SpeedY;
- //得分
- sorce += new Random().Next(50, 80) + 100;
- }
- }
- //小球與擋板碰撞
- if (ball.XPos + ball.Rect.Width - 5 >= board.XPos && ball.XPos <= board.XPos + board.Rect.Width - 5)
- {
- if (ball.YPos >= board.YPos - ball.Rect.Height-2)
- {
- switch (Direction)
- {
- case BoardDirection.Left:
- {
- ball.SpeedX = -(new Random().Next(2,5));
- }
- break;
- case BoardDirection.Right:
- {
- ball.SpeedX = (new Random().Next(2, 5));
- }
- break;
- default:
- break;
- }
- ball.SpeedY = (new Random().Next(2, 5));
- }
- }
- }
- //移動擋板
- public void MoveBoard()
- {
- board.Run();
- }
- //小球運動
- public void RunBall()
- {
- ball.Run();
- }
- //遊戲結束
- public bool IsGameOver()
- {
- if (ball.Rect.Y >= height-22)
- {
- isGameOver = true;
- }
- return isGameOver;
- }
- //遊戲通關
- public bool IsSuccess()
- {
- bool isSucess = false;
- //沒有磚塊
- if (brick.Rects.Count == 0)
- isSucess = true;
- return isSucess;
- }
- public Rectangle Board
- {
- get { return board.Rect; }
- set { board.Rect = value; }
- }
- public BoardDirection Direction
- {
- get { return board.Direction; }
- set { board.Direction = value; }
- }
- }
這裏要提一下所用到的雙緩衝技術,其原理就是先在內存上畫好圖,再把圖畫在界面上,達到雙緩衝的目的,以減少畫面的閃爍,效果非常明顯。如果直接把圖畫在界面上,請務必小心你的眼睛,閃爍得太厲害了,傷眼不留情啊!
至此,遊戲的核心代碼都已經完成(用核心這兩字彷彿完成了一個“大工程”,而這只不過是簡單的類定義和簡單算法罷了,而且我覺得我寫的代碼有點亂,讓大家見笑了!),下面的窗體類只需實例控制類並調用其函數即可,遊戲的生命由Timer點燃,遊戲的存亡憑你們左右!
窗體類:SabBoyForm.cs
- public partial class SabBoy : Form
- {
- private Timer timer;
- private Timer timer_time;
- private Controler controler;
- private bool isKeyDown = false;
- //遊戲時間
- int h=0, m=0, s=0;
- public SabBoy()
- {
- InitializeComponent();
- timer = new Timer();
- timer_time = new Timer();
- controler = new Controler(this.Width, this.Height);
- timer.Interval = 10;
- timer.Tick += new EventHandler(timer_Tick);
- timer_time.Interval = 1000;
- timer_time.Tick += new EventHandler(timer_time_Tick);
- }
- //初始化遊戲界面
- private void SabBoy_Paint(object sender, PaintEventArgs e)
- {
- controler.InitGame(e.Graphics);
- }
- //遊戲驅動
- public void timer_Tick(object sender, EventArgs e)
- {
- if (!controler.IsGameOver())
- {
- timer_time.Start();
- if (isKeyDown)
- {
- controler.MoveBoard();
- }
- controler.RunBall();
- controler.Hit();
- controler.InitGame(this.CreateGraphics());
- txtSorce.Text = controler.sorce.ToString();
- if (controler.IsSuccess())
- {
- this.CreateGraphics().DrawString("You Win", new Font("Comic Sans MS", 25), new SolidBrush(Color.Red),
- this.Width / 2 - 100, this.Height / 2 - 50);
- timer.Stop();
- timer_time.Stop();
- }
- }
- else
- {
- this.CreateGraphics().DrawString("Game Over", new Font("Comic Sans MS", 25), new SolidBrush(Color.Snow),
- this.Width / 2 - 100, this.Height / 2 - 50);
- timer_time.Stop();
- }
- }
- private void SabBoy_KeyDown(object sender, KeyEventArgs e)
- {
- isKeyDown = true;
- switch (e.KeyCode)
- {
- case Keys.Left:
- {
- controler.Direction = BoardDirection.Left;
- timer.Start();
- }
- break;
- case Keys.Right:
- {
- controler.Direction = BoardDirection.Right;
- timer.Start();
- }
- break;
- default:
- break;
- }
- }
- private void SabBoy_KeyUp(object sender, KeyEventArgs e)
- {
- isKeyDown = false;
- }
- //因爲我設置窗體邊框樣式爲None(個人認爲缺省的邊框不好看),自定義了關閉按鈕
- //有時間的話再美化一下窗體,目前版本算是第一板吧,以後會更新
- private void lbClose_MouseMove(object sender, MouseEventArgs e)
- {
- lbClose.ForeColor = Color.Red;
- }
- private void lbClose_MouseLeave(object sender, EventArgs e)
- {
- lbClose.ForeColor = Color.White;
- }
- private void lbClose_Click(object sender, EventArgs e)
- {
- Application.Exit();
- }
- //遊戲時間
- public void timer_time_Tick(object sender, EventArgs e)
- {
- s++;
- if (s >= 59)
- {
- m += 1;
- s = 0;
- }
- if (m >= 59)
- {
- h += 1;
- m = 0;
- }
- txtTime.Text =h.ToString("00")+":"+m.ToString("00") + ":" + s.ToString("00");
- }
- }
看下游戲運行界面,醜陋了點,將就玩玩吧!
本想實現打到磚塊並不是磚塊消失(這樣的玩法太普遍了),而是磚塊砸下來,這樣可玩性和難度就有點提高了,但還沒想到比較理想的實現方式,便先擱置了!
遊戲欠缺之處:沒有開始畫面、沒有暫停功能、只有一關……這些都是將來要完善的地方!
遊戲製作環境:VS2010 c#
遊戲擴展性不怎麼好,如果大家能抽點時間看看我的代碼,並指導我該怎麼改善,我感激不盡!有你們的指導必然會少走很多彎路!3Q