贪吃蛇C#实现
貪吃蛇相于其它小游戲,算是簡單的一個。沒學GDI或WPF啥的,也不想學,恰好在C#編程課中學過WinForm,所以就用WinForm做了個簡單的貪吃蛇。
完整代碼在此:資源鏈接
游戲界面如下:
灰色邊框為一圈灰色的Button,設置Enable屬性為false,避免鼠標對它有影響(顏色變化)。
中間的空白地圖為24*24個PictureBox,即pictureBox0~pictureBox575。
游戲地圖可用一個圖類Graph表示,而單個位置又可通過一個點類Point表示。有了Graph類和Point類,則可創建一個Snake類,通過Graph類和Point類的支持,來模擬蛇的各種操作。
Point類的建立可方便對單一網格或圖中對應元素的操作,如可通過Point對象p來操作橫坐標為p.X,縱坐標為p.Y的PictureBox背景色,達到某些效果。也可通過p.X和p.Y獲取對應圖的元素信息。
//代碼只用于說明思路,沒有完整實現,具體實現詳見資源鏈接 public class Point {int x;int y;//判斷點是否合法,只有獲取valid值時才進行設置bool valid; //無參構造函數public Point() {}//有參構造函數public Point(int posX,int posY) {}//復制構造函數public Point(Point p) {}//判斷兩點坐標是否相同public bool equal(Point p) {}//設置點信息,通過坐標 (posX, posY)public void setPoint(int posX,int posY) {}//設置點信息,通過索引 indexpublic void setPoint(int index) {}//x的get和setpublic int X {}//y的get和setpublic int Y {}//valid的get,不能set,因為它標志著點是否合法public bool Valid {} }Graph類記錄游戲網格背后的數據,蛇的身體就是一些點組成,而這些點就反應在圖中連續(上下左右)元素值不為零,此外,還記錄了游戲中蘋果的位置(通過與蛇身體的值不同的值,來標記蘋果,如蛇身體標對應元素值為1,蘋果為2,圖中無任何內容的地方為0)。
public class Graph {int[,] graph; //圖對象//每個元素可取值0、1、2,//0: 此處為 空//1: 此處有 蛇身體//2: 此處有 蘋果//構造函數public Graph() {}//重置圖信息public void resetGraph() {}//設置值,通過點和值 (p, value)public void setValue(Point p,int value) {}//設置值,通過坐標和值public void setValue(int x, int y, int value) {}//獲取值,通過點public int getValue(Point p) {}//獲取值,通過坐標public int getValue(int x,int y) {} }Snake類通過Graph類和Point類的協助,進行蛇的設定、模擬蛇的移動以及吃掉蘋果等操作。蛇在移動的過程中,需要判斷它正前方的點的信息,如果是蘋果,則吃掉;如果是墻或自己的身體,則掛掉;如果是空,則更新蛇的身體,整體向前走一步。
class Snake {Point[] snake; //snake數組,0下標為snake尾,大下標為snake頭int count; //snake身體長度//構造函數public Snake() {snake = new Point[576]; //蛇身長最多為游戲界面的網格個數count = 0;}//重置snake信息public void resetSnake() {count = 0; //只需重置蛇身體長度信息,不必刪除各個點信息}//獲取snake頭public Point getHead() {Point p = new Point(snake[count - 1]);return p;}//添加點到末尾,即吃了applepublic void append(Point p) {if (!p.Valid) throw new Exception("點不合法");snake[count++] = new Point(p.X, p.Y);}//移動,pos可取 0: 上 1: 下 2: 左 3: 右//返回值 0: 掛了 1: 已移動 2: 吃了apple 3: 反方向移動public int move(Graph graph,int pos) {int x = snake[count - 1].X; //snake頭橫坐標int y = snake[count - 1].Y; //snake頭縱坐標Point p; //記錄蛇即將要走的點switch (pos) { //根據蛇即將要走的方向,獲取p點case 0:p = new Point(x - 1, y); break;case 1:p = new Point(x + 1, y); break;case 2:p = new Point(x, y - 1); break;case 3:p = new Point(x, y + 1); break;default:throw new Exception("方向信息錯誤");}//撞墻if (!p.Valid) return 0;//沒撞墻,但反方向走//必須先判斷是否反方向走,再判斷是否撞上了身體。因為反方向走時,下一個點為蛇身的//第二個點,會被誤判為撞上了身體if (p.equal(snake[count - 2])) return 3;//沒撞墻,也沒反方向走,但是撞上了身體(非snake第二個點)if (graph.getValue(p.X,p.Y) ==1) return 0;//有apple,吃掉if (graph.getValue(p.X,p.Y) == 2) { //此處有applesnake[count++] = new Point(p.X, p.Y); //將apple加入到snake頭graph.setValue(p, 1); //將apple添加到圖中return 2;}//可移動,更新snake信息及圖信息else {//snake尾在圖上消失graph.setValue(snake[0], 0);//身體往“前”移for (int i = 0; i < count - 1; i++) {snake[i].X = snake[i + 1].X;snake[i].Y = snake[i + 1].Y;}//snake頭更新snake[count - 1].X = p.X;snake[count - 1].Y = p.Y;graph.setValue(p, 1); //將snake頭添加到圖中return 1;}} }游戲主界面GameForm類:
public partial class GameForm : Form {Random random;Graph graph; //圖對象Snake snake; //snake對象int score; //游戲分數int record; //游戲最高分bool inGame; //游戲中bool canPress; //每次計時間隔內第一次按鍵有效,避免玩家頻繁操作bool isGameOver; //游戲結束標記int pos; //記錄snake當前前進方向,0: 上 1: 下 2: 左 3: 右Point apple; //apple點public GameForm() {//值初始化}//snake重生public void snakeBorn() {//設置默認的snake出生的三點//將三點添加到snake身體中//將三點添加到圖中}//開始游戲public void start() {//重置圖信息//重置snake信息//snake重生//顯示snake//刷新apple//重置分數//更新記錄標簽//更新分數標簽//開始計時//進入游戲狀態inGame=true//可按鍵canPress = true;//更新游戲結束標記isGameOver=false//初始化方向}//獲取新的apple并添加到圖中public void getNewApple() {}//游戲結束處理public void gameOver() {}//重置PictureBox背景色為白色public void resetPBBackColor() {}//根據圖信息刷新PictureBox的背景色,只刷新snake身體,即graph中元素值為1public void showSnake() {//重置PictureBox背景色//顯示蛇身體//顯示蛇頭,設置顏色為DodgerBlue}//將apple顯示在圖中public void showApple() {}//根據坐標獲取PictureBoxpublic PictureBox getPictureBox(int x, int y) {}//計時器事件觸發private void timer1_Tick(object sender, EventArgs e) {//假如游戲結束,進入結束處理//游戲未結束,且玩家按鍵,則根據玩家按鍵來更新PictureBox背景色//玩家沒按鍵或按鍵失效,則進入相應操作//顯示分數//刷新為可按鍵狀態}//點擊記錄按鈕private void recordBtn_Click(object sender, EventArgs e) {//游戲暫停//顯示游戲紀錄窗體//可能玩家已經重置記錄,需要更新recordLabel//點擊關閉后,游戲繼續}//點擊開始按鈕private void startBtn_Click(object sender, EventArgs e) {//每次點擊開始,游戲暫停//假如在游戲中,顯示確認放棄當前游戲的窗口}//按鍵時的操作,鍵盤按鍵事件的輔助函數public void keyPress(int n) {}//鍵盤按鍵事件,因為上下左右鍵會被窗體提前捕獲,因此用WASD來操控方向private void GameForm_KeyDown(object sender, KeyEventArgs e) {if (inGame && canPress) {canPress = false; //計時器觸發之前都按鍵無效,避免玩家頻繁操作switch (e.KeyCode) {case Keys.W: keyPress(0); break;case Keys.S: keyPress(1); break;case Keys.A: keyPress(2); break;case Keys.D: keyPress(3); break;default: break;}}} }需要注意的是,為了游戲的健壯性,需要判斷各種玩家的可能操作,如正在游戲中,玩家點擊了開始按鈕,為避免玩家的誤點擊,可提示玩家是否放棄當前游戲。同時,需要停止計時器,以避免在此期間蛇因為計時器導致的自動向前移動而掛掉。點擊記錄標簽頁是一樣。
在設定點和圖類的時候,需要自身進行輸入判斷,如傳入的點是否有效,值是否有效等,在輸入錯誤時執行某些預定操作。僅依賴外界提供正確信息,往往導致出錯后調試困難。
當游戲背景中網格很多時,通過for循環找到某個控件會導致性能低下,也可以通過switch-case進行定位,不過代碼量也會隨之增長。
總結
- 上一篇: 瑞星:病毒伪装“交行安全控件”盗取用户敏
- 下一篇: 棠儿治家