项目源码:https://github.com/silence1772/JavaFX-GreedySnake
(记得点star啊,收藏一个项目最好的方式是star而不是fork)
游戏下载:贪吃蛇大作战.exe
贪吃蛇大作战.jar
这个游戏是去年写的,用来交数据结构作业的,本来在大一暑假时我就开始写,那时老师还没提具体要求,我就用unity3d配合C#写了个高仿贪吃蛇大作战,自认为效果不错,如下图:
结果等到开学了老师布置了一下具体要求,只能用Java或者C++来写,没办法重新写就重新写吧,刚好新学期学Java,书上介绍的图形界面编程库是Javafx,于是就赶工写了上面的游戏交上去,代码质量肯定是有点不足,不过还是想分享出来给大家看一下。至于unity3d版的贪吃蛇大作战,项目打包下载:点击打开链接
项目结构如上图,代码全都在src文件夹,有两个包,第一个是core包,称之为内核,代码抄袭自点击打开链接还有点击打开链接。
第二个是snake包,主要的内容和操作都在这里面,时间隔了太久也记不清具体的实现细节了,所以就直接把当时交的实验报告里的内容复制上来,因为交作业嘛,大家都懂,瞎扯的内容居多,阅读体验肯定比不上自己写的博客,还请各位看官多加包涵。
---------------------------------------------------分割线---------------------------------------------------
2.1 确定技术路线
我们选择Java作为此次开发的语言,同时选用JavaFx库来进行图形化界面编程。开发平台为windows + eclipse。
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。
2.2 游戏整体架构
游戏的主要运行界面是基于JavaFx提供的canvas(画布)API来实现的,为了编写方便,同时也为了使程序更加简洁高效,我们先编写了一个基础的游戏框架,再以此框架为基础,进行游戏功能的开发。通过对要实现的游戏功能进行初步讨论确定,以及对各项功能进行技术可行性验证后,我们最终确定了如下图所示的整体架构:
2.3 程序开发分工
......
2.4 基础框架编写
基础框架的编写基于Javafx库,Javafx已经内置在我们使用的jdk8环境中,因此直接导入即可。编写的第一步便是建立程序入口,在Javafx中,Application类是程序的入口点,该类中的launch用于启动一个独立的Javafx应用,也就是说,程序的主类要继承Application类,并且重写 (Overriding)其start方法,然后在main方法中调用其launch方法来启动程序。
public abstract class SApplication extends Application {
private Group mGroup;
private Scene mScene;
@Override
public void start(Stage primaryStage) throws Exception {
before();
mGroup = new Group();
mScene = new Scene(mGroup, SContants.WIDTH, SContants.HEIGHT);
after();
showStage(primaryStage);
}
}
以上为SApplication类代码片段(如无说明下文代码均为选取片段),该类start方法中参数Stage对象是Javafx中的一个窗体,类似舞台,而Scene对象表示在舞台下的一个场景,Group用于存储场景中各种Object。当该类启动时,JVM便创建该类的一个实例,同时调用其start方法。start方法一般用于将UI组件放入一个场景,并且在舞台中显示该场景。
有了主类后,接下来我们需要在这个舞台、场景上进行表演,Javafx提供的Canvas(画布)便是表演的组织形式,这里我们引人游戏编程中的几个概念,一个是时间线,一个是帧,一个游戏可以看成是一个无限循环运行的程序,亦即一条长长的时间线,在这个循环中每次循环便是一帧,我们定义一个SScreen类继承自Canvas类,在每一帧中均重绘场景中的对象,便形成了一个动态运行的游戏画面。
public abstract class SScreen extends Canvas implements SEvent {
public SScreen(double width, double height) {
super(width, height);
initTimeLine();
}
private void initTimeLine() {
timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
keyFrame=newKeyFrame(Duration.millis(duration),newEventHandler() {
@Override
public void handle(ActionEvent arg0) {
draw(getGraphicsContext2D());
update();
}
});
timeline.getKeyFrames().add(keyFrame);
}
}
如上,我们初始化时间线,并且在时间线上添加关键帧,在每一帧中我们都调用draw()方法和update()方法,这两个方法主要是渲染图形和刷新图形。
在这个程序中,我们把所有游戏对象都抽象成一个基类SObject,这个类主要是对象的各种属性,如长度、宽度等,同时也有一个检测是否两个Object是否接触的方法用来检测贪吃蛇是否发生碰撞。
public abstract class SObject implements SEvent {
protected DoubleProperty widthProperty = new SimpleDoubleProperty(0);
protected DoubleProperty heightProperty = new SimpleDoubleProperty(0);
protected DoubleProperty xProperty = new SimpleDoubleProperty(0);
protected DoubleProperty yProperty = new SimpleDoubleProperty(0);
protected BooleanProperty updateProperty = new SimpleBooleanProperty(true);
protected BooleanProperty visibleProperty = new SimpleBooleanProperty(true);
}
有了以上三个基础类后整个游戏框架就基本建立起来了,再来就是配置类SCcontants用来管理窗口的大小,蛇的大小等,事件类SEvent表示键盘鼠标事件。
2.5 蛇相关类编写
蛇相关类是整个程序最重要的一部分,首先对于一条蛇,我们将其看作是由一个蛇头和蛇身组成的,分别对应BaseSnake类和BaseBody类,前者负责控制蛇头按照其Direction(方向)变量移动,后者则控制蛇身移动。这里蛇身的移动策略是这样的,首先它的构造函数有一个参数为BaseSnake类,这里将两者位置属性绑定在一起,蛇身是由一个个圆形排列在一起形成的,使用类似双端队列的结构(list)存储,每次移动时,就将这一串圆形的最后一个删除,同时添加一个新的圆形到蛇头的位置,因为蛇头也在这段时间内向前移动了,这样就形成了前进的效果,当然如果是吃到了食物的话,那就只添加圆形而不删除。如下图所示:
有了这两个基础类组成蛇之后,我们还需要分别设计两个类来继承BaseSnake类,一个作为被玩家使用键盘控制的蛇UserSanke类,另一个为电脑控制的蛇AISnake类。对于玩家来说,主要是通过方向键来控制蛇,蛇的移动有八个方位,玩家使用方向键可改变基类中的Direction变量,然后基类通过计算该方向在x轴、y轴的移动量来往这个方向移动。而电脑蛇的移动策略也比较简单,每个一段时间检测一次食物在哪个方位,然后更新方向变量即可。
2.6 界面UI
界面UI比较简单,主要是调用Canvas的渲染图像api把图形画出来,包括两个类,一个是Info类,负责显示游戏进行中的各种信息,如当前得分、剩余时间等,另一个是GameOver类,负责显示游戏结束时的提示画面。
2.7 食物类与时间类
食物类主要是随机选取一个位置生成食物,当食物被吃了之后,就继续生成一个。
时间类,其实就是提供给界面UI的倒计时,这个类单独开了一个线程,在这个线程上设置递减计数,每计一次线程就休眠一秒,再把计数器声明一个方法暴露出去,这样就形成了按秒的倒计时。
2.8 主游戏场景
这个GameScreen类继承自SScreen类,是游戏的主要运行场景,这里要引人一个层的概念,即游戏画面是由一层层的对象叠加而成的,在这个类中,我们将UI层放在最上面,将蛇(包括user蛇和AI蛇)放在中间层,最底层是食物,这样叠加起来就形成我们看到的游戏画面了。
同时在这个类中,我们进行大部分的操作,如调用UI更新信息的方法,检测是否吃到食物,是否发生碰撞等。
另外,这里还设置了一个功能,就是蛇的穿墙操作,当蛇前进到边界后,它会从边界的镜像方向出现,形成了穿墙,其实就是当蛇头进入边界外后,更新蛇的位置坐标到相反方向。
2.9 程序入口
MainEnter类既是程序的入口,也是程序的流程控制器,当进入程序后,它先显示出开始界面以及开始游戏的按钮,点击按钮后,程序会把主游戏场景挂载在该类下,也就是显示了主游戏场景,开始了游戏,当游戏结束或者点击返回按钮后,程序会把主游戏场景删除掉,继而回到开始界面,形成一个完整的游戏逻辑。