C#⼩游戏——贪吃蛇~详细过程+全部代码
⼀个好玩的 ……
⽂章⽬录
⼀、介绍
1)背景
⼀到期末课就基本没了。闲来⽆事,整理⼀下之前写的C#的⼀个⼩游戏——贪吃蛇。今年疫情在家写的,蛮有趣的吧!2)环境
系统:Windows 10
环境:.Net Framework 4.7
平台:VS 2019
3)演⽰
游戏过程
⽂件格式
4)基本逻辑概述
i 游戏主体
声明Label类型的⼆维数组,表⽰每⼀个格⼦,⽤不同的颜⾊来区分蛇和地图(例如,表⽰蛇⾝的label的BackColor设置为蓝⾊,⾷物设置为红⾊,其他的设置为绿⾊);
ii 刷新
通过timer刷新。每⼀次刷新,蛇会朝着其蛇头的⽅向移动⼀格。所以通过对timer的interval属性控制蛇移动的快慢。
iii 控制蛇的移动
在timer中,同时判断有⽆键盘(w,s,a,d)的输⼊,进⾏改变蛇的移动⽅向(也是通过修改label的颜⾊实现)。
iiii 保存最⾼记录
程序⾃动创建⼆进制⽂件MaxScore.dat,每次游戏结束时,读取数据,判断⼤⼩,并保存。
该⽂件路径是程序的bin⽬录Debug下。
iiiii 吃⾷物
random⽣成⾷物的位置,将该位置的label颜⾊设置为红⾊,就可以吃了。在timer中判断是否吃到了⾷物,进⾏增长的操作。
iiiiii 记录⽤户的输⼊(很重要)
有时⽤户有可能连续输⼊多个值(如上上上左左),这些值输⼊的间隔可能会⼩于timer的interval值,造成”上⼀个动作还没有完成,直接进⾏下⼀个动作了,从⽽导致误判死亡!”
我的解决办法是:⽤string字符串记录⽤户的输⼊,每次输⼊都加到该字符串的末尾。⽽每次的timer从该字符串的串⾸取值,这样⼀来,玩家可以的连续输⼊,不影响timer的刷新。
⼆、步骤
1)ControlFrm窗体
项⽬的初始运⾏的窗体,就三个按钮,分别打开不同的窗体:
代码逻辑如下:
namespace贪吃蛇
{
public partial class ControlFrm : Form
{
public ControlFrm()
{
InitializeComponent();
}
//“关于”按钮click
private void button3_Click(object sender, EventArgs e)
{
AboutMe hf =new AboutMe();
hf.ShowDialog();
}
//“玩法介绍”按钮click
private void button2_Click(object sender, EventArgs e)
{
HowToPlay htp =new HowToPlay();
htp.ShowDialog();
}
//“开始游戏”按钮click
private void button1_Click(object sender, EventArgs e)
{
GameFrm gf =new GameFrm();
gf.ShowDialog();
}
}
}
2)GameFrm窗体:游戏窗体
i 变量
//绘制地图
int mapX =40, mapY =20;//设定游戏地图边界
Label[,] mapArray;//定义地图格⼦的⼆维数组
List<int> snakeX =new List<int>{0,1,2};//snakeX[snakeX.Count-1]是蛇的头部。即初始时头部为2 List<int> snakeY =new List<int>{1,1,1};//利⽤集合存放蛇⾝
int foodX, foodY;//⾷物的坐标点
string kCode ="D";//控制蛇移动的⽅向,初始值向右
//level等级snakeX.Count>=10
int[] level ={300,250,200,150,100};//值是timer的时间,等级越⾼时间越短
int k =0;//等级
//枚举,游戏中的背景⾳乐,吃⾷物以及死亡⾳乐
public enum Sound
{
background, eat, gameover
};
//得分
int Score =0;
//历史最⾼分
int MaxScore;
//存储最⾼分记录的⽂件路径
string path ="MaxScore.dat";
//字符串队列,键盘每次的输⼊则⼊队
string ClikRecord ="";
#endregion
ii 窗体加载事件
private void Form1_Load(object sender, EventArgs e)
{
//从磁盘⽂件中获取历史最⾼分
GetMaxScore();
//1、创建地图
GreateMap();
//2、创建蛇⾝
GreateSnake();
//3、创建⾷物
GreateFood();
//4、蛇⾝移动(死亡、吃⾷物)
timer1.Start();//计时器开启
//5、播放⾳乐
PlayerMusic(Sound.background);
//初始化窗体下部的状态栏
toolStripStatusLabel3.Text ="        当前得分:"+ Score.ToString();
贪吃蛇的编程代码toolStripStatusLabel4.Text ="        历史最⾼得分:"+ MaxScore.ToString();
toolStripStatusLabel1.Text = DateTime.Now.ToString();//⽇期时间
toolStripStatusLabel2.Text ="                当前等级:"+(k +1).ToString();
}
iii 玩家敲击键盘的事件
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
string newKey = e.KeyCode.ToString();
//注意,当蛇向右时不能直接向左。同理,其他情况也类似
List<string> list =new List<string>(){"A","W","D","S","P","L"};
if(list.Contains(newKey)==false)//若按下的键不是这六个键的话
{
return;
}
if(kCode=="A"&&newKey=="D"||
kCode =="D"&& newKey =="A"||
kCode =="W"&& newKey =="S"||
kCode =="S"&& newKey =="W")
{
return;
}
if(newKey =="P")
{
timer1.Stop();
return;
}
if(newKey=="L")
{
timer1.Start();
return;
}
//修复BUG:
//这⾥⼀个BUG的情况:在游戏时,假如蛇的⽅向为D,快速敲击键盘W、D时,
//程序会来不及进⼊timer事件,这时蛇的状态不能经历W,直接到D,造成后⾯的程序
//会判断蛇咬到了⾃⼰。
//办法:加⼊string类型的ClikRecord当作键盘输⼊队列,在timer事件中取其第⼀个字符。
//同时仍要加⼊kCode=newKey,若不加的话,则前⾯的kcode可能会等于现在的newkey,即使他们没有挨着
kCode = newKey;
ClikRecord = ClikRecord + newKey;
}
#endregion
此处的“修复BUG”就是我开始的时候说的:有时⽤户有可能连续输⼊多个值(如上上上左左),这些值输⼊的间隔可能会⼩于timer的interval值,造成”上⼀个动作还没有完成,直接进⾏下⼀个动作了,从⽽导致误判死亡!”
iiii timer1 tick事件:
这个就是对蛇进⾏操作刷新的事件
private void timer1_Tick(object sender, EventArgs e)
{
//窗体下部的状态栏保持更新
toolStripStatusLabel3.Text ="                当前得分:"+ Score.ToString();    toolStripStatusLabel2.Text ="                当前等级:"+(k +1).ToString();
if(Score%10==0&& Score/10<=2)
{
k = Score /10;//当得分在20以内时,设置等级
timer1.Interval = level[k];//根据不同的等级设置不同的interval值,下同}
if(Score%10==0&& Score/10==4)
{
k++;//等级加1
timer1.Interval = level[k];
}
if(Score %10==0&& Score /10==6)
{
k++;
timer1.Interval = level[k];
}
if(ClikRecord.Length>0)
{
kCode = ClikRecord.Substring(0,1);//取第⼀个字符的字符串
ClikRecord = ClikRecord.Substring(1, ClikRecord.Length -1);
}
SnakeMove();//蛇移动⽅法
}
iiiii 相关⽅法
蛇移动的⽅法
private void SnakeMove()
{
//1、擦除
ClearSnake();
//2、修改坐标
if(kCode !="P"&&kCode!="L")
{
for(int i =0; i < snakeX.Count -1; i++)//snakeX.Count-1忽略头部
{
snakeX[i]= snakeX[i +1];
snakeY[i]= snakeY[i +1];
}
switch(kCode)
{
case"A":
snakeX[snakeX.Count -1]--;//头部坐标减1
break;
case"W":
snakeY[snakeX.Count -1]--;
break;
case"D":
snakeX[snakeX.Count -1]++;
break;
case"S":
snakeY[snakeX.Count -1]++;
break;
}
}
//3、吃⾷物

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。