C# 实现俄罗斯方块
概述
俄罗斯方块(Tetris)是一款由俄罗斯人阿列克谢·帕基特诺夫发明的休闲游戏,帕基特诺夫爱玩拼图,从拼图游戏里得到灵感,设计出了俄罗斯方块。由于上手简单、老少皆宜,从而家喻户晓,风靡世界。本文简述如何通过C#来实现俄罗斯方块,仅供学习分享使用,如有不足之处,还请指正。
涉及知识点
- BackgroundWorker 在单独的线程上执行操作(主要执行比较耗时的操作)。
- Action .NetFramework自带的一个委托方法。
- TableLayoutPanel 表示一个面板,它可以在一个由行和列组成的网格中对其内容进行动态布局,本文主要用作俄罗斯方块的容器。
方块流程图
如下图所示,描述了俄罗斯方块的设计流程图
俄罗斯方块效果图
如下图所示:主要包括状态,得分,开始按钮,停止按钮,按键盘左右箭头移动等功能
核心代码
1. 定义方块的形状
如下所示:共7中形状
1 /// <summary> 2 /// 俄罗斯方块的形状 3 /// </summary> 4 public enum TetrisStyle 5 { 6 S = 0, 7 Z = 1, 8 L = 2, 9 J = 3, 10 I = 4, 11 O = 5, 12 T = 6 13 }
2. 定义移动的方向
如下所示:默认向下移动,同时可以左右移动
1 /// <summary> 2 /// 俄罗斯方块移动方向 3 /// </summary> 4 public enum TetrisDirection 5 { 6 UP = 0,//上,表示顺时针旋转 7 DOWN = 1,//下,表示向下移动 8 LEFT = 2,//左,表示往左移动 9 RIGHT = 3, //表示向右移动 10 DEFAULT=4 //默认动作 11 }
3. 俄罗斯方块元素
如下所示,每一种形状都由四个方块组成,根据不同形状设置不同的位置
1 /// <summary> 2 /// 俄罗斯方块元素 3 /// </summary> 4 public class TetrisElement 5 { 6 /// <summary> 7 /// 构造函数 8 /// </summary> 9 /// <param name="style"></param> 10 public TetrisElement(TetrisStyle style) { 11 this.style = style; 12 } 13 14 /// <summary> 15 /// 构造函数 16 /// </summary> 17 /// <param name="style">形状</param> 18 /// <param name="content">内容</param> 19 /// <param name="location">位置</param> 20 public TetrisElement(TetrisStyle style, Point[] content, Point location) 21 { 22 this.style = style; 23 this.content = content; 24 this.location = location; 25 } 26 27 /// <summary> 28 /// 元素字母类型 29 /// </summary> 30 public TetrisStyle style { get; set; } 31 32 /// <summary> 33 /// 内容 34 /// </summary> 35 public Point[] content { get; set; } 36 37 /// <summary> 38 /// 元素位置 39 /// </summary> 40 public Point location { get; set; } 41 44 /// <summary> 45 /// 位置改变 46 /// </summary> 47 /// <param name="x"></param> 48 /// <param name="y"></param> 49 public void move(int x, int y) 50 { 51 this.location = new Point(x, y); 52 } 53 54 public Point[] getContent(TetrisStyle style) 55 { 56 //内容由四个点组成,顺序:先上后下,先左后右 57 Point[] content = new Point[4]; 58 switch (style) 59 { 60 case TetrisStyle.I: 61 //I形状 62 content[0] = new Point(0, 0); 63 content[1] = new Point(0, 1); 64 content[2] = new Point(0, 2); 65 content[3] = new Point(0, 3); 66 break; 67 case TetrisStyle.J: 68 //J形状 69 content[0] = new Point(1, 0); 70 content[1] = new Point(1, 1); 71 content[2] = new Point(1, 2); 72 content[3] = new Point(0, 2); 73 break; 74 case TetrisStyle.L: 75 //L形状 76 content[0] = new Point(0, 0); 77 content[1] = new Point(0, 1); 78 content[2] = new Point(0, 2); 79 content[3] = new Point(1, 2); 80 break; 81 case TetrisStyle.O: 82 //O形状 83 content[0] = new Point(0, 0); 84 content[1] = new Point(1, 0); 85 content[2] = new Point(0, 1); 86 content[3] = new Point(1, 1); 87 break; 88 case TetrisStyle.S: 89 //S形状 90 content[0] = new Point(2, 0); 91 content[1] = new Point(1, 0); 92 content[2] = new Point(1, 1); 93 content[3] = new Point(0, 1); 94 break; 95 case TetrisStyle.T: 96 //T形状 97 content[0] = new Point(0, 0); 98 content[1] = new Point(1, 0); 99 content[2] = new Point(2, 0); 100 content[3] = new Point(1, 1); 101 break; 102 case TetrisStyle.Z: 103 //Z形状 104 content[0] = new Point(0, 0); 105 content[1] = new Point(1, 0); 106 content[2] = new Point(1, 1); 107 content[3] = new Point(2, 1); 108 break; 109 default: 110 //默认I 111 content[0] = new Point(0, 0); 112 content[1] = new Point(0, 1); 113 content[2] = new Point(0, 2); 114 content[3] = new Point(0, 3); 115 break; 116 } 117 return content; 118 } 119 }
4. 容器类
如下所示:容器类主要是移动方块元素,并更新页面上的值
1 /// <summary> 2 /// 俄罗斯方块容器 3 /// </summary> 4 public class TetrisContainer 5 { 6 private int[,] tetris = new int[10, 20];//定义二维数组,表示坐标信息,默认值为0 7 8 public Action<Point,Point[],TetrisDirection> onPartialChanged;//局部变更事件 9 10 public Action<int[,]> onFullChanged;//元素全变更事件,即有整行被清除事件 11 12 public Action onCompleted; //结束事件 13 14 public int scorce = 0; 15 16 /// <summary> 17 /// 状态发生改变 18 /// </summary> 19 /// <param name="element"></param> 20 /// <param name="direction"></param> 21 /// <returns></returns> 22 public TetrisElement change(TetrisElement element, TetrisDirection direction) 23 { 24 TetrisElement tmp=null; 25 //判断不同的方向 26 switch (direction) { 27 case TetrisDirection.DEFAULT: 28 //如果可以向下移动 29 if (checkDefault(element)) 30 { 31 //向下移动一个元素 32 element.move(element.location.X, element.location.Y + 1); 33 tmp = element; 34 } 35 else { 36 //如果不可以向下移动,则更新容器 37 updateTetris(element); 38 tmp = null; 39 } 40 41 break; 42 case TetrisDirection.DOWN: 43 break; 44 case TetrisDirection.UP: 45 break; 46 case TetrisDirection.LEFT: 47 if (checkLeft(element)){ 48 //判断是否可以向左移动 49 //向下移动一个元素 50 element.move(element.location.X-1, element.location.Y); 51 tmp = element; 52 } 53 break; 54 case TetrisDirection.RIGHT: 55 if (checkRight(element)) 56 { 57 //判断是否可以右左移动 58 //向下移动一个元素 59 element.move(element.location.X+1, element.location.Y); 60 tmp = element; 61 } 62 break; 63 } 64 65 //局部变更 66 if (onPartialChanged != null) 67 { 68 Point location = element.location; 69 Point[] content = new Point[4]; 70 element.content.CopyTo(content, 0); 71 72 for (int i = 0; i < content.Length; i++) 73 { 74 content[i].X = location.X + content[i].X; 75 content[i].Y = location.Y + content[i].Y; 76 } 77 onPartialChanged(location,content,direction); 78 } 79 80 //判断游戏是否结束 81 if (onCompleted != null) { 82 if (checkComplete()) { 83 onCompleted(); 84 } 85 } 86 87 //全部变更 88 if (onFullChanged != null) 89 { 90 //判断是是否有权为1的行,如果有则消掉 91 int[] rows = checkAllTetris(); 92 if (rows.Length>0) 93 { 94 updateAllTetris(rows);//消掉行 95 onFullChanged(tetris); 96 } 97 } 98 99 return tmp; 100 } 101 102 /// <summary> 103 /// 更新tetris 104 /// </summary> 105 /// <param name="element"></param> 106 private void updateTetris(TetrisElement element) 107 { 108 Point location = element.location; 109 Point[] content = element.content; 110 int minX = element.getMinX(element.style); 111 int maxX = element.getMaxX(element.style); 112 int minY = element.getMinY(element.style); 113 int maxY = element.getMaxY(element.style); 114 foreach (Point p in content) 115 { 116 if (location.Y + p.Y < 20 && location.Y + p.Y >= 0 && location.X + p.X >= 0 && location.X + p.X < 10) 117 { 118 this.tetris[location.X + p.X, location.Y + p.Y] = 1; 119 } 120 } 121 } 122 123 /// <summary> 124 /// 检查全部列 125 /// </summary> 126 private int[] checkAllTetris() 127 { 128 List<int> lst = new List<int>(); 129 //20行 130 for (int y = 0; y < 20; y++) 131 { 132 int col = 0; 133 //10列 134 for (int x = 0; x < 10; x++) 135 { 136 if (tetris[x, y] == 0) 137 { 138 break; 139 } 140 else 141 { 142 col += 1; 143 } 144 } 145 if (col == 10) 146 { 147 col = 0; 148 lst.Add(y); 149 } 150 } 151 return lst.ToArray(); 152 } 153 154 /// <summary> 155 /// 更新 156 /// </summary> 157 private void updateAllTetris(int[] rows) { 158 foreach (int row in rows) { 159 //当前行清掉 160 for (int x = 0; x < 10; x++) { 161 tetris[x, row] = 0; 162 } 163 //row行之上的往下移动一行 164 for (int y = row-1; y >=0; y--) { 165 for (int x = 0; x < 10; x++) { 166 if (tetris[x, y] == 1) { 167 tetris[x, y + 1] = 1; 168 tetris[x, y] = 0; 169 } 170 } 171 } 172 } 173 } 174 175 /// <summary> 176 /// 判断游戏是否结束 177 /// </summary> 178 /// <returns></returns> 179 private bool checkComplete() { 180 bool isComplete = false; 181 for (int i = 0; i < 10; i++) { 182 if (tetris[i, 0] == 1) { 183 isComplete = true; 184 break; 185 } 186 } 187 return isComplete; 188 } 189 190 /// <summary> 191 /// 更新得分 192 /// </summary> 193 /// <param name="s"></param> 194 public void updateScore(int s) { 195 this.scorce = this.scorce + s; 196 } 197 198 /// <summary> 199 /// 重置信息 200 /// </summary> 201 public void Reset() { 202 this.tetris = new int[10, 20]; 203 this.scorce = 0; 204 } 205 }
5. 随机生成方块元素和起始位置
1 /// <summary> 2 /// 静态函数,生成Tetris元素对象 3 /// </summary> 4 /// <returns></returns> 5 public static TetrisElement generate() 6 { 7 Random r = new Random(0); 8 //随机生成形状 9 int tstyle = getRandom(); 10 tstyle = tstyle % 7; 11 TetrisStyle style = TetrisStyle.I; 12 style = (TetrisStyle)Enum.Parse(typeof(TetrisStyle), tstyle.ToString()); 13 //随机生成起始坐标 14 int x = getRandom(); 15 x = x % 10; 16 int y = 0; 17 //根据形状生成位置信息 18 TetrisElement element = new TetrisElement(style); 19 //内容由四个点组成,顺序:先上后下,先左后右 20 Point[] content = element.getContent(style); 21 //获取最小坐标和最大坐标,防止越界 22 int minX = element.getMinX(style); 23 int minY = element.getMinY(style); 24 int maxX = element.getMaxX(style); 25 int maxY = element.getMaxY(style); 26 //修正起始坐标 27 x = (x <= minX) ? minX : x; 28 x = (x >= maxX) ? maxX : x; 29 y = minY; 30 Point location = new Point(x, y); 31 element.location = location; 32 element.content = content; 33 return element; 34 }
备注
源码下载链接
闲下来的时候,放一段柔情音乐,翻阅几页好书,然后睡个懒觉,快哉。
作者:老码识途
出处:http://www.cnblogs.com/hsiang/
本文版权归作者和博客园共有,写文不易,支持原创,欢迎转载【点赞】,转载请保留此段声明,且在文章页面明显位置给出原文连接,谢谢。
关注个人公众号,定时同步更新技术及职场文章