« July 2006 | Main | September 2006 »

August 31, 2006

玩了一下 Haskell

感觉 Haskell 很有趣,被它的 quicksort 的实现所吸引,花了一整天读官方的文档。感觉跟 lisp 很相象,但是比 lisp 更容易上手。

回忆当初玩 jam 的时候,jam 的语法也很有 functional programming 的味道。lua 也可以有类似的玩法:比如 http://lua-users.org/wiki/FunctionalTuples

August 27, 2006

黄万里教授的忌日

发几个链接:

http://cn.netor.com/m/box200204/m14193.asp?BoardID=14193

http://www.edubridge.com/erxiantang/library/sanxia_huangwanli.htm

http://chinsci.blogchina.com/2399148.html

August 25, 2006

编程的门槛

大约在游戏制作行业里,大家都把设计工作分成三大类:程序,美术,策划;其重要性相互之间都无分上下。这算是跟其他 IT 行业极大的不同点吧。

但这里面,程序的门槛是公认的最高。很少有听说有专职的美术或者策划人员,从对编程一无所知转变成一个程序高手的。按道理来说,编程需要的基础知识并不复杂,只需要加以时日磨练,任何人都有成为优秀程序员的潜质。谁都明白,当团队中的成员互相了解各个领域的工作可以提高整体的效率。可是,为何,许多人却经过努力,没有迈入程序设计的大门呢?结果,大多数美术人员不会编写 max/maya 脚本;策划没有能力编写复杂结构的脚本。

那么,编程的门槛究竟在哪里?是什么阻止了非编程人员在简单的程序脚本上更上一层楼?作为多年的程序员,我似乎忘记了当年入门时的心境。面对这样的问题时,便得在记忆深处挖掘答案。

现在看来,我和大多数有十年以上编程经验的程序员一样,认为学会这门技术是顺理成章的事情。学会编程的人也不会认为编程有什么困难。仔细的回忆,却想起事情并非如此。

记得第一次接触编程是大约 6 岁时在家中的计算机上,阅读父亲给我编写的 basic 程序,只有不到 10 行的那种。似懂非懂的修改了其中几行代码,满足的看着程序的运行结果因为我的修改而相应的变化。似乎那个时候就理解了程序的含义,但真的如此吗?

直到 8 岁的时候,我学会了解一次方程。对于一个小孩子来说,解二元一次方程还是很有挑战性的。那时的我曾经幻想把方程式输入计算机,就可以得到结果。有幸的是,父亲马上花了时间在这个机会给我重新讲解了变量和赋值语句的概念,从那个时候起,我真的理解了编程了吗?

10 岁的时候,我决定自己编写一个小游戏,唯一的参考资料是随计算机附送的一本 basic 教材。依稀记得是香港的版本,全是繁体字。同时,我跟着一个成人大专的 basic 课程随堂学习,每堂课程的作业都可以准确的完成。但是我在编写自己的游戏时却遇到了极大的困难,最终这个游戏流产。

最终,我认可自己已经迈过了编程的大门是在 12 岁,那时候妄想自己可以用程序解决任何问题了。觉得程序=算法+数据结构 就是唯一真理。同年,我开始学习汇编语言,非常的顺利;第二年,自学了几本 C 语言的教材。学习编程这件事情,已经不再需要老师的搀扶,这时距离第一次编程已经过去了 7 年。

在之后的很长一段时间里,我都觉得编程的门槛是很难跨越的。学会独立编程和学习新知识的能力,似乎需要一种顿悟。直到读大学时,同寝室的一个同学,不到半年就跨越了这个门槛让我明白,年龄也是一个重要的因素。成年人比小孩子更容易迈过这一步。

到底这个门槛是什么?我现在的理解是,学习新知识的方法。大多数人在人生的开始,学习新知识都是采取的类比的手法。了解一样事物的表面特性,就可以去推理类似事物的特性。比如教小孩子去打酱油,领着他到商店,把酱油瓶和钱交给售货员,然后把酱油拿回家。聪明的小孩下次不会教也会自己去买烟了。这是一种类比,然后发现了钞票在这件事情里的重要性;而酱油瓶就不那么重要了。

我们可以看到,很多人会做初级的脚本编写工作。但是他们需要一个范本,根据这个范本修修改改,就可以按自己的想法工作了。简单点的,只是改几个参数,换个颜色;复杂点的,可以调动一些逻辑。本质上,都是用类比的方式来学习编程。可惜的是,编程的本质却不那么容易在这个过程中被发现。

我想,依靠一张数学意义上严格的语法表,任何没学会编程的人都很难掌握一门编程语言。但是有经验的程序员却可以。这或许就因为,编程这件事情,本身就是依赖逻辑推理的。编程语言是建立在逻辑性上。这是思考世界的方法之一,很多成年人也未必熟练掌握这个方法,经验依然是大多数人的法宝。(当然,经验对于程序员也同样重要)

这么多年,我经常收到一些 email 向我索取各种各样的源代码。在网上见到“求代码”这样的帖子也是司空见惯。到底源代码有多重要?我想,对于真正的程序员来说,源代码的作用最多的是复用,省去自己的工作。从这点上来说,二进制复用更加有意义。通过例子代码来学习新知识也是为很多人喜爱的,毕竟不是每个人都喜欢读枯燥的技术定义文档。包括学习新语言,有几段范例代码来琢磨语言本身也是很直观的事情。但是,通过找到一些类似代码来解决类似问题,几乎是天方夜谈。平时我们也能见到一些部分伪程序员,为了解决代码中的问题,这里改改试试,那里改改试试;或者调整几行语句的位置,删掉几行代码,看看问题解决没有。我想这都是尚未迈入编程大门的表象吧。

如今理工科大学生,或多或少会开编程的课程。但是本科毕业生不会编程的人比比皆是。通过背出一段代码来应付考试已经不只是笑话了。编写教材和授课的老师应该检讨,是否教学方法出了问题。

August 21, 2006

虚拟物品交易研究

前两天跟公司策划部门的同事开了个会,会上了解了一些数据,让我想了很多东西。

我特地咨询了梦幻西游中,点卡交易系统每天的交易量。由于具体数字可能涉及公司机密,这里暂不透露。但是用其数字可以估算出,梦幻西游的玩家中,大多数玩家仅仅靠点卡交易系统,就可以做到不花钱来玩游戏了。虽然事先就预料到了这一点,但是被证实后,依然被打动。有了数据的背景,这让我可以更放心的花更多精力去考虑相关的问题。

梦幻西游的游戏模式绝对不是一蹴而就的,也不是天生被刻意设计出来的。它是经历了几年的时间,运营者和玩家不断的互动缓慢的形成的。而梦幻本身庞大的用户群使得整个系统相对稳定,能够抗击一定的因为游戏设定改变而受到的冲击。所以,梦幻的成功,每一方面都在相互作用,我真切的感觉到,这种模式的成功很难被复制,但是却有很多值得研究的地方。它会是一本很好的教材。

梦幻的玩家,因为系统内点卡交易系统的存在,很容易的把自己在游戏中的一切以现实社会的金钱来衡量。大约一万梦幻币就可以和一块钱人民币划上等号。我虽然不玩这个游戏,但因为自己和这个游戏有千丝万缕的关系,也经常关注梦幻的官方与非官方论坛。

游戏的策划者大多以挨骂的角色成为玩家口诛笔伐的靶子。有时候,为了直接的理由:延长游戏的寿命,让玩家更长时间的停留在游戏中。策划会增加一些高级玩法,让高级玩家消耗更多的梦幻币在游戏中。或者有时候,为了某些原因,提高一些游戏中的消费。这些在玩家口中,就变成了责难的对象。因为对玩家个体来看,梦幻币等于人民币。当他多花一万梦幻币就等于多花一块钱玩游戏。反而,在线时间花掉的点卡被弱化了一些。虽然,玩家也会计算每个玩法会消耗他的多少游戏时间,从而折算成点卡费用。但是对于把游戏做为生活的重要一部分的玩家来说。每天固定的游戏时间是变化不大的,除非不再玩这个游戏。

可是从宏观上来看,作为运营游戏的公司来看,无论怎样修改游戏的玩法,公司并不会因此得到直接的收益。反而对于一个稳定的系统,任何修改都是有风险的,对系统造成过大冲击的话,引起玩家流失甚至产生雪崩效应让游戏提前进入衰退期都是有可能的。最终,游戏给公司带来的直接收益只来至于同时在线人数与单位时间的点卡价格之积。点卡涨价的可能性不太大的前提下,唯一能增加收益的方法只有提高同时在线人数。这时候,游戏里玩家的手头上的虚拟货币变成了游戏系统的稳定剂,作为运营商的公司没有直接贩卖梦幻币的情况下,玩家获取和消耗梦幻币的速度并不成为直接影响收入的因素。据我所知,公司也从来没有贩卖过梦幻币,看起来以后也不会。因为现在改变梦幻的模式实在是太危险了,也无此必要。

那么,对梦幻币的控制究竟影响了什么呢?由于不方便公布我了解到的数字,这里虚拟一组数据(跟真实情况有差距)来解释我的理解。

在一个梦幻的服务器中,大约有 7 成的玩家是靠贩卖梦幻币换取点卡进行游戏的, 2 成的玩家即不买也不卖梦幻币,游戏中自给自足。另外 1 成的玩家靠贩卖点卡,收购梦幻币在游戏中生活。那么,我们可以说,这 10% 的玩家养活了现实中 70% 的玩家;反过来也可以说,游戏中 70% 的玩家在供给 10% 的玩家去在游戏中享受高人一等的生活。(注,这个比例的具体数值为云风虚构,只为说明问题,具体情况有较大偏差)

之所以有这么多的玩家心甘情愿的在游戏中受到少数人的剥削,最大的原因是,玩游戏的成本对他们来说还是过高了。

当然这里还有另外一个因素,梦幻中黑市交易的存在。一定还有大量的梦幻币通过黑市直接变成了人民币到了系统外,大量的人民币并没有购买游戏点卡在消费到游戏中。不过这里可以不考虑这个因素,因为我们这里探讨的人数上的 7:2:1 这个关系,并不因为这点而发生变化。

游戏本身质量的提升,最大影响的人群是中间 20% 的人数。其次对 10% 的人民币玩家也有影响。这 2 成和 1 成的人群之间也会相互转换(当然也可能从自给自足玩家转换到免费玩家)。这种转换在游戏初期,玩家迅速升级的过程会更加明显一些。因为那段时间,相对的高级玩家对梦幻币的需求变化很大。

但是,对于 70% 的所谓免费游戏玩家,影响总人数最大的因素不在于游戏品质,而在于,那 10% 的人民币玩家到底有多少梦幻币需求,能够养活多少人。对于,这 10% 的人的现实中的消费能力,个人以为不是主要影响因素。毕竟网络游戏在今天的中国,依旧是一个低消费服务行业。少数高收入人群的消费能力还是很可观的。反过来对于游戏,10% 的玩家能否在游戏中按他们的喜好存活下来,也要看 70% 的人在游戏中的生产力是否足够。如果 70% 的免费玩家生产力不够,那 10% 的玩家会因为有钱买不到梦幻币而流失。

从以上分析看下来,人民币玩家和免费玩家是相互依存的。整个比例也不能轻易变动。比如 70% 的免费玩家群的生产力不够时,10% 的人民币玩家群内就会产生内部竞争,反应出来就是点卡交易系统中的点卡价格上涨。但是最终,一定会有一部分人被淘汰出局。被淘汰的人可能只是退出竞争,降低自己在游戏中的生活水平,等未来的一个新的平衡产生后重新进入;另一种可能是直接退出游戏。

如果免费玩家的生产力不够是因为人民币玩家的需求增加,而不是人口减少造成的时。会有两种截然相反的结果发生。如果那些人民币玩家中被淘汰的人群只是暂时推出竞争,点卡价格的上涨,会为免费玩家的扩容提供空间。更多的免费玩家进入,会支持更多的人民币玩家生活下去。也就是说,游戏社会被扩容了。这个时候,运营公司通过市场努力,加上游戏本身越来越大的吸引力,让更多的人进来游戏的话。那么,一度被淘汰的人民币玩家会回来。这样,梦幻币会在一个新的位置稳定下来。对于游戏系统来说,只是发生了一点小的通货膨胀。当这一种情况发生时,对游戏整体的收益是有好处的。但是,如果被淘汰的人民币玩家直接退出了游戏,剩下来的人民币玩家虽然个人平均人民币支出有所增加,人数的数量减少导致系统偏向不稳定。但后的效应更可能使得被养活的免费玩家数量减少。如此恶性循环,游戏会提前衰退。反映在点卡交易系统上,应该是价格剧烈波动。

游戏世界的经济系统的稳定,就成为了游戏系统本身稳定的度量仪。轻微的通货膨胀,是整个游戏系统的良性发展的标尺。


梦幻的模式不那么容易被复制。对于赢利,也容易到达一个发展的瓶颈。因为它太依赖同时在线的玩家数量。而我们知道,网络游戏这个社群的人数最终是有限的。好在西游系列天生的优势是 client 对系统资源的占用量少,让玩家可以方便的多开,或者挂机,无形中扩大了游戏缴费帐号和自然人的比例。我想,这可能是游戏技术开发人员做的最大贡献之一吧 (有点自卖自夸的嫌疑)。

既然这样,我们不妨探讨一下,其他的收费模式。也就是最近业界比较流行的所谓免费游戏模式。

有几种免费游戏,一种是单纯的卖虚拟形象。很有代表的是《劲乐团》,《劲舞团》。qq 游戏也勉强算上吧。这个跟 mmo rpg 相差太远,暂时不讨论。

另一种是直接卖道具装备,典型的是《征途》。这款游戏感觉现在在业内名声挺臭的。

还有一种是卖虚拟服务,也就是提供给玩家一些升级的方便。我听说的有《传奇》中的闯天关一类,还有金山的一些游戏提供的离线升级,等等。

一开始,我个人是很排斥所谓的免费游戏策略的。这种变相的收费方式来讹诈玩家的钱财似乎违背了游戏设计者的本意。也让游戏的公平性遗失。但是,前段时间对 eve 的一些思考 以及这次和公司其他组策划的交流让我静下心来想了更多。

当我们不以赚钱,赚更多钱为目的去考虑时,贩卖虚拟世界的东西并非全无意义。而之前作为一个游戏设计者的道德观迷住了自己的眼睛。

我们的同事有一种观点是,让现实社会的钱直接进入游戏,一定会干扰游戏世界的公平性,因为作为游戏世界来说,一部分的人通过后门得到了上帝的恩赐。虽然不公平在任何地方都一定存在,也不是我们一定要在游戏中保证完全的公平性。但是,虚拟物品买卖对公平性的损害一定存在,只是程度问题。这种观点下的结论就是,我们应该试探玩家对公平性失衡的底线所在,以此设计游戏。

而我却觉得,我们应该从另一个角度看这个问题。

大家应该都记得海盗分金子的问题,最弱的海盗得到了最大的利益。而大家却赞同了这个结果。另一个例子是现实中经常见到的:当利益分配不均时,只要相关人员都得到了一定的好处,即使分配方案看起来极其不公平,这个集体往往也是对分配方案比较满意的。最终的满意度在于人,而不在于客观的公平性。

如果我们的游戏设计能让上帝的恩赐每次降临到游戏的集体上,而不是准确的为个人投入的现实货币的数量来决定到个人。即使游戏集体内部分配不太公平,大家也是可以接受的。我们设计游戏应该考虑的是整个游戏运营过程中,游戏设计本身可以带来的整体赢利额,而不用太可意追求游戏中每个单体如何为游戏付费。一个设计合理的游戏(包括其付费机制也是合理的),最终总有一部分人会为群体付费,就如同梦幻今天这样。而梦幻只是模式之一罢了。

这次在广州跟同事讨论中,我提出的一个大思路是这样的:

设计一种模式的游戏,我们可以免费让玩家来玩,如果游戏性满足玩家的要求,有足够多的人来玩的话。玩家群中一定会出现一些有能力高消费的潜在的人民币玩家。

假设,我们的游戏设定的游戏发展空间是 10 年。玩家群体一定会不能忍受漫长的发展过程。然后我们提供一种手段,可以用人民币来缩短这个过程,但是这个依然受到游戏设计本身的控制。比如极限的发展速度是 2 年。那么整个玩家群体会倾向于投入人民币来达到这个极限速度。而支付这笔费用的人会是那批人民币玩家。

但是在整个设计中,所谓的 10 年发展空间并不是针对玩家个体的,而是玩家整个团体。网络游戏本身本来也有社会化的趋向。例如城市,帮派这些设定会越来越丰富的出现在每个 mmorpg 游戏中。其重要性完全可以和个人修炼并驾齐驱,甚至超过以往早期游戏的个人修炼的地位。

如果个人玩家投入金钱的效果的设定在提高个人能力和提高团体能力间变得模糊的话。在宏观上看,投入金钱是一种集体行为。即使不投入金钱的玩家,也会在游戏中以虚拟货币和人民币交换,进一步模糊人民币在游戏中的作用。(即,梦幻玩家把梦幻币作为硬通货,并跟人民币建立等价关系)

这样,对于整个游戏,集体投入人民币来加速游戏社会的发展,就成了集体意志的导向,而非个人行为了。公平的问题也很容易被玩家所接受。最终,运营游戏的公司也得到了收入,作为开发和维护游戏的付出,这些也是应得的。这样的方式,如果正确的执行,应该比梦幻的模式更富弹性,但是也更有风险。设计游戏的人应该对游戏运营内在的规律要有更为清晰的了解。

限于某些原因,我没有在这里写出更为具体的实现思路。希望简单的一篇 blog 还是可以给感兴趣的朋友提供一些灵感。

August 20, 2006

广州归来

原来计划在广州住一个晚上的,后来感觉太累,居然住了三晚。

第一天的策划会议讲的挺累的,不过我觉得还是有些效果。至少部分的观点得到了大家的认同。第二天的技术讲座由于准备的太仓促,估计没什么意思。结果,会后讨论的最多的不是我写到 ppt 里的东西,反而是没写进去的一些。看来临时赶工的 ppt 就是不得重点。

广州的新大楼修的的确漂亮,到处是眼前一亮的感觉,不愧是出至中国美院的设计。顶上两层活动的空间还没有装修好,看到那些墙壁的造型,我又意淫了一下,如果以后装上抱石墙该有多 cool 啊。

只是出入没以前方便了,门口坐了保安检查工牌,每层楼办公区还有单独的门禁。好在我把丁老大的工牌弄了过来,享受了一把最高权限,出入无限 :D

看到大家都很好,很开心。工业园变化也很大。门口的路修了,多了座星级酒店。吃饭的选择也多了很多。只是很可惜的,这几天好多人休年假都没见着。办公区坐了一大半不认识的新人,偶尔可以看到一些去年面试过的面孔。似乎除了我们这里的小办公室里的饮料还在收费,现在包括广州的办公室里也大量供应免费饮料和零食了。不过我还是不太赞成免费,这样大家都很难喝到冷饮。因为新供应的饮料放在冰柜里温度没下来就被分光了。

此行最大的收获是把桥牌传播到了广州办公室,至少教会了十个人打牌。乐观一点,桥牌最后可以被做到 popo 游戏里去了。

目前公司的游戏业务处在低谷期,大唐的运营情况不容乐观,天下短期内上不了市。不过看过大话三和大话 2.5 以及 popo 游戏的进展,还是满有信心的。至少比我的期望值高多了。相信网易会良好的发展下去。

August 17, 2006

临时决定出差

晚上 6 点接到电话,需要回趟广州。明早的机票已经帮我订好了。

其实一直有计划回去一次,不过没想到这么快。原来打算有机会回广州给技术部的同事做一个讲座的,介绍一下,我们这边一年来的一些工作成果。这次这么急着回去开个会,什么都没有准备。

一开始打算带代码和 demo 回去讲的,晚上改变了主意,还是连夜做了十多页 PPT 。

就这样了,以我出差的风格。是不会带笔记本的,自然接下来几天也不会上网了。网络对我来说,似乎越来越无所谓了,如同我的手机 :)

August 14, 2006

Windows 下以非阻塞方式读取标准输入

最近遇到一个小问题,游戏的 client 在开发调试阶段需要接收控制台的输入指令。这个需求其实一直都有,只不过以前是自己写的控制台,那样反而好控制一些。使用 Windows 标准控制台也不是第一次,但是这个输入问题都没有好好的解决。这次又碰到这个问题,决定找个好点的解决方案。

读取标准输入的 C 函数,像 scanf , gets 这些都是阻塞方式的。一经调用,程序就塞在那里不动了。起初的想法是,既然控制台输入就是一个标准输入文件,那么把这个文件修改成非阻塞模式就可以了。google 了一下,似乎 windows 下并没有 fcntl 或是 ioctl 这样的东西可以修改 stdin 为非阻塞模式。或许有别的 windows API 吧,没精力去查。

比较丑陋的方法是用 _kbhit 检测键盘输入,这个方法不太符合我的审美观。想了一下,觉得还是另外开个线程清爽一点。反正是调试用,虽然从资源占用与效率角度看不太美妙,姑且也可以凑合了。

以下代码可以工作 :)

#include "windows.h"
#include "process.h"
#include "stdio.h"

#define BUFFER_MAX 1024

char g_nbstdin_buffer[2][BUFFER_MAX];
HANDLE g_input[2];
HANDLE g_process[2];

DWORD WINAPI console_input(LPVOID lpParameter)
{
    for (;;) {
        int i;
        for (i=0;i<2;i++) {
            fgets(g_nbstdin_buffer[i],BUFFER_MAX,stdin);
            SetEvent(g_input[i]);
            WaitForSingleObject(g_process[i],INFINITE);
        }
    }
    return 0;
}

void create_nbstdin()
{
    int i;
    DWORD tid;
    CreateThread(NULL,1024,&console_input,0,0,&tid);
    for (i=0;i<2;i++) {
        g_input[i]=CreateEvent(NULL,FALSE,FALSE,NULL);
        g_process[i]=CreateEvent(NULL,FALSE,FALSE,NULL);
        g_nbstdin_buffer[i][0]='\0';
    }
}

const char* nbstdin()
{
    DWORD n=WaitForMultipleObjects(2,g_input,FALSE,0);
    if (n==WAIT_OBJECT_0 || n==WAIT_OBJECT_0+1) {
        n=n-WAIT_OBJECT_0;
        SetEvent(g_process[n]);
        return g_nbstdin_buffer[n];
    }
    else {
        return 0;
    }
}

void main()
{
    create_nbstdin();
    for (;;) {
        const char *line=nbstdin();
        if (line) {
            printf(">%s",line);
        }
        else {
            Sleep(0);
        }
    }
}

这个程序会用一个额外的线程去读取 stdin ,我实现了一个叫做 nbstdin() 的函数,其作用有点像 gets() 。但这个函数是非阻塞的,如果控制台没有新的行输入,它会返回一个空指针。

这个程序用了两个输入 buffer 乒乓切换,这样做可以避免在两次调用 nbstdin() 之间对输入 buffer 的处理被读线程破坏掉。

程序结束的时候并没有释放创建出来的 Event 也没有主动关闭读输入的子线程,我觉得这样做更简洁,该 os 处理的事情留给 os 吧 :)

August 08, 2006

popo 的语音通话

popo 自从用了跟 google talk 一样的语音引擎后,语音通话的质量好了很多,已经非常实用了。

今天突然想,popo 语音前既然跟电话一样嘟嘟等人接,那何不做成彩铃的。还可以自己录,应该很好玩的 :)