這次在工作之餘用C#寫了一個簡單的貪吃蛇程式,一般的都是WinForm形式的,這次弄了個控制台版本的,因為C# Console全部都是輸入輸出資料流,要在CMD視窗做這種有前台UI介面的程式應該是不適合的,但是想起之前的DOS版本的系統,我覺得應該是可以做到了,所以就花了幾個晚上弄了這麼一個東西,先上個截圖:
介面比較簡單,一個CMD視窗,其他的就是由字元構成的各種形狀,做這種Console的貪吃蛇有以下幾個需要注意的地方:
1.理解Console這個東西,它是一個標準的I/O輸入輸出資料流;
2.控制台有2個術語 :螢幕緩衝區和控制台視窗,我們一般擷取的大小是控制台大小而不是緩衝區大小,這個可以看看MSDN;
3.關於貪吃蛇本身的有一下幾個問題:如何讓蛇移動。如何判斷蛇吃了食物。如何判斷蛇碰了邊框。等等,下面就詳細的說說。
首先,我想說,不管做什麼程式或者軟體,設計資料結構非常重要,資料結構設計好了,問題也就變的簡單了,真的如此。
按照物件導向的要求,設計Snake類,由於Console程式中全部是以字元形式的,所以畫Snake必須要有點,點構成線,首先設計Point類如下:
1.Point.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Snake{ /// <summary> /// Class Point /// </summary> public class Point : IComparable<Point> { private int x = 0; /// <summary> /// Gets or sets the x /// </summary> public int X { get { return x; } set { x = value; } } private int y = 0; /// <summary> /// Gets or sets the y /// </summary> public int Y { get { return y; } set { y = value; } } /// <summary> /// Compare the two point /// </summary> /// <param name="other">Other point</param> /// <returns>-1 if x < other.X && y < other.Y , 0 if x == other.X && y == other.Y , otherwise 1</returns> public int CompareTo(Point other) { if (x < other.X && y < other.Y) { return -1; } else if (x == other.X && y == other.Y) { return 0; } else { return 1; } } }}
這裡需要說一下這個point繼承了IComparable介面,是為了下面的比較和判斷蛇是否觸碰了其他物體或者自己本身,好了下面就可以設計Snake.cs類了。
2.Snake.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Snake{ /// <summary> /// Class Snake /// </summary> public class Snake { private Point head = null; /// <summary> /// Gets or sets the snake's head /// </summary> public Point Head { get { return head; } set { head = value; } } private Point[] body = null; /// <summary> /// Gets or sets the snake's body /// </summary> public Point[] Body { get { return body; } set { body = value; } } private Point tail = null; /// <summary> /// Gets or sets the snake's tail /// </summary> public Point Tail { get { return tail; } set { tail = value; } } }}
為什麼要有蛇頭、蛇身和蛇尾呢。簡單的來說,按照物件導向的話你會想到的,複雜的就要看代碼的邏輯處理了,這裡不多說了,自己體會。接下來就是食物類Food.cs。
3.Food.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Snake{ /// <summary> /// Class Food /// </summary> public class Food { private Point position = null; /// <summary> /// Gets or sets the food's position /// </summary> public Point Position { get { return position; } set { position = value; } } }}
食物就是一個位置上的一個點就足夠了,沒有其他屬性。這樣基本的資料結構就構造好了,剩下的就是怎麼將這些結構聯絡起來構造這個遊戲的邏輯了,首先,進入遊戲,肯定是要按方向鍵來控制蛇的移動的,Console裡面就有這樣判斷鍵盤輸入的方法和屬性,如下:
ConsoleKey key = Console.ReadKey(true).Key;
ConsoleKey這個類裡面就有屬性判斷是屬於按下了鍵盤上的哪個鍵,這個可以看MSDN。ConsoleKey這個搞定了之後再來看看如何畫出遊戲介面,這個就不是太複雜,Console類裡面有這樣的一個方法如下:
Console.SetCursorPosition(i, j);
設定游標所在的位置,這樣就可以再這個為寫入字元了,如果沒有了這個方法,那我們就需要每次蛇移動都要重新畫整個介面,這樣會導致一個問題:控制台視窗螢幕會一直閃,因為我們是繪製整個控制台,有了這個方法,我們只需要重新整理局部地區就可以了,比如蛇移動我們只需要更新蛇的位置就可以了。 最後說一下蛇碰到食物和邊框之後怎麼處理,當蛇碰到食物,這時候我們就可以將食物作為當前的蛇頭,之前的蛇頭作為蛇的身體的第一個節點,這樣蛇身長度加1,蛇尾還是原來的蛇尾;當蛇頭碰到邊框遊戲就結束。還有,當蛇移動且沒碰到食物也沒碰到邊框的時候,這時蛇頭就變成蛇當前移動到的點,蛇身的第一個點就變成了之前的蛇頭的那個點,以此類推,蛇就像前移動了一個位置。 好了,大概就這麼多了,最後就是畫介面,代碼如下:
/// <summary> /// Draw console ui /// </summary> private void DrawConsoleUI() { Console.Clear(); Point tempPoint = null; Console.SetCursorPosition(0, 0); Console.WriteLine("Name : " + name); Console.SetCursorPosition(0, 1); Console.WriteLine("Score : " + score); Console.SetCursorPosition(0, 2); Console.WriteLine("Press R to restart game after you losed the game, press E to exit game"); for (int i = 0; i < uiWidth; i += 2) { Console.SetCursorPosition(i, 3); Console.Write("."); } for (int i = 2; i < uiHeight; i++) { for (int j = 0; j < uiWidth; j++) { tempPoint = new Point(); tempPoint.X = j; tempPoint.Y = i; if (tempPoint.CompareTo(currentFood.Position) == 0) { Console.SetCursorPosition(j, i); Console.Write(GetFoodElement()); } else if (tempPoint.CompareTo(currentSnake.Head) == 0) { Console.SetCursorPosition(j, i); Console.Write(GetHeadElement()); } else if (IsSnakeBodyCoverPoint(currentSnake.Body, tempPoint)) { Console.SetCursorPosition(j, i); Console.Write(GetBodyElement()); } else if (tempPoint.CompareTo(currentSnake.Tail) == 0) { Console.SetCursorPosition(j, i); Console.Write(GetTailElement()); } } } }
其中一些方法看到名字就知道什麼意思了,這個實現起來比較簡單,這裡就不上代碼了。。。
源碼下載