C#之四十八 俄罗斯方块设计

网友投稿 619 2022-05-29

1        系统设计要求

1.1   需求分析

本系统为一个用C#实现的为我们所熟悉的简单的俄罗斯方块游戏,该系统的具体功能如下:

1).       能简便的开始游戏,游戏中的方块的功能与日常我们所熟悉的游戏的功能一致,各种块的设置也一致,包括块的旋转,加速下降,平移,满行消去,到顶游戏结束功能;

2).       能够自定义游戏中功能键的具体按键,显示下一方块提示信息,以及游戏数据的统计;

3).       考虑需要解决的问题:怎么样设置图形显示;怎样获取鍵盘输入;怎样控制方块的移动;怎样控制时间间隔(用于游戏中控制形状的下落);游戏中的各种形状及整个游戏空间怎么用数据表示;游戏中怎么判断左右及向下移动的可能性;游戏中怎么判断某一形状旋转的可能性;按向下方向键时加速某一形状下落速度的处理;怎么判断某一形状已经到底;怎么判断某一已经被填满;怎么消去已经被填满的一行;怎么消去某一形状落到底后能够消去的所有的行;(如长条最多可以消去四行)怎样判断游戏结束,关于“下一个”形状取法的问题。

2        设计思路

2.1   用面向对象的方法分析系统

从游戏的基本玩法出发,主要就是俄罗斯方块的形状和旋转,在设计中在一个图片框中构造了一个20*20(像素)的小块,由这些小块组合成新的形状,每四个小块连接在一起就可以构造出一种造型,总共设计了7中造型,每种造型又可以通过旋转而变化出2到4种形状,在游戏窗体中用户就可以使用键盘的方向键来控制方块的运动,然后对每一行进行判断,如果有某行的方块是满的,则消除这行的方块,并且使上面的方块自由下落,其中,方块向下的速度是有时钟控件控制的。俄罗斯方块游戏设计主要包括以下10个方面:

1).         游戏界面的设计。

2).         俄罗斯方块的实现。

3).         键盘输入信息的获取。

4).         俄罗斯方块的移动(向左,向右和向下)。

5).         俄罗斯方块的变换。

6).         方块自动下落与速度的选择。

7).         慢行的判断与消行。

8).         游戏结束判断。

9).         用户配置保存。

在主窗口中,通过调用俄罗斯方块类来实现程序的表示层,在该窗口中通过两个Panel控件来实现方块叠放窗口和下一方块信息窗口;调用设置窗口,保存设计窗口类传回的信息,并设置到游戏中去,保存在配置文件中;

在设置窗口中,以良好的界面提供用户自定义快捷键的接口,保存相应设置参数,以提供给调用窗口。

2.2运用的控件和主要对象

在设计过程中主要用到的控件有:PictureBox控件,MenuStrip控件,Button控件,Label控件,Timer控件,winmm组件,DirectSound等等。

3        系统功能实现

3.1   屏幕信息初始化

用来显示状态信息的框

privateSystem.Windows.Forms.GroupBox statusBox;

开始按钮

privateSystem.Windows.Forms.Button btnStart;

显示“下一块”的标签

privateSystem.Windows.Forms.Label label3;

显示“分数”的标签

privateSystem.Windows.Forms.Label label2;

显示“等级”的标签

privateSystem.Windows.Forms.Label label1;

用来画下一块方块的区域

privateSystem.Windows.Forms.PictureBox panel1;

游戏区域

privateSystem.Windows.Forms.PictureBox gameArea;

实现如下主界面效果图(图3-1):

1.1   方块的实现

在程序中每一个方块都是一个Block类的实例。Block包括的参数有方块的宽度,高度,最左端横坐标,最上端纵坐标,方块的数组表示。其中一共有7中形状的方块,以数组表示为:

11     11    1       11     010    10     01

01     10    1       11     111     11    11

01     10    1                      01     10

1

方块的7种形状分别以数字0-6来代表,在构造函数中,随机生成0-6中数字,以此来随机生成方块的形状。用来在界面上显示方块的贴图也以0-6的数字来代表,同样以随机数的形式来随机的现实方块的颜色。

1.2   键盘输入事件处理

因为在界面上有一个按钮,并且只有一个按钮,所以该按钮在通常情况下都是默认为焦点。在这种情况下按下某些键,比如空格,就会产生出发按钮事件的情况。因此必须重载整个WinForm的ProcessCmdKey来避免这样的问题。

当按向左,向右及旋转按钮时,只要相应的处理方块的位置或者形状即可,但是当按向下或者立即下落时,需要不同的处理。向下移动时,如果移动到最底部但还未固定,则需要重新设置计时器间隔时间,从而使自动下落时,底部未固定的方块到固定的时间相同。如果方块在最底部而未固定的时候,向下移动,则立即固定。这两种情况,当方块固定后,都需要判断是否消行,立即下落时,需要判断是否消行。

1.3   方块的移动

游戏中方块的移动分为向左移动,向右移动,向下移动和立即落下。

向左移动:

public void MoveLeft() {

int xPos =runBlock.XPos-1;

intyPos = runBlock.YPos;

for(int i = 0; i < runBlock.Length; i++)

{

if(xPos + runBlock[i].X <0)//如果超出左边界则失败

{

return;

}

if(!coorArr[xPos + runBlock[i].X, yPos - runBlock[i].Y].IsEmpty)//如果左边有东西挡则失败

{

return;

}

}

runBlock.erase(gpPaltte);//擦除原来位置的转块

runBlock.XPos--;

runBlock.Paint(gpPaltte);//在新位置上画转块

}

向右移动:

public void MoveRight()

{

intxPos = runBlock.XPos + 1;

intyPos = runBlock.YPos;

for(int i = 0; i < runBlock.Length; i++)

{

if(xPos + runBlock[i].X >_width-1)//如果超出右边界则失败

{

return;

}

if(!coorArr[xPos + runBlock[i].X, yPos - runBlock[i].Y].IsEmpty)//如果右边有东西挡则失败

{

return;

}

}

runBlock.erase(gpPaltte);//擦除原来位置的转块

runBlock.XPos++;

runBlock.Paint(gpPaltte);//在新位置上画转块

}

向下移动:

public void Drop() {

timerBlock.Stop();

while(Down()) ;

timerBlock.Start();

}

1.4   方块的变换

public voidDeasilRotate() //顺时针旋转

{

for (int i = 0; i< runBlock.Length;i++ )

{

int x = runBlock.XPos + runBlock[i].Y;

int y = runBlock.YPos + runBlock[i].X;

if (x < 0 || x > _width - 1)//如果超出左右边界,则失败

return;

if (y < 0 || y > _height - 1)//如果超出上下边界,则失败

return;

if (!coorArr[x, y].IsEmpty)//如果旋转后的位置上有转块,则失败

return;

}

runBlock.erase(gpPaltte);//擦除原来位置的转块

runBlock.DeasilRotate();

runBlock.Paint(gpPaltte);//在新位置上画转块

}

public voidContraRotate() //逆时针旋转

{

for (int i = 0; i< runBlock.Length; i++)

{

int x = runBlock.XPos - runBlock[i].Y;

int y = runBlock.YPos - runBlock[i].X;

if (x < 0 || x > _width - 1)//如果超出左右边界,则失败

return;

if (y < 0|| y > _height - 1)//如果超出上下边界,则失败

return;

if (!coorArr[x, y].IsEmpty)//如果旋转后的位置上有转块,则失败

return;

}

runBlock.erase(gpPaltte);//擦除原来位置的转块

runBlock.ContraRotate();

runBlock.Paint(gpPaltte);//在新位置上画转块

}

1.5   判断方块是否到底

public voidCheckAndOverBlock()//检查转块是否到底,如果到底则把当前转块归入coorArr,并产生新的转块

{

boolover = false;//设置一个当前运行转块是否到底的标志

for(int i = 0; i < runBlock.Length;i++ )//遍历当前运行转块的所有小方块

{

intx = runBlock.XPos + runBlock[i].X;

inty = runBlock.YPos - runBlock[i].Y;

if(y == _height - 1)//如果到达下边界,则结束当前转块

{

over = true;

break;

}

if(!coorArr[x, y+1].IsEmpty) //如果下面有转块,则当前转块结束

{

over = true;

break;

}

}

if(over)

{

for(int i = 0; i < runBlock.Length; i++)//把当前转块归入coordinateArr

{

coorArr[runBlock.XPos +runBlock[i].X, runBlock.YPos - runBlock[i].Y] = runBlock.BlockColor;

}

//检查是否有满行情况,如果有则删除

CheckAndDelFullRow();

//产生新转块

runBlock = readyBlock;//新的转块为准备好的转块

runBlock.XPos = _width / 2;//确定当前运行转块的出生位置

int y = 0;//确定转块Ypos,确定刚出生的转块顶上没空行

for (int i = 0; i < runBlock.Length; i++)

{

if (runBlock[i].Y > y)

y = runBlock[i].Y;

}

runBlock.YPos = y;

//检查新生成的转块所占用的地方是否已经有转块存在,如果有,游戏结束

for (int i = 0; i

{

if (!coorArr[runBlock.XPos+ runBlock[i].X, runBlock.YPos - runBlock[i].Y].IsEmpty)

{

StringFormat drawFormat= new StringFormat();

drawFormat.Alignment =StringAlignment.Center;

gpPaltte.DrawString("GAME OVER",

newFont("Arial Black", 25f),

newSolidBrush(Color.White),

newRectangleF(0, _height * rectPix / 2 - 100, _width * rectPix, 100),

drawFormat);

timerBlock.Stop();//关闭定时器

return;

}

}

runBlock.Paint(gpPaltte);

//获取新的准备转块

readyBlock = bGroup.GetABlock();

readyBlock.XPos = 2;

readyBlock.YPos = 2;

gpReady.Clear(Color.Black);

readyBlock.Paint(gpReady);

}

}

1.6   满行判断并消行

privatevoid CheckAndDelFullRow() //检查并删除满行

{

//找出当前转块所在行的范围

int lowRow = runBlock.YPos - runBlock[0].Y;//lowRow代表当前转块的y轴的最小值

int highRow = lowRow;//highRow代表当前转块y轴的最大值

for (int i = 0; i < runBlock.Length; i++)//找出当前转块所占行的范围,放入low和high变量内

{

int y = runBlock.YPos - runBlock[i].Y;

if (y < lowRow)

lowRow = y;

if (y > highRow)

highRow = y;

}

bool repaint = false;//判断是否重画标志

for (int i = lowRow; i <= highRow; i++) //检查是否满行,如果有,则删除

{

bool rowFull = true;

for (int j = 0; j < _width;j++ )

{

if (coorArr[j,i].IsEmpty)//如果有一行为空,则说明这行不满

{

rowFull = false;

break;

}

}

if (rowFull) //如果是满行,则删除这一行

{

repaint = true;//如果有要删除的行,则需要重画

for (int k = i; k > 0;k--) {//把第n行的值用n-1行的值来代替

for (int j = 0; j <_width; j++) {

coorArr[j, k] =coorArr[j, k - 1];

}

}

for (int j = 0; j <_width;j++ )//清空第0行

{

coorArr[j, 0] = Color.Empty;

}

}

}

if(repaint)//重画

{

PaintBackground(gpPaltte);

}

}

3.8  产生下一方块

public bool GeneBlock(int shapeNO, Point firstPos, Colorcolor)//产生下一方块

{

this.SetLastPos();

this.EraseLast();

this.SetPos(shapeNO,firstPos);

if(!this.CanRotate(this.pos))

{

this.pos=null;

returnfalse;

}

else

{

this.color=color;

returntrue;

}

}

3.9        游戏设置

保存配置

private void SaveSetting()

{

try

{

XmlDocumentdoc = new XmlDocument();

XmlDeclarationxmlDec=doc.CreateXmlDeclaration ("1.0","gb2312",null);

XmlElementsetting=doc.CreateElement("SETTING");

doc.AppendChild(setting);

XmlElementlevel=doc.CreateElement("LEVEL");

level.InnerText=this.startLevel.ToString();

setting.AppendChild(level);

XmlElementtrans=doc.CreateElement("TRANSPARENT");

trans.InnerText=this.trans.ToString();

setting.AppendChild(trans);

XmlElementkeys=doc.CreateElement("KEYS");

setting.AppendChild(keys);

C#之四十八 俄罗斯方块设计

foreach(Keysk in this.keys)

{

KeysConverterkc=new KeysConverter();

XmlElementx=doc.CreateElement("SUBKEYS");

x.InnerText=kc.ConvertToString(k);

keys.AppendChild(x);

}

XmlElementroot=doc.DocumentElement;

doc.InsertBefore(xmlDec,root);

doc.Save("c:\\setting.cob");

}

catch(Exceptionxe)

{

MessageBox.Show(xe.Message);

}

}

4  总结

本设计通过Vusial Studio 方便的Windows表单设计界面,增加了相应的按钮单击响应函数,通过与用户的交互,反馈回用户所需要的信息。

设计出的程序符合设计需求,既有基本的游戏逻辑功能,又能保存用户的设置,取得较好的实验结果。

这个学期“C#程序设计”课程让我接触了面向对象的程序设计,Visual stdio的可视化编程环境让我们可以制作界面友好的Windows环境,利用IDE可以快捷地开发出所要的可视化的环境。C#是是一种完全面向对象的语言,使用对象的思想来编程,既可以对相应的数据进行保护,也可以相应的与其他的类共享,有利于程序的结构化,方面程序的编写。Viusal Studio下我们可以快速的进行开发。但是,也要看到其对WindowsApi函数的封装也导致了我们在学习的时候对Windows程序的运行机制不了解,导致了学习时候的迷惑。本学期配套的书籍<< C#实用教程>>虽然简单明了,但是对于机制原理的解释和说明过少,因此,学习的时候应该不只满足于这本书中的内容,应该多找一些书籍进行知识的扩展了加深。

C#

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:HttpServletRequest
下一篇:MDG建行站成立
相关文章