« January 2018 | Main

February 24, 2018

群星 2.0 汉化项目

年前就留意了群星会在年后发布 2.0 ,我一直在跟踪它的版本升级维护汉化 mod ,既然这次是一个大版本升级,更新的文本肯定非常多,所以对这次的工作量有了充分的心理准备。

在 2.0 发布的前一天就返回了广州,我反思了过去汉化 mod 的维护过程。虽然有 github 这个方便的工具,可以联合很多同好的力量,但每次还是弄得人很累,尤其是第一天的整理工作。

我以前的做法是,更新完游戏,把新的英文文本上传,根据 github 上的 diff 来整理中文的翻译。过去选择人肉做中文的工作是因为希望人肉把关,有时候英文原文只是调整了版式,修改了错别字,或是修正里里面提到的数字等等。那么在合并中文版的时候就可以保持原样,或顺手改过来。同时也大致了解了游戏的变更。

大量新增的条目、大幅修改的条目,通常我就在中文版中保留英文原文,做个记录,在 github 上发 issue 分类,征集人手来分头翻译。

想着大版本升级并不多,每次人肉过一遍也起了一定的校对作用,对新版本引入的新术语有了大致的了解。扫完所有的新内容后,再慢慢做翻译质量也高一些。

不过每次这么做一遍都要花掉一整个晚上的时间,其中有很多时候,感觉自己在做机械劳动。每次有这种感觉的时候就给自己找上面的借口,也安慰自己也不是经常做,一次做完就搞定了。

这次却不想这样了,即使是一次性工作,也应该先做工具来减少机械劳动量啊。不能为自己脑力想偷懒,就嫁祸到体力上。当我真的考虑清楚,写了一些脚本来减少体力劳动,发现也就是一两个小时的工作而已。主要就是写脚本做差异分析,用程序尽量识别出诸如版式修改,数值修改,大小写变更这种小差异。然后对照旧英文文本、新版英文文本、旧中文翻译,提取出 diff 文件。然后再做一个对应的合并工作,用一些简单的规则生成新版本的中文翻译文本。这样可以极大的减少工作量。分工合作的时候也容易的多。

等群星 2.0 真发布后,我发现官方文本中居然交代了部分已经翻译好的中文文本,只是没有公开在界面上可以选择而已。有了头一天写好的脚本,稍微修改了一下,就支持了四方文本的提取:即多考虑一份官方中文文本。只是按之前的经验,官中的质量是很糟糕的,仅供参考吧,后来在慢慢校对替换。

有兴趣参加汉化的同学,请去 github 贡献。只需要对 diff 目录下我提取出的 diff 文件做修改,增加新翻译的条目即可。具体规则见这个 issue


最后,因为忙于做汉化,游戏还没怎么玩,只是略微看了一下。 P 社居然把整个游戏翻天覆地了大改了一番,还真是有魄力啊。这还是真是 P 社独有的商业模式,只要你一开始入坑,那么隔上一段时间就能玩到一个新游戏,通过 DLC 不断的收费,我们这种掉进坑的人那是心甘情愿的付费。

每个 DLC 基本都不需要增加什么新的美术素材,几乎都是在修改玩法而已。可谓是 100% 游戏规则付费了。回头看看我们已经做了 3 年的 MMORPG ,已经改了 4,5 版了,同样是策划天翻地覆的重构(程序叫苦连天的忙着加班推倒重来),还一分钱没收到呢 :D

February 12, 2018

最近玩的几款游戏

最近和一位新同事一起在开发新的 3d engine 。还在构建基础的东西。从第一次提交到现在已经过去了 24 天,有 129 个 commits 。短期内还不太可能开源(即使开放仓库,估计也没几个人知道怎么构建出来)。提一句,只是说明这个项目正在进行中。

从 2017 年底到现在倒是玩到了不少非常不错的游戏。这些游戏没有用到什么华丽的技术,但它们都有一些能抓住玩家的不一样的东西。

首先是 Slay the Spire 。这款结合卡牌构建和 Roguelike 元素的游戏,从我第一眼看到起就深深的被吸引了。上一款类似的游戏是 Dream Quest ,但这款各方面都远超前者。Rogue Like 的元素可以增加很大的耐玩性,每局游戏能收集到的东西都是未知的,所以需要对当前的收集情况做很多预判。而永久死亡机制会让人决策的时候非常小心。达成目标后的惊喜感是可以不断 S/L 的玩法所无法比拟的。不断的重来,积累的更多是对游戏的理解,也同时减轻了复杂系统的学习难度。

我玩的时候还没有中文,但学习门槛却不高,现在加了中文支持应该更容易了。只需要失败几次,就能掌握更多。

我一直认为,Rogue Like 的核心机制能带给各种类型的游戏更多的玩点,只是过去对此挖掘不够,上面谈到的卡牌构筑类是一例,另一例就是前段时间吞噬了我一百多小时的 They are Billons 了。

如果非要给 TAB (They Are Billons)贴一个标签,我会称它为 Rogue Like 塔防。但实际上,它或许更像 即时战略、城市建造、 塔防的综合体。当然 Rogue Like 的核心机制之一:不能反悔、永久死亡是它的灵 魂。

它的界面外观第一眼看上去是一个即时战略,像极了星际。玩过的人也都称它还原了当年星际的 7v1 地图的核心乐趣。不过我觉得,一旦即时战略加上了随时暂停,并鼓励暂停,就完全不是一种体验了。

同样是修筑大量防御建筑阻挡海量的无脑敌军的进攻,TAB 和传统的塔防游戏也有不大相同的体验。之前很少有塔防游戏把 2d 开放地图的地形元素做得如此重要,以至于玩 TAB 时,开局都需要把基地周围的环境探查一遍就可以在心中制定出截然不同的计划。是早点造兵去清图、还是速攀科技、还是用防御塔为主抵御中期敌人的进攻,战略的制定和随机出来的地图息息相关。能让随机地图变得有趣,我觉得是 TAB 的成功点之一。

利用资源的相互制约,把建筑所需地块面积也纳入核心制约条件中,我认为是 TAB 对传统塔防的最大改进:你铺的摊子越大,防守越难,成本越高;用更合理的布局,来发挥建筑用地的最高效能,这原本是城市建造类游戏的核心玩法,就这样无形融合到了一款塔防游戏中,简直是完美。

我大概花了 120 个小时才用 100% 难度通了第一张图,只要打过一次,后面就基本没有难度了。虽然没有看别人的攻略,但这个时间来看我的悟性算比较低的。在我的好友圈子里打听,悟性高的人平均 60 个小时就能破解作者的设计。

我很享受这个过程。头 20 个小时基本都是在学习游戏的基本策略,怎样在游戏前期活下来。然后的数十小时都是在试错。试错的时间成本很高,每局平均要 4 到 5 小时,才会有所新的认识。但这个时间也充分让玩家积累情绪,认识深刻。我是很晚才意识到不仅要节流,还要开源的。整个游戏过程中不能停止发展,才是通关的要素。而之前花了十多盘的失败,尝试各种精妙的操作,企图用最少的投资构建精妙的阵形来完成游戏。最终意识到正确的玩法后,有种恍然大悟的感觉。

我想,我喜欢的那类游戏都有这种共性:在玩的过程中不断的失败,设计者通过玩家的失败而不断的把设计点传达出来。玩的过程就是在不断的选择中,找到设计者想表达的那个正确选择。如果玩的过程中选择不够丰富、或是正确的选项太容易找到,都会让人感觉无趣。


另外还在 Switch 上玩到了几款有趣的游戏。

Darkest Dungeon 之前在 PC 上玩了许久,搬到 Switch 上重新支持了一把。好游戏,但这次不多展开说了。

Overcooked 非常推荐给和大一点的小孩或情侣一起玩。作为一个双人(多人)游戏,比 1-2 Switch 不知道高到哪去了。

重点想说说的是 沙漠老鼠团 (Of mice and sand) 。自从 Hayday 之后,我比较少玩这种单纯的制造链管理游戏。但玩了这一款后,我突然发现我对这类游戏的理解远远不够,其实这里面是可以挖掘出很多新鲜的玩点出来的。很惭愧,前几年有过一个类似机制的游戏的构思,但是因为找不到什么兴奋点,所以也就没有没有进展了。

我拿 Hayday 来比较其实是不恰当的,沙漠老鼠团完全不是一个放置游戏。我玩游戏的过程中基本没有闲下来过,关掉游戏也不会有任何的离线放置奖励。它完全就是在做制造链管理。但它却给我了许多新的启发。

比如:一个物品的制造配方其实不应该是单一的,如果多设计几条制造路线,其实会给游戏带来许多乐趣。玩家可以在不同维度的资源下作平衡。工厂、工人(老鼠)、时间的不同会让玩家在不同的资源配置情况下选择不同的制造方案。注意,这里的时间并不是放置内游戏的时间,而是游戏中的一种资源要素。更多的时间意味着要给游戏中的工人提供更多的食物来养活它们。

游戏中基础原料的产出来源于不同的区域,加上油料和时间的限制,也解决了传统制造类游戏中低级原料家孩子和高级原料价值相差太大的问题。对于网游也是如此:网游通常为了延长玩家的游戏时间,把各种资源链做的很长,最后新手玩家接触的资源对于高级玩家来说变得完全没有价值。

btw, 在沙漠老鼠团之前,我玩过缺氧。但是从时间上来看缺氧出的更晚一些。我严重怀疑缺氧的游戏概念深受了沙漠老鼠团的启发。


今天谈到的游戏,都可以在 steam 上或 switch 的 eshop 上用名字搜索到,我就不一一给出链接了。

February 07, 2018

向量库的一点改进

前段为 3d engine 写的向量运算库小伙伴在用,提了很多意见,所以这段时间一直在改进。

一开始觉得逆波兰表示法的运算表达式不太习惯,觉得需要绕个弯想问题,希望做一个表达式编译的东西,但是用了几天后,又觉得其实不是什么大问题,习惯了就好了。

但心智负担比较大的地方是那个 id 的正负号约定,也就是生命期管理。我想了一下,人为的去管理生命期,有些对象是要长期持有的,有些对象只在当前渲染帧使用,在使用的时候严格区分它们不太现实。

一开始的版本,我需要使用者在计算表达式中用一个 mark 'M' 指令,把一个临时对象转换成一个持久对象,这极大的增加了使用者的负担。尤其是更新一个对象的时候,需要先解除老对象的持久状态,再 mark 新生成的对象。使用的时候需要一直考虑这个对象是不是要更新,用起来太困难了。虽然有强检查,不会把程序弄混乱,但是稍不注意就会报告运行时错(对象 id 失效)。

今天,我做了极大的调整,去掉了之前 mark 语义,增加了引用语义。

之前没有实现引用语义是因为觉得值语义就够用了。现在看起来是不对的。不过引用语义比较难实现,一开始也没有找到合适的实现方式。考虑了很久后,我发现了一条实现引用语义的途径,那就是把引用语义的对象实现为 lua 中的 userdata 。

换句话说,假设 lua 中的 foobar 变量引用了一个 vector ,我们就显示的定义 foobar = math3d.ref "vector" 。

这里 math3d.ref "vector" 会生成一个引用 vector 的对象,在之后的程序中,我们可以改变 foobar 引用的值,但是不再修改 foobar 在 lua 中的值。

之后,我们在原来的指令操作栈中增加一个赋值指令 = ,如果我们想让 foobar 引用一个具体的 vector { 1,0,0,1 } ,就应该写:

command(foobar, { 1, 0, 0, 1} , "=")

这条命令的语义是,将 foobar 这个引用对象和 { 1,0,0,1 } 这个 vector 常量压栈,然后把栈顶的元素赋值给栈次顶的引用对象,然后将栈顶两个东西弹出。

我们之前在 command 序列中,数字 id 表示把一个对象压栈,字符串表示计算指令串,table 表示外部常量;现在需要增加特定的 userdata 表示引用语义的对象。

当然,我还给 foobar 增加了元表,可以用一系列便捷操作。例如 foobar(obj) 相当于把 obj 赋给 foobar , ~foobar 表示取得 foobar 引用对象的 C 指针(lightuserdata)用来传递给底层,等等。


我觉得这一版的设计要人性的多,但是实现上也遇到一些困难。

这是因为,原版的设计中,数据栈上都是数字 id ,所以可以很容易先实现一个 C 层的 stack 结构来操控。这次,数据栈上需要压入 lua userdata 了,就不再能简单的和 C 层数据结构对接。如果粗暴的用一个 hash table 来映射 lua 中的对象,其一,性能受到了损失;其二,userdata 的生命期管理变得非常复杂(需要很小心的加接引用)。

为此,我给 api 增加一个限制:引用语义的对象只可以在当前 command 指令串中使用,不可以把引用语义的对象压入堆栈,然后在下次 command 调用再赋值。所有压入指令栈的引用语义的对象在当前指令结束后,都会退化成值语义的量(如果指令结束还没有弹出)。

在实现层面,我没有修改之前的数据结构,而增加了一个专门保存引用对象的栈,让指令以双栈形式工作。引用对象栈记录的是当前 lua 栈上的数字 index ,等函数调用结束就自动失效了。实现也相对简单。