用vb.net制作贪吃蛇游戏

贪吃蛇游戏相信很多朋友都听说或者玩过,特别是以前使用过诺基亚手机的朋友,这在当时就是诺基亚手机的专配游戏。
本篇文章讲述如何在vb.net中编写此游戏代码。
一种方法是可以使用控件数组,在用户界面上使用如多个图片框构成整个背景,将相关图片框设置为蛇身来绘制出整条蛇。这种方法比较简单,处理速度也快。
另外一种方法是通过GDI+在用户界面上绘制此游戏,本文将采用此方法,

一、需要解决的问题
1、绘制蛇身。
2、蛇的移动
3、产生食物
4、当吃到食物(与食物发生碰撞时),使蛇增加一段长度。
5、当碰到边界或者自身时,游戏结束。

二、涉及到的类
1、单个蛇身(蛇头):clsSnakeSingleBody
2、整条蛇:clsSnakeBody
3、食物:clsFood

三、如何解决问题
1、不论单个的蛇身还是蛇头,都是一个clsSnakeSingleBody类,该类中有个nextBody 属性,这是clsSnakeSingleBody类型,指向了下一个段蛇身。
在clsSnakeBody类中,有一个snakehead(蛇头)属性,这是clsSnakeSingleBody类型。
当得到了snakehead(蛇头)后,通过它的nextBody属性不断地遍历,可以获得每一段蛇的坐标、大小(实际就是一个矩形),结合Graphics的FillRectangle方法,就可以绘制出整条蛇。
2、蛇(代码中实例为snake)的移动,实际最重要的是蛇头的移动。通过在窗口界面设置计时器组件(代码中为Timer1),每隔一段时间就按照clsSnakeBody类的direction(方向)属性计算蛇头坐标位置。如下图:

重新绘制蛇头,通过nextBody属性不断地遍历蛇身,获得的每一段蛇身的坐标都是前一段蛇身(头)的坐标,遍历的时候完成对蛇身的绘制。
3、设置食物坐标位置,通过Random类得到食物坐标,将得到的坐标与每一段蛇身(头)坐标对比,如果重合,那么重新获得食物坐标。
4、当蛇吃到食物(蛇头与食物碰撞)时,新建一个clsSnakeSingleBody实例(代码中为newsnakehead),坐标同食物坐标,使snake的snakehead属性指向newsnakehead,而newsnakehead的nextBody指向原来的蛇头,这样就顺利生成了新的一段。
5、通过Timer1的Tick事件移动蛇时,先预先计算蛇头下一步的位置,与边界位置比对,如果超出了边界,视为死亡,触发snake的snakeCrossWall事件。同样,预先计算蛇头下一步的位置,与蛇身各段坐标比对,如果坐标相同,视为发生了碰撞,触发snakeCrossBody事件。

四、扩展内容
1、在代码中设置了A按键和S按键来实现暂停和继续操作,实际对应Timer1的Stop和Start方法。
2、当蛇吃到食物时触发snakeEat事件,除了重新得到一个食物外,还可以获得积分。当吃的食物越多,获得的积分越高。

五、设计和代码
窗体界面如下:

1、clsSnakeSingleBody类

'一段蛇身/蛇头
Public Class clsSnakeSingleBody'宽度Property width As Integer'高度Property height As Integer'横坐标Property x As Integer'纵坐标Property y As Integer'颜色Property foreColor As Color'指向下一段蛇身Property nextBody As clsSnakeSingleBodySub New()width = 20height = 20End Sub
End Class


2、clsSnakeBody类

'整条蛇
Public Class clsSnakeBody'移动步长Private Const moveStep As Integer = 20'场地宽度Property bgWidth As Integer'场地高度Property bgHeight As Integer'运动方向Property direction As String'蛇头Property snakehead As clsSnakeSingleBody'事件:吃到食物Event snakeEat()'事件:蛇头和蛇身发生碰撞Event snakeCrossBody()'事件:撞墙Event snakeCrossWall()'构造函数Sub New(ByVal g As Graphics, ByVal bgwidth As Integer, ByVal bgheight As Integer)Me.bgWidth = bgwidthMe.bgHeight = bgheight'初始化的整条蛇是1个蛇头加上两个蛇身'这里蛇各段坐标Dim snakeStartHead As New clsSnakeSingleBodysnakeStartHead.x = 40snakeStartHead.y = 0snakeStartHead.foreColor = Color.RedDim snakeStartBody1 As New clsSnakeSingleBodysnakeStartBody1.x = 20snakeStartBody1.y = 0snakeStartBody1.foreColor = Color.BlueDim snakeStartBody2 As New clsSnakeSingleBodysnakeStartBody2.x = 0snakeStartBody2.y = 0snakeStartBody2.foreColor = Color.BluesnakeStartHead.nextBody = snakeStartBody1snakeStartBody1.nextBody = snakeStartBody2snakehead = snakeStartHeaddirection = "right"Call DrawSnake(g)End SubPublic Sub move(ByVal g As Graphics, ByVal food As clsFood)'判断是否死亡Dim isDead As Boolean = False'状态Dim skState As String = "normal"'判断是否吃到食物Dim isEat As Boolean = False'保存头部坐标Dim previewX, previewY As IntegerpreviewX = snakehead.xpreviewY = snakehead.y'变化后的头部坐标Dim nextX, nextY As IntegernextX = snakehead.xnextY = snakehead.y'获得头部坐标,并判断头部是否碰撞,是否碰到身体Select Case directionCase "up"'是否碰到四周If (snakehead.y - moveStep >= 0) ThennextY = snakehead.y - moveStepElseskState = "wall"Exit SelectEnd If'是否碰到自己If checkCross(snakehead.x, snakehead.y - moveStep) = False ThennextY = snakehead.y - moveStepElseskState = "body"Exit SelectEnd If'是否碰到食物If nextX = food.x AndAlso nextY = food.y ThenskState = "food"Exit SelectEnd IfCase "down"'是否碰到四周If snakehead.y + moveStep + snakehead.height < bgHeight ThennextY = snakehead.y + moveStepElseskState = "wall"Exit SelectEnd If'是否碰到自己If checkCross(snakehead.x, snakehead.y + moveStep) = False ThennextY = snakehead.y + moveStepElseskState = "body"Exit SelectEnd If'是否碰到食物If nextX = food.x AndAlso nextY = food.y ThenskState = "food"Exit SelectEnd IfCase "left"'是否碰到四周If snakehead.x - moveStep >= 0 ThennextX = snakehead.x - moveStepElseskState = "wall"Exit SelectEnd If'是否碰到自己If checkCross(snakehead.x - moveStep, snakehead.y) = False ThennextX = snakehead.x - moveStepElseskState = "body"Exit SelectEnd If'是否碰到食物If nextX = food.x AndAlso nextY = food.y ThenskState = "food"Exit SelectEnd IfCase "right"'是否碰到四周If snakehead.x + moveStep + snakehead.width < bgWidth ThennextX = snakehead.x + moveStepElseskState = "wall"Exit SelectEnd If'是否碰到自己If checkCross(snakehead.x + moveStep, snakehead.y) = False ThennextX = snakehead.x + moveStepElseskState = "body"Exit SelectEnd If'是否碰到食物If nextX = food.x AndAlso nextY = food.y ThenskState = "food"Exit SelectEnd IfEnd SelectSelect Case skStateCase "wall"'如果碰到墙壁,那么触发snakeCrossWall事件food.drawFood(g)Call DrawSnake(g)RaiseEvent snakeCrossWall()Case "body"'如果碰到自身,那么触发snakeCrossBody事件food.drawFood(g)Call DrawSnake(g)RaiseEvent snakeCrossBody()Case "food"'如果吃到食物,那么触发snakeEat事件Dim newsnakehead As New clsSnakeSingleBody()newsnakehead.x = food.xnewsnakehead.y = food.ynewsnakehead.foreColor = Color.Redsnakehead.foreColor = Color.Bluenewsnakehead.nextBody = snakeheadMe.snakehead = newsnakehead'身体其他部分不动Call DrawSnake(g)'触发snakeEat事件RaiseEvent snakeEat()Case "normal"'向前移动snakehead.x = nextXsnakehead.y = nextYDim currentX, currentY As Integer'移动身体各部分Dim snakesinglebody As clsSnakeSingleBody = snakehead.nextBodyDo While IsNothing(snakesinglebody) = FalsecurrentX = snakesinglebody.xcurrentY = snakesinglebody.ysnakesinglebody.x = previewXsnakesinglebody.y = previewYpreviewX = currentXpreviewY = currentYsnakesinglebody = snakesinglebody.nextBodyLoopfood.drawFood(g)Call DrawSnake(g)End SelectEnd Sub'判断头部是否和身体碰撞Private Function checkCross(ByVal x As Integer, ByVal y As Integer) As Boolean'Dim snakesinglebody As clsSnakeSingleBody = snakehead.nextBodyDo While IsNothing(snakesinglebody) = FalseIf x = snakesinglebody.x AndAlso y = snakesinglebody.y ThenConsole.WriteLine("碰撞:" & x & " " & y)Console.WriteLine(snakesinglebody.x & " " & snakesinglebody.y)Return TrueEnd Ifsnakesinglebody = snakesinglebody.nextBodyLoopReturn FalseEnd FunctionPrivate Sub DrawSnake(ByVal g As Graphics)'绘制头部Dim fillBrush As New SolidBrush(snakehead.foreColor)g.FillRectangle(fillBrush, New Rectangle(snakehead.x, snakehead.y, snakehead.width, snakehead.height))'绘制身体Dim snakesinglebody As clsSnakeSingleBody = snakehead.nextBodyDo While IsNothing(snakesinglebody) = FalsefillBrush.Color = snakesinglebody.foreColorg.FillRectangle(fillBrush, New Rectangle(snakesinglebody.x, snakesinglebody.y, snakesinglebody.width, snakesinglebody.height))snakesinglebody = snakesinglebody.nextBodyLoopEnd Sub
End Class


3、clsFood类

Public Class clsFoodConst width As Integer = 20Const height As Integer = 20'食物颜色Property foreColor As Color'食物横坐标Property x As Integer'食物纵坐标Property y As IntegerSub New(ByVal g As Graphics, ByVal snake As clsSnakeBody, ByVal bgwidth As Integer, ByVal bgheight As Integer)'设置颜色foreColor = Color.Green'设置坐标Call getPos(snake, bgwidth, bgheight)'绘制食物Call drawFood(g)End Sub''' <summary>''' 得到食物的坐标位置''' </summary>Private Sub getPos(ByVal snake As clsSnakeBody, ByVal bgwidth As Integer, ByVal bgheight As Integer)Dim widthsection As Integer = (bgwidth \ 20)Dim heightsection As Integer = (bgheight \ 20)Dim rnd As New RandomDim posx, posy As IntegerDim hasCross As BooleanDohasCross = Falseposx = 20 * rnd.Next(widthsection)posy = 20 * rnd.Next(heightsection)If (posx = snake.snakehead.x) AndAlso (posy = snake.snakehead.y) ThenhasCross = TrueElseDim snakesinglebody As clsSnakeSingleBody = snake.snakehead.nextBodyDo While IsNothing(snakesinglebody) = FalseIf (posx = snakesinglebody.x) AndAlso (posy = snakesinglebody.y) ThenhasCross = TrueExit DoEnd Ifsnakesinglebody = snakesinglebody.nextBodyLoopEnd IfLoop While hasCross = Truex = posxy = posyConsole.WriteLine(posx & ":" & posy)End SubPublic Sub drawFood(ByVal g As Graphics)'绘制食物Dim fillBrush As New SolidBrush(foreColor)g.FillRectangle(fillBrush, New Rectangle(x, y, width, height))End SubEnd Class


4、窗体代码

Public Class Form1Const boxWidth As Integer = 20Const boxHeight As Integer = 20Dim WithEvents snake As clsSnakeBodyDim food As clsFoodDim snakeCount As IntegerPrivate Sub DrawLines(ByVal g As Graphics)Dim penGrid As New Pen(New SolidBrush(Color.Black), 1.0)For i As Integer = 0 To picBackGround.Width Step boxWidthg.DrawLine(penGrid, New Point(i, 0), New Point(i, picBackGround.Height))NextFor j As Integer = 0 To picBackGround.Height Step boxHeightg.DrawLine(penGrid, New Point(0, j), New Point(picBackGround.Width, j))NextEnd SubPrivate Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.ClickDim g As Graphics = picBackGround.CreateGraphicsg.Clear(Color.White)snake = New clsSnakeBody(g, picBackGround.Width, picBackGround.Height)food = New clsFood(g, snake, picBackGround.Width, picBackGround.Height)snakeCount = 0Timer1.Start()End SubPrivate Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.TickDim g As Graphics = picBackGround.CreateGraphicsg.Clear(Color.White)snake.move(g, food)End Sub'这个对方向按键无效Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDownEnd SubProtected Overrides Function ProcessDialogKey(keyData As Keys) As BooleanSelect Case keyDataCase Keys.UpIf snake.direction = "up" Or snake.direction = "down" Then Exit Selectsnake.direction = "up"Case Keys.DownIf snake.direction = "up" Or snake.direction = "down" Then Exit Selectsnake.direction = "down"Case Keys.LeftIf snake.direction = "left" Or snake.direction = "right" Then Exit Selectsnake.direction = "left"Case Keys.RightIf snake.direction = "left" Or snake.direction = "right" Then Exit Selectsnake.direction = "right"Case Keys.ATimer1.Stop()Case Keys.STimer1.Start()Case ElseReturn MyBase.ProcessDialogKey(keyData)End SelectEnd Function'碰到墙壁Private Sub snake_snakeDead() Handles snake.snakeCrossWallTimer1.Stop()MessageBox.Show("碰到墙壁")Call gameover()End Sub'吃到食物Private Sub snake_snakeEat() Handles snake.snakeEatDim g As Graphics = picBackGround.CreateGraphicsfood = New clsFood(g, snake, picBackGround.Width, picBackGround.Height)snakeCount += 10lblCount.Text = snakeCountEnd Sub'碰到自身Private Sub snake_snakeCrossBody() Handles snake.snakeCrossBodyTimer1.Stop()MessageBox.Show("碰到自身")Call gameover()End Sub'结束本局Private Sub gameover()End SubEnd Class

六、运行结果

七、遗留的问题
1、蛇死亡(超过边界或者碰到自身)时,按下“S”按键,蛇就复活了。
解决办法:在按下S按键时检查snake是否已经挂了,如果挂了,S按键就不再发挥作用。
2、在检测蛇头和蛇身碰撞时,实际上由于代码中我们仅仅预测了蛇头的下一步坐标位置,而没有预测蛇身每一段的下一个坐标位置,所以代码中如果蛇头恰好碰撞蛇尾(最后一段蛇身),在实际上蛇头和蛇尾同时前进,是碰不到的。
解决方法:在蛇头和蛇身碰撞时,将蛇身每一段的坐标变化也考虑进去,在检测是否碰撞。
3、当蛇占据整个游戏区域时,并没有结束游戏,因为没有相关代码。
要给比较简单的解决方法:获得游戏区域的大小,计算可以分隔的方块(蛇身)数量,如果蛇身长度等于方块总数,那么表示已经占据整个游戏区域。

 

相信各位粉丝看了以上内容,可以熟练地做出属于自己的贪吃蛇程序。

 

由于.net平台下C#和vb.NET很相似,本文也可以为C#爱好者提供参考。

学习更多vb.net知识,请参看vb.net 教程 目录

目前我已经将VB.Net的教程汇集到《Visual Basic.Net 循序渐进》,喜欢编程的朋友不要错过。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/37018.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用python自带的tkinter做游戏(一)—— 贪吃蛇 篇

用python自带的tkinter做游戏&#xff08;一&#xff09;—— 贪吃蛇 篇 本人新手&#xff0c;刚自学python满半年&#xff0c;现分享下心得&#xff0c;希望各位老手能指点一二&#xff0c;也希望和我一样的新手能共勉&#xff0c;谢谢~ 大家都知道用python做游戏用的比较多的…

python笔记—>贪吃蛇游戏制作

1、安装需要的库pygame Python Pygame 是一款专门为开发和设计 2D 电子游戏而生的软件包&#xff0c;它支 Windows、Linux、Mac OS 等操作系统&#xff0c;具有良好的跨平台性。Pygame 由 Pete Shinners 于 2000 年开发而成&#xff0c;是一款免费、开源的的软件包&#xff0…

手把手教你使用 Python 制作贪吃蛇游戏

贪吃蛇游戏是有史以来最受欢迎的街机游戏之一。在这个游戏中&#xff0c;玩家的主要目标是在不撞墙或不撞墙的情况下抓住最大数量的水果。在学习 Python 或 Pygame 时&#xff0c;可以将创建蛇游戏视为一项挑战。这是每个新手程序员都应该接受的最好的初学者友好项目之一。学习…

使用pygame制作贪吃蛇小游戏

使用pygame制作贪吃蛇小游戏 开发基本思路效果展示 具体实施步骤制作窗口&#xff0c;插入音频与图片绘制蛇与果实按键控制生成食物死亡设置其他设置 整体代码 开发基本思路 1、制作窗口&#xff0c;插入音频与图片 2、绘制蛇与果实 3、按键控制 4、生成食物 5、结束判断 效果…

应对新的挑战!ChatGPT将如何改变多域作战?

​公众号博主推送内容&#xff0c;未经许可&#xff0c;不得转载或者引用。 原文&#xff1a;Exploring the Possibilities of ChatGPT in Rugged Military AI Applications 《ChatGPT&#xff1a;利用最先进的技术支撑多域作战》 ChatGPT是一款基于GPT-3大型自然语言模型的…

Unity 接入巨量引擎今日头条广告投放SDK

巨量引擎 头条上报SDK接入 1.进入巨量引擎平台 ① 下载头条上报SDK SDK接入文档 我用的是5.3.0的官网上是3.3.12 SDK下载完了之后。 ②将unity工程导出android 工程。然后将下载的aar包导入UnityLibrary/libs文件夹下&#xff08;如图文件夹&#xff09; 并在改build.gradle文…

字节跳动 AI Lab 火山翻译在 WMT2022 非洲语向任务夺得桂冠

动手点关注 干货不迷路 1. 非洲语言的现状 非洲是世界上面积第二大的大洲&#xff0c;也是世界上第二个人口大洲&#xff0c;其大陆上存在 2146 种语言——约占世界语言总数的三分之一。广袤的面积、繁多的种族&#xff0c;还有宗教传播和殖民统治的影响&#xff0c;使其成为世…

Java开发经典面试题

重写与重载的区别重写与重载的区别_重写和重载_carrie呀carrie的博客-CSDN博客。。。。。。。。。。。https://blog.csdn.net/luoro/article/details/118307852?ops_request_misc%257B%2522request%255Fid%2522%253A%2522167875749216800211587145%2522%252C%2522scm%2522%253…

2023前端面试时遇到的面试题

2023前端面试时遇到的面试题 1.数组有哪些方法 push、pop、unshift、shift、join、forEach、filter、map、find… 2.filter和some返回的分别是一个什么 filter返回的是一个数组&#xff0c;some返回的是一个布尔值 3.es6新增了哪些方法 let和cconst、模板字符串&#xff…

Promise面试题

Promise面试题&#xff0c;带你搞懂同步异步执行顺序 前置知识面试题面试题一面试题二面试题三面试题四 分析面试题一分析面试题二分析面试题三分析面试题四分析 前置知识 Promise中的then方法 then&#xff1a;指定用于得到成功value的成功回调和用于得到失败reason的失败回调…

移动版OpenAI,手机的杀手还是救星?

AI登陆手机&#xff0c;难度因人而异。 原本小编还以为「2023年是属于元宇宙的一年」&#xff0c;实在没想到以ChatGPT为代表的AI技术能在这么短的时间内抢走元宇宙的风头&#xff0c;成为2023年的技术关键词。从OpenAI到搜索引擎、从绘图工具到游戏公司&#xff0c;几乎所有「…

领导讲话云山雾罩像糊弄鬼,这些职场潜规则你知道吗

上一篇&#xff1a;入职银行科技部半年&#xff0c;我已经丧失跳槽的能力了&#xff01; 混迹职场时间久了&#xff0c;就会发现领导职务越高&#xff0c;说话越有水平&#xff0c;也有高度&#xff0c;可是有时让你听的却是云山雾罩&#xff0c;不知道该如何应对。 聪明的人能…

代码的艺术(一)

目录 代码整洁的必要性 为什么要有代码 为什么要写得整洁 整洁的艺术 混乱的代价 整洁的定义 规整端正&#xff0c;符合规范 简洁精炼&#xff0c;便于阅读 什么是整洁的代码 怎样写出整洁的代码 不断改进 不要糊弄 具体做法 有意义的命名 使用函数 注释 勤加…

〖程序员的自我修养 - 优质简历篇①〗- 知己知彼,面试官是如何分析一份简历的

人之所以会觉得迷茫&#xff0c;本质上是欠缺对自己的一个控制力、识别庞杂信息、去伪存真的独立思考能力与认知能力。 说明&#xff1a;该文属于 程序员的自我修养 专栏&#xff0c;购买任意白宝书体系化专栏可加入易编程社区&#xff0c;早鸟价订阅模式除外。福利&#xff1a…

chatgpt赋能python:Python抠图教程:用代码实现高效抠图

Python 抠图教程&#xff1a;用代码实现高效抠图 什么是抠图&#xff1f; 在设计、美术、广告等领域中&#xff0c;经常需要把一张图片中的某个物体或人物单独提取出来&#xff0c;以便于进行后续的处理、叠加、合成等操作。这个过程就叫做抠图。 传统的抠图方式需要用到PS、…

ChatGPT大升级?它去考研能考上物理所吗?

3月15日本应是小编勤勤恳恳搬砖的平凡一天&#xff0c;但小编刚睡醒就发现朋友圈被ChatGPT刷屏了&#xff1a; 新版GPT-4震撼发布&#xff01; 大升级&#xff01;强到爆炸&#xff01; 这些字眼引起了我的兴趣&#xff0c;于是小编迅速入(ke)手(jin)了gpt-4&#xff0c;经过简…

快速构建一个简单的对话+问答AI (上)

文章目录 前言part0 资源准备基本功能语料停用词问答闲聊语料 获取 part01句的表达表达one-hot编码 词嵌入大致原理实现简单版复杂版如何训练 转换后的形状 part02 循环神经网络RNNRNN投影图RNN是三维立体的 LSTM&GRU part03意图识别分词FastText分类FastText网络结构优化点…

Postman请求具有拦截器(登录限制)接口

Postman请求具有拦截器&#xff08;登录限制&#xff09;接口 方法一&#xff1a;注释掉拦截器。 方法二&#xff1a;使用cookie信息 1.登录登陆页获取cookie信息 2.正式调用接口

postman访问提示限制

postman访问http://10.10.171.204:9090/platform/honda/addRtmVehicleTbox提示限制的原因&#xff1a; 被拦截 Websense 类别“Private IP Addresses”已筛选。解决办法&#xff1a; 关掉postman代理设置

PostMan如何模拟get请求

PostMan为调试接口提供了方便的操作&#xff0c;可以配置各种参数&#xff0c;那么使用postman如何模拟get请求&#xff0c;本文对此问题进行简要讲述。 工具/原料 Postman 方法/步骤 1 打开postman&#xff0c;创建一个新的请求。这两有两种方式创建&#xff0c;第一种&…