俄罗斯方块C#

  • 格式:doc
  • 大小:153.05 KB
  • 文档页数:27

一.设计题目:设计一个俄罗斯方块游戏要求:1.游戏可以进行很好的人机交互界面,方便玩家对游戏进行各种操作;2.游戏具有良好的游戏界面,即玩家可以对游戏界面进行自定义;3.游戏支持声音处理,玩家可以对这个声音进行自定义;二.设计思路:1.用图例设计:2.类的设计:Brick类表示“砖块”,它是构成Block的基础,它们之间是聚合关系,一个Block类应该对应一个BlockType类,BlockType类用来标识当前的Block类的状态。

Game类是整个游戏的一个总调度,它负责所有的Block类以及当前玩家的分数等,它与GameStatus类是一对一的关系。

GameStatus类用来表示Game类的状态是“运行”,“暂停”还是“结束”。

启动程序以后首先创建一个Game类的案例,同时启动定时器开始处理周期性动作,最后调用Sound的play方法来播放当期音乐。

通过这些步骤,程序的基本架构就已经搭建好了。

移动方块的动作:首先MainForm对象得到当前玩家按下的键的值,然后再将这个值传递给Game对象,Game对象得到这个值以后会根据这个值来判断执行相应的操作,比如玩家按左键,Game对象就会让当前的方块向左移动一个单位。

移动完方块后MainForm调用Sound的play方法来播放音乐,同时对当前的游戏面板PictureBox进行刷新。

记录最高分的动作:首先MainForm对象创建一个HighScoreEntry对象来记录对象来记录当前玩家的姓名和分数,然后调用RecordHighScore方法来记录当前这个分数,最后创建一个HighScore对象并调用它的IncludeHighScore方法真正地将这个分数加入到注册表中。

如果当前形状(指那个正在下落还没有落到底的那个形状)向左移动,不与游戏板现有状态发生冲突,则可以向左移动。

具体做法是:先假设当前形状已经向左移动了,判断此时是否与游戏板现有状态发生冲突。

如果不发生冲突,则可以向左移动。

否则,不可以向左移动。

判断索引号为ShapeIndex的形状在坐标(x,y)是否与游戏板当前状态发生冲突的代码如下:enum bool Confilict(int ShapeIndex,int x,int y){int i;//对组成索引号为ShapeIndex的形状的四个方块依次判断//i分别取0,2,4,6for(i=0;i<=7;i++,i++){//如果四个方块中有任何一个方块的x坐标小于1或大于10//表示超出左边界或右边界,此时,发生冲突if (shapes[ShapeIndex].xy[i]+x<1||shapes[ShapeIndex].xy[i]+x>10)return true;//如果四个方块中某个方块的y坐标小于1//表示整个形状还没有完全落入游戏板中if (shapes[ShapeIndex].xy[i+1]+y<1) continue;//如果四个方块中有任何一个方块与游戏板当前状态发生冲突//则整个形状在(x,y)处与游戏板当前状态冲突if (board[shapes[ShapeIndex].xy[i]+x][shapes[ShapeIndex].xy[i+1]+y])return true;}}这个游戏只选择了最基本的7中造型,包括长条型,正方型,正S型,反S 型,正7型,反7型,T型。

如果需要我们可以添加更多的造型。

将游戏界面的游戏区图片框分割成10*20的小块,每个小块放置一个command控件,预览区图片框按同样比例分割成4*4的小块,同样有command控件构成。

这个俄罗斯方块游戏主要是利用command控件的visible属性完成效果的,其实在游戏区图形框可以看成是由许多的command小方块组成,方块运动的过程就是造型里方块显示或者隐藏,就像现在的霓虹灯效果一样,由时钟控件控制visible 属性改变的速度,上一层的消失,下一层的显示,这样,从视觉效果可以看到方块的下落运动效果。

方块在下落的过程中会自动判断每一行方块的visible属性,如果全部为true 时,就会将这一行小方块的visible属性全部变成false,在将上面的小方块向下移动,利用for语句进行循环判断,将所有这样情况的行改变小方块visible属性。

当有多行同时出现这样情况时使用递归调用,实现连续消行。

具体程序代码如下:shape=new Brick[this.Width,this.Height];shape[0,0].Filled=true;shape[1,0].Filled=true;shape[2,0].Filled=true;shape[3,0].Filled=true;//分别对这些砖块设置图片shape[0,0].BrickImage=blockImage13;shape[1,0].BrickImage=blockImage14;shape[2,0].BrickImage=blockImage13;shape[3,0].BrickImage=blockImage14;//设置当前的Shape属性this.Shape=shape;break;对于设计一个游戏来说,,一般的也是最常用的控件就是定时器,在C#中这个控件的名称为Timer,它是整个游戏的关键。

游戏在启动的时候主要负责初始化这个控件,初始化的内容主要包括设定定时器的事件触发时间间隔以及触发的事件,用户需要根据自己的需求来实现相应的定时触发事件,这个事件的名称为Tick。

这个这个方法是整个游戏的关键,在该方法中,程序将当前的方块向下移动一个单位并同时判断它是否已经到达最底部,如果到达了,则判断当前有无已经被填满的行,并继续创建一个新的方块。

如果当前方块的最顶部的坐标大于游戏面板中方块的高度,则游戏结束。

程序另外一个比较重要的方面就是玩家在旋转一个方块的时候,如何判断当前方块可以被旋转,比如一个1型方块贴着方块容器的边缘时,玩家是无法对这个方块进行旋转的,因为容器的边缘与1型方块旋转后的矩形区域交叠了,所以,如何判断这种情况就是一个复杂的问题,在本程序中,应用一个二维数组来表示方块的容器,二维数组中设置为true的单元表示这个位置已经有方块存在了,当旋转一个方块的时候,首先判断旋转以后的位置是否与当前容器的二维数组中含有true 值的单元有相交的地方,如果有则放弃旋转,否则就正常的将这个方块进行旋转。

三.核心代码:public struct Brick{///<summary>///表示这个砖块是否被移除/// </summary>private bool filled;///<summary>///显示这个砖块的图片///</summary>private Image Filled{get{retuen filled;}set{filled=value;}}public Image BrickImage{get{return brickImage;}set{brickImage=value;}}}为使方块有丰富效果,分别为构成每个方块的砖块赋予特有的填充方式和图片属性,这样用户可以通过自定义的方式变换方块的外观。

BlockType类是标示方块用的,他主要是枚举型,这个类型记录了当前游戏中所有的方块种类的名称:public enum BlockType{///<summary>///未定义的类型,用户可以通过扩展它来创建新的方块///</summary>Undefined,///<summary>Straight,///<summary>///正方形发块///</summary>Rectangle,///<summary>///(L)型方块///</summary>CornerLeft,///<summary>///(J)型方块///</summary>CornerRight,///<summary>///(T)型方块///</summary>Cross,///<summary>///(Z)型方块///</summary>Zigzag1,///<summary>///(S)型方块///</summary>Zigzag2,}Block类是真正实现“方块”的生成类,也就是说,游戏过程中的每一个方块都是一个Block类的案例,Block不仅负责方块的创建而且负责在游戏面板中创建这个方块,也即是根据玩家的要求旋转这个方块:public Block(){//创建一个随机生成器Random randomGenerator=new Random();//随机生成一个方块类型,1~8代表了8中不同的方块BlockType randomBlockType=(BlockType)(randomGenerator.Next(1,8));//设置当前方块的类型this.BlockType=randomBlockType;}randomGenerator是一个Random类型的变量,它是System命名空间的一个类,负责根据用户指定数值范围随机生成一个随机数。

第二条语句负责根据这个生成的数值所对应的BlockType中的方块类型来获取这个类型的实例,第三条语句负责将此类型的实例赋值给当前的Block实例,这个赋值过程同时也执行了方块的初始化:public partial class Form2 : Form{private Keys left = Keys.Left;private Keys right = Keys.Right;private Keys down = Keys.Down;private Keys change = Keys.Space;private Keys start_puase = Keys.F2;private Keys setkeys = Keys.F3;private Keys newgame = Keys.F4;private int grade = 1;private mainform form1;public Form2(mainform form1)//用主窗口传递过来的实例初始化,便于与主窗口类进行通信{InitializeComponent();this.form1 = form1;}private void left_keyDown(object sender, KeyEventArgs e){left_textBox.Text = e.KeyCode + "";left = e.KeyCode;}private void right__keyDown(object sender, KeyEventArgs e) {right_textBox.T ext = e.KeyCode + "";right = e.KeyCode;}private void down_keyDown(object sender, KeyEventArgs e) {down_textBox.T ext = e.KeyCode + "";down = e.KeyCode;}private void change_keyDown(object sender, KeyEventArgs e) {change_textBox.T ext = e.KeyCode + "";change = e.KeyCode;}private void sp_keyDown(object sender, KeyEventArgs e){sp_textBox.Text = e.KeyCode + "";start_puase = e.KeyCode;}private void set_keyDown(object sender, KeyEventArgs e){set_textBox.Text = e.KeyCode + "";setkeys = e.KeyCode;}private void newgame_keyDown(object sender, KeyEventArgs e) {newgame_textBox.Text = e.KeyCode + "";newgame = e.KeyCode;}private void ok_Click(object sender, EventArgs e){try{grade = int.Parse(grade_textBox.T ext);if (grade < 1 || grade > 10){grade = 1;MessageBox.Show("级别只能是在1-10之间!");return;}}catch{MessageBox.Show("设置级别时,请输入1-10之间的数字!");grade = 1;return;}if (left == right || left == down || left == change || left == start_puase || left == setkeys || left == newgame ||right == down || right == change || right == start_puase || right == setkeys || right == newgame ||down == change || down == start_puase || down == setkeys || down == newgame ||change == start_puase || change == setkeys || change == newgame ||start_puase == setkeys || start_puase == newgame ||setkeys == newgame){MessageBox.Show("检测到有相同的键位设置!");return;}form1.left = left;form1.right = right;form1.down = down;form1.change = change;form1.start_puase = start_puase;form1.setkeys = setkeys;form1.grade = grade;form1.newgame = newgame;form1.left_label.Text = left_textBox.Text;form1.right_label.Text = right_textBox.Text;form1.down_label.T ext = down_textBox.Text;form1.change_label.Text = change_textBox.Text;form1.sp_label.Text = sp_textBox.Text;form1.set_label.Text = set_textBox.T ext;form1.grade_lagel.T ext = grade_textBox.Text;form1.newgame_label.Text = newgame_textBox.Text;form1.mytimer.Interval = 600 - 50 * grade;this.Close();}private void reset_Click(object sender, EventArgs e){left = Keys.Left;right = Keys.Right;down = Keys.Down;change = Keys.Space;start_puase = Keys.F2;setkeys = Keys.F3;grade = 1;newgame = Keys.F4;left_textBox.Text = "Left";right_textBox.Text = "Right";down_textBox.Text = "Down";change_textBox.Text = "Space";sp_textBox.Text = "F2";set_textBox.Text = "F3";grade_textBox.Text = "1";newgame_textBox.T ext = "F4";}private void Form2_Load(object sender, EventArgs e)//设置键位的窗口在加载时,显示当前的键位设置{left_textBox.Text = form1.left_label.Text;right_textBox.Text = form1.right_label.Text;down_textBox.Text = form1.down_label.Text;change_textBox.Text = form1.change_label.Text;sp_textBox.Text = form1.sp_label.Text;set_textBox.Text = form1.set_label.Text;grade_textBox.Text = form1.grade_lagel.Text;newgame_textBox.T ext = form1.newgame_label.Text;}private void cancel_button_Click(object sender, EventArgs e){this.Close();}}}public BlockType BlockType{get{//返回当前方块的类型return blockType;}set{//创建一个新的方块Brick[,] shape;//判断当前方块是否是“未定义”的if (blockType==BlockType.Undefined){//设置当前方块的类型blockType=value;//根据不同的方块类型对方块进行不同的初始化switch (blockType){case BlockType.Straight:case BlockType.Rectangle:case BlockType.CornerLeft:case BlockType.CornetRight:case BlockType.Cross:case BlockType.Zigzag1:case BlockType.Zigzag2:}}else{//当前方块是未定义的,抛出异常throw new ArgumentExceptiion(“方块未定义错误!”);}}}从上面的代码可以看出,程序最重要的部分集中在Switch/Case语句段,它的主要作用是根据当前指定的不同方块类型来相应的创建不同方块,对Straight类型的方块处理如下:this.Width=4;this.Height=1;this.Top=0;this.Left=3;//创建当前的方块主要作用是根据当前方块类型所占据的区域大小来创建一个二维矩阵,然后对这个矩阵中要填充的部分设置tiue值shape=new Brick[this.Width,this.Height];shape[0,0].Filled=true;shape[1,0].Filled=true;shape[2,0].Filled=true;shape[3,0].Filled=true;//分别对这些砖块设置图片shape[0,0].BrickImage=blockImage13;shape[1,0].BrickImage=blockImage14;shape[2,0].BrickImage=blockImage13;shape[3,0].BrickImage=blockImage14;//设置当前的Shape属性this.Shape=shape;break;其他方块创建方式基本和Stright类型的方块一样,唯一区别就在于他们所创建的二维矩阵大小以及需要填充的区域不同。