Win Form图形编程实践——打砖块
引言
本项目学习自际为软件事务所的C#实现一个打砖块游戏 Step By Step,特此鸣谢。
最初,博主只是在简单学习了Win Form之后写一个有图形化界面的小游戏来锻炼一下自己的图形化编程技巧,思前想后选择了打砖块这个游戏。相较于贪吃蛇一类的小游戏,打砖块还是很有难度的,因为要控制球的运动轨迹以及球和砖块、挡板的碰撞问题,设计起来较为繁琐。幸好有上文提到的教程,在OOP方面提供了一个很好的思路,再次感谢。
另:为避免版权问题,游戏中所有涉及到的图片及图标等均为个人绘制。
思路
在寻找教程之前我自己写了一个导图来整理这个游戏的大体思路。如下:
整合网络教程以及我个人的思路,实现这个打砖块游戏的具体过程如下:
- 设计开始界面,具体如导图所示
- 标题
- 操作提示
- [开始游戏]按钮
- 开始界面背景图
- 设计游戏界面,主要参照教程进行:
- 游戏界面背景图
- 绘制挡板以及实现挡板的移动
- 绘制小球以及实现小球的移动
- 绘制砖块以及实现墙体集合
- 重新使用双缓冲技术实现绘图操作
- 实现小球与砖块和挡板的碰撞检测
- 设计展示界面,具体如导图所示:
- 计分板
- 游戏进行时间
- 排行榜
- 设计游戏结束界面,具体如导图所示:
- 标题
- 判断分数
- 如果进入前三名:记录玩家姓名并更新排行榜后展示[结束游戏]按钮
- 如果没进入前三名:直接展示[结束游戏]按钮
- [结束游戏]按钮
实现过程
开始界面
-
设置窗口格式
将AutoSize设置为False,Size为450x620.
设置窗口图标。
将Locked设置为True。
设置窗口内默认字体,这样在添加Label或者TextBox时就不用重新设置字体了,但有可能仍需要重新调整字体大小。
-
设置背景图片
将图片文件夹放置在所建项目文件夹中的bin\Debug中便于检索文件。
将这个操作放置在[Form]BricksBreaker的Load事件中,具体代码如下:
private void BricksBreaker_Load(object sender, EventArgs e) { Welcome(); string backgroundImagePath = Application.StartupPath; backgroundImagePath += @"\imgs\Welcome\BackGround.png"; this.BackgroundImage = Image.FromFile(backgroundImagePath); this.BackgroundImageLayout = ImageLayout.Stretch; }
Systems.Windows.Forms.Application.StartupPath是运行时.exe文件的位置,采用相对路径可以方便后续实现安装的操作。
-
Welcome()函数
最开始是为了便于操作主界面以及实现[再来一局]按钮创造的函数,将各个组件的初始化放在了这个函数中,但是进行到左后发现其实并不能简化而且难以在复杂的Welcome()函数中分离出适合于第二局游戏的语句,便搁置在此。
private void Welcome() { GameStart.Visible = true; GameTitle.Visible = true; Hint.Visible = true; GameBox.Visible = false; Suggestion.Visible = false; ScoreBoard.Visible = false; ScoreRec.Visible = false; PlayersTitle.Visible = false; BestPlayers.Visible = false; GameTime.Visible = false; GameOverTitle.Visible = false; NameRecTitle.Visible = false; NameRec.Visible = false; NameConfirm.Visible = false; Close.Visible = false; }
[Label]GameStart为[开始游戏]按钮,[Label]GameTitle为标题,[Label]Hint为操作提示。
其他为后续实现过程中逐渐添加的语句,故不在此叙述。
-
[Label]GameStart的Click事件
private void GameStart_Click(object sender, EventArgs e) { GameStartFunc(); this.GameBox.Refresh(); } private void GameStartFunc() { GameStart.Visible = false; GameTitle.Visible = false; Hint.Visible = false; string gamePageImagePath = Application.StartupPath; gamePageImagePath += @"\imgs\GamePage\GamePage.png"; GameBox.BackgroundImage = Image.FromFile(gamePageImagePath); GameBox.BackgroundImageLayout = ImageLayout.Stretch; GameBox.Visible = true; Suggestion.Visible = true; ScoreBoard.Visible = true; ScoreRec.Visible = true; PlayersTitle.Visible = true; BestPlayers.Visible = true; GameTime.Visible = true; objectList.Add(board); objectList.Add(ball); objectList.Add(bricks); string nameListPath = Application.StartupPath; nameListPath += @"\data\PlayersNameList.txt"; System.IO.StreamReader streamReader = new System.IO.StreamReader(nameListPath); BestPlayers.Text = streamReader.ReadToEnd(); streamReader.Close(); players.Add(new Player(BestPlayers.Lines[0])); players.Add(new Player(BestPlayers.Lines[1])); players.Add(new Player(BestPlayers.Lines[2])); }
同样的GameStartFunc()也是为了方便[再来一局]按钮的事件而做出来的冗余函数(lll¬ω¬)。
部分语句为后续添加。
游戏界面
-
建立[PictureBox]GameBox
[PictureBox]GameBox的相关属性:
Dock: True
Size: 432x573
Locked: True
GameStartFunc()内相关初始化语句为:
string gamePageImagePath = Application.StartupPath; gamePageImagePath += @"\imgs\GamePage\GamePage.png"; GameBox.BackgroundImage = Image.FromFile(gamePageImagePath); GameBox.BackgroundImageLayout = ImageLayout.Stretch; GameBox.Visible = true;
选择PictureBox作为图形的载体,在[PictureBox]GameBox的Paint事件中置入各个组件的绘制语句。
private void GameBox_Paint(object sender, PaintEventArgs e) { Bitmap bitmap = new Bitmap(GameBox.Width, GameBox.Height); foreach(Object @object in objectList) { // @object.Draw(e.Graphics, this.GameBox); @object.Draw(Graphics.FromImage(bitmap), this.GameBox); } e.Graphics.DrawImage(bitmap, 0, 0); }
其中[List]objectList为各个Object的集合,通过foreach简化代码。
重绘[PictureBox]GameBox可以用GameBox.Refresh()实现。
(注释部分为直接绘制,未注释部分为双缓冲操作)
添加一个计时器[Timer]Action,利用其Tick事件来刷新图形以及控制刷新频率:
private void Action_Tick(object sender, EventArgs e) { ball.Run(GameBox, board, bricks, ref score); if (ball.touchedBound) { Action.Stop(); GameOver(); } GameBox.Refresh(); }
同时Tick事件中还可以添加其他不同功能的语句,将会在后续过程中解释。
-
在[PictureBox]GameBox中绘制图形
-
Object类
在编写Board类和Ball类后将相似代码提取出来构造一个基类Object类。
class Object { public int xPos { get; set; } public int yPos { get; set; } public Rectangle rectangle { get; set; } public bool isDelete = false; public virtual void Draw(
-