基于C#实现的贪吃蛇小游戏

Change

发布日期: 2019-04-09 16:54:40 浏览量: 1342
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

1.设计目的和内容

1.1 设计目的

随着人们生活节奏的加快,人们的精神压力也逐渐增加,利用工作之余放松一下自己十分重要。近段时间,微信小游戏的火爆反映了人们喜欢通过一些小的游戏来舒缓自己的生活压力,因此开发一款休闲益智小游戏——贪心的小蛇,帮助工作一天的人放松一下是一项不错的事情。

1.2 设计内容

一款面向大众的小游戏——贪吃蛇,用以放松劳累工作的人,丰富其业余生活。该游戏虽然年代久远,但依然经典,都是每一代人美好的回忆,休闲的时候不妨还玩一玩贪吃蛇,放松心情,愉悦身心,动动手指,回味经典。

主要功能:

  • 登录和注册玩家

  • 游戏操作,挑战极限

  • 查看游戏记录,分析得分折线图

  • 玩家排名榜,查看其它玩家的排名,自己在游戏中的排名

2.设计思路

该软件采用mvc架构,视图和逻辑分离,用户在UI层进行操作,数据通过逻辑层传至数据库并修改数据库中的数据,当用户需要数据库中的数据时,数据从数据库中提出经过逻辑层传至UI层。UI层方便用户进行各种操作;逻辑层连通UI层和数据层,进行数据的分析和传输;数据库为系统提供数据的存储。

3.基本功能描述

3.1 基本功能

3.1.1 用户的登录注册

想玩游戏的用户必须注册,登录成功后才能进入游戏页面,该游戏会记录玩家的游戏记录,包括分数,游戏时间等

3.1.2 游戏玩耍

玩家可以通过键盘操控贪吃蛇的方向和速度,吃食物,长身体,得分数,刷排名,挑战极限。

3.1.3 查询个人游戏记录

用户点击查询历史记录,获得他整个的每一次的游戏记录信息,包括时间和分数。

3.1.4 记录分析功能

该功能将玩家的游戏记录以折线图的形式给出,玩家可以清晰的看到自己的分值变化情况。

3.1.5 玩家排名

在这里,玩家可以看到该游戏所有玩家的一个排行榜,可以查看自己的排名,并且有前三名的光荣榜。

3.1.6 帮助

在这里,玩家可以了解游戏的玩法,操作方式,也可以查看帮助文档,获取我们的联系方式和通信地址。

3.2 功能模块图

4.软件设计

4.1 设计步骤

  • 调查用户需求,书写功能需求文档

  • 查阅相关资料

  • MVC架构的设计

  • 设计数据库:设计数据库中所需的每个表以及表中各个字段的名称及类型

  • 设计用户登录界面,并加入各个功能控件

  • 编写用户登录时业务逻辑层代码

  • 编写数据库帮助类代码

  • 设计用户主界面,并同时编写其业务逻辑层代码,加入事件绑定等

  • 使用单元测试测试各个模块的功能

  • 将所有功能模块连接起来,测试整个软件的功能,并不断调试

4.2 系统开发工具

  • 系统开发平台:Microsoft Visual Studio 2017

  • 系统开发语言:C#

  • 数据库管理软件:Microsoft Access 2016

  • 软件体系架构:MVC

4.3 数据库逻辑设计

数据库由game_user、score_record两张表组成。

game_user(用户表)

score_record(分数记录表)

4.4 界面设计

登录界面

注册界面

信息提示界面

游戏页面

历史记录页面

玩家排名界面

4.5 关键功能

贪吃蛇的生成,蛇体是由10*10大小的label组成的,存到一个label数组中。

  1. //初始化一个label蛇体,长度为5个label,一个label height= width = 10
  2. for (int i = 0; i < 5; i++)
  3. {
  4. //蛇段
  5. Label Snake_Boby_content = new Label();
  6. Snake_Boby_content.Height = 10;
  7. Snake_Boby_content.Width = 10;
  8. //蛇段的位置
  9. Snake_Boby_content.Top = 200;
  10. Snake_Boby_content.Left = 200 - i * 10;
  11. //背景色
  12. //this.BackColor = Color.White; //游戏背景颜色
  13. //this.pictureBox1.BackColor = Color.;
  14. Snake_Boby_content.BackColor = Color.White;
  15. Snake_Boby_content.Text = "0❤";
  16. Snake_Boby_content.ForeColor = Color.Black;
  17. //获取或设置包含有关控件的数据的对象。
  18. Snake_Boby_content.Tag = i;
  19. //加入蛇体
  20. Snake_Boby[i] = Snake_Boby_content;
  21. this.pictureBox1.Controls.Add(Snake_Boby_content);
  22. //this.panControls.Add(Snake_Boby_content);
  23. }
  24. //每隔一段时间发生一次右移
  25. // timer_game.Tick += new EventHandler(timer1_Tick_1);
  26. //按键时发生的事件监控
  27. this.KeyDown += new KeyEventHandler(Form1_KeyDown);
  28. Snake_food(); //food 生成
  29. timer_game.Start(); //Timer 开始计时

食物的生成及其位置的随机生成

利用随机数,随机产生2个数x,y当做食物的坐标。

  1. public void Snake_food()
  2. {
  3. Label Food = new Label();
  4. Food.Width = 10;
  5. Food.Height = 10;
  6. //生成一个随机位置的food
  7. Food.Top = R.Next(1, 18) * 20;
  8. Food.Left = R.Next(1, 19) * 20;
  9. Food.Text = "❤";
  10. Food.Tag = "food";
  11. Food.BackColor = Color.Red;
  12. Food.ForeColor = Color.Red;
  13. this.pictureBox1.Controls.Add(Food);
  14. }
  15. //将食物移位
  16. foreach (Label lb in this.pictureBox1.Controls)
  17. {
  18. if (lb.Tag.ToString() == "food".ToString())
  19. {
  20. lb.Top = R.Next(1, 18) * 20;
  21. lb.Left = R.Next(1, 19) * 20;
  22. lb.Text = "❤";
  23. lb.ForeColor = Color.Red;
  24. lb.BackColor = Color.Red;
  25. }
  26. }

蛇身移动

遍历 Snake_Boby[]中的label,依次将label移动一个单位。

  1. public void Snake_move(int x, int y)
  2. {
  3. //记录xy的中间变量
  4. int temp_x = 0, temp_y = 0;
  5. //遍历蛇身进行移动
  6. for (int i = 1; Snake_Boby[i] != null; i++)
  7. {
  8. if (i >= 3)
  9. {
  10. //将记录的前一个蛇段位置赋给中间变量
  11. temp_x = Snake_Boby_content_x;
  12. temp_y = Snake_Boby_content_y;
  13. }
  14. if (i == 1)
  15. {
  16. //将记录蛇头的改变前的位置的x y赋给第一个蛇段,并记录蛇段的位置
  17. temp_x = Snake_Boby[i].Left;
  18. temp_y = Snake_Boby[i].Top;
  19. Snake_Boby[i].Left = x;
  20. Snake_Boby[i].Top = y;
  21. }
  22. else
  23. {
  24. //将记录前一个个蛇段的改变前的位置temp_赋给第二个蛇段,并记录改前位置
  25. Snake_Boby_content_x = Snake_Boby[i].Left;
  26. Snake_Boby_content_y = Snake_Boby[i].Top;
  27. Snake_Boby[i].Left = temp_x;
  28. Snake_Boby[i].Top = temp_y;
  29. }
  30. }
  31. }

蛇身自己移动

通过timer和记录的方向状态,每隔一段时间,将蛇移动一下。

  1. private void timer1_Tick_1(object sender, EventArgs e)
  2. {
  3. //用来记录snake的head的xy坐标
  4. if (isalive)
  5. {
  6. int x, y;
  7. x = Snake_Boby[0].Left;
  8. y = Snake_Boby[0].Top;
  9. if (Key_Name == "start") //键盘状态处于初始状态
  10. {
  11. Snake_Boby[0].Left = x + 10;//Snake_Boby[0]右移10
  12. Snake_move(x, y);//调用
  13. }
  14. if (Key_Name == "Right")//键盘状态处于 向右 状态
  15. {
  16. Snake_Boby[0].Left = x + 10;
  17. Snake_move(x, y);
  18. }
  19. if (Key_Name == "Up") //键盘状态处于 向上 状态
  20. {
  21. Snake_Boby[0].Top = y - 10;
  22. Snake_move(x, y);
  23. }
  24. if (Key_Name == "Down")//键盘状态处于 向下 状态
  25. {
  26. Snake_Boby[0].Top = y + 10;
  27. Snake_move(x, y);
  28. }
  29. if (Key_Name == "Left") //键盘状态处于向左 状态
  30. {
  31. Snake_Boby[0].Left = x - 10;
  32. Snake_move(x, y);
  33. }
  34. Eat_time();
  35. Snake_dead(x, y);
  36. }

判断蛇的死亡并更新分数记录

如果蛇撞到四周的墙,就会死亡,其他情况均不死亡,死亡时,将本局分数上传score_record表,并判断是否刷新了个人记录,如果刷新了,就修改game_user中该玩家的最高分记录。

  1. public void Snake_dead(int x,int y)
  2. {
  3. if (x<pictureBox1.Location.X||x>pictureBox1.Location.X+pictureBox1.Width-20||y<pictureBox1.Location.Y||y>pictureBox1.Location.Y+pictureBox1.Height-20)
  4. {
  5. this.playAgain.Enabled = true;
  6. this.button2.Enabled = false;
  7. gameover.Visible = true;
  8. gameover.Text = "呜呜呜,游戏结束,得分:"+score;
  9. timer_gameover.Enabled = false;
  10. timer_gameover.Interval = 2000;
  11. timer_gameover.Enabled = true;
  12. timer_game.Enabled = false;
  13. isalive = false;
  14. Array.Clear(Snake_Boby, 5, 5+score);
  15. Key_Name = "stop";
  16. //获取游戏结束时的时间
  17. System.DateTime currentTime = new System.DateTime();
  18. currentTime = System.DateTime.Now;
  19. year = currentTime.Year;
  20. month = currentTime.Month;
  21. day = currentTime.Day;
  22. hour = currentTime.Hour;
  23. min = currentTime.Minute;
  24. sec = currentTime.Second;
  25. string gameover_time = year + "年" + month + "月" + day + "日 " + hour + "时" + min + "分" + sec + "秒";
  26. //上传数据(玩家账号,本次游戏分数,游戏结束时间)
  27. string username = login.username;//获取游戏玩家的ID
  28. OleDbConnection connection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=user.mdb");
  29. connection.Open();
  30. string insertScore = "insert into score_record ([username],[playtime],[score])values('"+username+"','"+gameover_time+"','"+score+"')";
  31. OleDbCommand insertScoreCommend = new OleDbCommand(insertScore, connection);
  32. insertScoreCommend.ExecuteNonQuery();
  33. //判断该分数是否刷新本人分数,若刷新,则更新本人最高分数
  34. int highscore;
  35. string gethighscore = "select highscore from game_user where [username]='" + username + "'";
  36. OleDbCommand getHighScoreCmd = new OleDbCommand(gethighscore, connection);
  37. OleDbDataReader oleDbDataReaderHighScore = getHighScoreCmd.ExecuteReader();
  38. oleDbDataReaderHighScore.Read();
  39. highscore = int.Parse(oleDbDataReaderHighScore["highscore"].ToString());
  40. if (highscore < score)
  41. {
  42. string updateHighScore = "update game_user set [highscore]='" + score + "'where [username]='"+username+"'";
  43. OleDbCommand updateHighScoreCmd = new OleDbCommand(updateHighScore, connection);
  44. updateHighScoreCmd.ExecuteNonQuery();
  45. }
  46. }
  47. }

历史记录的显示

利用dataGridView显示该玩家的历史游戏记录,包括时间和分数。

  1. //初始化历史记录表中的数据
  2. OleDbConnection connection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=user.mdb");
  3. connection.Open();
  4. string select_history = "select playtime as 游戏时间,score as 分数 from score_record where [username]='"+login.username+"'";
  5. OleDbDataAdapter oleDbDataAdapter = new OleDbDataAdapter(select_history, connection);
  6. DataSet history_dataSet = new DataSet();
  7. oleDbDataAdapter.Fill(history_dataSet, "score_record");
  8. dataGridView_history.DataSource = history_dataSet.Tables["score_record"];
  9. //初始化历史记录中其他信息
  10. //玩家昵称显示
  11. this.user.Text = "昵称:" + login.username;
  12. //玩家历史最高分
  13. int highscore;
  14. string gethighscore = "select highscore from game_user where [username]='" + login.username + "'";
  15. OleDbCommand getHighScoreCmd = new OleDbCommand(gethighscore, connection);
  16. OleDbDataReader oleDbDataReaderHighScore = getHighScoreCmd.ExecuteReader();
  17. oleDbDataReaderHighScore.Read();
  18. highscore = int.Parse(oleDbDataReaderHighScore["highscore"].ToString());
  19. this.highscore.Text = "最高分:" + highscore;
  20. //获得玩家总的游戏次数
  21. string getGameTimes = "select count(*) as cou from score_record where [username]='" + login.username + "'";
  22. OleDbCommand getGameTimesCmd = new OleDbCommand(getGameTimes, connection);
  23. OleDbDataReader oleDbDataReaderGameTimes = getGameTimesCmd.ExecuteReader();
  24. oleDbDataReaderGameTimes.Read();
  25. int counts = int.Parse(oleDbDataReaderGameTimes["cou"].ToString());
  26. this.count_times.Text = "游戏次数:" + counts;

记录的分析

利用Chart技术,将记录装换称折线图的数据图,便于分析自己的游戏记录。

  1. //初始化玩家游戏分数走势
  2. //设置网格的颜色
  3. chart_history.ChartAreas["ChartArea1"].AxisX.MajorGrid.LineColor = Color.LightGray;
  4. chart_history.ChartAreas["ChartArea1"].AxisY.MajorGrid.LineColor = Color.LightGray;
  5. //设置坐标轴的名称
  6. chart_history.ChartAreas["ChartArea1"].AxisX.Title = "时间";
  7. chart_history.ChartAreas["ChartArea1"].AxisY.Title = "分数";
  8. chart_history.Series["时间"].ChartType = SeriesChartType.Line;
  9. chart_history.Series["时间"].XValueType = ChartValueType.String;
  10. chart_history.Series["时间"].IsValueShownAsLabel = true;
  11. chart_history.Series["时间"].MarkerStyle = MarkerStyle.Cross;
  12. chart_history.Series["时间"].Color = Color.Green;
  13. chart_history.Series["时间"].BorderWidth = 3;
  14. DataView dataView = new DataView(history_dataSet.Tables[0]);
  15. chart_history.Series["时间"].Points.DataBindXY(dataView,"游戏时间",dataView,"分数");
  16. this.chart_history.Visible = false;
  17. this.button2.Enabled = false;

删除某一记录,折线图会及时更新

删除记录时,更新折线图的数据,从而刷新折线图。

  1. private void button4_Click(object sender, EventArgs e)
  2. {
  3. OleDbConnection connection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=user.mdb");
  4. connection.Open();
  5. string selectRecord = this.dataGridView_history.SelectedCells[0].Value.ToString();
  6. string deleteRecord = "delete from score_record where [username]='" + login.username + "'and [playtime]='" + selectRecord + "'";
  7. OleDbCommand deleteRecordCmd = new OleDbCommand(deleteRecord, connection);
  8. deleteRecordCmd.ExecuteNonQuery();
  9. dataGridView_history.Rows.Remove(dataGridView_history.CurrentRow);
  10. //更新折线图
  11. string select_history = "select playtime as 游戏时间,score as 分数 from score_record where [username]='" + login.username + "'";
  12. OleDbDataAdapter oleDbDataAdapter = new OleDbDataAdapter(select_history, connection);
  13. DataSet history_dataSet = new DataSet();
  14. oleDbDataAdapter.Fill(history_dataSet, "score_record");
  15. dataGridView_history.DataSource = history_dataSet.Tables["score_record"];
  16. DataView dataView = new DataView(history_dataSet.Tables[0]);
  17. chart_history.Series["时间"].Points.DataBindXY(dataView, "游戏时间", dataView, "分数");
  18. }

玩家排名

使用数据库自带的排序功能,将记录按分数高低排序。

  1. string pai_ming = "select username as 昵称,highscore as 最高分 from game_user order by highscore desc";
  2. OleDbDataAdapter oleDbDataAdapter_paiMing = new OleDbDataAdapter(pai_ming, connection);
  3. DataSet paiMing_dataSet = new DataSet();
  4. oleDbDataAdapter_paiMing.Fill(paiMing_dataSet, "game_user");
  5. dataGridView_paiMing.DataSource = paiMing_dataSet.Tables["game_user"];
  6. this.label1.Text =(String)paiMing_dataSet.Tables[0].Rows[0]["昵称"];
  7. this.label2.Text = (String)paiMing_dataSet.Tables[0].Rows[1]["昵称"];
  8. this.label3.Text = (String)paiMing_dataSet.Tables[0].Rows[2]["昵称"];

5.结论与心得体会

最初选题时,十分纠结,因为之前一直在在做Javaweb学生信息管理系统,几乎花了一个多月时间,留给该项目的时间几乎很少,精神被javaweb信息系统搞得有点崩溃,开始上4399玩小游戏,突然想到做一个类似于贪吃蛇这样的小游戏的idea.立刻充满斗志与想法,这样不仅缓解自己的压力,也做了之前从未做过的领域,重拾童年的乐趣。

此外,在这个过程中我所收获的不仅是C#知识的提升,还学到了将.net平台提供的控件利用数据结构中学到的知识加以应用,实现贪吃蛇的蛇身结构,蛇身的遍历等问题。还学到游戏的背景音乐的播放,图表的绘制等技能,包括对之前数据库中学到的知识的深层理解,如多张表的连接等,主键外键,及其级联删除等。同时,通过课程设计,我也发现了自己有以下不足之处:

  • 做项目开发没能遵循项目开发的规范流程,如做需求分析,设计等编码之前的操作,而是凭自己的主观想法一步步编码,做项目期间曾有多次功能的替换,造成时间的浪费。降低了开发效率

  • 基础薄弱,当用到某一知识时,根本不清楚,需要重新学习。并且一些编程思想没理解,像面向对象设计的继承,多态等,不能很好地应用到编程中

6.附录

6.1 调试报告

  • 从数据库的game_user表中检查用户是否存在时,出现如下异常:from子句语法错误

    原因:语句中某一单词为Access中的关键字。如:select * from user。其中user就是系统保留关键字。和mysql中对数据库的sql语句语法大同小异

    解决方法:在User两边加上中括号,如:select * from [user]

  • 运行时的窗体大小和设计时的大小不一样,并且出现窗体中的控件错位等情况

    原因:在控件上放背景图片时,没有设置sizemode等格式,导致窗体因背景图片大小而变化

  • 至少一个参数没有被指定值

上传的附件 cloud_download 基于C#实现的贪吃蛇小游戏.7z ( 9.47mb, 10次下载 )
error_outline 下载需要13点积分

发送私信

如果哪天我们真的久别重逢,我希望你别来无恙

13
文章数
14
评论数
最近文章
eject