1、C#程序设计实训报告1 系统设计要求1.1 需求分析本系统为一个用C#实现的为我们所熟悉的简单的俄罗斯方块游戏,该系统的具体功能如下:1) 能简便的开始游戏,游戏中的方块的功能与日常我们所熟悉的游戏的功能一致,各种块的设置也一致,包括块的旋转,加速下降,平移,满行消去,到顶游戏结束功能;2) 能够自定义游戏中功能键的具体按键,设置游戏的级别,即方块下降的速度等,并在设置后立即生效,在游戏中立刻体现,在下次程序运行时读取上次设置的游戏环境;3) 显示下一方块提示信息,以及游戏数据的统计,如速度,按键速度,分数行者等等;4) 游戏有级别设置,级别越高,方块下降的速度越快,当到达一定分数后进入下一级
2、别;1.2 界面要求要求界面简洁美观,简单易用。1.3 设计环境本程序选择Visual Studio 2008作为实验环境。2 设计思路2.1 用面向对象的方法分析系统对于俄罗斯方块来说,我们可以定义一个类,专门来描述俄罗斯方块,在这个类中,包含与之相关的方法、属性和字段,通过封装,实现其业务逻辑。其中,每一个俄罗斯方块都有相同的特征,由4个小正方形构成,有旋转,移动,下落的动作,行被填满除去构成行的小正方体块。基中块的形状类型有7种:田、一、L、倒L、Z、倒Z、上。在主窗口中,通过调用俄罗斯方块类来实现程序的表示层,在该窗口中通过两个Panel控件来实现方块叠放窗口和下一方块信息窗口;调用设
3、置窗口,保存设计窗口类传回的信息,并设置到游戏中去,保存在配置文件中;在设置窗口中,以良好的界面提供用户自定义快捷键的接口,保存相应设置参数,以提供给调用窗口。2.2 概要设计根据设计思路,可以为系统设计三个类,实现俄罗斯方块其本块的类RussiaBlock继承Object类,主窗体类MainForm类,设置窗体类SettingForm类。1) RussiaBlock类字段 private int shapeNO;/形状号 private Control con;/绘图控件 private Point pos;/当前位置 private Point lastPos;/最后位置 private
4、int leftBorder;/左边界 private int bottomBorder;/下边界 private int unitPix;/每块象素数 / / 当前块总个数 / private int blockNum = 0;/当前块总个数 private int rowDelNum = 0;/已构成满行被消去的行数 private bool, aroundExist;/周边的位置是否有块 private Color, aroundColor;/周边的着色 private Color color;/当前块颜色属性 public int RowDelNum/当前块总个数 public int
5、 BlockNum/当前块总个数私有方法private void SetLastPos()/设置方块的最后位置private void SetPos(int shapeNO, Point firstPos)/设置方块的当前位置,shapeNO方块号,firstPos初始位置 private bool CanMove(int direction)/块是否还可继续移动包括下和左右 private bool CanRotate(Point pos)/判断当前块是否是可以进行翻转,位置确定则不能,还没确定则可以 private void DelRows()/一行中所有格填满,删除该行. private
6、 void DrawOne(int x, int y, Color color, Graphics gra)/在指定的位置绘制方块中的一块 公共方法public void EraseLast()/最后方块位置的显示.原理:将显示的窗口方块位置无效,强制发生重绘事件 public void FixBlock()/填补行 public bool GeneBlock(int shapeNO, Point firstPos, Color color)/产生下一方块public bool Rotate()/旋转动作 public bool Move(int direction)/移动 public vo
7、id Drop()/直落 public void DrawBlocks(Rectangle rec)/在指定的位置绘制方块,包括块小块 2) MainForm类主要字段private RussiaBlock block;/ 方块实例private RussiaBlock nextBlock;/ 下一基本块实例private int nextShapeNO;/ 下一基本块形状号private bool paused;/已暂停private DateTime atStart;/开始时间private DateTime atPause;/暂停时间private TimeSpan pauseTime;
8、/暂停间隔时间private Keys keys;/按键private int startLevel;/ 开始级别private bool trans;/ 改变级别主要方法private void MainForm_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) / 按键事件private void panel1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) / 游戏窗格绘制void timer1_Tick(object sender, System
9、.EventArgs e) / 定时时钟处理函数,使基本块自动下降private void panel2_Paint / 下一块窗格绘制btnPause_Click(object sender, System.EventArgs e)/ 暂停游戏btnSetting_Click(object sender, System.EventArgs e) / 设置环境label5_MouseEnter(object sender, System.EventArgs e) / 显示帮助private void btnStart_Click / 开始游戏private void Initiate()/ 初
10、始化环境private void Start()/ 开始游戏private void Fail()/ 游戏失败private bool CreateBlock()/ 建立基本块private void CreateNextBlock()/ 建立下一基本块private void FixAndCreate()/ 填补方块视图并建立新块private void SaveSetting()/ 保存设置SettingForm类主要方法:private void ChangeKey (TextBox textBox,System.Windows.Forms.KeyEventArgs e,int i)3
11、系统功能模块图3.1 方块类RussiaBlock3.2 主窗口类MainForm3.3 设置窗口类SetttingForm3个类图放在一起,如下所示4 系统流程图、类的层次图4.1 流程图4.2 类的结构图5 调试过程5.1 程序主界面5.2 设置界面5.3 开始游戏5.4 游戏结束5.5 暂停游戏6 关键程序代码6.1 RussiaBlock类以下涉及的词语基本块指的是由四个小方块构成的实体,四个小方块指的是小正方形。方块与基本块同义。1) 构造函数,通过给定的参数:控件实例,左边界,底边界,小方块(一个基本块由4个小方块构成)单位象素,形状号,起始位置,颜色。public RussiaB
12、lock(Control con,int leftBorder,int bottomBorder,int unitPix,int shapeNO,Point firstPos,Color color)/构造一个游戏中方块实例的构造函数,如Z形块等this.con=con;this.leftBorder=leftBorder;this.bottomBorder=bottomBorder;this.unitPix=unitPix;this.SetPos(shapeNO,firstPos);this.color=color;this.aroundExist=new boolleftBorder+1,
13、bottomBorder+1;this.aroundColor=new ColorleftBorder+1,bottomBorder+1;this.lastPos=new Point4;/每一个基本块中都相同的拥有N(4)个方格,通过N个方格的左上角位置可以确定块在视图中的位置2) 设置方块的最后位置,用来保存上次方块的位置private void SetLastPos()/设置方块的最后位置 for (int i = 0; i this.pos.Length; i+) this.lastPosi = this.posi; 3) 是否能移动,如果移动后与其他小方块重叠,则不能移动,可通过类的私
14、有成员aroundExist数组来确定,该数组保存的就是现方块图中各个位置是否已存在小方块,true表示存在。private bool CanMove(int direction)/块是否还可继续移动包括下和左右 bool canMove = true; if (direction = 0)/左 foreach (Point p in this.pos) if (p.X - 1 this.leftBorder | this.aroundExistp.X + 1, p.Y) canMove = false; break; else/下 foreach (Point p in this.pos)
15、if (p.Y + 1 this.bottomBorder | this.aroundExistp.X, p.Y + 1) canMove = false; break; return canMove; 4) 当前块是否是可以进行翻转,如果已靠底或到达边界,或者翻转后是与已有的小方块重叠则不能进行翻转。private bool CanRotate(Point pos)/判断当前块是否是可以进行翻转,位置确定则不能,还没确定则可以 bool canRotate = true; foreach (Point p in pos) if (p.X this.leftBorder | p.Y this.
16、bottomBorder | this.aroundExistp.X, p.Y) canRotate = false; break; if (canRotate = true) this.SetLastPos(); return canRotate; 5) 在指定的位置绘制方块中的一块private void DrawOne(int x, int y, Color color, Graphics gra)/在指定的位置绘制方块中的一块 gra.FillRectangle(new SolidBrush(color), x * unitPix + 1, y * unitPix + 1, this.
17、unitPix - 1, this.unitPix - 1); gra.DrawRectangle(new Pen(Color.Black, 1), x * unitPix, y * unitPix, unitPix, unitPix); 6) 最后的一块方块位置的显示.原理:将窗口显示的方块位置无效,强制发生重绘事件public void EraseLast()/最后方块位置的显示.原理:将显示的窗口方块位置无效,强制发生重绘事件foreach(Point p in this.lastPos)this.con.Invalidate(new Rectangle(p.X*unitPix,p.Y*
18、unitPix,unitPix+1,unitPix+1);7) 填补方块图,如果发生满行情况则删去满行public void FixBlock()/填补方块图this.blockNum+;foreach(Point p in this.pos)this.aroundExistp.X,p.Y=true;this.aroundColorp.X,p.Y=this.color;this.DelRows();8) 产生下一方块public bool GeneBlock(int shapeNO, Point firstPos, Color color)/产生下一方块this.SetLastPos();th
19、is.EraseLast();this.SetPos(shapeNO,firstPos);if(!this.CanRotate(this.pos)this.pos=null;return false;else this.color=color;return true;9) 旋转动作,该函数处理过程复杂,相应代码详见工程文件。通过对各种情况的判断,调用CanRotate函数,成功则进行旋转。10) 移动动作,按照下、左、右三个方向之一来移动public bool Move(int direction)/移动int offx=0,offy=0;if(direction=0 & this.CanMo
20、ve(0)/左offx=-1;offy=0;else if(direction=1 & this.CanMove(1)/右offx=1;offy=0;else if(direction=2 & this.CanMove(2)/下offx=0;offy=1;else return false;this.SetLastPos();for(int i=0;ithis.pos.Length;i+)posi.Offset(offx,offy);return true;11) 地6.2 MainForm类1) 时钟触发处理函数,使方块自动的向下移动,每0.1秒触发一次private void timer1
21、_Tick(object sender, System.EventArgs e)if(block!=null & !this.failed)if(!block.Move(2)this.FixAndCreate();elseblock.EraseLast();2) 初始化,读取配置private void Initiate()tryXmlDocument doc = new XmlDocument();doc.Load(c:setting.cob);XmlNodeList nodes=doc.DocumentElement.ChildNodes;this.startLevel=Convert.T
22、oInt32(nodes0.InnerText);this.level=this.startLevel;this.trans=Convert.ToBoolean(nodes1.InnerText);keys=new Keys5;for(int i=0;inodes2.ChildNodes.Count;i+)KeysConverter kc=new KeysConverter();this.keysi=(Keys)(kc.ConvertFromString(nodes2.ChildNodesi.InnerText);catchthis.trans=false;keys=new Keys5;key
23、s0=Keys.Left;keys1=Keys.Right;keys2=Keys.Down;keys3=Keys.Up;keys4=Keys.NumPad0;this.level=1;this.startLevel=1;this.timer1.Interval=500-50*(level-1);this.label4.Text=级别:+this.startLevel;if(trans)this.TransparencyKey=Color.Black; 3) 建立基本块private bool CreateBlock()Point firstPos;Color color;if(this.nex
24、tShapeNO=0)Random rand=new Random();this.nextShapeNO=rand.Next(1,8);switch(this.nextShapeNO)case 1:/田firstPos=new Point(4,0);color=Color.Turquoise;break;case 2:/一firstPos=new Point(3,0);color=Color.Red;break;case 3:/土firstPos=new Point(4,0);color=Color.Silver;break;case 4:/zfirstPos=new Point(4,0);c
25、olor=Color.LawnGreen;break;case 5:/倒zfirstPos=new Point(4,1);color=Color.DodgerBlue;break;case 6:/LfirstPos=new Point(4,0);color=Color.Yellow;break;default:/倒LfirstPos=new Point(4,0);color=Color.Salmon;break;if(this.block=null)block=new RussiaBlock(this.panel1,9,19,25,this.nextShapeNO,firstPos,color
26、);elseif(!block.GeneBlock(this.nextShapeNO,firstPos,color)return false;block.EraseLast();block.Move(2);return true;4) 建立下一基本块private void CreateNextBlock()Random rand=new Random();this.nextShapeNO=rand.Next(1,8);Point firstPos;Color color;switch(this.nextShapeNO)case 1:/田firstPos=new Point(1,0);colo
27、r=Color.Turquoise;break;case 2:/一firstPos=new Point(0,1);color=Color.Red;break;case 3:/土firstPos=new Point(0,0);color=Color.Silver;break;case 4:/zfirstPos=new Point(0,0);color=Color.LawnGreen;break;case 5:/倒zfirstPos=new Point(0,1);color=Color.DodgerBlue;break;case 6:/LfirstPos=new Point(0,0);color=
28、Color.Yellow;break;default:/倒LfirstPos=new Point(0,0);color=Color.Salmon;break;if(nextBlock=null)nextBlock=new RussiaBlock(this.panel2,3,1,20,this.nextShapeNO,firstPos,color);elsenextBlock.GeneBlock(this.nextShapeNO,firstPos,color);nextBlock.EraseLast();5) 填补块视图并产生下一块private void FixAndCreate()block
29、.FixBlock();this.label1.Text=手速:+Math.Round(double)block.BlockNum/(TimeSpan)(DateTime.Now-this.atStart).Subtract(this.pauseTime).TotalSeconds,3)+块/秒;this.label2.Text=块数:+block.BlockNum;this.label3.Text=分数:+block.RowDelNum*MainForm.rowScore;if(this.level=30)this.rowDelNum+=30;this.level+;this.timer1.
30、Interval=500-50*(level-1); this.label4.Text=级别: +this.level;bool createOK=this.CreateBlock();this.CreateNextBlock();if(!createOK)this.Fail();6) 保存配置private void SaveSetting()tryXmlDocument doc = new XmlDocument();XmlDeclaration xmlDec=doc.CreateXmlDeclaration (1.0,gb2312,null);XmlElement setting=doc
31、.CreateElement(SETTING);doc.AppendChild(setting);XmlElement level=doc.CreateElement(LEVEL);level.InnerText=this.startLevel.ToString();setting.AppendChild(level);XmlElement trans=doc.CreateElement(TRANSPARENT);trans.InnerText=this.trans.ToString();setting.AppendChild(trans);XmlElement keys=doc.Create
32、Element(KEYS); setting.AppendChild(keys);foreach(Keys k in this.keys)KeysConverter kc=new KeysConverter();XmlElement x=doc.CreateElement(SUBKEYS);x.InnerText=kc.ConvertToString(k);keys.AppendChild(x);XmlElement root=doc.DocumentElement;doc.InsertBefore(xmlDec,root);doc.Save(c:setting.cob);catch(Exce
33、ption xe)MessageBox.Show(xe.Message);7 总结7.1 设计总结本设计通过Vusial Studio 方便的Windows表单设计界面,增加了相应的按钮单击响应函数,通过与用户的交互,反馈回用户所需要的信息。设计出的程序符合设计需求,既有基本的游戏逻辑功能,又能保存用户的设置,取得较好的实验结果。通过本次实验,学习并实践了面向对象的方法,以及类之间的继承与派生的关系,成员函数、变量的调用的问题,达到了设计的预期效果。通过本次实验,深入了解到C#语言的基础知识,掌握面向对象编程的分析设计方法,以及与面向对象技术相关的一些软件开发技术,掌握在 Visual Stu
34、dio环境下进行可视化程序设计技术。通过本次实践具体的项目,为进一步开展相关领域的学习和科研打下良好的基础7.2 设计体会这个学期“.Net架构与C#程序设计”课程让我接触了面向对象的程序设计,Visual stdio的可视化编程环境让我们可以制作界面友好的Windows环境,利用IDE可以快捷地开发出所要的可视化的环境。C#是是一种完全面向对象的语言,使用对象的思想来编程,既可以对相应的数据进行保护,也可以相应的与其他的类共享,有利于程序的结构化,方面程序的编写。Viusal Studio下我们可以快速的进行开发。但是,也要看到其对WindowsApi函数的封装也导致了我们在学习的时候对Wi
35、ndows程序的运行机制不了解,导致了学习时候的迷惑。本学期配套的书籍虽然简单明了,但是对于机制原理的解释和说明过少,因此,学习的时候应该不只满足于这本书中的内容,应该多找一些书籍进行知识的扩展了加深。通过本次课程设计,编写一个熟悉的俄罗斯方块游戏程序,让我们熟悉了C#下用GDI+的使用,加深了类以及对象概念的理解,为进一步学习其他完全面对对象的语言打下了良好的基础。开发一个工程时,应该先制定好程序的框架,规划好相应的功能模块,使程序模块化,易于日后的扩展和完善。其次是程序的数据结构,良好的数据结构能使程序高效化,功能强大。本次实现中最重要的是方块类的编写,其定义的好坏和封装性的良好是整个程序
36、运行的基础,属于程序的业务逻辑功能块,主框架中通过调用该类,实现程序的表示层。再之,优秀的算法能提高程序的效率。优秀的资料查阅能力也是非常基本并且十分重要的。本次程序中,当方块构成完整的行时应该消去,其逻辑怎么实现是一直困惑着我的问题,在网上寻找了很久,终于在网上找到一份教程,在看完教程后,按照其指导,终于完成了该逻辑功能的编写,如果单凭自己的能力,在两个星期内想做出一个功能完整的程序实属不易。善于借鉴他人的结果和理论是减少重复劳动的好办法。最后,良好的程序编写风格有利于交流和日后的维护。一个真正实用的程序不单单这样的简单,常常需要多人的合作与协商,因此,良好的风格是塑造自己成为一个优秀的程序员的必经之路。8 参考书目1) Visual C# 2005编程实例精粹罗斌 越飞 等编著2) C#程序设计教程郑阿奇 梁敬东主编3) C#范例解析张家祥 方凌江 张贤坤 何鑫 等编著