C# 實現俄羅斯方塊

来源:https://www.cnblogs.com/hsiang/archive/2019/08/26/11415810.html
-Advertisement-
Play Games

俄羅斯方塊(Tetris)是一款由俄羅斯人阿列克謝·帕基特諾夫發明的休閑游戲,帕基特諾夫愛玩拼圖,從拼圖游戲里得到靈感,設計出了俄羅斯方塊。由於上手簡單、老少皆宜,從而家喻戶曉,風靡世界。本文簡述如何通過C#來實現俄羅斯方塊,僅供學習分享使用,如有不足之處,還請指正。 ...


概述

俄羅斯方塊(Tetris)是一款由俄羅斯人阿列克謝·帕基特諾夫發明的休閑游戲,帕基特諾夫愛玩拼圖,從拼圖游戲里得到靈感,設計出了俄羅斯方塊。由於上手簡單、老少皆宜,從而家喻戶曉,風靡世界。本文簡述如何通過C#來實現俄羅斯方塊,僅供學習分享使用,如有不足之處,還請指正。

涉及知識點

  1. BackgroundWorker 在單獨的線程上執行操作(主要執行比較耗時的操作)。
  2. Action .NetFramework自帶的一個委托方法。
  3. 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         }

備註

源碼下載鏈接

閑下來的時候,放一段柔情音樂,翻閱幾頁好書,然後睡個懶覺,快哉。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JavaFX的對話框主要分為提示對話框和文件對話框兩類,其中提示對話框又分作消息對話框、警告對話框、錯誤對話框、確認對話框四種。這四種對話框都使用Alert控制項表達,並通過對話框類型加以區分,例如AlertType.INFORMATION表示消息對話框,AlertType.WARNIN表示警告對話框 ...
  • 攔截器配置類使用繼承寫法導致jackson的全局配置失效,採用配置類實現WebMvcConfigurer介面解決問題 ...
  • Scala解釋器的使用 ·REPL:Read(取值)-> Evaluation(求值)-> Print(列印)-> Loop(迴圈)。scala解釋器也被稱為REPL,會快速編譯scala代碼為位元組碼,然後交給JVM來執行。 ·計算表達式:在scala>命令行內,鍵入scala代碼,解釋器會直接返回 ...
  • Django之視圖,內容包括 視圖,HttpReqeust對象,ajax請求。其中,視圖 包括 視圖函數的使用,url匹配的過程,錯誤視圖處理,捕捉url參數,普通登錄案例;HttpReqeust對象 包括 屬性,QueryDict對象;ajax請求 包括 python和ajax結合使用,ajax同... ...
  • 從Java1.4開始,為了替代Java IO和網路相關的API,提高程式的運行速度,Java提供了新的IO操作非阻塞的API即Java NIO。NIO中有三大核心組件:Buffer(緩衝區),Channel(通道),Selector(選擇器)。NIO基於Channel(通道)和Buffer(緩衝區) ...
  • 近日,在閱讀《Fluent Python》的第2.9.2節時,有一個關於記憶體視圖的例子,當時看的一知半解,後來查了一些資料,現在總結一下,以備後續查詢; 示例覆述 添加了一些額外的代碼,便於更好理解 我的理解和疑惑 是一個 類型的數組; 是使用上述數組創建的一個 "memoryview" ,即 記憶體 ...
  • 12.3 APP 12.31 創建APP 一個Django項目可以分為很多個APP,用來隔離不同功能模塊的代碼 用命令行創建一個APP: 創建好APP,記得告訴Django,app的名字,在settings.py中添加: 12.32 Django中的ORM Django項目使用MySQL資料庫 1. ...
  • 本文將通過實際的例子來演示如何在ASP.NET Core中應用JWT進行用戶認證以及Token的刷新方案(ASP.NET Core 系列目錄) 一、什麼是JWT? JWT(json web token)基於開放標準(RFC 7519),是一種無狀態的分散式的身份驗證方式,主要用於在網路應用環境間安全 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...