« March 2016 | Main | May 2016 »

April 30, 2016

有创意必须实现出来才有意义

这两天在一个游戏设计论坛潜水,读了很多帖子,感觉挺有收获的。

尤其是有一贴,楼主贴了一长篇他的 idea ,应当是想了很久的,算是比较完整,只差动手了。按他的说法,想先在论坛上收集一些反馈。这个论坛聚集了许多核心玩家,和相当数量的游戏开发者,按道理说,他找对地方了。

可是,在下面的回帖中,某人对此作了严厉的批评。

回帖说,你不给出个可玩的程序,根本没可能给反馈,尤其是帖了这么一大版文字,更不可能。

创意往往很有趣,每个创意说出来都能吸引人心。但就算你先写下几百篇文档,把玩法描述的一清二楚,不通过试玩,还是不可能知道它是否行的通、是不是真的有意思。

在桌面游戏设计圈里,有条基本设计准则就是,不去试玩而去写游戏机制就是在浪费时间。从一开始就应该试玩,设计过程中要不停的试玩。

所以,在论坛上可以根据非常具体的问题收集意见,但一大版文字描述整个游戏的创意却没有意义。在创意阶段收集到的意见也不会有真正的价值。每个人都是根据文字做出自己的想象,这和把东西实现出来后的结构截然不同。


我最近两年脑子里冒出了无数想法,没有一次真的完整实现出来,非常惭愧。最近在学 html5 + javascript ,想做点原型。采用 html5 开发是方便传播,可以快速给身边的同学试玩。

如果你对我说的论坛有兴趣,[这里是我提到的帖子

btw, 其实这个同学的 idea 我还是很喜欢的。

另外,javascript 真是门恶心的语言。

April 16, 2016

暴击英雄上架了

不知道这个 blog 的读者中还有多少记得我们承诺要出的单机游戏《暴击英雄》。他终于上架了。

点上图可以去 steam 商店看看,目前首发 7.9 折,30 人民币,如果你读完商店页面下面的好评和差评后,觉得你或许会喜欢这个游戏,那么欢迎你支持我们一下。

ps. 如果你希望用支付宝,可以去衫果买。只不过在衫果买的话,不喜欢不能退款。


按原来的计划,这款游戏应该是在 2015 年底完成的,作为试水产品在 steam 上发售。结果,不出意料的跳票了。开发期整整延长了半年,原因自然是对游戏一直不满意。至于现在的版本,依旧不满意,所以目前还是 Early Access 阶段,接下来两个月会想办法修改已知的问题,希望可以做得好一点点。

至于为什么没有在完全满意后再发布,是因为如果一直这么做下去而不获得真正玩家的反馈,就改成无底洞了。很多内部意见争议也很大,不被玩家骂几句,大家是没那么大动力改的。

其实我个人最近半年在忙其他的事情,完全没有管这个项目。这次上架,第一时间自掏腰包买了一个支持。反正也不贵。周末大约玩了四个小时,想法很多,记录一下。说起来,大家都想做并不是 pay to win 而注重 gameplay 的游戏,但真做起来经验不足。许多地方都是心有余。

据我所知,这个项目从有创意开始,开发团队就没想过把游戏做成一个无脑刷刷刷的游戏。他们设计的核心玩点是为若干角色设计特别的攻击轨迹和攻击范围,然后把 4 个角色组合起来(最早的设计是 5 个,变化更多一些),通过玩家反复观察不同招式,尝试最好的搭配和顺序来最大效率的杀敌。

然后再通过设计不同的 boss 战,挑战玩家对这些角色的组合能力。


但我玩下来,即使我知道有这么个核心玩法,还是不那么容易踩中设计者的节奏。当然我本人游戏偏好更倾向于回合策略游戏,喜欢可以慢慢思考,若这不是我们自己公司的作品,我早就因为不是我的菜而放弃了。本来这个游戏是有回合策略这个方向的潜质的,但主创人员觉得一定要把格斗游戏的那种见招拆招的反应做进去(他是个格斗游戏迷),就坚持了下来。

最终,我来玩暴击英雄的时候,手忙脚乱,死了又死,被虐得不行。

在我看来,理性的玩家在玩过前三个大关后,大多不会觉得这个游戏可以无脑通关了。很多人连第一个小 boss 都打不过去,挫折感很强。我觉得这是很失败的,在完全没有把游戏核心玩法传达出来之前,就赶走了一部分玩家。

第一个要求六连击的关卡,我想原意是引导玩家尝试不同的武将次序,让出招可以连起来,算是一种引导吧。但没有任何游戏内提示,我自己都是死了三四次才突然醒悟过来,退出关卡,试着把张飞调到凤雏后面(因为凤雏有一招可以把敌人打到面前来,然后张飞的抓技可以一把抓住很多敌人给予重击)。经过少量调整后,比较容易的就完成了连击任务。

经过自己的搭配,在打出长连击,让敌人悬浮在空中一直掉不下来时,突然感受到了爽快感。只是来得晚了一点。

我向开发组吐槽了几个小时(比我玩游戏的时间多多了)。在突出核心玩法上,有很多细节没有做好:比如如果队伍中有人死了,其实展现核心玩法的机会就少了很多,玩家通常只能硬抗才能过关,这时技巧性非常低。我个人认为在这种情况下,应该迅速引导玩家重试才对。

如果玩家用一个队伍组合反复尝试,却不知道换阵型,非常有可能拼命重试。或者通过升级角色,提高攻击力硬打过去。我觉得这些都是违背设计理念的。我想,在重来界面那里有个方便的更替阵型的入口,至少提示玩家,试着用别的武将组合更重要。

我和策划也讨论过,是不是应该放开限制,不要把武将组合做成战前策略,而允许一定条件的在战中更换。这点还没有定论,但是必须要想办法改进的。btw, 玩到第四章后面,四个武将都有了援助技后,在战中调整次序的主动性高的多,这个问题会极大缓解。可惜很多玩家没能玩到那里。

而我在玩了三个小时之后,终于感觉有点上道了。我想我们接下来在 early access 阶段的工作,应该集中在怎么更好的引导玩家进入我这花了这么长时间才领会出游戏乐趣的状态。

ps. 幸运的是,通过开发组主持的玩家 qq 群:152081004 。很多玩家通过交流都迈过了这道门槛。群里的气氛相当之好,看到自己做的游戏为大家带来乐趣,这是让人最满足的事情了。


最后说点和这次销售有关的事情,关心游戏行业的同学会更有兴趣一些。

出乎我的意料的是,就第一天的数据,这次我们在欧美市场的销售额是超过国内市场的。销量是中国市场更高,大约可以占整个销量的一半。但仅仅美国和英国的销售额就反超了国内,因为欧美市场的定价会高一些。

我们并没有在中国以外的市场做任何的宣传。反而国内是有个一定预热的。上架期间,我和我的同事还通过私人社交网络对朋友做了宣传。许多熟人捧场购买(顺便感谢那些支持的同学)。

从 google 分析看商店页面的访问,英语环境的浏览远远超过了中文环境;国内的用户仅占 18% 。看起来 steam 真的是一个质量非常之高的游戏销售全球平台(大部分流量都是直接流量,引荐流量最多来源也是 steam 社区本身:玩家动态、截图等等)


我想说,以一个资深 steam 用户的身份,这次我特别提前告诫了为这个产品做市场的同学,不要动用我们在国内营销网游的手段。尤其是不要刷商店里的好评。担心引起社区的反感。最终,除有四五篇好评出于公司同事之手外(他们只是爱护自己产品,以个人身份写的),其他大多数好评都并非软文或水军。整体来说,我觉得目前 77% 左右的好评率是符合我心目中对这款游戏的评价的 —— 我给他打 75 分吧。

一些外文的好评或差评能看的出来,欧美的玩家反而对我们的游戏容忍度更高一些,即使批评也客气一点。当然我也非常理解国内的玩家的“戾气” —— 打上引号是想说,我没能找到一个更中性一点的词来表达—— 毕竟国内的游戏市场被 pay to win 游戏占领的太久了,几乎到了不做这类游戏就活不下去的地步。对于玩正版单机游戏的小圈子,特别讨厌目前的氛围。

你觉得游戏中带有 pay to win 的影子不奇怪,毕竟为这款游戏写程序、做美术,设计玩法的人都为其他 pay to win 游戏工作过好多年。但这一款游戏真的没打算设计 IAP 啊,每个关卡也都是围绕预定的 gameplay 绞尽脑汁去设计并实现的(或许设计的并不好),并没有打算让你(无论是用钱还是用时间)刷刷刷,而是期待你找到合适的解法。


我们为这款游戏投入了 200 万 RMB 的研发费用,steam 上有很多小朋友表示不信,不理解为什么需要这么多钱,我想我还是解释一下吧。

制作这款游戏的同学年纪都不小了,他们需要养家。除了最早期的原型阶段,后来的开发都属于公司行为。所以我们不能因为所谓 “情怀” 去克扣人家的应得的工资。在广州这样的城市,从事游戏开发行业,一个有多年开发经验的开发人员,每个月公司为其的人力支出是不会低于 2 万 RMB 的。(不仅仅是发工资,还有办公开销、国家要求的基本福利,各种税务。)

即使只是一个五人团队(目前为这款游戏工作的已经不只五人了),每个月的开销也不会低于 10 万。另外,就目前游戏的制作量,我们还需要一定的外包,而外包的成本是大于自己开发的成本的(毕竟多一层关系,外包公司也需要有自己的利润)。

这款游戏已经开发了接近 18 个月,所以……


当然,上面这些是算给不懂行的同学们听的。国内同行的评价是,按目前这款游戏内容的体量,预估的制作成本将是我们现在的三到四倍。只是因为喜欢,所以努力用更少的钱做更多的东西啊。成本这么高,我个人是真没办法,这就是国内游戏行业的现状。

看起来回本很难,如果亏本了,我认为最主要的原因是我们自己没有把游戏做好。对于单机游戏市场,无论是全球还是国内,都比过去好太多。以后会更好的。

不知道下次干这种任性的事情是哪年了。或许等我们再做点 pay to win 游戏多赚些大 R 的钱吧 ;)

April 14, 2016

排行榜奖金的发放方法

最近有数据显示,我们的游戏《流星庄园》有玩家利用规则漏洞,通过排行榜奖励刷钻石。

我问了一下设计人员一些细节,感觉现在的规则设计是很有问题的。下面记录一下我的想法,和一些改进建议。

排行榜奖励问题不只在我们这样特定的游戏中有,现在几乎所有的 pay2win 游戏都会涉及到。我们最初的规则是,每周为排名前列的公会按名次发放一定的钻石奖励,鼓励大公会竞争,促进消费。

后来,有玩家反应,如果公会无法排在前列,就没有动力竞争了。所以又加了补充规则,如果公会排名又提升,也有相应的奖励。


这个设计是有悖于鼓励玩家冲上更高名次的。如果将玩家看成一个整体,玩家若想获得更高收益,最恰当的做法是不断的变换名次,降低或升高都可以,最差就是保持原来的名次。

作为设计理想,玩家不会成为一个整体,他们之间有竞争。更高的名次有更高的奖励,所以不会轻易降分。

但事实上,由于玩家可以充分的沟通相互信任,不会陷入囚徒困境。竞争多花的钱而获得的高名次奖励,远远比不上交替名次而获得的系统额外发放。另外,在原来的低名次段,原本没有名次奖励的位置,也可以通过操纵排名而获益。


我的规则改进思路是这样的:

  1. 对排行前若干名的奖励做成一个奖金池。奖金池的大小就是原有对排行奖励的钻石的总量。对低分数段的排名也固定发放一小笔,计算入奖金池内。

  2. 计算奖励的时候,首先按常规的排行计算奖金。即名次越高,奖金越高,低排名区也会有很少的配额。总发放奖金额等于奖金池的总量。

  3. 比较这次排行和上周排行。计算每个公会是否有超过上周的排在他前面的公会,注:并不是统计排行榜上升情况,即使名次下降,也有可能超过若干其他公会。

  4. 对于每个公会,统计超越他的其他公会(上周排行较低,而本周排名靠前者),按现有排名排序。扣除本公会这次获得的奖金的一个比例。建议对第一名计 10% ,按排名依次 90% 递减。 即,有一个超过本公会的,扣除奖金 10% ,第二名扣除 9%, 第三名扣除 8.1% , 以此类推,直到全部扣完 (如果按上面 90% 的公式,其实最多刚好扣完:10% * (1-0.9^n) / (1-0.9) 趋近于 1。

  5. 扣除部分全部计算完毕后,把扣除额加回应该获得奖金的公会。即:被超越的扣除,和超越别人的奖励是分开计算的。扣除部分不会超过现有排名位置的奖励,超越奖励不会为负数。

在这个规则下,每周发放的总奖金数是固定的,同时又鼓励了玩家间的竞争。

April 12, 2016

lua 常量表优化

今天花了一天尝试给 lua vm 做了一点优化:

现在 lua 的函数原型里保留有一张常量表,引用了 string ,number ,nil ,boolean 类型的常量。

table 是不能为常量的,所以当你想迭代一个常量数组的时候,

for _, v in ipairs { "one", "two", "three" } do

其实每次都会临时构建一张表,并依次插入 "one", "two", "three" 。

或者你想返回一个常量构成的表:

function foo()
  return { x=1, y=2 }
end

每次 foo 函数都会为返回值重新构建 table 。

其实,我们可以认为所有键值都是常量的 table ,都有可能是一个常量表。那么,在 parser 阶段,和 string 等类型一样,预先建立好 table 对象,保留在 prototype 的常量表中,可以极大的提高运行性能。

那么在上面这种 return {x=1, y=2} 的写法中,实际返回的可以仅仅是一个 table proxy ,里面只有一个指向常量表的指针即可。

如果事后我们真的需要改写这个 table ,再 clone 一份常量表,把这个 table proxy 对象还原成真正的 table 对象即可。也就是所谓的 COW 技术。

由于常量表一定是一个单层简单表结构,可以直接 memcpy 复制,比一项项添加 key/value 要快很多(实测可以快 2-3 倍)。

我们为之付出的代价仅仅是每次访问 Table 对象时要多做一次判断,但收益巨大。

我的实现放在自己的仓库 ,有兴趣的同学可以看看最近的提交。


下面是一个简单的测试:

function f(a)
end

local t = os.clock()

for i=1, 10000000 do
    f { x=1, y=2 }
end

print(os.clock() -t)

上面这段代码,在原始 lua 5.3.2 中运行花掉 3.276s ,修改过的版本减少到 1.278s 。

如果在 function f(a) 中修改 a 这个表,加入了 mutable 化过程:

function f(a)
    a.x = 3
end

local t = os.clock()

for i=1, 10000000 do
    f { x=1, y=2 }
end

print(os.clock() -t)

修改后的版本也有巨大的优势:原始版本 3.376s ,修改版本 1.413s 。

April 11, 2016

我需要一个怎样的中文输入法

不堪被 windows 升级弹窗骚扰,终于升到了 windows 10 。最不能忍受的是新的中文输入法,之前用的智能 ABC 已经完全不能用了。btw, 我爹他老人家就是因为没有 ABC 完全输入不了汉字,又重装了系统,退回 windows 7 去了。我没那么能折腾,所以还是想别的招。

很多年前我就写过,为什么我需要 智能 ABC ,到今天观点还是没有变。我听说 QQ 拼音加入了 ABC 模式,其实就是加了 5 个笔画,它并没有理解 ABC 为什么那样设计,所以做的并不好。

并不是说 ABC 设计的多完美,如果我来设计我期望的中文输入法,肯定有很多想法和 ABC 的设计不同。但是其核心是一样的:那就是,赋予每个汉字更多的编码形式,结合词(不是句子)尽可能的减少重码。


从读音看,汉字有 10 多个常用音节,30 多个次常用音节,60 多个不太常用音节。一共 100 多个,用来表示 3000 多个常用字已经是 20 倍以上的重码率了。所以对于一些同音字多的音节上,一次 10 个候选字都需要排好几页。光用拼音输入单字肯定是效率非常低下的,这也是为什么早期有很多形码输入法的原因。

如果以词为单位输入,即使仅靠读音,已经可以大大减少重码率。但问题在于不断有新词出现,尤其是很多谐音词,本身就是刻意和传统词同音的。如果我们为了行文需要去修改已有词里的单字,只靠单字的读音来索字就会变得输入效率及其低下。

大部分人都会在需要输入单字的时候(最常见是输入非名人的人名)刻意去组词,然后删掉不需要的辅字。当需要的字是在词前的时候,多敲一次退格键即可;而在需要的字在词后的时候,就麻烦的多:你需要用方向键前移,删除,再移回去。

ABC 发明了一种方法,就是用 [ ] 直接从词索字。方法是你可以输入一个词,直接加上 [ ,就只提交该词的首字。这是个好主意,但解决的还不够彻底,我认为并没有看到本质,后面会继续展开。

当用词输入还是解决不了重码造成的输入效率低下时,许多人想到了用整句输入。因为人之间口头交流时,很少有听完完整的句子还产生误解的。只要提高断句算法的正确率,辅以海量的云词库,总可以进一步的减少同音重码率的。

我认为这是一条邪路。可能某些人觉得好,但至少不适合我。

我希望在输入汉字的时候,总是能保证立刻得到正确的字,而不是整句输入后,再检查有没有错误,并回头修改。人总是懒的,往往也就不改了。时刻关注自己过去输入的整句中有没有别字,程序有没有纠正过来,也是极大的负担。更何况,输入单字,以及词库中没有的词(尤其是人名)是无法回避的需求,靠断句来自动选字是不可能的。

我的个人观点:即使是整句输入,由人主动来分词断句就足够了,不必苛求算法。人敲键盘的效率是远远超过理想状态下输入汉字的速度需求的。如果我们在输入汉字的时候,每次完成一个词多按一次空格表示分词,这多一次的键击,并不会降低输入速度,但却可以完成算法很难做到,做好的工作。


下面来谈谈我认为的 ABC 输入法的核心理念:每个汉字都有多种编码方案,而不是一种。

汉字的读音是汉字的一种编码。由于汉语拼音的普及,可以视为额外记忆量最少的自然编码。当然也有例外,像我爹几乎没有学过汉语拼音,但他懂英文,所以靠读音能判断出每个字的声母,但韵母继续是无法正确拼写的。这也是他依赖 ABC 而无法用其他拼音输入法的原因。

由于方言口音的存在,事实上输入法在实现的时候都允许了一字多码。比如 z c s 和 zh ch sh 的混用。通常,这是专门写代码实现的,并非真的把汉字赋予多个编码。

汉字的字形是另一种编码,但如何把二维结构的汉字拆分成更简单的元素没有定法。早期的形码为了追求更短的编码,更少的重码,都强迫人去机械记忆大量的知识。除了五笔,有人还记得 windows 早期带的郑码么?但大多数人是讨厌机械记忆的,只要系统不默认提供,势必淘汰。

能留下来的形码只能是笔画了。横竖撇捺折对应 1 2 3 4 5 ,记忆负担并不比汉语拼音难记。虽然一笔笔写字效率很低,但偶尔用用尚可。

ABC 的编码方式是每个汉字可以用全拼,可以用全拼加笔画,可以声母加笔画。以词为输入的单位,即用户输入他认为的一个词元素(可以是双字词、多字词或单字)后,主动敲一下回车作为分词。构成词的单字的编码可以任意选择:可以用全拼、声母、全拼加笔画、声母加笔画。

由于其实我们日常用的词和字并不多,尝试输入几次后,你一定可以找到重码最少的组合。这种学习成本非常低,但是输入效率是极高的。

ABC 在提高笔画的效率方面还额外做了一些工作。比如它会把汉字拆分成自然部分,左右结构,上下结构,包围结构等。每个部分最多只支持 3 笔。这回避了很多同音字的偏旁相同,结果连输很多笔并不能去重。

对于常见的起笔,比如交叉和口字,单独安排了 7,8 两个数字表示(6 表示弯,用的不多)。我认为实践中非常有用,但有人觉得增加了记忆量而不喜欢。

其实这是 ABC 自身没有设计好的一点,它完全可以贯彻一字多码的核心思想。例如,吴这个常见单字,你可以编码成 wu8 (8 表示起笔是个口)同时你也可以编码成 wu25 (竖折),用户输入哪个都没关系。像 QQ 拼音模仿的 ABC 模式,只支持 12345 并一笔笔编码到底的方式,我个人认为是制作人员懒得制作码表的缘故。(汉字笔画表可以在互联网找到,而汉字拆分的信息,虽然有研究者在做,但数据很难免费得到。有兴趣的同学可以 根据这个索引资料。)

其实原版的 ABC 还包含全形码的编码,不知道为什么在开发 windows 时被去掉了。我认为完全不影响使用,爱用不用,不会增加记忆负担。

另外,以词定字的功能也仅支持输入单字,而不能扩展成对单字的编码。例如,大部分输入法在第一次输入不存在的词的时候都可以默认造词;我们如果明知道这个是新词希望造词时,都会尽量提供更多信息。如果要输入一个人名,姓陈,我们平常会组个词,比如说名人:陈毅的陈 chenyi[ ,或耳东陈 erdongc] 。那么 chenyi[ 和 erdongc] 其实都是对陈的编码。这里 [ ] 是使用 ABC 规则,使用前面的词的首字或末字。

ABC 是不支持将 [ ] 规则用在组词中的,我觉得完全应该支持,也并不麻烦。


我理想中的给自己用的汉字输入法应当是这样的:

允许对单个汉字做非常多的编码,包括全拼、声调、笔画,以及它们的组合。

以词为输入单位,每个自然词(或单字)结束后,主动按空格表示输入结束,进入选词状态。构成词的单字可以用任意编码做搭配。编码有足够的容错性。不存在词库中的词立刻造出来,保存在用户词库。由于日常输入需要的词不会太多,所以适应一段时间后就能满足用词需要。

具体编码方案:笔画使用 1-9 ,1-5 留作五个基本笔画; 6-8 保留 ABC 方案(我个人已经积累了大量 ABC 经验,不需要放弃)9 表示这是个独立字或结构过于复杂(无法拆分,或拆分不确定)可选加在编码最后一位。

每个偏旁部首,只做两个数字的编码(ABC 是做多三个);汉字最多拆分成两个部分(明显的偏旁加剩余的部分);所以笔画编码最多 4 个数字,笔画不是为了确定唯一汉字,而是辅助去重。

声调可以用 01 02 03 04 05 来表示,掺杂在编码中可选。不必强求写在笔画前面还是笔画后面,或是只输入声调不输入笔画。0 起头是为了严格和笔画数字分开。

支持全笔画选单字,只用于不常用字。以 v 开头,加上两个部分的笔画。所以通常是 v 加 4 个数字或是 v 加两个数字加 9 表示。同时支持偏旁不用数字而使用偏旁的读音。比如 dr 单人旁,sr 双人旁,ts 提手旁等等。同时可以把部首的第二部分输入全拼。这对于拿不准读音,读半边字的情况,或是不知道怎么组词,但却想输入的单字特别有用。

比如:

墉 可以编码成 vtuyong (提土旁加一个 yong 字)

镛 v31yong (偏旁起笔为撇3横1, 右边是个 yong 字)

这些也是作为额外辅助编码,爱用就用,不用不用管的特性。整个单字输入的重点就在于,提供可以想到的检索方案放在那里,想用的时候就用的到,不用的时候也不干扰输入。和五笔这些传统形码不同,不追求怎么编码短,重码低;毕竟日常输入不常见单字的需求并不多,力求做到不用记忆,宁可编码长一些也尽量准确。


我在自己用的输入法上面已经花了几天时间做了一些开发工作。强调是自己用是因为可以无视别人的需求,不用和人解释为什么会设计成这个样子。每个人的情况不同,照顾大多数的习惯就会强迫自己改变习惯;同样自己已经熟悉的东西,比如 ABC 的笔画方案继承下来完全没有记忆负担,换个人会因为不想记忆而排斥。

我会以开源的方式开发,但不会宣传,这里也不会给出 github 地址。没有给自己定计划,想到了就做一点吧。明天可能会继续写一些 windows 下坑爹的输入法开发踩的坑。


ps,前几天在微博曾经吐槽现在的输入法对程序员及其不友好。中英文混合输入,以及偶尔写几个汉字,大部分时间敲英文的场合也照顾的不好。怎样让中文输入法尽量配合我这种大部分时间在敲代码,或是控制台命令的人,尽量少去主动切换中英文状态,关于这方面的想法过几天再专文写写。