经典游戏
c#这科是由赵大伟老师教的,并且打分方式是根据这个项目。而我素来对游戏比较感兴趣,而且Visual Studio操作很方便,所以当知道需要做一个结课项目的时候,我就开始构思制作一个游戏了。
- 使用语言:c#
- 制作工具:·Visual Studio 2010·
推箱子

游戏规则:通过键盘控制移动我的箱子,并且在碰撞到其他箱子的时候,与被撞到的箱子一起移动。而在撞到地形的时候,则不能移动,当目标箱子到达指定底线,游戏结束。
思路:整体显示用PictureBox控件显示图片,然后通过键盘进行操作。
移动
- 在构思的时候,一个游戏,我首先想到的是移动问题。
在窗体程序里,显示一个我能控制的箱子,然后移动它,在这里我用到了PictureBox的控件。更改了他的背景颜色和名字(myBox)。

- 然后就要移动这个箱子了,在学习的时候,学习过textBox的keyDown事件,我想能不能用这种方法控制图片的移动,但是PictureBox并没有这个事件,所以用了一个取巧的方法,
设置一个textBox控件,用键盘的keyDown事件控制textBox的移动,并且在移动事件里控制PictureBox的位置与textBox的位置一致,并且,PictureBox始终在textBox的上方将其盖住。
下面是源代码,以向左移动为例子

private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
//设置移动距离
int rightLocal = myBox.Width - 1;
int bottomLocal = myBox.Width + 2;
//用switch语句判断键盘按下的是哪个键
switch (e.KeyCode)
{
case Keys.Left:
//检查碰撞 没撞到的情况下返回的是true
if (checkLeftCrash(myBox))
{
//向左移动textBox1
textBox1.Left = textBox1.Left - rightLocal;
//判断是否撞到箱子
if (checkBoxLeftCrash(rightLocal, targetBox1, targetBox2))
{
//判断能不能移动目标箱子,如果可以,直接移动
checkLeftMove(rightLocal, targetBox1);
}
else
{
//将textBox1的位置调整回来
textBox1.Left = textBox1.Left + rightLocal;
break;
}
if (checkBoxLeftCrash(rightLocal, targetBox2, targetBox1))
{
checkLeftMove(rightLocal, targetBox2);
}
else
{
textBox1.Left = textBox1.Left + rightLocal;
break;
}
}
break;
}
myBox.Left = textBox1.Left;
myBox.Top = textBox1.Top;
showBuShu();
showStatus();
checkSuccess();
}
地图设置
- 而后,我建立了地图,用方法比较笨拙,从设计页面挨个设置了每一个pictureBox,然后向PictureBox中添加地图的图片。其实可以用更简便的方法,从代码load事件中,foreach遍历添加PictureBox。
难点
- 在设置地图的时候,我发现在设计视图中设定的Size值,与在代码里的width,与height是不同的。
例如,设置size为50,50 然而width和height会变成其他比较奇怪的值。这对后面的碰撞验证造成了影响。
设计页面添加pictureBox的缺点再次体现,用代码生成将会避免此缺陷。
碰撞检测
- 碰撞检测一共分为三类
地图边缘检测
- 这部分的检测比较简单,只需要检测,四面最边缘的墙壁。
其他墙面检测
- 其他墙面的检测,则需要挨个写入墙的位置。很麻烦,很蠢。
下面是代码
//以向上为例
public Boolean checkUpCrash(PictureBox box)
{
Boolean b = true;
//边缘
if (box.Top == 40)
{
b = false;
}
//其他墙
if ((box.Top == 80 && box.Left == 259) || (box.Top == 80 && box.Left == 296) || (box.Top == 120 && box.Left == 37) || (box.Top == 120 && box.Left == 111) || (box.Top == 120 && box.Left == 148) || (box.Top == 120 && box.Left == 185))
{
b = false;
}
return b;
}
- 在被这蠢方法折磨后,我想到了一个稍微聪明点的想法。
如果当时墙壁是用代码生成的,那么也就可以生成一个墙壁,就把这个墙壁的位置放到一个数组里,当需要位置碰撞的判定时,遍历数组然后判断即可。
目标箱子碰撞检测
- 先判断我的箱子移动后,位置是否与目标箱子重叠。如果重叠,则可以推这个箱子。
- 接下来判断目标箱子移动后,是否会碰撞到墙。如果会碰撞到墙,那么此次移动不生效,将位置数值回调。
实例代码
//此处以向右为例
//向右推箱子
public void checkRightMove(int local,PictureBox box)
{
if (myBox.Left + local >= box.Left)
{
//判断是否碰撞
if (box.Top < myBox.Top || box.Top > myBox.Top || box.Left < myBox.Left)
{
//没碰撞则什么都不做
}
else
{
//目标箱子的位置就更改为我的箱子的位置+两倍的移动长度
if (checkRightCrash(box))
{
//目标箱子移动
box.Left = myBox.Left + local * 2;
}
else
{
//位置回调
textBox1.Left = textBox1.Left - local;
}
}
}
}
- 还有一种情况,则是两个目标箱子紧挨着的时候,这时碰撞,也不能移动。
代码
//检查两个箱子紧挨时向右的碰撞
public Boolean checkBoxRightCrash(int local,PictureBox Fbox,PictureBox Sbox)
{
Boolean b = false;
//判断是否碰撞
if (myBox.Left + local != Fbox.Left || myBox.Top!=Fbox.Top)
{
b = true;
}
else
{
if (Fbox.Left + local != Sbox.Left || Fbox.Top != Sbox.Top)
{
b = true;
}
}
return b;
}
胜利判断
当所有目标箱子都到达指定区域的时候,游戏结束,并提示恭喜过关。
//胜利判断
public void checkSuccess()
{
if (((success1.Left == targetBox1.Left && success1.Top == targetBox1.Top) || (success2.Left == targetBox1.Left && success2.Top == targetBox1.Top)) && ((success1.Left == targetBox2.Left && success1.Top == targetBox2.Top) || (success2.Left == targetBox2.Left && success2.Top == targetBox2.Top)))
{
MessageBox.Show("恭喜你,过关了!");
timer1.Stop();
String conStr = "provider=microsoft.jet.oledb.4.0;data source=fx.mdb";
OleDbConnection conn = new OleDbConnection(conStr);
conn.Open();
String sql = "insert into ranklist(username,[time],stepnum) values('"+Login.username+"',"+time+","+bushu+")";
OleDbCommand cmd = new OleDbCommand(sql, conn);
if (cmd.ExecuteNonQuery() > 0)
{
MessageBox.Show("添加记录成功");
}
textBox1.Enabled = false;
textBox1.Focus();
}
}
不足总结
- 地图的设置过于麻烦,可以通过foreach简化。
- 在设计页面添加PictureBox,细节不够准确,会对游戏的运行有影响。
- 通过使用数组来简化碰撞检测。
- 写方法时,Boolean值设置混乱,有的是碰撞到了是true,有的是碰撞到了是false,应该统一!
- 检测多个箱子紧挨着的时候,写法过于笨拙,应考虑更加实用的方法。
网友评论