大风起兮云风扬的 BLOG: May 2008 Archives

skynet 中使用了一个 hash 表结构来保存服务和 32bit 数字地址的映射关系。
一个 skynet 的服务,其实是一个 C 对象。在有沙盒的系统中,尤其是并行构架,我们很少直接用 C 对象指针来标识一个 C 对象,而是采用数字 id 。用数字做 handle 可以让系统更健壮,更容易校验一个对象是否还有效,还可以减少悬空指针,多次释放对象等潜在问题。比如,操作系统为了把用户程序隔离在用户态,像文件对象就是用数字 id 的形式交给用户程序去用的。
和操作系统通常管理文件句柄的方式不同,skynet 尽量不复用曾经用过的 id 。这样,当你持有了一个已经不存在的 id ,不太会误当作某个新的对象。(操作系统的文件 handle 很容易造成这样的问题:你记住了一个文件,在别的流程中关闭了它,然后又打开了新文件,结果复用了过去的 handle ,导致你操作了错误的文件。。)
但是,一旦尽量不复用 id 。用简单的数组来做映射就很难了。必须使用 hash 表来保证映射效率。在 skynet 中我是这样做的:
每次分配新的 id 时,首先递增上次分配出去的 id (允许回绕,但会跳过 0 。0 被当成无效 id )。然后检查 hash 表中对应的 slot 是否有冲突,若有冲突就继续递增。如果 hash 表的池不够用了,则成倍扩大池,并将内部的数据重新 hash 一次。
这样虽然会浪费一些 id ,但是可以保证 hash 表类的 key 永远不发生碰撞,这样可以让查询速度最快。hash 表的实现也相对简单一些。
我觉得这样的一个数据结构有一定的通用性,今天花了一点时间把 skynet 的这个部分单独抽出来,当成一个独立开源项目重新写了一遍。有兴趣的同学可以。
2d 游戏通常都用到很多图素,由于显卡硬件特性,我们又不会把单个图素放在独立贴图中。这样会导致渲染批次过多。在移动设备上,非常影响渲染效率。
所以,游戏运行时,这些图素一般都会合并在很少几张贴图上。要么采用离线合并的方式(利用 texture packer 这样的工具),或者在运行时使用装箱算法。
最近,朱光同学一直在为 ejoy2d 编写运行时合并图素的模块。今天我们讨论了一下他做的诸多尝试。
去赌场参观过的同学应该都见过那种押大小的骰子游戏。庄家投掷三枚骰子,把骰盅盖住,玩家可以押大或小。开盅后,如果发现三个数字之和大于等于 11 就算大,小于等于 10 就算小。如果你猜对了,庄家就 1 赔 1 算给你筹码;否则输掉筹码。另外,还可以以不同赔率压数字,或压三个相同。
为了保障庄家利益,三个相同的数字算不大不小。从概率上讲,这让长时间内庄家必胜。。
如果把这个游戏搬到网络上如何呢?(注意:网上赌博在很多国家是被禁止的,这里只做技术分析而已)
如何让玩家相信庄家没有作弊,真的产生了随机的骰子呢?
这篇文章是在大半年前, 我们的 刚刚做预研的时候写给公司内部分享的。由于这个项目公司不希望在开发阶段对外暴露信息,所以文章也没有在 blog 贴出来。
现在 就没有消息保密的需求了,我就可以把当初开发时写的一些东西逐步贴出来。
所谓类似 COC 这样的斜视角引擎,在英文社区中叫作 Isometric Tileset Engine。它在早期计算资源匮乏的年代用的很多,后来内存不是问题了后,就很少有人用了。由于没有 Z-Buffer ,这种引擎的绘制次序其实很讲究。比如下图:
最近抽时间在读 Milo Yip 翻译的《游戏引擎架构》。书还没有出版,我应邀给这个译本写序,所以先拿到了电子版,正在加紧读一遍。全书接近 800 页,我已经读了 600 页左右,希望这个周末可以抽时间读完。
这本书是由顽皮狗的主程之一 Jason Gregory 写的,内容很精彩,Milo Yip 翻译的也相当不错。有很多章节我读的很有共鸣,想先挑一点来写写。
由于作者的主要技术背景是 Console game 开发,而 Console 目前内存非常有限,且没有虚拟内存,对内存的管理和使用就非常苛刻。很多 PC 平台上几乎被忽视的问题到了 Console 平台上就需要仔细考虑了。我很有共鸣是因为 10 多年前在开发大话西游时,要求在 64M 内存上跑起来,同样写了好多内存管理相关的代码。
比如栈式内存管理,就是在堆上模拟一个栈,只管分配,然后记住一个标记点一起释放。
又比如双端分配器,自己管理一大块内存,根据需求不同从两端向中间分配,这个还可以配合上面的分配器一起工作。
把对象的生命期绑定在渲染帧上,在一帧渲染完后把当帧的临时内存全部释放;或者做的更复杂一点,做一个乒乓开关,临时内存可以保留到下一帧结束。呵呵,这些以前都写过。
04 年的时候在网易公司内部做个一个比赛,就是在一块固定大小的内存内实现自己的内存管理器。用我们从梦幻西游,以及大话西游的客户端中实际采集来的数据做比赛评分,分别按允许速度和碎片率打分。我自己虽然没有参赛,但当时也写过一份程序拿到了最高分 :) 当时比赛的前三名现在都是网易项目(或离职后是新公司的主程)的主程。
前两天阿楠同学想用链表实现一个消息队列,虽说是链表,但是没有从中间删除节点或添加节点的需求。只需要先压入的消息先处理。为了完成这个需求,最后实现了一个双向链表。
当然这个东西是用 Lua 写的,做一个双向链表也不算复杂。采用单向链表也可以做到,只需要多记录一个尾节点。不过今天想说的是,如果这个玩意在 C 里实现的话,其实只需要用一个指针的空间就可以搞定一个双向队列,可以从任意一头进出数据,可以以两个方向遍历,而实现需要的代码量却和单向链表类似。因为算法非常有趣,在 C 语言之外很少见到,知道并使用过的人也就不太多了。这就是所谓的
今天吃晚饭的时候想到,我需要一个定制的内存分配器。主要是为了解决
中的字符串池的管理。
这个内存分配器需要是非入侵式的,即不在要分配的内存块中写 cookie 。
而我的需求中,需要被管理的内存块都是很规则的,成 2 的整数次幂的长度。
刚好适用。
算法很简单,就是每次把一个正内存块对半切分,一直切到需要的大小分配出去。回收的时候,如果跟它配对的块也是未被使用的,就合并成一个大的块。标准算法下,分配和释放的时间复杂度都是 O(log N) ,N 不会特别大。算法的优点是碎片率很小。而且很容易做成非入侵式的,不用在被管理的内存上保存 cookie 。只需要额外开辟一个二叉树记录内存使用状态即可。
我吃完饭简单 google 了一下,没有立刻找到满足我要求的现成代码。心里估算了一下,C 代码量应该在 200 行以下,我大概可以在 1 小时内写完。所以就毫不犹豫的实现了一份。
然后,自然是开源了。有兴趣的同学可以。这样就省得到再需要时再造轮子了。嘿嘿。
昨天读到了
的中文介绍,觉得非常有意思。不过上面这篇文章解释的非常不靠谱,我花了一晚上去 仔细研究了一下,总算理解了其原理。感觉非常有启发,尤其是对虚拟货币的流通和发行有许多借鉴意义。今天写这篇 Blog 理一下。
什么是货币呢?货币就是商品(包括服务)交换的媒介。现在我们通行的货币是由有信誉的银行发行的,基本上是由其信誉来担保的。只要用的人都认可,那么我们就可以用它来交易。货币有一定的保值特性,我把我的劳动/服务/所有的商品换成货币后,银行担保我在日后的某一天,我还可以用它交换会差不多等值的东西。这个保证的前提是,银行不会滥发新的货币以及大家都信任这一点。
数据结构的序列化是个很有用的东西。这几天在修改原来的资源管理模块,碰到从前做的几个数据文件解析的子模块,改得很烦,就重新思考序列化的方案了。
Java 和 .Net 等,由于有完整的数据元信息,语言便提供了完善的序列化解决方案。C++ 对此在语言设计上有所缺陷,所以并没有特别好的,被所有人接受的方案。
现存的 C++ serialization 方案多类似于 MFC 在二十年前的做法。而后,boost 提供了一个看起来更完备的方案( boost.serialization )。所谓更完备,我指的是非侵入。
boost 的解决方案用起来感觉更现代,看起来更漂亮。给人一种“不需要修改已有的 C++ 代码,就能把本不支持 serialize 的类加上这个特性”的心理快感。换句话说,
就是这件事情我能做的,至于真正做的事情会碰到什么,那就不得而知了。
好吧,老实说,我不喜欢用大量苦力(或是高智慧的结晶?)堆积起来的代码。不管是别人出的力,还是我自己出的。另外,我希望有一个 C 的解决方案,而不是 C++ 的。
所以从昨天开始,就抽了点时间来搞定这件事。
去年写过几篇过于 。将 AOI 服务独立出来的设计早就确定,并且一定程度上得到了其他项目组的认可。(在公司内部交流中,已经有几个项目打算做此改进)
最近一段时间,我在这方面做进一步的工作。(主要是实现上的)
首先,基于 KISS 的考虑,删除了原有协议中的一些不必要的细节。比如,不再通知 AOI 模块,对象的移动速度。移动速度虽然对优化很有意义,但毕竟是一冗余数据。考虑这些,容易掉入提前优化的陷阱。
其次,增加了一条设置默认关心区域的协议。这是出于实用性考虑。
几年前我曾经写过一篇 blog 介绍我发明的一种 。最近几天,我将这个算法用于目前正在开发的 engine ,如之前所料,效果不错。由于数据被压缩,更改善了资源加载的速度。并在同等内存条件下,让用户得到更高的体验。(这是因为,现代操作系统,都会把暂时不用的物理内存全部用于磁盘缓存,更小的文件体积同时意味着在同等内存条件下能缓存更多的数据)
因为我们需要做动态地形数据的加载,所以地形高度数据也被切割成了一小块一小块。采用这个算法后带来的一个问题是:由于压缩有数据损失,两个相临块之间不能严丝合缝的衔接在一起。
为了解决这个问题,这两天想了好几个方法。
曾经看过这样一种赌徒的策略:假设在一场赌大小的赌博游戏中,赔率是 1:1 ,而庄家不会出千,开大和开小的概率均等(皆为 50%)。赌徒一开始压一块钱,如果他压错了,那么下一次就压两块,再错继续加倍。一旦压对,赌徒永远可以保证有一块钱的进帐。并且从 1 块钱重新开始。
看起来,这种策略能保证永远包赚不赔。但实际上为什么没有人用这个方案发财呢?
放到现实中,试图采用这个策略去赌博的人,几乎都会赔的倾家荡产(当然只要是赌博,差不多都是这个结局)。不要怪运气,不要怪庄家出千,因为这个策略从一开始就注定了失败。
今天从 svn 中取下一个同事的代码,浏览了一下。其中一段是关于洗牌的。感觉实现的很乱,算法也没有选好。自己重新写了一个。
因为国庆的缘故,负责这块代码的同事提前回家了。我只好自己读代码实现。看完后,发现旧的实现是这样的:对于 N 张牌,每次随机抽出一张来,检查这张牌是否抽过。如果被抽取过,就重复抽取过程,直到不重复。然后记录下抽取的牌的位置。重复这个过程,直到得到一个随机序列。最后用这个序列将原始排列打乱。
这个方案给我的第一感觉并不好,因为当 N 较大时,最后需要重复抽取多次才可以成功。我估算了整个的时间复杂度大约是 O(N^2)
当然,洗牌这件事情可以有许多算法来解决。我重新写了一个,采用了最简单实现的方案。
缘起:听组的同事谈起,游戏的资源数据量太大,导致了加载时间过长,影响到了操作感。最终不得不对数据进行压缩。
当玩家配置的物理内存足够大时,往往资源数据压缩会适得其反。因为多出来的数据解压缩的过程会占用掉太多的 CPU 时间。物理内存够大的话,操作系统会尽量的拿出物理内存做磁盘 IO 的 cache ,即使你的程序不占用太多的逻辑内存,而反复从硬盘加载大块数据,也不会感觉到磁盘速度的影响。当数据量大一个一个临界时,被迫做不断的外存内存的数据交换,这个时候,数据压缩就会体现出优势。因为解压缩的 CPU 时间消耗是远远小于硬盘 IO 所消耗的时间的。
btw, 玩现在的 MMORPG 往往配置更大的物理内存得到的性能提升,比升级 CPU 或显卡要划算的多,就是源于这个道理。通常 MMORPG 都需要配置海量的资源数据,来应付玩家的个性化需求。
天下组的同事暂时只做了骨骼动作信息的压缩。这个这里暂且不谈,我想写写关于模型顶点数据的压缩。记录下这两天在思考的一个有损压缩算法。
估计是因为我在上大学时在网上留过一篇文章,里面有一个简单的使用 A* 算法寻路的源程序,导致了这近七年来,不段的收到 email 和我讨论这个算法。
对,毫不夸张。那个简陋的代码。在七年前我随手写出来扔在网上后,已经遍布中文网络世界的犄角旮旯,让我在痛恨那段代码的时候,想收回都不可能。
我最大的困惑在于,为什么课堂上最为基础的算法知识,却有如此多的人行入误区。难道写游戏的程序员都不去读读基本的课本?
A* ,读作 A-Star 。它仅仅是一个启发式搜索算法。就是说在一个可以被穷举的有限解空间集中,用比较有效的方法(主要是利用估价函数)求出最优解的算法。A* 跟寻路算法没有太多关系,我们只是借助了这个方法来解决寻路这个问题,正如四则运算对于数学题一样,它只是一个基本工具。寻路算法本身有很多种,我们找到算法后,借助 A* 这个工具实现这个算法而已。
3d游戏中的室外场景通常用高度图来表示,用一个二维数组来描述对应平面坐标的高度信息。
如果每个高度信息用一个 byte 来表示,很多情况下不太够用。因为对于很大的场景, 256 级的高度是远远不够的。通常我们会选择用 word 或者 float 来表示。
相比较而言,float 最为合适。这样,我们不太需要考虑缩放因子这个问题。但是 float 需要 4 个字节,这导致数据量很大。这里云风给出一个有损压缩方案用于压缩这些信息。应该是在 blog 上第 2 次讨论 AOI 的问题了,另外还要算上一篇写完没有公开的。
昨天刚出差回来,晚上跟前大唐的服务器主程聊了一下。扯到了 AOI 的问题,我提了个分离这个模块的方案,在此记录一下。
AOI 主要有两个作用,一个是在服务器上的角色(玩家或 NPC )做出动作时,把消息广播到游戏地理上附近的玩家。当进程中负责的玩家不是很多的时候,可以直接对整个进程内的连接广播。这样的处理最简单,随着硬件的提高,可能是未来的主流方法。但是目前,为了减少处理的数据量,尤其是带宽,我们通常需要 AOI 模块裁减一些数据。
第二,当玩家接近 NPC 时,一旦进入 NPC 的警戒区域,AOI 模块将给 NPC 发送消息通知,以适合做一些 AI 的反应。
AOI 的实现算法很多,这里不展开来谈。我目前关心的是,如果 AOI 在游戏中非常重要(尤其对于即时战斗模式的大场景 MMO )能否把这个模块抽离到独立进程中。
首先我们来看第一个需求。其实在游戏中,需要广播的消息大多次序不敏感。比如移动,是 A 先移动还是 B 先移动并不重要;又比如攻击,是先攻击再移动,还是先移动再攻击也不重要。我指的先后是指 client 接收次序。攻击是否启动虽然跟距离有关,但是发出攻击指令前,服务器逻辑已经校验过合法性。也就是说,服务器发过来的信息都是合法的,client 就不用太在意位置改变在攻击前还是攻击后了。
那么,我们把 AOI 广播的这个职责拆分到独立进程做,就不需要跟逻辑进程协调发包的次序。这是个好消息。
设计一个 AOI 进程,让逻辑进程在任何一个对象改变位置时发一个包通知 AOI 进程,这样等于复制一份对象位置状态在 AOI 进程中。以后,当对象需要广播它的状态变化,只需要把需要广播的消息发到 AOI 进程里,然后由 AOI 进程过滤一下(筛出附近的对象,做一个组播)。
需要注意的是,由于消息都是异步发生。我们在 client 接收对象 id 时,应该过滤掉一些过期的 id 号。因为有可能逻辑服务器已经删除了某对象,但是 AOI 服务器迟一些发送了跟这个对象相关的消息。(把对象删除的消息交给 AOI 服务器转发是一个糟糕的想法,因为这增加了 AOI 服务器的职责,增加了复杂度)
对于 AOI 消息通知的问题,可以让 AOI 服务器在检测到对象侵入别人的警戒区时,向逻辑服务器发会一条消息。这个消息不需要实时反应。比如,策划要求一个 NPC 有 20 米的警戒区,其实不必在玩家刚好 20 米的边界上触发,稍微延迟半秒是无所谓的。而且警戒区越大,允许的延迟时间就越长。
单独的 AOI 服务,可以根据警戒区的半径,进行优先级排序。然后有条不紊的通知消息一个个发回。
对比单独的 AOI 模块,独立 AOI 进程有更低的耦合度。通过协议耦合而不是接口耦合。更容易维护和日后的优化。没有和逻辑部分的共享状态,减少了 bug 滋生的可能。
ps. 最近跟新同事讲了一节课。其实没讲太多技术上的东西。只是介绍了游戏行业,以及计算机行业的硬件发展历程,以及游戏的进化,还有一点点软件开发上的进步。
我想,一切的历史都在见证,只有把东西设计的足够简洁,才能够顺利的向前发展。如果不够简洁,有一天我们会抛弃它们,或者为之付出代价。希望可以给刚从学习毕业的同学们一些启示。
前天开会前抽空读了一小时新运营的大唐二的服务器代码。追查出一个隐藏很深的 bug 。据说这个项目刚对外开放的这几天情况不太好,老是当机。程序员应该明白,其实也不一定是很多 bug ,但是一个就够你受。
大唐 2 的服务器写的挺“精巧”,C++ 和 lua 之间绕来绕去的,差点没把我绕晕。太复杂了……
Keep it simple , stupid .
stupid 不是愚蠢的愚,而是大智若愚的愚。有时候,我们写程序做设计就是不够智慧,就只有点聪明。觉得自己可以把复杂的问题用巧妙的方法解决。这个巧妙的方法,保正了效率,节约的内存。真可谓聪明。
但聪明不是智慧,真正的智慧是看到将来。在不断的演化中它们还能不能保持这个巧妙。如果不那么巧妙,我们到底会损失些什么,我们真正需要什么,而失去的那些换来的到底是什么。
simple 也不是为了避免麻烦。保持 simple 比解决麻烦要麻烦的多。
最近老有人跟我说啥 2.5D 3D 什么的,有点反感这个词。纯属不懂瞎参合。当然我也知道名词就是用来区分不同事物的,不能强求。
人就是奇怪,明明不相信数学可以解释这个世界,偏偏又喜欢拿数值去套所有的概念。一切都是一维数轴上的点。不是多一点,就是少一点。
我决定改天也造个新词儿出来。
嗯,我们不做 3d 游戏,多俗。2.5D ? 太土了。我们是 2.71828 D 。哦,记不住没关系,反正也没做完,暂时称为 2.7D 吧。
周末跟同事聊天,问了一下最近公司新项目的一些需求。发现现在无论是玩家还是策划,纷纷要求引擎可以实现自动寻路。虽然我对此不以为然,但其实这也不是什么难题,打算随便做一个好了。
我们现在的 engine 是用矢量线段描述场景中的障碍的。这些线段无所谓人工标记还是从场景模型中自动生成出来,又或者是用传统的打格子的方法变换出来。在这些障碍线构成的矢量场景中寻路其实不是什么难事。
我们只需要在场景中标记出若干 waypoint ,这些 waypoint 一般在大片无障碍区域的中央,以及分叉路口。游戏中故意设计迷宫为难玩家绕来绕去又没有什么玩点内容在里面是没有什么意义的。(除非是“不可思议的迷宫”这种以迷宫为重要玩法的游戏)所以,大多数游戏场景,路径的拓扑关系复杂度非常有限。 waypoint 靠人工设置就足够了。
btw, 自动生成 waypoint 也不是难事,反正这个东西不会是性能要点,机器生成 waypoint 不会比人工设置的糟糕太多。算法就不展开谈了。waypoint 的设置要点是:场景中所有可达区域都能看到至少一个 waypoint ,这个要求可以用程序检测。
我们把所有 waypoint 间可以直线连接的线路连起来,得到一张图。可以预存下这张图,以后的工作则非常简单。
每次需要得到场景中任意两点间的路径,就从起始点和目标点各取一个临近可见的 waypoint 。之后求这两个 waypoint 间在图上的最短路径。Dijkstra 算法即可。
得到路径后,角色在场景中行走时,间隙的判断是否可以直接看到路径上的下一个 waypoint ,只要可以看到,就沿直线方向朝路径上最远可见的 waypoint 移动。最终可以得到一条较为自然的运动轨迹。
嗯,没啥高深的算法,随便写写就能实现了吧。
最近在公司附近的一个小区会所办了张健身年卡。几年没动了,体力和力量都下降了不少。关键是平时精神不太好,感觉没前几年有活力。是应该每天流点汗,吃好睡好,这样脑子更好使。
已经坚持去了三次了,重量大了会有点头晕,得控制一下。刚开始恢复,浑身肌肉酸痛。昨天做了几组深蹲,今天走路都有点痛。不过这几天居然有了不小的食欲。白切牛肉可以吃一大盘,冰箱里的鸡蛋一扫而光。是个好兆头。
这个周末尽力的离开办公室,离开网络,离开计算机。
今年第一次逛超市(似乎去年也没去过),买了点吃的。离开零食很久了,都不知道没什么好。
看了一场电影,当然不是《赤壁》。因为听说很雷,我怕被雷到了。所以我选择了《功夫熊猫》。
去剪了头发,很久没去理发了,开叉的许多,修理了一番,不过没换发型。当然我从来就没换过。
想约人吃饭,但这好象很不容易,想必周末大家都有安排吧。
开车去玩桌游,一路上很顺利,居然都是绿灯。这次尝试了几个新游戏。《电力大亨》不错,设计的很精巧,规则也不复杂。不过另一个《吸血鬼》,让我啃了一个半小时的规则书硬是没弄明白。本来想尝试一下另一个二战的游戏。老板说,还是等下次吧。早上过来,估计到吃夜宵的时候可以完成一局的。
我想我会试试,就冲着那满地图的塑料小兵人了。
一个放松的周末。
最近事情比较杂,所以没心情写 blog 。
travian 玩了一年多,终于快结束了。奇迹修好就解脱了。这个游戏教了我许多设计思路,并展示了一些问题,需要好好盘点一下。
这两天有人问我,为啥我玩了二十多年电脑,眼睛就不近视呢?这个问题好多人问过了,我也想过好几次,终于有了个结论。就是因为我从小玩电脑,所以才没近视的。
道理其实很简单啊。这么多年,坐在电脑前,干的最多的事情是写程序。这当然不同于大多数非程序员的电脑使用者。非程序员用电脑,需要眼睛需要专注的看屏幕,是屏幕对人体的信息输入。而写程序是个相反的过程,是人体对计算机输入,基本是在用脑而不是用眼。眼睛自然没那么疲劳啦。而正是从小到大坐在显示器前,电视也没怎么看。当然比那些小时候泡在电视前的小孩子们把眼睛保养的好啦。
同事做的天下2又一次开始试玩期了,据说从玩家数字和收入情况上看还可以。虽然比前一个版本更泡菜,但是这也是大众口味了。大众口味被骂是正常的。不是现在的玩家越来越喜欢快餐式游戏,而是玩家群体扩大了。我相信作为个体的玩家是会有越来越高的游戏追求的,但群体则不然。
前两天跟人聊天,说大街上美女身边几乎都不是帅哥,所以长的丑更容易找个美女当老婆。我说,这是青蛙们用来安慰自己的。学过点概率的同学稍微想一下就能明白,帅哥配不到美女,不是因为美女倾向于找青蛙,也不是帅哥喜欢恐龙。根本原因是因为帅哥美女在人群中出现的比例太小了。
长的漂亮,找到相貌出众的配偶的概率一定比普通人高,这是生物学规律。但是概率比别人高,不等于这件事发生的概率就高。
假设一个美女有 100 个追求者,其中一个是帅哥,另外 99 个是青蛙。即使帅哥追到美女的概率比他的竞争者高 10 倍,那么他成功的概率也不过 9% 而已。所以美女最后选择的更有可能不是他了。
其实我想说的是,无论做什么有竞争性质的事情,提高自己的竞争力是可以加大相对于别的竞争者胜利的概率。但并不一定保证你能成功。好比买彩票,你花一万块下注,比花 10 块钱下注的人更容易中奖。但是事实是,往往出现只花 10 块钱的就中了大奖的人。这绝对不能说明,花 10 块钱下注的人比花一万块的人更有优势。而只能说明,花点小钱玩彩票的人比倾家荡产买彩票的人多的多;或者说明买彩票的人大多数都是穷人。
如果你尽力了却失败了,不用太怨天尤人,抱怨 RP 不好,老天不公。其实这个世界的物理法则是公平的,只是你没理解公平的规则而已。
说起公平,看到天下2 里做了个很可笑的开宝箱的设定。可以先让玩家看一眼宝箱里有些什么,然后转轮盘。通常箱子里有很多垃圾,也有一些好东西。游戏还允许玩家自己事先调整一下那些东西的位置再转。我还没问开发组是不是在程序里作弊,但是据玩家说,宝箱里一般大约有 50% 的垃圾物品,但是转轮盘转到垃圾的概率是远远大于 50% 的。
我想这个设计应该就是个幌子了。这种东西也就骗骗小学生吧。想想也是,宝箱以 50% 概率开出好东西,这个概率的确也太高了。我们是不是应该想个更好的点子来让玩家的感觉上的概率和实际的一致?
这让我想起密码学里的一个交换信息的学问了。就是我先把信息交给你,但是你不能看,事后大家在核对。保证双方都没有作弊。
密码学里这样的策略有很多,不过不太好解释给玩家。以众多玩家的小学数学水平(非贬义,根实际统计数据,现在网游玩家中低学历人群比例相当的大)很难理解并自行校验。
我想了个法子,让玩家信任服务器没有作弊。
当玩家转轮盘之前,系统先把结果产生出来,并随机发给一个玩家。并告诉这个转轮盘的这个人,结果提前发给了谁。玩家需要在一秒内下注,然后得到结果。
事后,玩家可以向那个事先得到结果的玩家确认系统是否作弊。
这个过程的时间很短,短到不可能针对性的询问特定玩家结果就够了。再加上系统信息都加上时间,可以方便事后核查。
当然这只是一个草案,实际想做的话,需要完善许多。比如加入竞争关系,阻止提前收到信息的人广告结构等等。就不展开讨论了。
别的方案也有许多,比如由服务器发一个 100 位的整数,问玩家这个数字模 7 等于几。如果限制时间回答,假设玩家没有外挂帮他计算的话,也能起到同样效果。(因为以人脑的计算能力,不可能在很短的时间内得到答案,只能考猜)
我们还可以综合以上两个方案,玩家在转轮盘之前,把这个 100 位整数发给随机一个玩家。并告诉转轮盘者发给了谁。并允许他们事后相互验证。这样,靠一个人做外挂作弊得到答案也就几乎不可能了。

我要回帖

更多关于 大风起兮云风扬 的文章

 

随机推荐