你敢相信吗,这些游戏竟是用Java来完成的


你敢相信吗,这些游戏竟是用Java来完成的文章插图
我知道传统认为 , Java并不适合用来做游戏 , 马上我们将会谈到Java不适合做游戏的原因 , 随后我也会一一解释如何解决这些问题 , 随着时间的推移 , 以前很多认为不可能完成的事 , 现在也都已经不再是障碍了 。 比如Java做的这两款游戏:Minecraft(我的世界)Spiral Knights on Steam(螺旋骑士)
你敢相信吗,这些游戏竟是用Java来完成的文章插图
【你敢相信吗,这些游戏竟是用Java来完成的】这说明了一点 , 就是Java是可以用来做游戏滴 , 关键看使用的人如何去用 。 使用Java的优点 , 应该很多人都知道 , 开发快 , IDE支持好 , 网络支持好 , 类库多 , 跨平台等等 , 那么为什么Java并没有被用来制作游戏的几个常见理由: 1.Java的程序运行需要预先在各个操作系统上安装虚拟机; 2.Java有GC停顿 , 会阻碍客户体验; 首先第一个问题 , 这个的解决依赖于大概14年时候Java推出的JavaFX新一代图形控件 , 该控件有一个native compiling的工具 , 可以将JavaFX的代码打成不同操作系统上的独立运行的包 , 比如打成Windows上的exe文件 , 打成MacOSX上的dmg文件 , 用户拿到之后 , 双击就可以运行啦 。 而在最新版本的Java中 , JavaFX已经集成进Java , 成为Java的一部分啦 , 所以不需要单独下载JavaFX , 只需要下载Java , JDK之后 , 就能使用JavaFX , 以及native compiling工具 , 是不是很方便呢?Native Compiling打包可以参考专栏之前写的一篇文章:JavaFX的几个新特性 , 让Swing彻底过时 。 第二个问题 , GC停顿 , 这个是一个很大的topic , Java的GC里面讲究甚多 , 这里只能简单说明一下 , Java传统的GC算法呢 , 在启动GC的时候 , 会暂停整个虚拟机的执行 , 然后等GC完成之后 , 再继续执行 , 而人对于超过一定时间的停顿 , 是可以感知到的 , 一旦GC停顿超过该感知的界限 , 玩游戏的人的体验就会变差 , 嗯 , 这么说有些过于感性了 , 我们来一点数字 。 一般游戏的帧数是20帧到60帧之间 , 少数会冲到90帧 , 帧数意思就是每一秒游戏画面刷新的次数 , 60帧的意思就是一秒钟内游戏画面刷新60次 , 为什么游戏的帧数会在20-60之间呢?因为低于20帧 , 人就能看出画面的停顿 , 超过60帧 , 帧与帧之间的停顿时长 , 基本上就超出了人可以感知的范围了 , 也就是说 , 如果两个不同画面的停顿超过1/20秒 , 能看到的就是一幅一幅不同的图片 , 而不是动画 , 而要让人看到动画的效果 , 不同两个画面之间的停顿 , 至少要在1/20秒也就是50ms以内 , 而人视觉感知的极限 , 就是1/60秒 , 也就是16或者17ms , 如果停顿时间短于16ms比如是10ms , 那么人在视觉上是很难感知到的 , 也就是说 , 一秒刷新60次 , 跟一秒刷新90次 , 在人看来 , 几乎没啥差别 , 所以就能看到iphone或者android手机的广告 , 60帧如丝般顺滑 , blablabla , 简而言之 , 就是我们要把刷新画面做到一秒刷新20次以上 , 60次最佳 。 那这个时候我们就能看出来啦 , GC停顿如果超过50ms , 客户就能感受到GC , 如果低于16ms , 那就很完美 。 说明一下虽然超过50ms就能感知到 , 但是一般情况下 , 只要这种50ms级别的停顿不频繁发生 , 客户体验并不差 , 比如10min触发一次50ms的停顿 , 这有关系吗?玩个游戏又不是搞导弹拦截 , 偶尔来个50ms的卡顿会死人还是会怀孕啊?而且网游里面 , 公网的延迟经常超过50ms这个量级 , 尤其是手游的破网络 。 有技巧 , 首先要修改GC的策略 , 在新版本的Java中 , 加入了新的GC策略G1 , 该策略将会在Java9中成为缺省的GC策略 , 该策略允许设置目标停顿时间 , 解释一下目标停顿时间的意思 , 就比如我们设置目标停顿时间为10ms , 那么GC会尽可能在10ms内结束 , 如果完不成该目标 , 则暂停GC , 程序恢复执行 , 等下一次GC继续 , 如此反复 , 所以G1策略的总停顿时间 , 会超过CMS策略 , 但是每一次GC停顿 , 都会控制在一定时长以内 , 但是不能保证一定在该时长以内结束 , 只是虚拟机会尽量完成这个目标 , 这是一 。 在打包时候 , ant build file里面用fx:deploy, fx:platform和fx:jvmarg来设置JVM参数 , which包括了GC策略等参数 。 其次 , 我们要减少GC的时长 , 肿么做?将对象pool起来 , 也就是说 , 需要复用对象 , 不能频繁生成并销毁对象 , 这样非常消耗GC资源 , 会明显增加GC停顿时长 。 举个例子 , 如果我们做的是一个射击游戏 , 那么发射出去的子弹 , 我们需要pool起来 , 比如把子弹对象都放在一个list里面 , 子弹出了边界之后 , 标记对象为无效状态 , 但是并不从list中remove掉 , 这样因为有list的引用在 , 该子弹对象不会被GC掉 , 下一次发射的子弹 , 先找一遍有没有标记为无效状态的对象 , 如果用 , 则使用该对象 , 如果找不到 , 再增加一个新的对象 , 放入list , 这样子弹对象数量随着游戏的进行 , 会趋近于一个定值 , 而因为有list这个引用在 , 所以对象不会被GC掉 , 这样GC的压力就小了很多 , 即便触发了full gc , 其执行时间也在一定范围之内 , 有助于我们实现目标GC停顿时间 。 渲染时候尽可能调用底层的API , 也就是View的部分 , 请使用诸如Canvas之类的控件 , 在安卓上 , 就用SurfaceView , 在IOS上好一点 , 有SpriteKit可以使用 , 而不是其它的控件 , 比如ImageView等 , 因为普通的ImageView需要换Image的时候 , 需要重新生成一个Image对象 , 图像对象可是很大一个东西 , 频繁生成销毁这种对象 。以上是Java做游戏当中出现的一些问题 , 希望大家能够有所收获 , 学到一些知识 , 大家如果有什么不懂的问题欢迎大家提出来 , 跟我一起交流交流 。


推荐阅读