《单子的魔法》第8集”偶尔进行演示”
第一話 画像が表示できました ←初回
第七話 オープンワールドという泥沼 ←前回
这是一个使用BabylonJS和PureScript自由快乐地制作游戏的日记,我会随便写一些想到的事情。
示威
我已经让角色可以四处走动了,所以准备了一个简单的演示。打开这个页面后,经过一段有点不安的加载时间后,游戏将开始。
- https://aratama.github.io/cubbit/
可以使用WASD键来进行移动。点击左上角的”add”按钮,就可以用鼠标堆放方块了,点击”remove”按钮可以删除方块。我是在Chrome上开发的,所以我认为Chrome是最佳选择,但是在我的环境中,Firefox和Edge也可以正常运行。尽管我只在我的机器上进行了测试,并且使用了WebGL,可能存在一些环境依赖,但我认为应该可以正常工作。将网页作为平台,可以方便地展示演示,这很方便。我认为没有人想使用可疑的本地应用程序。
顺便提一下,这个也可以在 Electron 上运行。虽然只是个心理问题,但是用 Electron 来运行的话就有种大作游戏的预感,让人有点兴奋。在浏览器上玩的话,虽然是同款游戏,但总觉得只是一些轻松简单的小游戏,会产生这种刻板印象,这样的刷印真可怕。
现在我已经能够建立Voxel世界并在其中行走,因此作为一个沙盒游戏框架,它基本上已经成形了。经过充分的重构之后,我打算将其与原始项目整合在一起。嗯,图形部分已经完全重写了,所以只需要继承Firebase部分…… 基本上是重新编写…… 但是,对于碰撞实现的方法,我仍然无法决定。在上面的演示中,碰撞检测非常不准确,因此会在台阶上跳起来。但最终我也必须修复这个问题。是要自己实现碰撞检测,还是引入像cannon.js这样的真正的物理引擎,这是令人烦恼的问题。使用cannon可以增加各种自由度,但另一方面,使用真正的物理引擎会更容易导致意想不到的行为,控制起来更加困难。还有性能问题可能会成为瓶颈。而自己实现虽然非常困难,但更容易进行性能调优,并且可以控制意外的行为,从这个意义上来说,实现起来更加轻松。不知道该怎么办……。
另外,世界完全停止的情况是不自然的。若不给屏幕带来一些动态,就无法营造出真实感。我想要像Minecraft的影子模组一样,让草木在风中摇曳,水面流动起来。还希望能让动物行走。因为懒得写成文章形式并提供代码,如果需要的话可以看一下我的GitHub。我希望这对于学习纯函数式编程有所帮助,所以我公开了所有源代码(但不是开源的)。纯函数式编程语言也能制作游戏!
一人稱視角對於三人稱視角
从第一人称视角开始,immersiveness会增加,但是动作的执行会变得困难,并且难以展示玩家的角色。像Minecraft这样没有故事情节的游戏完全可以只用第一人称视角,但对于新玩家来说,这样的游戏会变得不容易上手,而且我认为给玩家一些故事情节或任务会更容易玩。因此,基本上我们会采用第三人称视角,并在需要时可以切换到第一人称视角。然而,第三人称视角的处理也有很多困难的地方。
当地下进入第三人称视角后,摄像机会穿越墙壁,呈现截面图形状。这种方式看起来就像是在玩《泰拉瑞亚》一样,很容易上手,但同时也失去了地下环境所特有的封闭感。如果使用第一人称视角,这个问题就不会存在,但我们也要考虑如何解决这个问题。通常的方法是让摄像机离墙壁远一些,避免穿透,但这样做会导致摄像机晃动较大,视觉体验变得困难,而且在狭小的空间内,摄像机距离过近会让人感到更加压抑。存在很多问题需要解决。
着色器
水面目前还是果冻般的质感。Babylon提供的水渲染器无法完美运行,但我认为只需复制并修改一下就能运行,所以之后会进行实现。听说如果Babylon的水渲染器能正常工作,会有这样的感觉。顺便说一下,我也随机生成了一些草。杂乱得像便当里的“藻类”一样。
我想让它更加融入画面的感觉。该怎么办呢……。
另外有一個遠足測試。 (Mata enpitsu tesuto)
世界は16*16*16ブロックの『チャンク』に分割されて管理されるのですが、キャラクターの周囲のチャンクは自動的にロードされ、同時に遠くにあるチャンクは自動的にアンロードされていきます。このため巨大な世界を歩き回ってもメモリが足りなくなったりしないというわけです。それで、キーボードのwキーの上に乾電池を置いて数分放置するという自動化されたテストを実施したところ、水平方向のZ座標で1万メートルを超える位置に来ても落ちたりせずにちゃんとプレイできるのが確認できました。ただ、数千メートルを超える位置に来るとシャドウがおかしくなる問題が見つかりました。原因は調査中ですが、Babylonjsのバグを疑っています(babylonのバグを踏みすぎて疑心暗鬼)。
垂直方向についてもテストするためにひたすらブロックを縦に積み、これで上空1000メートルです。1000回クリックしました(手動)。
由于地面很远而无法加载,所以看不到,即使能看到,也因为雾太浓而几乎无法辨别。即使能看到,也很快会被卸载而看不见,这将减少超高层建筑的乐趣,因此我们正在考虑如何解决这个问题。
如果没有高度限制,即使在写作中也会增加问题。只读取部分块不足以判断阳光是否照射在那里。该怎么办呢……说起来,我准备很快进行大规模重构,所以测试也该自动化一下了……游戏自动测试应该怎么做呢……。
再次进行规模的概算。
在与原有项目合并之前,我会重新考虑一下在Firebase上是否能处理好数据。如果只是一个简单的聊天应用的话还好,但如果是一个世界无限开放的游戏,那在开发测试阶段就有可能瞬间就用完Firebase的免费额度,所以我觉得在这方面最好一开始就做好节省。
チャンクを読み込む際は配列で読み込むと効率がいいのでしょうが、firebaseは配列を直接扱えません。一つの方法としてはJSON.stringifyして文字列として転送してしまう方法が考えられますが、この表現にすると今度はブロックをひとつ置くたびにチャンク全体を送らなければならなくなります。1チャンクが16*16*16=4096ブロックで、カンマ区切りの数値を文字列にしてケチって表現すると、UTF8だとして1チャンクあたり10キロバイトほど。一方で、オブジェクトとしてチャンクを表現するともっと効率が悪そうです。つまり、
[0, 0, 1, ..., 0, 1]
中文:以类似这样的数组
{
"0": 0,
"1": 0,
"2": 1,
...
"4094": 0,
"4095": 1
}
以后我们将以这种对象的方式进行发送和接收。由于在游戏开始时必须接收整个区块,所以这一部分会变得相当大。然而,一旦游戏开始,只需在放置方块时更新该部分,因此与将整个区块压缩为一个字符串相比,效率应该要高得多。为了未来的考虑,我们决定将其表示为对象。
另外,如果将所有地形数据预先存储在firebase上,就不需要在客户端端生成了,但是数据量会变得过大。如果要加载到10个区块的距离,那么就需要加载21 * 21 * 21 = 9261个区块,大约是10kbyte * 9261,大约92兆字节。这样一来,稍稍走动一下,很容易就会消耗数百兆字节的存储空间。对于本地来说,这个数字并不大,但光是启动应用程序,这个数量就可能迅速耗尽firebase的1GB的免费配额。而且,除了存储空间有限,下载配额也只有10GB,如果不小心只是重复进行测试游戏,很快就会耗尽这边的配额。因此,我认为只有保存放置或删除方块的区块才是需要存储的。
写作
顺便提一下,我们使用顶点色为方块添加了一种简易的阴影效果。这被称为“环境遮蔽”,但其实就是说当物体凹陷时,光照较少,所以会变暗,就是这么简单的原理。还有一篇解说的文章。
- Ambient occlusion for Minecraft-like worlds
如果没有这个,外观会变得相当平坦,凹凸难以辨别,不仅影响外观,还会影响可玩性。物理上严格计算是困难的,但只要在模拟中看起来像样即可。虽然与上述文章中的方法有一些不同,但作者也实施了类似的操作。
それと、NotchさんによるMinecraftのライティングシステムの簡単な解説も見つけたのでメモです。まあこれだけじゃ情報が少なすぎてあんまり参考にはならない気がしますが……。
- Per request: This is how the new lighting will work
以下是故事的下一个:
- 第九話 サウンドエフェクトの作業をしてコーディングで荒んだ心を癒やします