俄羅斯方塊(Tetris)是一款由俄羅斯人阿列克謝·帕基特諾夫發明的休閑游戲,帕基特諾夫愛玩拼圖,從拼圖游戲里得到靈感,設計出了俄羅斯方塊。由於上手簡單、老少皆宜,從而家喻戶曉,風靡世界。本文簡述如何通過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 }
備註
源碼下載鏈接
閑下來的時候,放一段柔情音樂,翻閱幾頁好書,然後睡個懶覺,快哉。