1、贪吃蛇游戏设计
1|0WavesBrightt - 贪吃蛇 - 开发
2|00、游戏设计核心部分解析(后期补充)
- 当看到这片区域的时候,整个游戏的开发差不多已经结束了,在结束之前,我对当前项目做一个总结
- 我们的游戏效果是通过GUI功能实现的,而将小蛇绘制上去,则是在GUI当中的面板类=>JPanel实现的
- JPanel当中提供了许多的方法供我们在这个面板上面进行图片的绘制
- 图片资源源于网络
- 整个小蛇的身体坐标由二维数组构成,食物也是同理的
- 要达成小蛇的动态移动和通过键盘控制方向,需要借助两个东西
- 一是定时器
- 二是键盘监听
- 下面就是对于整个项目开发过程当中的每个点的总结
3|01、游戏素材获取
3|11.1、图片素材获取
3|21.2、游戏概念
- 如何让游戏动起来?
- 首先我们了解任何一款游戏的时候,游戏是动画,蛇会动,无论是我们玩的游戏还是什么,都有一个概念,叫做帧,游戏是一帧一帧运行的
- 那么我们的 时间片 足够小,例如一秒30帧,一秒60帧,这样的话,他一个静态的图片,他由于连续转动,也可以达到类似动画annotion的效果,能够让咱们的游戏,动起来
- 如何实现的呢,通过定时器实现,这个定时器让页面不断地刷新,从而让游戏动起来,达成动画的效果
- 键盘响应
- 我们需要根据键盘的监听,来对贪吃蛇进行操控
- 如何才做到上述的要求?
3|31.3、逻辑布局
- 实际上就是一张一张静态的图片通过切换组合形成了我们眼前的这些游戏动画
- 那么通过键盘监听,我们可以对小蛇的行动方向进行改变
- 当小蛇吃到所谓的食物的时候,让小蛇的身体变长,这就是我们这个游戏的核心理念
4|02、绘制静态窗口
4|12.1、创建demo
这是一个普通的java项目
4|22.2、项目结构
4|32.3、GUI编程设计
- 我们整体的页面是使用GUI进行设计的
- GUI也被称作窗体,我们可以在这个窗体当中,通过绘制面板,绘制图形图像,来展示我们的贪吃蛇游戏
- 下面是关于GUI窗体,JFrame的窗口设计
5|03、外部数据类设计
5|14.1、概述
- 我们是不是在静态资源当中存储了图片,但是这个图片需要在GUI页面当中成功加载出来需要经过如下步骤
- URL:首先我们需要读取到这个图片在项目当中存储的位置,这里使用的是相对路径,且只能使用相对路径
- ImageIcon:该对象就是在GUI当中的图片对象了,该对象的实例化方法需要一个URL参数,这个URL参数就是上述方法的参数
- 这是我项目当中的statics目录
5|24.2、一个图片文件转换成ImageIcon的步骤
1、概述
- 图片类 =URL,可以获取到图片的资源,定位图片地址
- ps:通过getResource可以获取到我们当前项目下的图片,需要传递的参数为图片的相对路径
- 转换成了URL对象后我们才能通过这个URL对象转换成ImageIcon对象
- 将我们URL 定位 到的 图片地址 绘制成一张图片,然后在通过面板接口Jpanel当中提供的函数,将图片绘制到面板上
- ImageIcon 图片
- new ImageIcon(headerUrl),实例化图片对象,需要的参数为URL对象解析的图片地址
- 这样我们就可以在面板Panle上绘制我们的图片了
2、代码设计
5|34.3、代码改进
1、概述
- 上述只是简单的设置了一张图片,但是图片的库存当中有很多图片
- 如果按照上述的代码进行图片的转换,那么我要设置多少个变量才行?
- 这里想到了使用Map集合类型,以图片的前缀作为键名,转换URL对象集合和ImageIcon对象集合
- 首先创建上述两个集合我们得手动创建一个数组,这个数组是静态数组,我们会对这个数组进行迭代得到我们组装的URL=>Map集合
- 在通过迭代URL集合从而转换为我们最终需要的ImageIcon集合
2、设计
1、静态数组设计
2、URL和ImageIcon的Map集合设计
3、URL集合的数据填充
- 实例化一个Map集合<String,URL>,获取当前类下的静态资源数组对象
- 对数组进行迭代,对迭代项进行字符串切割=> split,因为是根据 点. 进行切割的,所以要注意转义字符的使用
- 在迭代过程当中实例化URL对象,将我们字符串切割的前缀 + "/statics/"进行拼接得到我们最后的URL实例化对象
- 将前缀设置为Map集合的键名,URL设置为value值,这样我们整个数组的URL都获取到了
- 通过迭代这个URL集合,我们可以得到最终需要转换的ImageIcon对象集合
4、ImageIcon集合数据填充
- 迭代我们当前类的URL集合
- 创建Map集合<String,ImageIcon>
- 获取value值
- 实例化ImageIcon对象,将value值传递进去
- 以URL的key值作为我们ImageIcon集合的键值,将实例化的ImageIcon传递进去
5、设置构造器,此对象被new出来的时候加载我们上述设计的两个方法
6|04、绘制小蛇
6|14.1、绘制游戏面板 => Jpanel
- JPanel是swing包下的面板类容器,我们可以在这个面板容器当中的绘制我们的游戏内容
- 并且最主要的一点就是,JPanel是可以加载在JFrame对象当中的,这也是我们绘制面板对象的原因
- JPanle类设计
6|24.2、按键监听
-
与我们键盘输入的Scanner不一样,接下来的这个键盘监听是实时监听的,我们需要这个自定义面板的基础类上,去实现一个接口 => KeyListener
- KeyListener:键盘监听对象,是event包下的接口
- 我们需要对这个接口内部的方法进行实现,从而完成对我们键盘事件的监听
-
实现这个接口需要实现该接口当中的三个方法
-
6|34.3.继续绘制面板
1、概述
- 首先实例化我们刚刚创建的外部数据类对象SnakeData,我们需要从这个数据类对象当中获取到我们小蛇的头部,身体,还有顶部标题的图片资源,并将其绘制到面板上
- 在面板类当中定义如下变量
2、设置面板类的构造器,初始化小蛇各类参数信息
- 初始化小蛇的身体长度
- 初始化小蛇的头部坐标
- 初始化小蛇的第一节身体长度坐标
- 初始化小蛇第二节身体长度坐标
- 初始化小蛇的头部方向=> 默认为右边 => R
代码设计
6|44.4、绘制面板内容
1、概述
- 从之前的讲述当中我们知道,继承自这个面板类JPanle,我们如果要自己绘制面板,我们需要重写这个实现类当中的方法=>paintComponent
- 在这个方法当中,我们可以通过 画笔对象 => Graphics g,来对我们的面板进行绘制
- 那么接下来我们在画板类当中进行了如下操作
- 实例化外部数据类对象,我们需要从该对象中获取到封装完毕的Map集合 => ImageIcon图片类型对象集合
- 绘制我们面板的背景颜色
- 绘制顶部的标题栏 => 通过集合当中指定key值的图片,借助画笔,将他们绘制到面板上
- 绘制当前游戏区域 => g.fillRect(指定xy轴坐标,宽高大小),这里我们设计的是850宽600高,小蛇一节身体的长度是25像素
- 绘制我们的小蛇,和广告栏一样,但是需要对小蛇的头部进行判定,头部的头像有四个图片,根据四个方向来渲染头部的四个方向
- 绘制小蛇的身体,使用迭代进行绘制,为什么要用循环?小蛇是要吃食物的,吃食物身体长度就会增加,如果不用迭代那么身体的长度就固定死了
- 判断当前游戏是否开始,gameStart == false?如果当前游戏没有开始,则在面板上绘制字符串提示内容
2、代码设计
7|05、键盘监听
7|15.1、设置键盘监听事件
1、概述
- 通过三个键盘事件,我们这里应该选择使用keyPressed事件进行重构=> ps:键盘按下
- 通过事件源对象KeyEvent,获取我们输入的键盘信息
- 对键盘信息的值进行判断,如果当前按下的键,为空格,那么就将我们的gameStart取反即可
2、代码设计
7|25.2、完善构造器当中的方法
1、概述
- 获取键盘的监听事件,目的是为了让键盘输入焦点能够聚集在游戏上面
- 设置监听的对象是谁,KeyListener这个接口的实现类,也就是我们自定义的面板类对象,所以是this
2、代码修改
8|06、定时器Timer
8|16.1、概述
- swing包下的Timer对象,对其进行实例化
- 实例化需要传递以下几个参数
8|26.2、参数设计
0.1s刷新一次,监听对象为我们设计的这个面板对象
8|36.3、设置监听内容
1、概述
- 对接口(ActionListener)的actionPerformed方法完成事件的监听,进行重构
- 在这里我们会实现,游戏启动时,小蛇身体的移动
2、小蛇移动图解
3、代码设计
- 我们定义了一个Timer类型的变量,定义这个Timer变量后,我们需要实现awt.event包下的ActionListener接口当中的方法actionPerformed,通过设置该方法,我们在游戏页面开始的时候就可以对面板进行刷新帧率的判断了
8|46.4、构造器改进,定时器启动=>start()
该方法为定时器的启动方法,不启动的话定时器是无法对面板进行刷新的
构造器改进
9|07、控制小蛇的方向
9|17.1、概述
- 我们需要继续在键盘监听事件当中完善我们的键盘输入方法
- 我们需要监听的按键有如下几个
- 空格键:管理游戏面板展示和开始
- 上:小蛇往上移动
- 下:小蛇往下以偶定
- 左:小蛇往左移动
- 右:小蛇往右边移动
- 0 : 使用神秘力量
- 上述变量如果进行if-else的话显然太麻烦了,而且需要做很多无谓的判断,所以我们这里采用switch进行判断
- 我们设计的事件有如下几个
- 在对方向进行判断的时候,我们需要增加一个条件
- 当进行方向位移判断的时候,你要移动的方向,不能与上一次移动的方向相反,否则就会出现头撞击身体的情况,这显然是不允许的
- 当然,经过我后续的测试,还是发现了一个bug,暂时没时间解决,下面会单独提出来讲一下
9|27.2、设计事件
1、空格
2、上
3、下
4、左
5、右
9|37.3、小蛇身体的移动=>定时器代码修改
1、概述
- 在我们原先定时器当中,我们小蛇默认是一直朝右边移动的,但是我们现在通过键盘输入对小蛇的方向
- 我们自然可以控制小蛇在移动时候的头部方向坐标判断了
- 由于switch本身占据的代码行数比较多,我们将小蛇头部方向的移动进行了一个单独的封装
- 在进行定时器判断的时候,只需要将当前的小蛇头部方向传递给该函数即可
2、代码图解
我们这里依旧采用switch进行判断,到达边界值的时候,进行了一点游戏改化,不把小蛇弄死,从另一边冒出来
2.1、小蛇朝右边移动
2.2、小蛇朝左边移动
2.3、小蛇朝上移动
2.4、小蛇朝下移动
3、代码设置
10|08、吃食物设计
10|18.1、概述
- 按照之前的开发模式,现在我们想添加一个食物就很简单了
- 设计当前这个食物的坐标,采用随机数种子进行设计 =>Random
- 在构造方法当中初始化我们的食物坐标
- 在面板绘制当中,绘制我们的食物
- 在定时器的方法当中追加对食物的判断
10|28.2、新增变量,初始化数据方法的修改
1、新增变量
foodCoordinateX和foodCoordinateY
2、init方法的修改
- 为什么前面要加一个25,当出现随机数种子为25 * 0的时候,左边保底都有一个25的格子
- 同理,当纵坐标的随机数种子为25 * 0 的时候,顶部保底都有一个25的格子,也就是 50 + 25 ,广告的高度 + 25(一个格子的距离)
3、代码修改
10|38.3、面板绘制新增内容
1、概述
加一行代码就可以了,生成图片对于现在的我们来说太简单了
2、代码修改
10|48.4、定时器判定
1、概述
- 在定时器当中新增一条判断
- 在游戏开始的时候,如果当前小蛇,蛇头的坐标和食物的坐标重合
- 那么小蛇的身体长度就增加一位
- 然后我们重新生成随机数种子=>随机生成食物坐标
2、代码设计
11|09、小蛇死亡判定
11|19.1、概述
- 新增一个判定,小蛇的死亡状态判定
- 当小蛇的脑壳,与任何一节身体的坐标值相等的时候,小蛇直接GG,并出现对应的死亡提示
- 如何实现上述的概念?
- 新增全局变量snakeStatus,默认值为false,代表小蛇当前未死亡
- 在小蛇数据初始化方法当中,初始化小蛇的状态
- 在绘图面板当中,新增小蛇的死亡判定文字描述,当小蛇死亡的时候,游戏停止
- 完善定时器当中的判断
11|29.2、新增变量,初始化小蛇状态
11|39.3、画板当中绘制游戏失败的图标
11|49.4、定时器当中新增死亡判断
- 当小蛇的头部坐标,和小蛇,除了头部以外的身体坐标重复的时候
- 判定当前小蛇死亡,并将当前的游戏状态设置为暂停
- 除了头部以外的身体,使用迭代判断
- 判断条件为,坐标重合
- 对定时器运行条件,新增条件,定时器的运作,除了需要游戏开始,还需要当前小蛇的死亡状态为false
11|59.5、点击空格重新开始游戏
- 点击空格的时候,新增判断
- 如果当前小蛇死亡状态为true,那么就重新调用小蛇初始化方法,然后再开始游戏
- 如果小蛇当前死亡状态为false,那么就按照之前的判定,对游戏状态进行取反即可
12|010、得分功能添加
12|110.1、概述
- 新增得分功能,该功能可以计算当前蛇的身体长度和得分分数,默认吃一个食物得10分
- 新增全局变量score,该变量表示分数变量
- 在数据初始化方法=> initGameData当中新增初始化分数
- 在画板当中绘制我们的得分项和长度判定
- 在定时器函数当中追加得分分数加成
12|210.2、新增变量和初始化数据函数追加
12|310.3、面板绘制当中绘制当前得分项和身体长度
12|410.4、定时器当中追加对分数的判定
1、概述
分数如何判定?肯定是吃到食物以后对我们的分数进行累加即可
2、代码追加
13|011、bug产生
13|111.1、概述
- 我之前编写关于头部移动的代码的时候,增加过一条这样的判断
- 即,在头部进行转向的时候,你按键输入的方向,不能为与你当前移动方向相反的键位
- 例如现在小蛇正在往下走,你输入了一个向上走的指令,蛇头直接180度偏转,那不是直接死掉
- 代码写好了以后,确实不会按照相反方向进行位移了,但是测试的时候还是出现了上面这种情况
- 为什么呢?这个小蛇很明显就是相反位移导致的死亡
- 出现这个情况的原因是,按键输入的速度太快了
13|211.2、如何解决呢,有空解决
14|012、神秘力量
14|112.1、概述
- 小蛇要吃到这个食物,需要通过输入键盘的事件来进行判定,但是输入键盘的目的是为了什么?修改当前蛇头的方向,他只是修改这个蛇头方向的字符串而已。
- 吃到食物以后,才会生成新的食物坐标
- 那么以上图为例,我如果要吃到这个食物,有很多种方式,我这里就选择,控制一个方向来吃掉这个食物
- 怎么自动输入?
- 先不考虑运行的算量关系,我相信我添加这么一个小功能,应该还是能做到的
- 我现在需要考虑的是,这个脚本,应该在什么时候运行?脚本运行的时机,修改方向的时机是什么时候?
- 食物是一开始初始化变量的时候,生成的坐标,往后,吃到食物,生成坐标是在定时器的方法当中生成的
- 可以通过单方向修改吃到食物,但是会出现蛇的身体过长导致脑壳撞到身体
14|212.2、上手
This is 神秘力量 の 雏形
看不懂是不是,没关系,一步一步来
- 我认为运行脚本这个方法应该加在定时器的函数当中
- 在生成随机食物的判定当中,是这么写的
- 我这里就可以设计,没有生成新的随机食物的时候,运行一个脚本函数,这个脚本函数需要有一个开关,即,决定你这个玩家,是否要开启当前脚本
14|312.3、脚本设计 => plugIn
1、概述
- 在运行这个脚本的时候,我们脚本会做一次运算,这个运算是为了计算,小蛇要吃到这个食物,需要输入的方向键
- 我们只需要输入一次方向键即可吃到食物,但具体是上左下右呢,就要看我们具体的运算了
- 这里我想到的是,根据当前蛇头的方向来判断小蛇吃到这个食物需要输入的方向
2、图解
2.1、蛇脑壳方向是上或者下情况的时候
- 当前蛇头是上或者下的,那么我只需要通过输入左、右方向键即可吃到这个食物
- 那具体怎么吃呢?计算当前蛇头的横坐标与食物横坐标的差值,脑壳方向是上、下的时候才计算横坐标的差值哈
- 如果当前横坐标差值为负值,那么就说明食物在脑壳的左边,否则就在右边
2.2、蛇脑壳方向是左、右的时候
- 同理,当前蛇脑壳的方向是左或者右,那么我要吃到这个食物只需要输入上、下方向键即可吃到
- 怎么吃?蛇头上下计算横坐标的差值,那么蛇头方向左右的时候就计算纵坐标差值即可
3、根据图解设计代码
- 我们上述的计算方式,只需要运行一次即可,为什么只需要运行一次?我一次就能得到结果我为啥还要每次调用的时候都计算?没必要吧
- 所以计算下一次运行方向这一块的算法,运行一次即可,设计一个开关控制他,在哪里设计这个开关?
- 在没吃到食物,并且,脚本开关是开启的时候,在第一次调用该方法之后,取消位置计算
脚本运行方向算法设计
4、下一次的方向计算出来了,那么什么时候改变方向?
- 定时器是每次都会调用我们这个方法的,我们这个方法除了顶部需要判断下一次的运行方向之外
- 还需要不断地判断当前坐标值(X,Y轴的值是否和食物重复),单项判断,看图解就明白了
4.1、根据下一次运行方向判断
蛇头方向为上下,计算出下一次运动方向为左右
当蛇头的纵坐标值和食物的纵坐标值相等的时候,改变我们蛇头的方向
将 下一次运动方向的值 赋值给蛇头方向
蛇头方向为左右的时候,那么下一次运行方向为上或者下
当蛇头的横坐标和食物重合的时候,就可以修改我们当前蛇头的方向了
5、代码设计,蛇头什么时候修改方向
- 该方法不需要其他条件的约束,因为是在定时器当中运行的
- 所以会一直判断,直到条件满足为止
- 但是我这里的判断就做的比较粗略了,没有考虑最短路径等等,时间复杂度空间复杂度
- 而且有bug,蛇的身体过长就会把自己创死
6、脚本代码完整
__EOF__

本文作者:muzlei
本文链接:https://www.cnblogs.com/wavesbright/p/data.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/wavesbright/p/data.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?