终于到了逻辑层,分析一下游戏中的逻辑关系:
小球与砖块碰撞检测、小球与挡板碰撞检测、判断游戏结束、积分规则等……
物体碰撞检测方法很多,其中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