前言
上一篇《小菜学习Winform(一)贪吃蛇》中实现了简单版的贪吃蛇,在文章末也提到需要优化的地方,比如使用oo、得分模式、速度加快模式和减少界面重绘。因为是优化篇,实现方式上一篇有,这一篇大家看看代码就行。当然小菜不是搞游戏开发的,程序可能有很多问题,这里点到即止,有时间小菜会加强学习。
实现
说到oo可能一说一大堆,这里面小菜只是简单的把贪吃蛇抽象出来,先来说蛇,具有的属性和行为,属性比如蛇的长度、蛇的宽度、蛇的行动方向等;行为比如是否吃到食物、是否撞墙等,那我们可以抽象一个蛇的类,这样实现:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Drawing; 6 7 namespace SnakeGame 8 { 9 public class Snake 10 { 11 public int SnkWidth = 16;//蛇的每个点的宽度 12 public const int SnakeMaxLength = 500;//最大长度 13 public Point[] SnkLct = new Point[SnakeMaxLength]; 14 public int SnkLen;//蛇的长度 15 public byte SnkDrt = 2;//方向 1上 2右 3下 4左 16 17 /// <summary> 18 /// 属性初始化 19 /// </summary> 20 public void SnakeInit() 21 { 22 int start = 0; 23 this.SnkLen = 5; 24 for (int i = 5; i >=1; i--) 25 { 26 this.SnkLct[i].X = start; 27 this.SnkLct[i].Y = 32; 28 start += this.SnkWidth; 29 } 30 this.SnkDrt = 2;//初始方向 31 } 32 /// <summary> 33 /// 判断是否撞到自己 34 /// </summary> 35 /// <returns></returns> 36 public bool CheckSnakeHeadInSnakeBody() 37 { 38 return this.CheckInSnakeBody(this.SnkLct[1].X, this.SnkLct[1].Y, 2); 39 } 40 /// <summary> 41 /// 检查输入的坐标是否在蛇的身上 42 /// </summary> 43 /// <param name="x"></param> 44 /// <param name="y"></param> 45 /// <param name="snkHead"></param> 46 /// <returns></returns> 47 public bool CheckInSnakeBody(int x, int y, int snkHead) 48 { 49 for (int i =snkHead; i <=this.SnkLen; i++) 50 { 51 if(x==this.SnkLct[i].X&&y==this.SnkLct[i].Y) 52 { 53 return true; 54 } 55 } 56 return false; 57 } 58 /// <summary> 59 /// 判断是否撞墙 60 /// </summary> 61 /// <returns></returns> 62 public bool CheckSnakeBodyInFrm() 63 { 64 if (this.SnkLct[1].X >= 560 || this.SnkLct[1].Y >= 512 || this.SnkLct[1].X < 0 || this.SnkLct[1].Y < 32) 65 return true; 66 else 67 return false; 68 } 69 /// <summary> 70 /// 判断是否吃到食物 71 /// </summary> 72 /// <param name="FoodLct"></param> 73 /// <returns></returns> 74 public bool EatedFoot(Point FoodLct) 75 { 76 if (SnkLct[1].X == FoodLct.X && SnkLct[1].Y == FoodLct.Y) 77 { 78 if (SnkLen < SnakeMaxLength) 79 { 80 SnkLen++; 81 SnkLct[SnkLen].X = SnkLct[SnkLen - 1].X; 82 SnkLct[SnkLen].Y = SnkLct[SnkLen - 1].Y; 83 } 84 return true; 85 } 86 else 87 return false; 88 } 89 /// <summary> 90 /// 设置Point数组坐标 91 /// </summary> 92 /// <param name="drc"></param> 93 public void Forward(int drc) 94 { 95 Point tmp = new Point(); 96 tmp.X = SnkLct[1].X; 97 tmp.Y = SnkLct[1].Y; 98 for (int i = SnkLen; i > 1; i--) 99 {//蛇头动,把蛇头的坐标逐个后移(蛇身往蛇头方向位移) 100 SnkLct[i].X = SnkLct[i - 1].X; 101 SnkLct[i].Y = SnkLct[i - 1].Y; 102 } 103 104 switch (drc) 105 {//根据设置的方向,计算蛇头的坐标 106 case 1: 107 SnkLct[1].X = tmp.X; 108 SnkLct[1].Y = tmp.Y - SnkWidth; 109 break; //上 110 case 2: 111 SnkLct[1].X = tmp.X + SnkWidth; 112 SnkLct[1].Y = tmp.Y; 113 break; //右 114 case 3: 115 SnkLct[1].X = tmp.X; 116 SnkLct[1].Y = tmp.Y + SnkWidth; 117 break; //下 118 case 4: 119 SnkLct[1].X = tmp.X - SnkWidth; 120 SnkLct[1].Y = tmp.Y; 121 break; //左 122 } 123 } 124 } 125 }
抽象完蛇,那我们再说下贪吃蛇游戏,贪吃蛇这个游戏具有的元素有:蛇、食物、得分等,那我们这样来表现:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Drawing; 6 7 namespace SnakeGame 8 { 9 public class Game 10 { 11 public Point FoodLct = new Point();//缓存食物的坐标 12 public int score = 0;//得分 13 public bool pause;//暂停 14 public Snake snake = new Snake(); 15 /// <summary> 16 /// 游戏初始化 17 /// </summary> 18 public void GameInit() 19 { 20 this.pause = false; 21 this.score = 0; 22 this.snake.SnakeInit();//初始化 23 } 24 /// <summary> 25 /// 随机显示食物 26 /// </summary> 27 public void ShowFood() 28 { 29 Random rmd = new Random(); 30 int x, y; 31 x = rmd.Next(0, 35) * this.snake.SnkWidth; 32 y = 32 + rmd.Next(0, 30) * this.snake.SnkWidth; 33 while (this.snake.CheckInSnakeBody(x,y,1)) 34 { 35 x = rmd.Next(0,32)*this.snake.SnkWidth; 36 y = 32 + rmd.Next(0, 30) * this.snake.SnkWidth; 37 } 38 FoodLct.X = x; 39 FoodLct.Y = y; 40 } 41 /// <summary> 42 /// 分数 43 /// </summary> 44 /// <returns></returns> 45 public bool AddScore() 46 { 47 if(this.snake.EatedFoot(FoodLct)) 48 { 49 this.score += 10; 50 return true; 51 } 52 return false; 53 } 54 /// <summary> 55 /// 判断游戏是否结束 56 /// </summary> 57 /// <returns></returns> 58 public bool Dead() 59 { 60 return this.snake.CheckSnakeBodyInFrm()||this.snake.CheckSnakeHeadInSnakeBody(); 61 } 62 } 63 }
其实使用oo还有很多地方需要细化,这里不是讲oo,所以只是简单的带过下。
然后是得分模式,就是蛇吃到一个食物就加10分,这个在AddScore()方法中有实现:
1 /// <summary> 2 /// 分数 3 /// </summary> 4 /// <returns></returns> 5 public bool AddScore() 6 { 7 if(this.snake.EatedFoot(FoodLct)) 8 { 9 this.score += 10; 10 return true; 11 } 12 return false; 13 }
再者是加速模式,蛇吃到一定量的时候后,难度会增加,爬行的速度会更快,这个可以在timer事件里面根据得分设置timer的执行间隔时间:
1 if (this.game.score < 100) 2 { 3 this.timMove.Interval = 150; 4 } 5 else if (this.game.score < 150) 6 { 7 this.timMove.Interval = 100; 8 } 9 else 10 { 11 this.timMove.Interval = 50; 12 }
最后是减少界面重绘这个问题,因为小菜不是搞游戏开发的,那我只能减少界面的重绘数量,这边我是这样实现的,食物用label控件,显示用定位来体现,蛇的动作还是用GDI+来绘制。
主程序代码:

运行截图:
程序下载:贪吃蛇2