之前一直想写一个贪吃蛇小游戏,上个周末终于有时间做了一个,现在和大家分享。
界面
界面比较简单,一个按钮和一个积分器组成。
基本逻辑:每五个食物出现一次大食物,小食物1分,大食物5分。
控制:撞墙和缠绕时,游戏暂停。
代码详解
详细介绍下代码
一、代码分详解
1.界面是在pc上,所以移动以上下左右键来控制移动,为了及时响应,所以增加如下代码
1
2
3
4
5
6
7
8
9
|
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.做个枚举,来标记某个时间状态下蛇的移动方向,配合键盘按键
/// 方向枚举
///
public enum SnakeDirection
{
Left,
Right,
Down,
Up
}
///
/// 键盘和鼠标按下时 处理 但必须有控件
///
///
///
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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
///
|
3.声明一个蛇的类,存储所有蛇的相关信息,如所有蛇身体的节点位置
主要变量如下
1
2
3
4
5
6
7
8
|
public static SnakeDirection Sd;//蛇进行的方向 上下左右四个方向
public int Location_X;//0为x,1为y 蛇的初始位置
public int Location_Y;
public static List Snakebody = new List();//蛇身子的具体位置,每次位移都刷新一次 整个蛇身子的所有位置 最后一位是蛇头
public static int width = 10, height = 10;
public static int CurScore = 1;//当前食物的分数
public static int AllScore = -1;//总分数
public static int MaxScore = 5;//大分出现频次
|
4.声明一个食物的类,同时增加一个方法,随机在界面上的一个位置显示食物
1
2
3
4
5
6
7
8
9
10
11
12
|
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.点击开始按钮,触发开始相关方法,主要是启动刷新的时间器,初始化食物位置,初始化积分等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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毫秒,也可以做一个前端设置按钮,增加游戏趣味性,同时也可以设置根据不同的积分处理此设置
定时器中有画蛇身体的方法、随机一个食物位置的方法、蛇移动的方法、蛇是否吃到食物的方法、游戏有没有结束的方法
/// 开始的定时器
///
///
///
private void StartTimer_Tick(object sender, EventArgs e)
{
DrawSnake();
DrawFood();
Snake.Move(Snake.Sd);//蛇移动 移动的方向键盘控制
EatFood();
IsOrNotGameOver();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
///
|
7.画蛇的方法如下
/// 画蛇
///
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
///
|
8.食物显示的方法如下,其中MaxScore表示小食物出现的次数,每出现一次此值减一,当为0时,食物变为大食物。
/// 食物的位置
///
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);//随机了食物的位置
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
///
|
9.当每次刷新时,蛇都会移动,相应的蛇整体的位置也会发生变化,但蛇身有痕迹,所以使用如下方式进行蛇体变化
/// 每次移动一次 改变蛇身体的相对位置
///
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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
//蛇的移动操作 每次移动 都需要改变蛇身体
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;
}
}
///
|
10.刷新蛇身体后,要判断食物位置是否和蛇头碰撞,当发生碰撞时,表示蛇吃掉了食物,食物刷新,蛇身体增长
/// 吃掉食物 判断蛇头位置和食物位置相同时,食物重新刷新
///
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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
///
|
11.食物被吃掉后,食物刷新,同时判断小食物出现的次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
///
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.每次定时器刷新,蛇身会发生变化,同时就需要判断蛇是否触发终止规则
蛇身体缠绕,蛇头碰壁表示游戏终止
///
///
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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
///
|
以上就是整个蛇运行的逻辑
二、完整代码
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;//随机点出现食物
}
}
}