作为本人学习Java的第二篇博客,也是自己练手的第一个小项目。耗费了大量的时间和精力,遇到的问题分享给大家,希望大家可以有所收获。
因为所有的游戏都要有一个帧的概念,也就是看似动态的画面实则是由多个静态的、变化的画面组成的,当这样的画面足够多的时候,就有了一个动态的观感。这就是我理解的帧的概念。
有了这个思路,我们只要设计好贪吃蛇每一个静态画面,把它的变化连续起来就可以。
那么他们的变化有什么规律呢?
答案就是,我们把蛇在画面中的运动看做是坐标的不断变化,蛇的每一节与前一节的值都是相等的。
比如说,最开始蛇头的坐标是(1,1),蛇身的坐标是(0,1)当蛇运动的时候,蛇头的坐标变成了(2,1),那么蛇身变成(1,1),蛇的每一节一直等于他的前一节,我们利用这样的规律来制作一个贪吃蛇的小游戏。
对于 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)这行代码可以进行扩展一下;
setDefaultCloseOperation(int operation)是设置用户在此窗体上发起 “close” 时默认执行的操作。方法中的参数解释如下:
为“0”或DO_NOTHING_ON_CLOSE:
不执行任何操作;要求程序在已注册的WindowListener 对象的 windowClosing 方法中处理该操作。
为“1”或HIDE_ON_CLOSE
调用任意已注册的 WindowListener 对象后自动隐藏该窗体。此时没有关闭程序,只是将程序界面隐藏了.
为“2”或DISPOSE_ON_CLOSE
调用任意已注册 WindowListener 的对象后自动隐藏并释放该窗体。但继续运行应用程序,释放了窗体中占用的资源。
为“3”EXIT_ON_CLOSE(在 JFrame 中定义):使用 System exit 方法退出应用程序。仅在应用程序中使用。结束了应用程序。
默认情况下,该值被设置为 HIDE_ON_CLOSE。
我们这个小项目,会相应的使用一些素材,比如身体的小绿方块,四个方向的蛇头。这些素材我单独的放在一个类进行储存,方便后面的使用。
我的相应图片放在了项目文件夹的image文件夹,使用ImageIcon这个类与图片建立联系。
前面说过,我们想要让蛇运动起来,需要作出一个静态的、变化的画面,并且把他们连续起来。所以在这门我们采用了继承JPanel类,JPanel 是 Java图形用户界面(GUI)工具包swing中的面板容器类,包含在javax.swing 包中,是一种轻量级容器,可以加入到JFrame窗体中。之后在这个画板里面对蛇的被一个变化不断的绘制。
继承JPanel之后,需要对paintComponent方法进行重写,paintComponent()是swing的一个方法,相当于图形版的main(),是会自执行的。如果一个class中有构造函数,则执行顺序是先执行构造函数,再执行这个。我们想要唉主窗体显示的所有画面都要在这个函数里面完成。
此外,我们也要对小蛇默认的一些参数进行设置(例如:长度,开始的坐标等)
相关代码及代码注释如下:
这样我们就绘制了一个静态的小蛇,那么怎么让他动起来呢?
如果我们不断的改变蛇的坐标,并且不断地执行这个代码,小蛇不就动起来了?
前面说过,小蛇改变坐标的方法就是蛇的后一节一直等于前一节,我们改变蛇的方向和行走的距离,只需要改变蛇头方向就可以完成了。
所以,改变蛇坐标的代码如下:
因为我小蛇的坐标是不断的变化,我们需要让小蛇坐标变化的代码不断的运行就利用到了定时器timer的概念。
什么是定时器timer?
在java.swing包中有一个timer类,可以使用它在到达给定的时间间隔时发出通告或者说警告。在构造定时器时,需要设置一个时间间隔,并告知当达到时间间隔时需要做些什么。
如何告知呢? java的类库采用的是面向对象方法,可以将某个类的对象传递给定时器,然后定时器调用这个对象的方法。由于对象可以携带一些附加信息,所以传递一个对象比传递一个函数要灵活的多。
不过定时器需要知道调用哪个方法,那么我们可以使用接口给它提供一个规范。timer类会固定调用ActionListener接口的actionPerformed()方法。 那么我们确定功能类的时候,可以让它实现ActionListener接口。
需要注意的是actionPerformed()方法的ActionEvent参数,这个参数提供了事件的相关信息。
先上代码,看不懂不要紧
怎么实现画面的不断刷新呢?
这个问题困扰了我很久,这个方法是重写paintComponent方法,这个方法是给画面输出新的布局。但是我从来没有调用过他,但是他却可以在窗口里面不断地刷新画面。
首先paintComponent方法,并不仅是JPanel的方法,而是继承自JComponent的方法。
这个方法是被swing调用来画组件的,应用不应该直接调用paint,而应该调用repaint。paint这个方法实际上代表了三个protected的方法。 *paintComponent,paintBorder,paintChildren. *里面一次调用者三种方法来确保…(略)
其次,那么为什么不应该直接调用paintComponent而应该调用repaint呢?查了很多资料,有一个说的很简单,直接。
repaint()是重要概念,它是在图形线程后追加一段重绘操作,是安全的!是系统真正调用的重绘!所以如果你需要某个部件刷新一下界面,记得调用repaint(),千万不要直接调用paint()!
Graphics是一个抽象类,其实现大都是平台相关的,所以不容易自己创建一个graphics实例。一般graphics的实例会由依照你所在的桌面环境给出。
简答的说, repaint方法先清空了画面的内容,然后间接调用了paint方法,paint方法调用了paintComponent方法,完成了画面的刷新。
所以只要在定时器里面调用了这个方法,就会实时输出当前的画面。
此外,我们需要对小蛇的位置进行控制,这个比较好解决,设置对键盘的监听,如果键盘的输入时“↑”,与之作出蛇头向上的操作就可以了。
对键盘监听要实现KeyListener接口,重写三个方法,分别是:
在相应的方法内写自己要执行的操作就可以了。
本项目用的是keyPressed设置的是按空格键和方向键。
小蛇吃食物设置与吃食物的判断怎么处理?
首先食物的这是,可以利用随机数创建类,给食物的x,y坐标赋予随机数。
那么吃到食物怎么判断呢?
上面的代码写到小蛇是20个坐标进行行走,那么我们认为小蛇的x坐标和y的坐标分别都要食物的坐标相差19个绝对值,我们就可以认为他是最接近小蛇的位置,我们认定他被吃掉。
我也给出了其他的解法,请仔细阅读最后完整代码注释内容。
至于游戏的结束,小蛇到达边界值的判定,分数显示的设置等小功能的编写,这些内容比较简单,如果前面代码有所理解,这些都可以很好的解决。所以这里不单独给出实现代码,只说思路。
游戏的结束:当蛇头的坐标,与其中的一个蛇身的x,y坐标完全相等,我们认为他撞到了自己,游戏结束。
小蛇到达边界值:我们只需要判断蛇头的坐标到达了我们规定的坐标以外,我们给蛇头赋值窗口最开始的坐标。
分数的设置:画笔设置好参数,选择好位置,直接写就行。
避免蛇身向相反运动造成的死亡:小蛇正在向右运动,如果给予一个向左的指令,那么蛇头与蛇身坐标必然相等,则判定游戏结束。所以避免这样的操作,我们在给方向赋值的时候先判断他是不是我们不想要的操作,如果是这次给予指令不成立,等待下一次给予指令。
代码已上传csdn,一积分下载,觉得这片博文有用请支持一下老弟。
完整素材及代码下载地址
如果没有积分baidu云自提
链接:https://pan.baidu.com/s/1iVrUI1NzDbu315-AAq1zVQ
提取码:r35o
最后更新:
修复了小蛇和食物重叠显示问题
修正为小蛇和食物在同一运动路线
加快小蛇运动速度
链接:https://pan.baidu.com/s/1c8zvQH9RLRIoE57z88BZpA
提取码:75ce
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/java-jiao-cheng/17471.html