时间:2022-06-26 10:34:45 | 栏目:.NET代码 | 点击:次
本文实例为大家分享了C#实现贪吃蛇小游戏的具体代码,供大家参考,具体内容如下
之前一直想写一个贪吃蛇小游戏,上个周末终于有时间做了一个,现在和大家分享。
界面比较简单,一个按钮和一个积分器组成。
基本逻辑:每五个食物出现一次大食物,小食物1分,大食物5分。
控制:撞墙和缠绕时,游戏暂停。
详细介绍下代码
一、代码分详解
1.界面是在pc上,所以移动以上下左右键来控制移动,为了及时响应,所以增加如下代码
protected override bool ProcessDialogKey(Keys keyData) { //遇到上下左右键 就自定义处理 if (keyData == Keys.Up || keyData == Keys.Down || keyData == Keys.Left || keyData == Keys.Right) return false; else return base.ProcessDialogKey(keyData); }
2.做个枚举,来标记某个时间状态下蛇的移动方向,配合键盘按键
/// <summary> /// 方向枚举 /// </summary> public enum SnakeDirection { Left, Right, Down, Up } /// <summary> /// 键盘和鼠标按下时 处理 但必须有控件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void SnakeForm_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Up) { Snake.Sd = SnakeDirection.Up; } else if (e.KeyCode == Keys.Down) { Snake.Sd = SnakeDirection.Down; } else if (e.KeyCode == Keys.Left) { Snake.Sd = SnakeDirection.Left; } else if (e.KeyCode == Keys.Right) { Snake.Sd = SnakeDirection.Right; } }
3.声明一个蛇的类,存储所有蛇的相关信息,如所有蛇身体的节点位置
public class Snake
主要变量如下
public static SnakeDirection Sd;//蛇进行的方向 上下左右四个方向 public int Location_X;//0为x,1为y 蛇的初始位置 public int Location_Y; public static List<Snake> Snakebody = new List<Snake>();//蛇身子的具体位置,每次位移都刷新一次 整个蛇身子的所有位置 最后一位是蛇头 public static int width = 10, height = 10; public static int CurScore = 1;//当前食物的分数 public static int AllScore = -1;//总分数 public static int MaxScore = 5;//大分出现频次
4.声明一个食物的类,同时增加一个方法,随机在界面上的一个位置显示食物
public class Food { public int Food_Location_X; public int Food_Location_Y; public Food() { Random x = new Random(); Random y = new Random(); Food_Location_X = x.Next(0, 49) * 10;//随机点出现食物 Food_Location_Y = y.Next(0, 49) * 10;//随机点出现食物 } }
5.点击开始按钮,触发开始相关方法,主要是启动刷新的时间器,初始化食物位置,初始化积分等
public void refreshScore() { Snake.AllScore = Snake.AllScore+ Snake.CurScore; this.SnakeScore.Text = Convert.ToString(Snake.AllScore); } private int FoodX = 0;//食物X轴 private int FoodY = 0;//食物Y轴 private void BeginBtn_Click(object sender, EventArgs e) { Snake.Start();//创建蛇 CreateNewFood(); StartTimer.Start(); StartTimer.Interval = 100;//毫秒刷新一次 }
6.定时器启动后,定时刷新,我这边设置的是100毫秒,也可以做一个前端设置按钮,增加游戏趣味性,同时也可以设置根据不同的积分处理此设置
定时器中有画蛇身体的方法、随机一个食物位置的方法、蛇移动的方法、蛇是否吃到食物的方法、游戏有没有结束的方法
/// <summary> /// 开始的定时器 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void StartTimer_Tick(object sender, EventArgs e) { DrawSnake(); DrawFood(); Snake.Move(Snake.Sd);//蛇移动 移动的方向键盘控制 EatFood(); IsOrNotGameOver(); }
7.画蛇的方法如下
/// <summary> /// 画蛇 /// </summary> public void DrawSnake() { RectangleF[] rectangles = new RectangleF[Snake.Snakebody.Count]; int num; for (num = 0; num < Snake.Snakebody.Count; num++) { rectangles[num] = new RectangleF(Snake.Snakebody[num].Location_X, Snake.Snakebody[num].Location_Y, Snake.width, Snake.height); } this.Refresh(); Graphics g = this.Snake_Panel.CreateGraphics(); SolidBrush myBrush = new SolidBrush(Color.Black); g.FillRectangles(myBrush, rectangles); }
8.食物显示的方法如下,其中MaxScore表示小食物出现的次数,每出现一次此值减一,当为0时,食物变为大食物。
/// <summary> /// 食物的位置 /// </summary> public void DrawFood() { Graphics f = this.Snake_Panel.CreateGraphics(); SolidBrush mybrush = new SolidBrush(Color.Red); if (Snake.MaxScore==0) { f.FillRectangle(mybrush, FoodX, FoodY, 20, 20);//随机了食物的位置 } else { f.FillRectangle(mybrush, FoodX, FoodY, 10, 10);//随机了食物的位置 } }
9.当每次刷新时,蛇都会移动,相应的蛇整体的位置也会发生变化,但蛇身有痕迹,所以使用如下方式进行蛇体变化
//蛇的移动操作 每次移动 都需要改变蛇身体 public static void Move(SnakeDirection Sd) { int i = Snakebody.Count - 1;//根据不同的移动方向 需要对相对位置发生变化,这个操作保证蛇身按照蛇头的轨迹运转 switch (Sd) { case SnakeDirection.Up: ChangeSnakeLine(); Snakebody[i].Location_Y -= 10; break; case SnakeDirection.Left: ChangeSnakeLine(); Snakebody[i].Location_X -= 10; break; case SnakeDirection.Down: ChangeSnakeLine(); Snakebody[i].Location_Y += 10; break; case SnakeDirection.Right: ChangeSnakeLine(); Snakebody[i].Location_X += 10; break; } } /// <summary> /// 每次移动一次 改变蛇身体的相对位置 /// </summary> public static void ChangeSnakeLine() { int j = 0; int x, y; int i = Snakebody.Count - 1; for (j = 0; j < i; j++)//蛇 1位置变为蛇0 2=》1 3=》2 { x = Snakebody[j + 1].Location_X; y = Snakebody[j + 1].Location_Y; Snakebody[j].Location_Y = y; Snakebody[j].Location_X = x; } }
10.刷新蛇身体后,要判断食物位置是否和蛇头碰撞,当发生碰撞时,表示蛇吃掉了食物,食物刷新,蛇身体增长
/// <summary> /// 吃掉食物 判断蛇头位置和食物位置相同时,食物重新刷新 /// </summary> public void EatFood() { if (Snake.Snakebody[Snake.Snakebody.Count - 1].Location_X == FoodX && Snake.Snakebody[Snake.Snakebody.Count - 1].Location_Y == FoodY) { Snake.AddBody();//增加 蛇身增加 //吃掉食物之后 随机创建一个新的食物 CreateNewFood(); } } //吃一个节点 变为新的蛇头 public static void AddBody() { Snake S_eat = new Snake(); int xx = Snakebody[Snakebody.Count - 1].Location_X; int yy = Snakebody[Snakebody.Count - 1].Location_Y; switch (Sd) { case SnakeDirection.Up: S_eat.Location_Y = yy - 10; S_eat.Location_X = xx; Snakebody.Add(S_eat); break; case SnakeDirection.Left: S_eat.Location_X = xx - 10; S_eat.Location_Y = yy; Snakebody.Add(S_eat); break; case SnakeDirection.Down: S_eat.Location_Y = yy + 10; S_eat.Location_X = xx; Snakebody.Add(S_eat); break; case SnakeDirection.Right: S_eat.Location_Y = yy; S_eat.Location_X = xx + 10; Snakebody.Add(S_eat); break; } }
11.食物被吃掉后,食物刷新,同时判断小食物出现的次数
/// </summary> public void CreateNewFood() { refreshScore(); bool IsCreate = false; while (!IsCreate)//是否创建成功 { Food NewFood = new Food(); FoodX = NewFood.Food_Location_X; FoodY = NewFood.Food_Location_Y; //判断这个不会出现在蛇身上 IsCreate = true;//创建成功 for (int i = 0; i < Snake.Snakebody.Count; i++) { if (Snake.Snakebody[i].Location_X == FoodX && Snake.Snakebody[i].Location_Y == FoodY)//当坐标相同 终止for 未创建成功 { IsCreate = false; break; } } if (IsCreate) { if (!(FoodX <= this.Snake_Panel.Width-10 && FoodX >= 10 && FoodY <= this.Snake_Panel.Height-10 && FoodY >= 10))//食物不在范围内 重新刷新 { IsCreate = false; } } if (IsCreate) { if (Snake.Snakebody.Count != 4) { Snake.MaxScore--;//不是第一次创建食物时 } Snake.CurScore = 1;//当前小节点的分数 if (Snake.MaxScore == 0) { Snake.CurScore = 5;//当前小节点的分数 } else if (Snake.MaxScore<0) { Snake.MaxScore = 5;//初始化次数 } } } }
12.每次定时器刷新,蛇身会发生变化,同时就需要判断蛇是否触发终止规则
蛇身体缠绕,蛇头碰壁表示游戏终止
/// <summary> /// /// </summary> public void IsOrNotGameOver() { int x = Snake.Snakebody[Snake.Snakebody.Count - 1].Location_X; int y = Snake.Snakebody[Snake.Snakebody.Count - 1].Location_Y; bool IsGameOver = false; if (!(x <= this.Snake_Panel.Width && x >= 0 && y <= this.Snake_Panel.Height && y >= 0))//蛇头碰触到边框 { IsGameOver = true; } //蛇头碰到自己蛇身 for(int i=0;i< Snake.Snakebody.Count - 2; i++) { if(Snake.Snakebody[i].Location_X==x&& Snake.Snakebody[i].Location_Y == y) { IsGameOver = true; break; } } if (IsGameOver) { GameOver(); StartTimer.Stop(); } }
以上就是整个蛇运行的逻辑
二、完整代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace Project_Snake { public partial class SnakeForm : Form { public SnakeForm() { InitializeComponent(); } //为了上下左右键起作用 需要重写这个操作 protected override bool ProcessDialogKey(Keys keyData) { //遇到上下左右键 就自定义处理 if (keyData == Keys.Up || keyData == Keys.Down || keyData == Keys.Left || keyData == Keys.Right) return false; else return base.ProcessDialogKey(keyData); } /// <summary> /// 键盘和鼠标按下时 处理 但必须有控件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void SnakeForm_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Up) { Snake.Sd = SnakeDirection.Up; } else if (e.KeyCode == Keys.Down) { Snake.Sd = SnakeDirection.Down; } else if (e.KeyCode == Keys.Left) { Snake.Sd = SnakeDirection.Left; } else if (e.KeyCode == Keys.Right) { Snake.Sd = SnakeDirection.Right; } } /// <summary> /// 蛇的控件 记录蛇的基本方法 /// </summary> public class Snake { public static SnakeDirection Sd;//蛇进行的方向 上下左右四个方向 public int Location_X;//0为x,1为y 蛇的初始位置 public int Location_Y; public static List<Snake> Snakebody = new List<Snake>();//蛇身子的具体位置,每次位移都刷新一次 整个蛇身子的所有位置 最后一位是蛇头 public static int width = 10, height = 10; public static int CurScore = 1;//当前食物的分数 public static int AllScore = -1;//总分数 public static int MaxScore = 5;//大分出现频次 //当前方向 public Snake() { Location_X = 300; Location_Y = 300; } /// <summary> /// 蛇初始化 朝向 初始长度等 /// </summary> public static void Start() { MaxScore = 5; AllScore = -1; CurScore = 1; Sd = SnakeDirection.Left;//默认往左跑 Snakebody.Clear();//每次操作舍身重置 //添加初始化的小蛇位置 Snake s0 = new Snake();//蛇1 Snake s1 = new Snake();//蛇2 Snake s2 = new Snake(); Snake s3 = new Snake(); //默认四个长度 蛇的位置 Snakebody.Add(s0);//默认蛇1位置 Snakebody[0]是蛇头 s1.Location_X = s0.Location_X - 10; Snakebody.Add(s1); s2.Location_X = s1.Location_X - 10; Snakebody.Add(s2); s3.Location_X = s2.Location_X - 10; Snakebody.Add(s3); } //蛇的移动操作 每次移动 都需要改变蛇身体 public static void Move(SnakeDirection Sd) { int i = Snakebody.Count - 1;//根据不同的移动方向 需要对相对位置发生变化,这个操作保证蛇身按照蛇头的轨迹运转 switch (Sd) { case SnakeDirection.Up: ChangeSnakeLine(); Snakebody[i].Location_Y -= 10; break; case SnakeDirection.Left: ChangeSnakeLine(); Snakebody[i].Location_X -= 10; break; case SnakeDirection.Down: ChangeSnakeLine(); Snakebody[i].Location_Y += 10; break; case SnakeDirection.Right: ChangeSnakeLine(); Snakebody[i].Location_X += 10; break; } } /// <summary> /// 每次移动一次 改变蛇身体的相对位置 /// </summary> public static void ChangeSnakeLine() { int j = 0; int x, y; int i = Snakebody.Count - 1; for (j = 0; j < i; j++)//蛇 1位置变为蛇0 2=》1 3=》2 { x = Snakebody[j + 1].Location_X; y = Snakebody[j + 1].Location_Y; Snakebody[j].Location_Y = y; Snakebody[j].Location_X = x; } } //吃一个节点 变为新的蛇头 public static void AddBody() { Snake S_eat = new Snake(); int xx = Snakebody[Snakebody.Count - 1].Location_X; int yy = Snakebody[Snakebody.Count - 1].Location_Y; switch (Sd) { case SnakeDirection.Up: S_eat.Location_Y = yy - 10; S_eat.Location_X = xx; Snakebody.Add(S_eat); break; case SnakeDirection.Left: S_eat.Location_X = xx - 10; S_eat.Location_Y = yy; Snakebody.Add(S_eat); break; case SnakeDirection.Down: S_eat.Location_Y = yy + 10; S_eat.Location_X = xx; Snakebody.Add(S_eat); break; case SnakeDirection.Right: S_eat.Location_Y = yy; S_eat.Location_X = xx + 10; Snakebody.Add(S_eat); break; } } } public void refreshScore() { Snake.AllScore = Snake.AllScore+ Snake.CurScore; this.SnakeScore.Text = Convert.ToString(Snake.AllScore); } private int FoodX = 0;//食物X轴 private int FoodY = 0;//食物Y轴 private void BeginBtn_Click(object sender, EventArgs e) { Snake.Start();//创建蛇 CreateNewFood(); StartTimer.Start(); StartTimer.Interval = 100;//毫秒刷新一次 } /// <summary> /// 开始的定时器 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void StartTimer_Tick(object sender, EventArgs e) { DrawSnake(); DrawFood(); Snake.Move(Snake.Sd);//蛇移动 移动的方向键盘控制 EatFood(); IsOrNotGameOver(); } /// <summary> /// 画蛇 /// </summary> public void DrawSnake() { RectangleF[] rectangles = new RectangleF[Snake.Snakebody.Count]; int num; for (num = 0; num < Snake.Snakebody.Count; num++) { rectangles[num] = new RectangleF(Snake.Snakebody[num].Location_X, Snake.Snakebody[num].Location_Y, Snake.width, Snake.height); } this.Refresh(); Graphics g = this.Snake_Panel.CreateGraphics(); SolidBrush myBrush = new SolidBrush(Color.Black); g.FillRectangles(myBrush, rectangles); } /// <summary> /// 创建一个新的食物,也就是一个小点,红色 /// </summary> public void CreateNewFood() { refreshScore(); bool IsCreate = false; while (!IsCreate)//是否创建成功 { Food NewFood = new Food(); FoodX = NewFood.Food_Location_X; FoodY = NewFood.Food_Location_Y; //判断这个不会出现在蛇身上 IsCreate = true;//创建成功 for (int i = 0; i < Snake.Snakebody.Count; i++) { if (Snake.Snakebody[i].Location_X == FoodX && Snake.Snakebody[i].Location_Y == FoodY)//当坐标相同 终止for 未创建成功 { IsCreate = false; break; } } if (IsCreate) { if (!(FoodX <= this.Snake_Panel.Width-10 && FoodX >= 10 && FoodY <= this.Snake_Panel.Height-10 && FoodY >= 10))//食物不在范围内 重新刷新 { IsCreate = false; } } if (IsCreate) { if (Snake.Snakebody.Count != 4) { Snake.MaxScore--;//不是第一次创建食物时 } Snake.CurScore = 1;//当前小节点的分数 if (Snake.MaxScore == 0) { Snake.CurScore = 5;//当前小节点的分数 } else if (Snake.MaxScore<0) { Snake.MaxScore = 5;//初始化次数 } } } } /// <summary> /// 食物的位置 /// </summary> public void DrawFood() { Graphics f = this.Snake_Panel.CreateGraphics(); SolidBrush mybrush = new SolidBrush(Color.Red); if (Snake.MaxScore==0) { f.FillRectangle(mybrush, FoodX, FoodY, 20, 20);//随机了食物的位置 } else { f.FillRectangle(mybrush, FoodX, FoodY, 10, 10);//随机了食物的位置 } } /// <summary> /// 吃掉食物 判断蛇头位置和食物位置相同时,食物重新刷新 /// </summary> public void EatFood() { if (Snake.Snakebody[Snake.Snakebody.Count - 1].Location_X == FoodX && Snake.Snakebody[Snake.Snakebody.Count - 1].Location_Y == FoodY) { Snake.AddBody();//增加 蛇身增加 //吃掉食物之后 随机创建一个新的食物 CreateNewFood(); } } /// <summary> /// /// </summary> public void IsOrNotGameOver() { int x = Snake.Snakebody[Snake.Snakebody.Count - 1].Location_X; int y = Snake.Snakebody[Snake.Snakebody.Count - 1].Location_Y; bool IsGameOver = false; if (!(x <= this.Snake_Panel.Width && x >= 0 && y <= this.Snake_Panel.Height && y >= 0))//蛇头碰触到边框 { IsGameOver = true; } //蛇头碰到自己蛇身 for(int i=0;i< Snake.Snakebody.Count - 2; i++) { if(Snake.Snakebody[i].Location_X==x&& Snake.Snakebody[i].Location_Y == y) { IsGameOver = true; break; } } if (IsGameOver) { GameOver(); StartTimer.Stop(); } } /// <summary> /// 游戏结束 /// </summary> public void GameOver() { } } /// <summary> /// 方向枚举 /// </summary> public enum SnakeDirection { Left, Right, Down, Up } //食物类 食物的纵轴横轴 public class Food { public int Food_Location_X; public int Food_Location_Y; public Food() { Random x = new Random(); Random y = new Random(); Food_Location_X = x.Next(0, 49) * 10;//随机点出现食物 Food_Location_Y = y.Next(0, 49) * 10;//随机点出现食物 } } }