陌陌争霸鲜血诅咒,当前城防值低于30%。如何做该提升建筑的血量。

热门搜索:
您的位置:
→ 陌陌争霸城墙防御系统介绍
  今天小编为大家带来的是陌陌争霸城墙防御系统介绍,城墙在游戏中属于军事防御建筑的一种,玩家建造城墙的作用就是被动防御敌人,玩家建造城墙需要消耗200元宝,下面是城墙防御系统的详细介绍,需要的玩家可以了解一下。  城墙在现实中是指旧时农耕民族为应对战争,使用土木、砖石等材料,在都邑四周建起的用作防御的障碍性建筑。  在游戏里是属于军事防御建筑的一种,只能建造起来被动防御敌人。建造需要消耗金钱200。但是在时间上是无消耗的。
网名(您的评论需要经过审核才能显示)
] 楼取消回复陌陌争霸攻防图吧-百度贴吧
签到排名:今日本吧第个签到,
本吧因你更精彩,明天继续来努力!
本吧排名:
本吧签到人数:0
可签7级以上的吧50个
本月漏签0次!
成为超级会员,赠送8张补签卡
连续签到:天&&累计签到:天
超级会员单次开通12个月以上,赠送连续签到卡3张
发贴红色标题
签到六倍经验
兑换本吧会员
赠送补签卡1张,获得
助攻总额: 43W
发表后自动分享本贴
使用签名档&游戏信息/陌陌争霸
游戏名称:《陌陌争霸》游戏类型:塔防语言种类:中文操作系统:ios、安卓(Android)2.2以上适用设备:iPhone、ipad、Android终端陌陌争霸iOS版 陌陌争霸游戏版本:1.0.21游戏类别:策略游戏游戏大小:35.31MB下载平台:PP助手是否收费:免费游戏平台:手机游戏游戏更新:游戏语言:多国语言系统要求:需要 iOS 5.1.1 或更高版本与 iPhone、iPad、iPod touch 兼容。
游戏介绍/陌陌争霸
——陌陌游戏年终巨献——◆首款城堡抢夺手游默默争霸 996手游网截图◆引领2014年社交平台手游新潮流◆高富帅的战场,全球玩家真人对战,统帅千军征战天下◆白富美的秀场,陌陌“倾国红颜”入驻陪玩*【推荐理由】*◆高—全球同服,挑战全球高手,问鼎世界巅峰荣耀◆富—奢华城堡,私人定制,游戏新贵地位非凡◆帅—倾国倾城,美人相伴,附近真人美女游戏陪玩◆才—统帅三军,智谋千里,百变战局彰显不世之才*【游戏介绍】*
基本内容/陌陌争霸
陌陌争霸网游类别:策略语言:中文操作系统:ios、安卓(Android)2.2以上适用设备:iPhone、ipad、Android终端开发公司:广州EJOY简悦公司
游戏简介/陌陌争霸
游戏以城堡资源掠夺为基础,融入了诸多社交元素。是一款战争策略类手游。目前游戏已在IOS平台上线,并且深受玩家的好评。玩家可以充分发挥自己的创意思维,化身一城之主,扩充银库和粮仓,打造军队,发起战争,抢夺资源,建设城池,提高防御,每一步都能玩出自己的创意。感兴趣的玩家不妨和小编一起期待一下哦。
特色玩法/陌陌争霸
◆全球对战 挑战无界◆抛弃重复沉闷的刷副本,远离呆头呆脑的NPC怪物,对手是活生生的真人。来自全球玩家的智慧千变万华,新奇阵法层出不穷,新鲜、刺激,征服快感就在手边。争霸◆ 百变兵种 创意无限◆垃圾兵?打酱油?请不要怀疑你的将士,《陌陌争霸》的每一个兵种都有其存在价值。我们保证,绝无废物兵种,只有无限的搭配创意,玩出你的多样战斗永不单调。◆公平的智慧对碰◆向数值碾压say no!专门针对移动平台设计的城堡抢夺游戏,全凭玩家自由发挥,自由建设、自由对战,在公平的对战环境下,追求最纯粹的游戏乐趣。◆真人社区附近好友◆为陌陌用户量身打造,加深同城好友的交友乐趣。一起建公会、组群组,互相帮助共同发展。认识更多同城附近美女妹纸,做个温柔保护“倾国红颜”的高富帅吧!
游戏攻略/陌陌争霸
陌陌争霸是陌陌平台新推出的一款游戏哦,怎么样才能快速升级呢?下面就给大家分享几个小技巧。1、尽快将大本营的等级升级到5级大本营提升到5级的时候,可以解锁两个新的建筑:火焰祭坛和炼丹炉,这两个建筑分别是防守和进攻的利器哦!2、升级进攻能力有关的建筑和进攻相关的建筑分别有:训练所,营地,军械处等,提升这些建筑可以使得玩家的兵种、军队数量有很大的提升,进攻的胜率更加有保证,可以更快的积累金钱很粮食。3、别让你的大师人闲着玩家建造建筑都是离不开大师的,因此不要让你的大师闲着,尽量让他不断的建造或者是升级建筑,提高效率。4、扩大你的粮仓和银库扩大这两个仓库可以提高玩家的最大粮食和银两的上限,这对于中期的疯狂抢钱、抢粮有很大的帮助。5、粮食和银两才是关键进攻别人的时候,抢钱、抢粮才是重点,其次才是拿到分数。早期攻略陌陌争霸1、获得新手大礼包进入游戏在活动菜单(礼物标志)—长期活动—填码领奖栏输入邀请码(比如1303702)可获50000粮食奖励,对于新手前期还是很有用的。2、尽快将大本营的等级升级到5级大本营提升到5级的时候,可以解锁两个新的建筑:火焰祭坛和炼丹炉,这两个建筑分别是防守和进攻的利器哦!3、升级进攻能力有关的建筑和进攻相关的建筑分别有:训练所,营地,军械处等,提升这些建筑可以使得玩家的兵种、军队数量有很大的提升,进攻的胜率更加有保证,可以更快的积累金钱和粮食。4、别让你的大师人闲着玩家建造建筑都是离不开大师的,因此不要让你的大师闲着,尽量让他不断的建造或者是升级建筑,提高发展效率。从建造第三个大师房开始需要花费宝石,所以宝石最好都留着购买大师房。初期攻略1、保持进攻势头陌陌争霸可以做3件事:进攻、防御、资源生产。第1、第2、第3重要的事情,是进攻、进攻、进攻!2、学习其他玩家的布局发呆的时候,看看排行榜,参观参观别人家,学习下摆城。这是第4重要的事情,也很有乐趣。算计着让人家打不进来,也很有乐子嘛。3、升级城墙钱多的没处花?就升级你的城墙吧。城墙是秒升的,不要占用你宝贵建筑大师的持续时间。4、合理控制声望分数总是找到比你等级低很多的玩家,没多少钱可打,怎么办?这个时候还是打打声望,提升下排名吧。同理,每次进攻都是你打不动的,怎么办?多输几场,把声望降下来。5、扩大你的粮仓和银库扩大这两个仓库可以提高玩家的最大粮食和银两的上限,这对于中期的疯狂抢钱、抢粮有很大的帮助。6、粮食和银两才是关键进攻别人的时候,抢钱、抢粮才是重点,其次才是拿到分数。7、加入公会加入公会可以获得来自公会的援兵,可以用来进攻也可以用来防御自己的城堡。中期攻略1、尝试不同兵种组合5本之后,可以尝试各种兵种组合。比如神女甲兵流。甲兵(坦克)开道,死士炸墙,神女(治疗)加血,弓箭手往前推,摸金抢银粮。又比如,带上120个摸金,专业抢银粮,那叫一个爽。还有血鹰流,道士甲兵流等等。2、保护银库粮仓防御方面,优先护住银库粮仓。如果不打声望分(排行榜),大本放在外面给人打,输了还能获得保护,何乐不为呢?
宝石获得/陌陌争霸
陌陌争霸宝石怎么得 陌陌争霸宝石获得方法1、完成城主任务在游戏中,玩家可以点击进入城主任务,然后完成相应的任务,有时是会有宝石奖励的哦。2、完成成就成就系统中,玩家只要达到成就会获得经验奖励和宝石奖励,因此有针对性的进行游戏,宝石的获取是轻松的啦。3、游戏内购当然土豪玩家朋友们也可以通过内购的形式来获取宝石,但是宝石的内购价格有点坑,玩家可以自己好好考虑考虑。陌陌争霸宝石有什么作用 陌陌争霸宝石作用介绍玩家在游戏中一定要要注意充分利用宝石。宝石有很多作用如:可以缩短建筑的建造时间,可以缩短训练军队的时间,可以买粮食银两,可以买免战牌,可以买建筑大师。1、缩短建筑的时间缩短建筑的时间可以在最短的时间内建造更多的建筑,加快城池的发展,提高防御能力和资源的收集能力等等。2、缩短训练军队的时间这对于出征攻击敌人,增强自己的军队的实力是有很大的实际帮助的。快速的训练军队就能快速的拥有一支军队。3、购买粮食银两粮食和银两是升级建筑、训练军队的必须品,缺金少粮对于城池的发展是很不利的。4、购买免战牌购买免战牌就能免于敌人的攻击,保证自己的资源不会被敌人掠夺。5、购买建筑大师建筑大师是建造建筑的一个基础,更多的建筑大师可以提高建造的效率,能同时建更多的建筑。
阵型攻略/陌陌争霸
陌陌争霸八本阵形如何改进?是为大家搜集的指点。根据图片具体分析了一下,希望能给到大家不错的启发。感兴趣的玩家可以来具体了解一下。认为首先陷阱太集中了,八本很少保功勋的所以大营外放比较好。其次缺口放两三个就好了,放多了也没用。还有就是有资源就升防空尤其是祭坛 再者就是烟花。小编的分析纯属个人意见,有不对的地方还希望大家能够指正。
新手FAQ/陌陌争霸
新手FAQ总结:Q: 无法安装陌陌游戏怎么办?A: 么么您好,陌陌争霸的最低配置要求为 安卓(Android)2.2,请检查并确保你有足够的空间安装游戏。感谢您对陌陌游戏的支持,祝您游戏愉快!Q: 无法更新A1: 么么您好,建议您按照以下方式尝试解决:1,您删除现有游戏客户端和安装包后,再在陌陌客户端发现页内下载最新版的游戏客户端,2,在主流手机浏览器内,通过下列地址来下载最新的游戏。Q: 无法进入游戏A: 么么您好,建议您按照以下方式尝试解决:1.退出游戏重新登录或开关机进行尝试;2.将游戏版本升级到最高版本尝试。Q: 我可以再另一台设备上玩么?A: 么么您好,可以在另外一台设备上安装陌陌争霸并且使用之前主城的陌陌账号登陆即可。Q: 我可以在一个设备上玩多个账号么?A: 么么您好,可以通过切换不同的陌陌账号登录,系统可以读取不同陌陌账号内游戏的主城。Q: 我可以更改我的城镇名称么?A: 么么您好,每次登录游戏会自动读取当前对应陌陌账号设置的名称,更改陌陌内的名称再次载入游戏便可修改城镇名。Q: 下载后找不到游戏?A: 么么您好,不同的手机和浏览器会将游戏下载到不同的文件夹,还需您再下载时关注和设置下载位置。Q: 花屏问题A: 么么您好,陌陌争霸的最低配置要求为安卓(Android)2.2以上,请升级您的游戏设备系统,以便有更佳的游戏体验。Q: 游戏建筑/兵种信息查询A: 么么您好,士兵和建筑的信息在点击士兵或建筑旁的感叹号就可以看到了,同时建议您去陌陌吧的陌陌争霸吧和玩家交流游戏技巧和信息。Q: 如何挂免战牌A: 么么您好,免战牌获取方式有2种,1.使用宝石购买,2.当您的城市被摧毁后,系统会自动为您补充12个小时的免战牌。Q: 为什么没搜到我要找的公会/公会搜索是怎么设置的/公会搜索只显示30个A: 么么您好,搜索公会的结果是随机进行显示的,最多显示30个,如果您没有搜索到您要加入的公会,建议您可以重新进行搜索尝试,为您带来不便表示抱歉,感谢您对陌陌游戏的支持,祝您游戏愉快!Q: 如何请求支援?A: 么么您好,公会成员可以通过公会建筑中的“请求支援”功能请求援军:1.请求援军之后公会成员能随机从公会兵力公用池中获得兵力;2.申请援军功能每30分钟只能使用一次;3.从公用兵力池得到的援军的数量和等级由玩家的公会建筑的等级决定;4.当公会公用兵力池少于200时,公会进入被保护状态,公会成员不能从公会获得支援军队。
&|&相关影像
互动百科的词条(含所附图片)系由网友上传,如果涉嫌侵权,请与客服联系,我们将按照法律之相关规定及时进行处理。未经许可,禁止商业网站等复制、抓取本站内容;合理使用者,请注明来源于www.baike.com。
登录后使用互动百科的服务,将会得到个性化的提示和帮助,还有机会和专业认证智愿者沟通。
此词条还可添加&
编辑次数:17次
参与编辑人数:9位
最近更新时间: 16:32:42
贡献光荣榜
扫码下载APP现代 2d 游戏的图形地层绝大多数也是基于 3d api 实现的。为了提高性能,通常要把若干图元 (sprite) 装箱在整张贴图中。这个装箱过程可以是在线下完成,也可以是在运行期来做。
TexturePacker 就是做这件事的优秀商业工具。不过我认为把它放在开发工具链中还有一些不足。图元的装箱和根据装箱结果合成贴图是两件事情,如果我们是手工操作,合在一起完成当然方便;但如果是在自动化流程中,分开独立完成更好。因为迭代开发过程中,每次重新打包资源,都只会修改少部分图元,且图元的大小未必会改变。如果大小不变,就不必重新做装箱运算。
如果部分修改图元,则合成贴图的过程有可能能减少运算过程。通常我们需要对最终的贴图做一次压缩,生成类似 ETC 的压缩贴图类型,这是极消耗 cpu 的。而 ETC 压缩格式是基于 4x4 的区块独立压缩,只要保证图元尺寸是 4 的倍数、就可以先压缩,再合成。这样,没有修改过的图元就可以不必重新运算,直接从文件 cache 中读回。
有些时候不合成、仅保存装箱结果更适用。可以在运行时根据 altas 数据把分离的图元装载在贴图中。分开打包独立的图元资源更适合游戏更新。
第二,在提高装箱利用率上面 TexturePacker 做了很多的努力。很多 sprite 的边角会有大量的空白,仅仅按轴对齐的四边形包围盒裁剪还是浪费太大。它的最新版本支持了多边形装箱、即尽可能的把边角都裁剪下来。这种做法的代价是增加了运行时的多边形数量(对 2d 游戏来说,通常不太重要),但让装箱边角余料可能多填一些小 sprite 进去。
但我认为其实还可以找到更多方法。
这篇 blog 就想谈谈最近我在为公司新的 2d 项目完善 ejoy2d 的工具链,编写装箱工具时,做的一些工作。
上次说到,我们的引擎打算在 PC 上开发,设备上直接调试。如果是按传统的开发方式:运行前将 app 打包上载然后再运行,肯定是无法满足开发需要的。所以必须建立一套资源的同步机制。
目前,我们已经实现了基本的资源文件系统,大致是这样工作的:
所有的资源文件,包括程序代码本身(基于 Lua),都是放在开发 PC 上的。开发环境会缓存每个文件的 md5 值,文件系统将用此 md5 值为标准,默认 md5 相同的文件,其内容也是一致的,暂不考虑 md5 冲突的问题。
在设备上,用设备的本地文件系统做一个 cache ,cache 中分为两个区间,一是资源文件区,按所有资源文件的 md5 值为文件名(按 md5 的 16 进制码的前三字节散列在不同子目录中,防止单个目录文件数量过多)保存。二是目录结构区,每个子目录用一个递增数字 id 做文件名,内容则是可读文件名以及其内容对应的 md5 值或子目录编号。其中 id 0 是资源根目录。
这段时间玩 bgfx ,除了前段公布的 lua binding 外,还颇做了一些别的工作。
首先是帮 bgfx 的 directx 12 driver 打了补丁,使之可以用 mingw build 出来正确运行。也就是之前 blog 上提到的
。这个 pr 已经被官方接纳。
然后是我给 bgfx 的 debug text 做了中文的支持。 bgfx 专门做了一个层,模拟 VGA 的 text mode 。一开始我学着过去 dos 模式下做中文系统的方式增加了双字节支持,采用在模拟出来的 video memory 的 attribute byte 上设置几个特殊标记来表示接下来的两个 slot 是连起来的一个 unicode 字符。
这种让一个汉子占据两个 video memory slot 的方式在过去非常常见,不过弊端也很明显:容易产生半个汉字乱码问题,解决方案看起来比较 trick 。而且把汉字作为一种特殊字符来处理,而不是彻底解决 unicode 大字符集的解决方案感觉也很不美观。
接下来我花了一点气力实现一个更加通用的 unicode 方案:
至少要支持多个 code page
, 并把原本就支持的
dos 字符集当作 cp437 ,而可以由使用者执行添加新的 codepage ,比如汉字所用的 cp936 。再将不同的 codepage 统一转换为 unicode 。我为多 codepage 编写了一个简单的查找 cache ,字形贴图管理的新模块。cp936 里用到 15 点阵汉字字形是从文泉驿黑体中导出的。另外为了更好的支持 unicode ,把 virtual video memory 里保存 codepoint 的单字节扩展为 3 字节,可以把单个汉字放在一个 slot 里。不过,为了排版正确,还是需要在 debug print 的
api 中检测到汉字就在每个汉字后保留一个空格,让单个汉子占两个英文字符的位置。
,不过尚未合并到主干。看起来作者也没有拒掉,目前还留在 open 的 pr 列表中。
前两年有同学给我推荐了
这个库,第一眼被它吸引是它的口号:"Bring Your Own Engine/Framework" style rendering library 。这动不动就说自己是 3d engine 的时代,好好做好一个渲染库,仅仅做好渲染库,是多难得的一件事情。
今年国庆节的时候,偶然间我又翻到这个仓库,居然作者一直在更新。坚持了五年,一直在维护这么个小玩意,让我对这个项目多了点信心。节后我饶有兴趣的研究了一下它的代码。
现在我觉得,这个库的设计思想非常对我的胃口,核心部分几乎没有多余的东西:数据计算、平台 API 支持、数据持久化格式支持、等等都没有放在核心部分。它仅仅只做了一件事:把不同平台的图形 API :Direct X 、OpenGL 等等整合为一套统一的接口,方便在此基础上开发跨平台的 3d 图形程序。不同平台的 3d api 的差异,正是 3d 游戏开发中最脏最累的活了。
前段时间有一个友商来了一个技术团队到我公司交流,主要是想探讨一下他们即将上线的一款 MMORPG 游戏在内部测试中发现的网络卡顿问题是否有好的解决手段。
经过了解,卡顿主要是在压力测试中表现出的网络消息流量过大造成的,又没有找到合适的方案减少流量。
关于 MMORPG 的网络部分的设计,我之前写过很多 blog 。最近两年写过这样两篇: 、 。
昨天,我们自己的一个 MMORPG 项目中发生一个小 bug ,就这个 bug 我们做了一些讨论。我想借这个问题展开,在一个抽象层面谈谈我觉得 MMORPG 的网络同步应该怎样做。
Paradox 是我很喜欢的一个游戏公司,在所谓 P 社 5 萌中,十字军之王和钢铁雄心都只有浅尝,但在维多利亚和群星上均投入了大量时间和精力。
这些游戏基于同一套引擎,所以数据文件格式也是共通的。P 社开放了 Mod ,允许玩家来修改游戏,所以数据文件都是明文文本存放在文件系统中,这给了我们一个极好的学习机会:对于游戏从业者,我很有兴趣看看成熟引擎是如何管理游戏数据和游戏逻辑的。
据我所接触到的国内游戏公司,包括我们自己公司在内,游戏数据大都是基于 excel 这种二维表来表达的。我把它称为 csv 模式。这种模式的特点是,基础数据结构基于若干张二维表,每张表有不确定的行数,但每行有固定了列数。用它做基础数据结构的缺陷是很明显的,比如它很难表达树状层级结构。这往往就依赖做一个中间层,规范一些使用格式,在其上模拟出复杂数据结构。
另一种在软件行业广泛使用的基础数据结构是 json/xml 模式。json 比 xml 要简单。它的特点就是定义了两种基础的复合结构,字典和数组,允许结构嵌套。基于这种模式管理游戏数据的我也见过一些。不过对于策划来说,编辑树结构的数据终究不如 excel 拉表方便。查看起来也没有特别好的可视化工具,所以感觉用的人要少一些。
最开始,我以为 P 社的数据文件是偏向于后一种 json 模式。但实际研究下来又觉得有很大的不同。今天我尝试用 lpeg 写了一个简单的 parser 试图把它读进 lua vm ,写完 parser 后突然醒悟过来,其实它就是基于的嵌套 list ,不正是 lisp 吗?想明白这点后,有种醍醐灌顶的感觉,的确 lisp 模式要比 json 模式简洁的多,并不比 csv 模式复杂。但表达能力却强于它们两者,的确是一个更好的数据组织方案。
今天读了一篇
。这是根据 GDC 2017 上的演讲 Overwatch Gameplay Architecture and Netcode 视频翻译而来的,所以并没有原文。由于是个一小时的演讲,不可能讲得面面俱到,所以理解起来有些困难,我反复读了三遍,然后把英文视频找来(订阅 GDC Vault 可以看,有版权)看了一遍,大致理解了 ECS 这个框架。写这篇 Blog 记录一下我对 ECS 的理解,结合我自己这些年做游戏开发的经验,可能并非等价于原演讲中的思想。
Entity Component System (ECS) 是一个 gameplay 层面的框架,它是建立在渲染引擎、物理引擎之上的,主要解决的问题是如何建立一个模型来处理游戏对象 (Game Object) 的更新操作。
传统的很多游戏引擎是基于面向对象来设计的,游戏中的东西都是对象,每个对象有一个叫做 Update 的方法,框架遍历所有的对象,依次调用其 Update 方法。有些引擎甚至定义了多种 Update 方法,在同一帧的不同时机去调用。
这么做其实是有极大的缺陷的,我相信很多做过游戏开发的程序都会有这种体会。因为游戏对象其实是由很多部分聚合而成,引擎的功能模块很多,不同的模块关注的部分往往互不相关。比如渲染模块并不关心网络连接、游戏业务处理不关心玩家的名字、用的什么模型。从自然意义上说,把游戏对象的属性聚合在一起成为一个对象是很自然的事情,对于这个对象的生命期管理也是最合理的方式。但对于不同的业务模块来说,针对聚合在一起的对象做处理,把处理方法绑定在对象身上就不那么自然了。这会导致模块的内聚性很差、模块间也会出现不必要的耦合。
我觉得守望先锋之所以要设计一个新的框架来解决这个问题,是因为他们面对的问题复杂度可能到了一个更高的程度:比如如何用预测技术做更准确的网络同步。网络同步只关心很少的对象属性,没必要在设计同步模块时牵扯过多不必要的东西。为了准确,需要让客户端和服务器跑同一套代码,而服务器并不需要做显示,所以要比较容易的去掉显示系统;客户端和服务器也不完全是同样的逻辑,需要共享一部分系统,而在另一部分上根据分别实现……
昨天和人闲扯,谈到了 MMORPG 客户端的网络消息应该基于怎样的模型。依稀记得很早写过我的观点,但是 blog 上却找不到。那么今天补上这么一篇吧。
我认为,MMO 类游戏,服务器扮演的角色是虚拟的世界,一切的状态变化都是在游戏服务器仲裁和演化的。而客户端的角色本质上是一个状态呈现器,把玩家视角看到的虚拟世界的状态,通过网络消息呈现出来。所以、在设计客户端的网络消息分发框架时,应围绕这个职责来设计。
客户端发起的请求分两种:一种是通知服务器,我扮演的角色状态发生了改变,请求服务器仲裁;另一种是,我期望获取服务器对象的最新状态。后一种有时以服务器主动推送来解决,,两者主要的区别在于流量控制。
我们公司有两个项目的客户端在使用 git 做项目管理,三个项目使用 svn 管理。程序员比较爱 git ,但是为什么 svn 还存在?主要是在做客户端开发时,策划和美术人员始终迈不那道坎。即使已经在用 git 的项目,策划们还是反应用起来比 svn 跟容易犯错误,遇到自己无法解决的问题也更多。
我是非常想在公司全部推广使用 git 做项目管理的。所以需要仔细考察到底是什么东西阻止了策划(及美术)们掌握这个工具。
思考一番后,我认为一套完整的培训机制还是得建立起来。完全靠自学和口口相传是不靠谱的。尤其是在有 svn 基础的时候,非程序开发人员接受 git 其实比程序开发人员要困难的多。如果你硬要把 svn 的概念全部适配到 git 上,其实就是在把 git 当 svn 在用,不仅获得不了好处,反而增加了很多困扰。
而实用主义者,在没有外力的情况下,只会看到表面。不可能系统的从原理上系统理解 git 到底解决了什么问题、每步操作背后到底做了什么,如果出现了问题,问题是怎么引起的。我们知道,在用 git 的时候,由于分支和提交都比 svn 方便,分布式的结构也会更容易导致版本演化图变得异常复杂。当它乱成一团乱麻的时候,任何新的合并操作都会比之前遇到更多麻烦。如果使用者心里有清晰的概念,时刻保持演化关系简单,他遇到的问题自然会少。而你遇到问题乱解决一通,只满足于把现在的问题搞定,那么下次就会面临更大的灾难。
我们公司的一个 MMORPG 项目最近在内存方面碰到了红线,昨天开会讨论了一下。我提出了一个改进方案,写篇 blog 记录一下。
问题是这样的。在当下的手机及平板硬件设备条件下,操作系统留给应用的可用内存并不多,大约只有 500M 左右。
和 PC 环境不同,手机上是交换分区的机制来对应一些临时突发性内存需求的。而手机必须保证一些系统服务(某些高优先级后台业务)的运行,所以在接电话、收取推送等等意外任务发生时,有可能多占用一些内存,导致操作系统杀掉前台任务让出资源。
根据实际测试,游戏想跑在当前主流高端手机上必须把自己的内存占用峰值控制在 400M 内存以下,350 M 会是一个合理的值,而这个值是远远低于 10 年前的 PC 游戏标准的。而我们的项目却是一个写实类型的 拥有大场景的 MMORPG 。
网游里有很多抽卡、开箱子之类的赌性玩法,在最开始,游戏设计者实现的时候,仅仅给这些抽取概率简单的设置了一个值。比如抽卡抽出橙卡的概率是 10% ,那么就是说,玩家每次抽一张卡,有 90% 的可能是白卡,10% 的可能是橙卡。
但大 R 玩家是大爷,需要小心伺候。如果感受不好,人跑了就亏大了。概率这个东西靠改进是解决不了体验问题的,大爷要是连抽 20 张都出不来橙卡,那是要怒删游戏的。
连抽 20 张 10% 概率橙卡一张都抽不到的机会多不?一张抽不中的概率是 0.9 ,20 张都抽不中的概率是 0.9 ^20 = 12.2% 。这可不算小数字啊。平均 8 个大 R 就会碰到一次,一下子赶跑了 1/8 的金主,这个责任小策划可担当不起。
所以、一般网游都会用各种规则来避免玩家出现连抽不中的尴尬。例如,我忘记是谁发明的 10 连抽规则:如果你购买一个大包连抽 10 次,我在规则上就保证里面一定至少有一张橙卡。实现它应该并不困难,按常规概率生成 10 张的卡包,如果里面没有橙卡,那么我加一张即可。
很多使用 Unity3D 开发的项目,都不太喜欢 C# 这门开发语言,对于游戏开发很多人还是更喜欢 Lua 一些。而 Lua 作为一门嵌入式语言,嵌入别的宿主中正是它说擅长的事。这些年,我见过许多人都做过 U3D 的 Lua 嵌入方案。比如我公司的阿楠同学用纯 C# 实现了一个 Lua 5.2 (用于在 U3D web 控件中嵌入 Lua 语言的 UniLua );还有 ulua slua wlua plua xlua ... 数不胜数。我猜测,a-z 这 26 个字母早就用完了。
上面提到的项目的作者不少是我很熟悉的朋友,我们公司现在的 U3D 游戏也由同事自己实现了一套差不多的东西。所以我曾了解过这些方案。但我一直觉得这些方案要么做的过于繁琐,要么有些细节上不太完备,总是手痒想按自己的想法搞搞看。
Mono 和 C 通讯使用 P/Invoke ,用起来不算麻烦,但是要小心暗地里做的 Marshal 的代价,特别是对象传递时装箱拆箱的成本。Lua 和 C 通讯有一套完善的 C API ,但完全正确使用并不容易。核心难点是 Mono 和 Lua 各有一套自己的异常机制,让它们协调工作必须很小心的封装两个语言的边界,不要让异常漏出去。 。
我认为简单且完备的 Mono / Lua 交互方案是这样的:
U3D 的打包流程,谁用谁知道。
由于输出 ios 包必须在 xcode 环境,跑在 Mac 系统上,所以为了定期版本打包,我们采购了配置比较高的垃圾桶来做。一台大约要三万 RMB 左右。
但我觉得这个方案的性价比太低了。
经过简单的考察,我发现,打包流程中最慢的环节是贴图压缩。在不同的平台,需要把原始贴图文件压缩成对应平台的压缩贴图格式: ios 平台对应的是 PVR 压缩格式;Android 平台对应的是 ETC 压缩格式,等等。
u3d 自己也意识到压缩贴图太慢,所以官方给出了一个 CacheServer 方案。
在制作 2d 游戏时,通常我们需要把大量小图素合并到一整张大贴图上。这可以使用装箱算法 (Bin Packing)完成。当然,很多人很懒,那么就用别人做好的工具,比如 Texture Packer 。
但是在实际开发中, Texture Packer 有几个严重的缺陷 ,我个人还是建议自己来做合图的工具,完善工具链。
缺陷 1 :装箱过程其实并不需要了解图片上的内容,而只需要知道图片的尺寸。所以装箱过程需要的内存量应该只和被装的图素个数相关,和图片大小无关。而 texture packer 并不是这样做的,它把装箱和合成贴图两部工作放在一个黑箱里了,会导致运行时无谓的内存消耗(更不用说它本质上是一个 GUI 程序),不是很适合自动化工具链。
缺陷 2 :如果要求最终合并的图是 pvr 或 etc 压缩贴图,那么还需要最后再对目标图做一次压缩。通常这个压缩过程是比较慢的。
这个缺陷 2 在开发期需要反复打包资源时,对开发效率影响尤其大。但其实如果自己好好设计工具链,是完全可以避免的。
下面就谈谈应该怎么处理 ETC 这类压缩贴图的合并问题。
前面一篇谈了
,我想把其方法推广到其它类型的游戏,比如 MMORPG ,比如动作游戏。尤其是动作类游戏,非常需要客户端可以即时处理玩家的操作,而不能等待服务器确认。
我们来看看这些类型的游戏和放置类游戏的不同点。
放置类游戏大部分是玩家个人和服务器在玩,不涉及第三方的干扰。所以,只要操作序列一致,那么结果就一致。
MMORPG MOBA 动作游戏这些,是多人在玩。如果我们能同步所有玩家的操作,让所有玩家的操作序列在一条线上,那么也一定可以保证结果一致。这点,是上篇 blog 的结论。
最近想试着做一款类似 Shop Heroes 的放置类网络游戏。总结一下此类游戏的客户端服务器同步问题。
传统放置类游戏,比如小黑屋,是单机运行,不需要和服务器同步的。但出于防止作弊的目的(作弊会使玩家迅速失去游戏乐趣)或者希望加上多人玩法,我们通常希望把游戏进程放在服务器上管理,这样就有了网络同步问题。
加上了服务器后,我们依然想保持玩家单机游戏的流畅体验,这里该怎么做,还是有许多门道的。
上一篇谈到了 Shop Heroes 的经济系统,这一篇想谈谈它的公会系统。
和很多其它网络游戏不同,Shop Heroes 的公会系统并不是一个可以被剥离的系统,它和整个游戏,包括其经济系统是密不可分的。我觉得这个系统是为了完成其游戏的根本设计目的:引导玩家差异化发展从而制造出市场需求,而设计出来的东西。
在游戏中,不存在未加入公会的玩家。玩家在新手教学阶段就会被强制引导到一个叫做外城的环境,也就是游戏中的公会。你必须选择加入一个已有公会,或是自己创建一个单人公会,否则游戏无法进行下去。
在游戏的前几十小时,一个人玩其实也没有任何障碍。你可以把公会建筑看成是个人资产的一部分,该升级升级。但在上一篇提到过,游戏中用来升级的金币最终会成为紧缺资源,总有一天你的收入会承担不起。当然,作为一个固执的 RMB 战士,你还是可以一个人玩下去的,城市升级可以用钻石替代。而且很贴心的设计成非线性对应关系。正如上一篇提到的,金币随等级不同(生产能力成指数上升),其价值也不同。所以如果你想投资 20K 金币,可以用 80 钻替代;而一次投资 500K 的话,就只需要 300 钻而不是 2000 钻了。
相比一个人用钻石经营一座城市,更经济的方法是加入一个公会,大家共同建设一座城市;对于个人来说只有好处,没有任何坏处,何乐而不为呢。
最近两周在玩一个叫做 Shop Heroes 的游戏。知道这款游戏是因为它前两个月在 Steam 上线了 PC 版,玩了一下觉得有点意思。由于其 UI 一眼看上去就是手机风格,便在 app store 上搜索了一下,改到 ios 上玩。
游戏设计的很好,对我这种资深游戏玩家有莫大的吸引力(对于快餐手游氛围下进入的新玩家可能因为系统过于复杂而玩不进去)。它设计了无数个斯金纳箱,营造出文明的那种 one more turn 的心理感受,让人欲罢不能。具体不展开讲,有兴趣的同学自己玩玩试试。
让我感兴趣的是游戏内部的经济系统,经过数十小时的游戏体验,我隐约感觉的到设计者希望设计出一个以玩家交易税收为主体的商业模式。而这种模式很多人都提出(我从 10 年前就反复考虑,在 blog 上也做过一些思考记录)并希望实现,可全部都失败了。比如著名的 Diablo 3 ,曾经就宣称自己打算用交易税来维持游戏的利润,最后因为破坏了游戏体验而关闭了现金拍卖场。国内也有征途等打着以交易税为收入来源旗号的游戏,而实际上却只是个幌子。
而 Shop Heroes 似乎真正做到了这点。从结果上看,在市场上的中高等级玩家之间流通的物品全部是用钻石结算的。而游戏本身,玩家提交求购或售卖单的时候,都可以自由选择用钻石(现实货币)或金币(游戏货币)结算。虽然我是一个免费游戏玩家,但我尝试过用(免费获得的)钻石提单到市场,均快速成交,能感受的到市场的活跃。在这些钻石交易里,系统是要收取 25% 的交易税的。
我思考了好几天,游戏是怎么做到:引导玩家(同时包括 RMB 战士和免费玩家)进行钻石交易从而收税,这一点的呢?
按局打的纯 PVP 机制的游戏,面临最大的问题将是,在一个玩家想找人对战的时候,找不到对手匹配。
如果游戏的在线玩家达不到一定人数,那么这个问题会恶化:等不到人和你一起玩、放弃等待、新的玩家更找不到对手。
像皇室战争、王者荣耀、炉石传说这些火爆的 pvp 游戏都属于迈过了线的作品,玩家不太愁等不到人一起玩,提升了游戏体验,聚集了更多的玩家。而当玩家群有限时,同类产品就很难竞争,只要在线用户掉到一定限度以下,很可能导致(无非找到对手)体验下降,更多玩家流失。
那么,有没有办法解决初期玩家过少的问题呢?
直观的想法就是没人玩 AI 凑。可 AI 并不是真人,和 AI 在公平规则下对战乐趣会少很多,且高水品 AI 开发起来也非常困难。最关键的是,一旦玩家乐于和 AI 对战(无论是因为对战本身的乐趣,还是可以刷分刷掉落),你会进一步失去在线用户。
最近一个月,玩群星(Stellaris) 有点着魔。不同于 P 社之前我最喜欢的维多利亚2 ,这个上手更舒服。是我玩过的把大战略和 4X 结合的最好的游戏了。我很欣赏 P 社这种尽力降低玩家门槛的做法,让大战略和 4x 游戏不那么高冷,普通玩家也能很快领略其中的乐趣。
这里有我写了一篇。大致谈了群星是一个怎样的游戏,如何快速入门。小提示:即使是新手,也推荐用铁人/疯狂模式。第一次玩只需要把银河调小一点就好了。这样乐趣才能充分体现出来。
这次 P 社的引擎革新后,汉化变得很容易了。之前,大多数人和我一样使用的 3dm 版汉化 mod ;但这个 mod 翻译的时候,译者并没有怎么玩游戏,所以很多地方用词不当,还有一些仓促翻译导致的错别字。我实在受不了老式的闷头汉化的模式了,发现问题反馈到修正的周期太长,所以就自己维护了一个汉化 mod 。
。个人认为,利用 github 做合作创作,对于做汉化这件事特别合适。。
游戏文本的版本更新,可以直接体现在 diff 里。路人发现有错别字也可以顺手提个 pr 。而我自己一般是在玩游戏时瞟见翻译的不合理的地方,立刻打开文本编辑器校对一下。也正因为如此,游戏里出现的 event 都特别仔细的阅读;群星的开发者真是脑洞大开啊,游戏里的 event 涵盖了几乎我所有阅读过的科幻小说,看过的科幻电影的梗。光这一点就值回了票价。
在群星发布前,我还十分担心,如果没有了维多利亚里那种厚重的历史感,一个科幻题材的战略游戏该如何给玩家代入感;没想到它是通过这个手法来完美的解决了这个问题。
在上一篇 blog 里,我谈到游戏服务器其实只需要使用 req/resp 模式就够了。有同学表示不太理解,认为服务器主动推送,或者说 pub/sub 的消息模式必不可少。
在聊天中我解释了很多,是时候记录一下了。
从本质上来说,如果你只是想把一系列消息发送到客户端,req/resp 请求回应模式和 pub/sub 发布订阅模式并没有什么不同。你可以把 req/resp 理解成一次性的,只订阅一条(或有限条)消息;如果需要维持这类消息的推送,需要客户端在收到消息后再次发起订阅。
这和 timer 定时器系统一样,订阅一个定期触发的定时器这种功能并非必要。如果你需要一个每分钟触发一次的定时器,完全可以在触发一次定时操作后,立刻发起下一次定时任务请求。它在功能上是等价的。
潜在的实时性影响是:如果规定必须在收到回应消息后,才允许发起下一次请求;那么一个事件发生后推送到客户端的延迟可能增加了最多 2 倍。即,如果你刚刚回应了一条消息,这个时候又发生了新的事件;客户端需要在收到前一个事件后再提起新请求,然后你才可以把后发生的事件通过新请求的回应包发过去。
降低这个实时性影响的方法也很简单,协议上允许在客户端发起请求后未收到回应之前再次发起请求即可。对应同类消息许多少并发的请求,要根据这类消息的实时性要求有多高来决定。无限提高允许的并发量没有太大意义,毕竟物理上的延迟不可能完全消除。
这两天在一个游戏设计论坛潜水,读了很多帖子,感觉挺有收获的。
尤其是有一贴,楼主贴了一长篇他的 idea ,应当是想了很久的,算是比较完整,只差动手了。按他的说法,想先在论坛上收集一些反馈。这个论坛聚集了许多核心玩家,和相当数量的游戏开发者,按道理说,他找对地方了。
可是,在下面的回帖中,某人对此作了严厉的批评。
最近有数据显示,我们的游戏《》有玩家利用规则漏洞,通过排行榜奖励刷钻石。
我问了一下设计人员一些细节,感觉现在的规则设计是很有问题的。下面记录一下我的想法,和一些改进建议。
排行榜奖励问题不只在我们这样特定的游戏中有,现在几乎所有的 pay2win 游戏都会涉及到。我们最初的规则是,每周为排名前列的公会按名次发放一定的钻石奖励,鼓励大公会竞争,促进消费。
后来,有玩家反应,如果公会无法排在前列,就没有动力竞争了。所以又加了补充规则,如果公会排名又提升,也有相应的奖励。
过年回武汉家中,只有一台 2000 块的一体机可以用,自然是跑不动 3d 游戏的。我挑了一款 Invisible Inc 玩,居然很流畅。
这是款 XCOM like 的策略游戏,场景是 isometric 的。但是用 Q E 两个键可以旋转场景,虽然静止下来只有四个方向,但是旋转过程却以动画方式呈现。这让我一度认为它的场景是基于 3d 模型制作的。但令人惊讶的是,在低配置的一体机上却非常流畅。相比较,我最近在玩的 XCOM 2 却在 GTX 550 显卡上也有卡顿。
我仔细观察后,发现其实 Invisible Inc 的场景里的物件都只是 2D 图片。甚至只有正面和背面两张图,通过左右镜像得到了四个视角。它的旋转速度很快,并加了一定的模糊效果,欺骗了人眼。其实只有地板和墙壁是真正旋转的,其它物件只有坐标在做旋转,而图片本身在旋转过程中并没有变化。
也就是说,它的引擎其实是基于 2D 制作的,模拟出了 3d 才有的视觉效果。
一般游戏会把所需要资源数据打包成一个大文件,游戏运行时可以像访问普通文件一样,访问包内文件。如何打包如何更新资源包,是设计重点。
现在有很多资源包直接使用通用打包(压缩)格式,比如 zip
。也有自行设计的,多半是为了一些特殊需求。比如资源间有引用关系等。如果资源数量过多,通常还会对原始资源文件名做一次 hash 索引,加快包内文件检索效率。像暴雪的 mpq 格式,还有 unity3d 的 asset bundle 格式都是这样的。
一旦资源打包,原始文件名信息就不再需要了。应用程序可以在运行时通过文件名的 hash 值索引到包内文件。(所以第三方的 mpq 解包工具需要提供一份额外的文件名列表)
这两天都在忙一个事情, 把我们的一个新游戏提交到 steam 绿光计划上。
去年底开始,公司里就有两个人开始在忙一个小项目。最初的一个月,他们只是做了一个原型,本来打算演化成一个手机游戏,作为 2015 年公司的新项目去推广的。这个原型只有一个简单的战斗动画,提出想法的同学是一个格斗游戏迷,他只是想简化格斗游戏里的操作,提取出一些核心乐趣带给大众玩家。
当然,他同时也是一名优秀的美术设计人员,所以原型做的非常绚丽,一下子就吸引了公司里很多人。
对于这款游戏做出来后如何盈利,最初的两个开发人员(一个美术和一个程序)一点想法都没有。他们没有沉浸过任何网络性质的手游,不懂怎么挖坑赚大 R 的钱。只知道打磨那些 demo 中自己认为有趣的地方。所以对于这个项目是否立项,作为公司的决策人是很为难的。
昨天看到一个好玩的项目
可以将卡通图片高质量的放大到 2x ,比直接用普通过滤器放大效果好的多。我试了将那张小的卡通妹子不断迭代放大到 8
倍,效果依然是很不错的。
公司的程序群里,有同学提出,能不能把算法提取出来,用来做游戏资源图片的压缩。针对卡通类的图片,可以用很小的文件保存,在加载或安装时放大。我觉得有点意思,就做了一些研究。
最近想在游戏中加一点简单的环境光,因为游戏中使用的都是 2d 图片,那么最廉价的方法应该是给图片加上法线图了。
好在我们游戏的原始图素都是用 3d 建模,然后再用平面修整的。基本几何结构信息可以从模型提取出来。当然,我们并不真的需要自己写程序去从模型中计算出最终渲染图的法线信息。所有渲染软件都可以输出它。
比如 3ds max ,如果你把渲染结果输出成 .rla 文件,那么就可以勾选 normal zbuffer 等额外的通道输出。
记得我读大学时写过一个 rla 文件解析程序,当时是为了提取里面的 Z 通道。这都过了十多年了,果然完全找不回当年写的代码了,也忘记曾经怎么实现的,所以就从头来搞。
的同学都会惊叹于它的随机地形生成系统。如果你对 ascii art 无感,那么可以 google 一下
矮人要塞是 3d 的地形系统。在游戏 wiki 上有 。
大体上是这样的: 首先有一张高度图(Elevation),决定了每个坐标的高度。然后给出一张降雨图 (Rainfall),来影响当地的植被和河流。通常降雨图是根据高度图(海岸和季风影响)计算出来的。简单描述了这个过程。
温度图(Temperature)一般根据纬度以及当地的海拔计算出来,也会影响当地的动植物。
地质的排水情况(Drainage)可以影响当地能否形成湖泊、湿地和河流。同样影响了当地的土质(进一步影响植被)。
另外还有,火山的分布情况(Volcanism),以及野生物分布图(Savagery) 会改变当地的矿产和动物分布。
Unity3D 的 asset bundle 的格式并没有公开。但为了做更好的差异更新,我们还是希望了解其打包格式。这样可以制作专门的差异比较合并工具,会比直接做二进制差异比较效果好的多。因为可以把 asset bundle 内的数据拆分为独立单元,只对变更的单元做差异比较即可。
网上能查到的资料并不是官方给出的,最为流行的是一个叫做
的开源工具。它是用 java 编写的,只有源代码,而没有给出格式说明(而后者比代码重要的多)。通过阅读 disunity 的代码,我整理出如下记录:
注:陌陌争霸的数据库部分我没有参与具体设计,只是参与了一些讨论和提出一些意见。在出现问题的时候,也都是由肥龙、晓靖、Aply 同学判断研究解决的。所以我对 Redis 的判断大多也从他们的讨论中听来,加上自己的一些猜测,并没有去仔细阅读 Redis 文档和阅读 Redis 代码。虽然我们最终都解决了问题,但本文中说描述的技术细节还是很有可能与事实相悖,请阅读的同学自行甄别。
在陌陌争霸之前,我们并没有大规模使用过 Redis 。只是直觉上感觉 Redis 很适合我们的架构:我们这个游戏不依赖数据库帮我们处理任何数据,总的数据量虽然较大,但增长速度有限。由于单台服务机处理能力有限,而游戏又不能分服,玩家在任何时间地点登陆,都只会看到一个世界。所以我们需要有一个数据中心独立于游戏系统。而这个数据中心只负责数据中转和数据落地就可以了。Redis 看起来就是最佳选择,游戏系统对它只有按玩家 ID 索引出玩家的数据这一个需求。
我们将数据中心分为 32 个库,按玩家 ID 分开。不同的玩家之间数据是完全独立的。在设计时,我坚决反对了从一个单点访问数据中心的做法,坚持每个游戏服务器节点都要多每个数据仓库直接连接。因为在这里制造一个单点毫无必要。
根据我们事前对游戏数据量的估算,前期我们只需要把 32 个数据仓库部署到 4 台物理机上即可,每台机器上启动 8 个 Redis 进程。一开始我们使用 64G 内存的机器,后来增加到了 96G 内存。实测每个 Redis 服务会占到 4~5 G 内存,看起来是绰绰有余的。
由于我们仅仅是从文档上了解的 Redis 数据落地机制,不清楚会踏上什么坑,为了保险起见,还配备了 4 台物理机做为从机,对主机进行数据同步备份。
Redis 支持两种 BGSAVE 的策略,一种是快照方式,在发起落地指令时,fork 出一个进程把整个内存 dump 到硬盘上;另一种唤作 AOF 方式,把所有对数据库的写操作记录下来。我们的游戏不适合用 AOF 方式,因为我们的写入操作实在的太频繁了,且数据量巨大。
我们公司开始用 mongodb 并不是因为开始的技术选型,而是我们代理的第一款游戏《
》的开发商选择了它。这款游戏在我们代理协议签订后,就进入了接近一年的共同开发期。期间发现了很多和数据库相关的问题,迫使我们熟悉了 mongodb 。在那个期间,我们搭建的运营平台自然也选择了 mongodb 作为数据库,这样维护人员就可以专心一种数据库了。
经过一些简单的了解,我发现国内很多游戏开发者都不约而同的采用了 mongodb ,这是为什么呢?我的看法是这样的:
游戏的需求多变,很难在一开始就把数据结构设计清楚。而游戏领域的许多程序员的技术背景又和其他领域不同。在设计游戏服务器前,他们更多的是在设计游戏的客户端:画面、键盘鼠标交互、UI 才是他们花精力最多的地方。对该怎么使用数据库没有太多了解。这个时候,出现了 mongodb 这样的 NOSQL 数据库。mongodb 是基于文档的,不需要你设计数据表,和动态语言更容易结合。看起来很美好,你只需要把随便一个结构的数据对象往数据库里一塞,然后就祈祷数据库系统会为你搞定其它的事情。如果数据库干的不错,性能不够,那是数据库的责任,和我无关。看到那些评测数据又表明 mongodb 的性能非常棒,似乎没有什么可担心的了。
其实无论什么系统,在对性能有要求的环境下,完全当黑盒用都是不行的。
游戏更是如此。上篇我就谈过,我们绝对不可能把游戏里数据的变化全部扔到数据库中去做。传统数据库并非为游戏设计的。
比如,你把一群玩家的坐标同步到数据库,能够把具体某个玩家附近玩家列表查询出来么?mongodb 倒是提供了 geo 类型,可以用 near 或 within 指令查询得到附近用户。可他能满足 10Hz 的更新频率么?
我们可以把玩家的 buf 公式一一送入数据库,然后修改一些属性值,就可以查询到通过 buf 运算得到的结果么?
这类问题有很多,即使你能找到方法让数据库为你工作,那么性能也是堪忧的。当我们能在特定的数据库服务内一一去解决她们,最终数据库就是一个游戏服务器了。
这个项目一开始不叫这个名字,它在 2013 年中的时候,还只是一个 用来试水移动游戏的试验项目。最开始的目标很明确,COC 是打动我的第一款基于移动平台网络游戏,让我看到了和传统 MMO 不同的网络游戏设计方向。我觉得只需要把其中最核心的部分剥离出来,我们很快可以做出一个简单的却不同于以往 MMO 的游戏,然后就可以着手在此基础上发展。
至于后来找到陌陌合作,是个机缘巧合的故事。我们的试验项目完成,却没想好怎么推给玩家去玩(而这类游戏没有一定的玩家群体基本玩不起来),而陌陌游戏平台刚上线,仅有的一款产品(类似泡泡龙的游戏)成绩不佳。因为我们公司和陌陌的创始人都曾经在网易工作,非常熟悉。这款游戏也就只花了一个月时间就在陌陌游戏平台发布了。
一开始我们只把刚完成 启动器项目的阿楠调过来换掉我来做这个项目,我在做完了初期的图形引擎工作后,就把游戏的实现交给了他。我们只打算做客户端,因为只有这部分需要重新积累技术经验;而服务器不会和传统 MMO 有太大的不同。而我们公司已经围绕
这套服务器框架开发有很长一段时间了,随时都可以快速把这个手游项目的服务器快速搭建起来。
到 2013 年夏天,感觉应该开始动手做服务器部分了。晓靖在斗罗大陆的端游项目中积累了不少服务器开发的经验,也是除我之外,对 skynet 最为熟悉的人;如果这个试验项目只配备一个程序来开发服务器的话,没有更好的人选了。
从那个时候起,我们开始考虑服务器的结构,其中也包括了数据库的选型和构架。
skynet 有自己的 IO 模型,如果要足够高效,最好是能用 skynet 提供的 socket 库自己写 DB 的 driver 。因为 redis 的协议最简洁,所以最先我只给 skynet 制作了 redis 的 driver 。而我们代理的游戏狂刃的开发方使用的是 MongoDB ,为了运营方便,我们的平台也使用它做了不少东西,我便制作给 skynet 制作了初步的 mongodb driver 。到服务器开始开发时,我们有了两个选择。
去赌场参观过的同学应该都见过那种押大小的骰子游戏。庄家投掷三枚骰子,把骰盅盖住,玩家可以押大或小。开盅后,如果发现三个数字之和大于等于 11 就算大,小于等于 10 就算小。如果你猜对了,庄家就 1 赔 1 算给你筹码;否则输掉筹码。另外,还可以以不同赔率压数字,或压三个相同。
为了保障庄家利益,三个相同的数字算不大不小。从概率上讲,这让长时间内庄家必胜。。
如果把这个游戏搬到网络上如何呢?(注意:网上赌博在很多国家是被禁止的,这里只做技术分析而已)
如何让玩家相信庄家没有作弊,真的产生了随机的骰子呢?
同样是一篇老文章, 2013 年初我刚玩 COC 的时候写给公司内部分享的。由于当时公司还没有决定开手游项目,但有意向做一款 COC Like 的产品,并希望开发期间保密,所以相关技术文章都没有公开。
目前,,就可以把当初写的东西陆续贴到 blog 上了。
COC 的地图有 40x40 格,边界还有两格不可以放建筑的格子。所以整个地图是 44x44 格的。但在做坐标判定时,基于格子是远远不够的。COC 官方版本很可能是基于像素级的精度的,和本文所述方法不一致,所以本文仅提出一种可行的数据结构,
这篇文章是在大半年前, 我们的 刚刚做预研的时候写给公司内部分享的。由于这个项目公司不希望在开发阶段对外暴露信息,所以文章也没有在 blog 贴出来。
现在 就没有消息保密的需求了,我就可以把当初开发时写的一些东西逐步贴出来。
所谓类似 COC 这样的斜视角引擎,在英文社区中叫作 Isometric Tileset Engine。它在早期计算资源匮乏的年代用的很多,后来内存不是问题了后,就很少有人用了。由于没有 Z-Buffer ,这种引擎的绘制次序其实很讲究。比如下图:
我们的第一个手游差不多做完了,预计在明年 1 月初推广,目前内测的情况非常不错,我们也可以考虑开始下一步在手游领域立新项目了。
上个项目做的太匆忙,今年 4 月份才开始。因为决定做一个 2d 游戏,我觉得在 2d 游戏引擎方面我有超过 15 年的经验,使用一个流行的开源引擎,比如大家都在用的 Cocos2d-X 还不如自己写。这样对引擎的可控性更强,可以方便的定制自己需要的功能,并在性能上做针对性的优化。手机设备的硬件性能远不如 PC 机,即使程序性能足够,我们也要考虑硬件的能耗,让电池用的更久一点,让设备不那么放烫。优化引擎也是游戏程序员的乐趣之一。
我们这个项目还是做的太急了,只花了一个月时间做引擎,然后在上面做了太多应急的各种修改,到项目完成(10 月)时,已经很不堪了。我不想把这块东西带到下一个产品,所以打算重新把这块代码理一下。
开源是一开始就想做的事情,可以帮助到别人,也同时督促自己把代码写的更整洁。从这周开始,我满头写了一周代码,从头写了 4000 行代码,就有了这么一个项目: 。
当然它还很不完整,有兴趣的同学可以跟踪这个项目,我和我的同事会逐步完善它。同时欢迎其他同学推送 Pull-request 。下面对 ejoy2d 做一个简单的介绍:
我们的游戏中需要对渲染字体做勾边处理,有种简单的方法是将字体多画几遍,向各个方向偏移一两个像素用黑色各画一遍,然后再用需要的颜色画一遍覆盖上去。这个方法的缺点是一个字就要画多次,影响渲染效率。
前几年有人发明了另一种方法,google 一下 Signed Distance Field Font Rendering 就可以找到大量的资料。大体原理是把字体数据预处理一遍,把每个像素离笔画的距离用灰度的形式记录在贴图上,然后写一个专门的 shader 来渲染字体。好处是字体可以缩放而不产生锯齿,也比较容易缺点边界做勾边处理。缺点是字模数据需要离线预处理。
我们的手游项目以及 3d 端游项目都大量使用了勾边字体,我希望直接利用系统字体而不用离线预处理字体把字体文件打包到客户端中。前段时间还专门实现了一个 。btw, 苹果的平台提供了高层 API 可以直接生成带勾边效果的字模。
但是,勾过边的字模信息中同时包含了轮廓信息和字模主体信息,看起来似乎很难用单通道记录整个字模数据了。这给染色也带来了麻烦。
汉字的显示,是基于 3d api 的图形引擎必须处理的问题。和西方文字不同,汉字的字形很难全部放在一张贴图上,尤其是游戏中有大小不同的字体的需求更是如此。即使放下,也很浪费内存或显存。如果不想申请很大的贴图来存放汉字字形,图形引擎往往需要做动态字形贴图的处理。
即,动态生成一张贴图,把最近常用的汉字画在上面。几乎所有成熟的基于 3d api 的图形引擎都需要有相关的模块才可以对汉字更好的支持。但我到目前为止,还没有看到有把这个模块独立出来的。大多数开源引擎都是在自己的框架内来实现差不多的功能。我觉得,这部分管理功能和如何管理贴图其实没有关系、和取得字形的方法也没有关系、不必和 3d api 打交道,也不用涉及到底用 freetype 还是 os 自带的 api 取得汉字字形,所以值得独立实现。
我们的需求本质上是对一张贴图的区块进行管理。每个汉字都占据其中的一小块。当贴图填满时,最久没有用过的汉字块可以被淘汰掉,让新的汉字覆盖上去。同样的字体的最大高度是相同的,可以排列在一行,但宽度可以不同。横向排列时,少许的空洞的允许的。
我设计了如下接口:
struct dfont_rect {
struct dfont * dfont_create(int width, int height);
void dfont_release(struct dfont *);
const struct dfont_rect * dfont_lookup(struct dfont *, int c, int height);
const struct dfont_rect * dfont_insert(struct dfont *, int c, int width, int height);
void dfont_flush(struct dfont *);
最近在玩一款 iOS 游戏, 叫做 Clash of Clans 。这款游戏让我发现,手机/平板的网络游戏和传统网络游戏、网页游戏,除了 UI 方面,设计也是可以有很大的不同的。
对于移动平台,最大的不同在于,玩家的游戏时间碎片化。所以能集中精力的游戏持续时间不宜过长。应该在五分钟以下。当然,也需要提供玩家长时间持续游戏的途径。
玩家的游戏时间很自然的分为了在线时间和离线时间。所以,离线玩法相当重要。
COC 开创了一种:在线创造内容,离线后供其他玩家娱乐的模式。这是以往的游戏所不具备的。
这几天无意中发现一款开源的 3d engine ,名为
。 虽然不多,但写的很漂亮。从源码仓库 clone 了一份,读了几天,感觉设计上有许多可圈可点的地方,颇为有趣。今天简略写一篇 blog 和大家分享。
ps. 在官方主页上,pixel light 是基于 OpenGL 的,但实际上,它将渲染层剥离的很好。如果你取的是源代码,而不是下载的 SDK 的话,会发现它也支持了 Direct3D 。另,从 2013 年开始,这个项目将 License 改为了 MIT ,而不是之前的 LGPL 。对于商业游戏开发来说,GPL 的确不是个很好的选择。
这款引擎开发历史并不短(从 2002 年开始),但公开时间较晚(2010 年),远不如 OGRE 等引擎有名。暂时我也没有看到有什么成熟的游戏项目正在使用。对于没有太多项目推动的引擎项目,可靠性和完备性是存疑的。不推荐马上在商业游戏项目中使用。但是,他的构架设计和源代码绝对有学习价值。
今天收到人民邮电出版的杨海玲同学寄来的几本书,首先感谢一下。看来短期内是没有那么多精力全部去读了,所以先随便翻翻感兴趣的章节。
在《 》 和 《
》 中都分别有一章谈及模糊逻辑。记得前几年我的同事 Soloist 同学曾经研究过一小段时间,给我做过简单介绍,我便仔细把这两章书读了一遍。感觉都是点到为止,所以又翻了一下 Wikipedia 的
的介绍。午饭时跟做 AI 的同事交流了一下,觉得可以做一点笔记记录理解的部分。
在游戏人工智能编程中举了个实际的例子来说明这个问题:在一个 FPS 游戏中,NPC 有多种武器可供选择,这些武器的威力、射程等有所差异;策划决定根据 NPC 和玩家的距离以及 NPC 武器弹药的余量,两个因素来考虑 NPC 当下应该选择哪一种武器对抗玩家。这个例子不错,不过我有另一个自己的例子:在 MMO 中,NPC 在追击玩家时可能会考虑几个因素:离开他的出生点的距离,以及玩家的实力,或是自己的 HP 量等等。下面我用自己的例子来说明问题。
先甩开模糊逻辑是什么不谈,我们在做 AI 定制的时候会遇到怎样的很难决策的问题呢?
策划往往会定义规则:当 NPC 距离出生点很远时,他们停止攻击玩家并返回出生点。
这里有一个问题,即使是最新手的策划也看得出来,这个规则描述是不严谨的。到底什么叫“很远”。所以通常,需要加一个定义,追击半径,变成“当 NPC 距离出生点超过追击半径时,他们停止攻击玩家并返回出生点”。然后,在策划表格里估计就会多出一个表项:追击半径 40 米 之内的东西。
顺便吐槽:把设计这个规则(追击条件),和填写这个数字( 40 米追击半径)的工作分开,分别称做系统策划和数值策划,我怎么看都是件极不靠谱的事情。
在网络游戏中倒卖货物是一大乐趣(如果经济系统做的不坏的话) 。我见到许多 wow 玩家以此为乐,在国产游戏中,以梦幻西游为首,开店摆摊也让人乐此不疲。我最近对此有一些想法,和我们公司的策划交流以后,发现几句话很难说清楚。
大部分人第一反应都是增强版的拍卖场,比如增加求购系统,或是更方便的拍卖寄售系统,各种其它的拍卖场增强。
对于稀有品,我认为现在 wow 的拍卖场已经可以解决大部分问题,是不需要替换的。但一般商品,我认为应以增加流通,方便需求类玩家为主。最简单的方案是让系统根据商品流通速度来自动调节价格,系统统一销售和收购。但我们也知道光靠这种计划经济是很难得到一个合理的市场环境的,也少了许多玩家参与的乐趣。
所以我才有了下面这些想法:
魔兽世界从巫妖王之怒那个版本开始完善了他的
(Phasing) 技术。简单说,就是在场景的相同区域,不同的玩家根据一些条件(一般是任务进度)的不同,会感受到不同的环境:场景可以不同、能交互的 NPC 不同等等。
这个技术可以给玩家在 MMO 中带来许多以往在单机游戏中才能体验的融入感。在 wow 之前,许多游戏策划都曾经想过让玩家可以通过完成剧情改变游戏的环境,但苦于找不到好的表现方式。这或许是因为大部分游戏策划都在以现实世界为参考去考虑技术能够实现的可能性,而在这个微创新年代,没有摹本可以参考就没有了思路。但我有些奇怪的是,在 wow 把相位技术展现给世界已经超过 4 年了,为啥山寨大国还没有全力跟进呢?
莫非是因为这里面还真有什么技术难点?
我们的大部分设计是围绕单个进程进行的,所有的数据在进程内都可以方便的共享。只需要这些数据结构是线程安全的即可。
但最终,我们不会在单台机器上运营整个游戏服务器,所以还是要考虑玩家在不同物理机器间移动的问题。
虽然我们还没有开始进行跨机方面的开发,但是不少服务已经要开始考虑这个问题了。目前、每个玩家接入游戏服务器并认证完毕后,都会有一个 lua 虚拟机伴随他。也就是我称之为 agent 的东西。
agent 和场景服务 map 间会有高频率的互动,所以,我们必须要求 agent 和玩家所在 map 在同一个进程内。当玩家跳转到不在同一进程内的 map 上时,需要把 agent 迁移到对应的进程内。
迁移 agent 并不算难做:只需要把 agent 的数据持久化,然后在新的进程内启动起来即可。难点在于,新的 agent 的通讯 handle 变化了。为了性能考虑,我并不想实现一套跨机器的唯一 id 系统。那么,就需要解决一个问题:如果 handle 发生变化,如何通知持有这个 handle 的服务模块。
正如 记载的,我们第 2 里程碑按计划在 9 月 30 日完成,但因为赶进度,有许多 bug 。性能方面也有很大问题,大家都认为需要重构许多模块。所以,在最后几天修补 bug 时,许多补丁是临时对付的(因为整个模块都需要重写了)。为此,我们留下了一个月专门重构代码、修改 bug 、并对最后的结果再做一次评测。
这项工作,终于如期完成了。
半个多月前在白板上留下的工作计划还没擦掉。我列出了 12 点需要改进或重写的地方,考虑到内容较多,又去掉了 3 项。在大家的通力合作下,完成的很顺利。
,我们的老系统处理 80 人同一战场混战就让服务器支撑不住了。当时我们的服务器 CPU 达到了 790% 。虽然我们的服务器硬件比较老,配置的是两块 Intel Xeon E5310 @ 1.60GHz ,更新硬件可以有所改善。但这个结果绝对是不能满意的。从那个时候起,我从重写最底层框架开始一步步起着手优化。
昨天的测试结果基本让人满意,在同一台机器上,200 个机器人的混战 CPU 占用率平均仅 130% 左右,而机器人 client 边数据包延迟只有 1 秒,完全可以实用。这离我们的设计目标 ( 500 人同战场流畅战斗)还有一些距离,但考虑到今年新配置两块 Intel Xeon E5-2620 @ 2.00GHz 的话,按其性能指标,应当再有至少一倍的性能提升。
ps. 参考 ,我们计划采购的 [Dual CPU] Intel Xeon E5-2620 @ 2.00GHz Benchmark 16707 分,而目前使用的 [Dual CPU] Intel Xeon E5310 @ 1.60GHz 仅 4160 分。即使仅考虑单线程分数,也在两倍以上。
最近在接手改进我们的怪物 AI 的底层模块。这部分策划是希望可以由他们来直接配置而不是提交需求文档让程序实现。
我们的前一个版本有较大的性能问题,光是空跑一些场景,没有玩家的情况下 CPU 符合都相当之高了。我觉得是允许策划的配置项过于细节,以及底层模块实现的方式不对,导致了大量无用的 lua 脚本空转导致的。
目前的 AI 脚本是每个挂在独立的 NPC 上,利用心跳(大约是 0.5s 一次),定期让 NPC 去思考现在应该去干些什么。这些干些什么的具体逻辑在很细节的层面都是要去运行一个策划配置的脚本在一个沙盒中运行的。在实际监测中,一个心跳的一段 AI 脚本居然会跑上万行 lua 代码,想不慢都难啊。
游戏开发和很多其他软件开发的一个巨大区别就是,你无法把程序得到正确结果当成任务的完成。运行时间往往成为重要的约束条件。如果一件事情在规定的时间片执行不完,代码实现的再正确都没有意义了。而局部的优化热点往往也意义不大。因为如果只是需要小幅度的提高性能,那么采购好一些的硬件就够了。一个模块的性能,要从数量级上的提高,必须重新思考需求,改变需求,重定义我们要做什么。
我决定看看星际争霸2 的地图编辑器是如何工作的。
我没有玩过魔兽争霸3 的编辑器,也没有玩过星际 2 的。但似乎,它们可以让用户自定义出各种形式的游戏来,并不局限在 RTS 一种类型中。我相信这个发展了超过十年的自定义地图的编辑模式,一定有很多成熟的业务抽象。
有限的时间内,我没有从网上找到太多的相关资料。在暴雪的官方网站也没能看到完整的文档。星际2 的编辑器内建了一个叫做银河(Galaxy)的脚本语言,似乎所有 GUI 界面上的编辑器操作,都可以完整的对应成一段 galaxy 脚本。很可惜的是,暴雪似乎鼓励玩家用 GUI 编辑器创造地图,而脚本只是背后之物,galaxy 的手册我并没有找到。
我只好自己把弄编辑器,在自己的使用中,推想暴雪解决问题的思路。短短两天的研究肯定会有许多错误和遗漏,也希望借公开的 blog ,有行家看到能够赐教。
我们的项目的第二里程碑在中秋前结束了。这次耗时三个月的开发周期,堆砌了大量的游戏特性,大部分任务都是在追赶进度中完成的,导致最终有很多问题。
所以,节后我们决定调整一个月,专门用来重构不满意的代码以及修理 bug 。好在这次我们的代码总量并不算太大。服务器部分,所有已有的 C 底层之外的代码,全部 Lua 代码,总量在五万行左右。这是一个非常可控的规模。即使全部重写,在已有的开发经验基础上,都不是什么接受不了的事情。所以我对改进这些代码很有信心。
有部分代码有较严重的性能问题,节前我给底层增加了一些统计和检测模块,发现部分逻辑模块,单条消息的处理居然超过了一万条 Lua 指令。比较严重的是 AI 和怪物管理模块,以及战斗的 buff 和属性计算。
这几个问题,接下来我会一个个着手解决。这两天在优化属性计算的部分。
在半年前,我曾经参于过这部分的设计与实现。参见 。
不过在写完这个模块后,我的工作从这部分移开。其他同学接手正式实现策划的需求时,并没有使用我实现好的模块。因为策划的需求更为复杂,最终采用了更间接和可配置的方案。相关的实现我未能跟进,Mike 同学就用 Lua 实现了一个基本等价的东西。
最近在试图优化游戏服务器,提升承载能力。
加了一些时间检测模块。由于已经重新编写了服务器底层框架,并修改了消息协议,这样底层就有能力监控每条服务的相应时间了。
以前这方面没有做到,是因为:从底层看来,仅仅是一个个的消息包的流动。而一条完整的协议响应却是有多个包进出的。当处理一个服务请求的过程中,向外做了一次远程调用,那么当前服务体就被切出了,直到回应的 session 响应才继续。在新的设计中,session id 被暴露给底层模块可见,这就使监控变得可行。
另外,由于是完全用 C 语言编写而不再使用 Erlang ,我们可以使用成熟的 profile 工具。gprof 使用起来非常方便,容易对性能热点定位。
不过,经过实测,大量的 CPU 时间是消耗在 lua 虚拟机内部。也就是说,在底层框架上做改进已经没有多少可以压榨的了。或许进一步优化通讯协议还有一些工作可以做(合理的协议可以使得上层的数据处理更简洁),不过,工作中心移到 Lua 层面会更有效一些。
相关工作从多个方面入手。
首先想到的是使用 LuaJIT 。不过,我们一开始是工作在 Lua 5.2 上面,而 LuaJIT 似乎不打算完全支持 5.2 版,我需要适度的做一些向前兼容工作。之前我已经 。在写完上篇 blog 后,我加入了 LuaJIT 的 mailinglist ,LuaJIT 的作者相当的勤奋,基本上提交的 bug 都在半天内可以修正;大多数合理的功能需求也可以满足。或许向前兼容这个事情不一定需要完全自己搞定。我们不需要完全支持 Lua 5.1 ,只需要支持 LuaJIT 2.0 即可。那么,推动 LuaJIT 2.0 去支持一些 Lua 5.2 的新特性也不错。比如就在昨天,LuaJIT 支持了 Lua 5.2 中增强的 debug.getlocal 。
由于我们的进度压力,LuaJIT 的兼容问题暂时搁置了。还有一些工作需要其他同学的协力支持,而他们,正在为努力完成游戏特性而努力。暂时不太可能安排的出时间来做这些调整。
经过一个月, 我基本完成了
的 C 版本的编写。中间又反复重构了几个模块,精简下来的代码并不多:只有六千余行 C 代码,以及一千多 Lua 代码。虽然部分代码写的比较匆促,但我觉得还是基本符合我的质量要求的。Bug 虽不可避免,但这样小篇幅的项目,应该足够清晰方便修正了吧。
花在 Github 上的这个开源项目上的实际开发实现远小于一个月。我的大部分时间花了和过去大半年的 Erlang 框架的兼容,以及移植那些不兼容代码和重写曾经用 Erlang 写的服务模块上面了。这些和我们的实际游戏相关,所以就没有开源了。况且,把多出这个几倍的相关代码堆砌出来,未必能增加这个开源项目的正面意义。感兴趣的同学会迷失在那些并不重要,且有许多接口受限于历史的糟糕设计中。
在整合完我们自己项目的老代码后,确定移植无误,我又动手修改了 skynet 的部分底层设计。在保证安全迁移的基础上,做出了最大限度的改进,避免背上过多历史包袱。这些修改并不容易,但我觉得很有价值。是我最近一段时间仔细思考的结果。今天这一篇 blog ,我将最终定稿的版本设计思路记录下来,备日后查阅。
自从动了 的念头,最近忙的跟狗一样。每天 10 点醒来就忙着写代码,一句废话都不想说,一直到晚上 11 点回家睡觉。连续干了快一个月了。
到昨天,终于把全部代码基本移植到了新框架下,正常启动了起来。这项工作算是搞一段落。庆幸的是,我这个月的工作,并没有影响到其他人对游戏逻辑的开发。只是我单方面的同步不断新增的逻辑逻辑代码。
Skynet 的重写,实际上在半个月前就已经完成。那时,已经可以用新的服务器承载原有的独立的用户认证系统了。那么后半个月的这些琐碎工作,其实都是在移植那些游戏逻辑代码。
在 Skynet 原始设计的时候,api 是比较简洁的,原则上讲,是可以透明替换。但实际上,在使用中,增加了许多阴暗角落。一些接口层的小变动,增加的隐式特性,使得并不能百分百兼容。另外,原来的一些通讯协议和约定不算太合理,在重新制作时,我换掉了部分的方案,但需要编写一个兼容的链路层。
比如:以前,我们把通过 tcp 接入的 client 和 server 内部同进程内的服务等同处理。认为它们都是通过相同的二进制数据包协议通讯。但是,同进程内的服务间通讯明显是可以被优化的,他们可以通过 C 结构而不是被编码过的数据包交换信息,并可以做到由发起请求方分配内存,接受方释放内存,减少无谓的数据复制。在老的版本中,强行把两者统一了起来,失去了许多优化空间。在新版本里,我增加了较少的约定,修改了一点接口,就大幅度提升了进程内服务间信息交换的效率。
另一方面,一旦固定采用单进程多线程方案,之前的多进程共享数据的模块就显得过于厚重了。新的方案更为轻量,也更适合 lua 使用。这项工作在 中提到过。这和 skynet 的重写原本是两件事情,但我强行放在一起做迁移,增加了许多难度。但考虑到,原本我就需要梳理一次我们的全部服务器端代码(包括大量我没有 review 过的),就把这两件事情同时做了。
在这个过程中,可以剔除许多冗余代码,去掉一些我们曾经以为会用到,到实际废弃的模块。彻底解决一些历史变更引起的问题。过程很痛苦,但很值得。新写的代码各种类型检查更严格,就此发现了老的逻辑层代码中许多隐藏的 bug 。一些原有用 erlang 实现的模块,重新用 lua 实现了一遍,混合太多语言做开发,一些很疼的地方,经历过的人自然清楚。以后如非必要,尽量不用 lua 之外的语言往这个系统里增加组件了。
btw, 新系统还没有经过压力测试。一些优化工作也没有展开。但初步看起来,还是卓有成效的。至少,改进了数据共享模块,以及提出许多冗余后,整个系统的内存占用量下降到原来的 1/5 不到。CPU 占用率也有大幅度的下降。当然,这几乎不关 C 还是 Erlang 做开发的事,重点得益于经过半年的需求总结,以及我梳理了大部分模块后做的整体改进。
今天想重点谈谈下面一段时间我希望做的改进。是关于服务间 RPC 的。
最近我的工作都围绕
的开发展开。
因为这个项目是继承的 Erlang 老版本的设计来重新用 C 编写的。 再一些接口定义上也存在一些历史遗留问题. 我需要尽量兼容老版本, 这样才能把上层代码较容易的迁移过来。
最近的开发已经涉及具体业务流程了, 搬迁了不少老代码过来。 我不想污染放在外面的开源版本。 所以在开发机上同时维护了两个分支, 同时对应到 github 的公开仓库, 以及我们项目的开发仓库。
btw, 我想把自己的开发机上一个分支版本对应到办公室仓库的 master 分支, 遇到了许多麻烦。 应该是我对 git 的工作流不熟悉导致的。
这几天老在开会,断断续续的拖慢了开发进度。直到今天才把
的集群部分,以及 RPC 协议设计实现完。
先谈谈集群的设计。
最终,我们希望整个 skynet 系统可以部署到多台物理机上。这样,单进程的 skynet 节点是不够满足需求的。我希望 skynet 单节点是围绕单进程运作的,这样服务间才可以以接近零成本的交换数据。这样,进程和进程间(通常部署到不同的物理机上)通讯就做成一个比较外围的设置就好了。
为了定位方便,我希望整个系统里,所有服务节点都有唯一 id 。那么最简单的方案就是限制有限的机器数量、同时设置中心服务器来协调。我用 32bit 的 id 来标识 skynet 上的服务节点。其中高 8 位是机器标识,低 24 位是同一台机器上的服务节点 id 。我们用简单的判断算法就可以知道一个 id 是远程 id 还是本地 id (只需要比较高 8 位就可以了)。
我设计了一台 master 中心服务器用来同步机器信息。把每个 skynet 进程上用于和其他机器通讯的部件称为 Harbor 。每个 skynet 进程有一个 harbor id 为 1 到 255 (保留 0 给系统内部用)。在每个 skynet 进程启动时,向 master 机器汇报自己的 harbor id 。一旦冲突,则禁止连入。
master 服务其实就是一个简单的内存 key-value 数据库。数字 key 对应的 value 正是 harbor 的通讯地址。另外,支持了拥有全局名字的服务,也依靠 master 机器同步。比如,你可以从某台 skynet 节点注册一个叫 DATABASE 的服务节点,它只要将 DATABASE 和节点 id 的对应关系通知 master 机器,就可以依靠 master 机器同步给所有注册入网络的 skynet 节点。
master 做的事情很简单,其实就是回应名字的查询,以及在更新名字后,同步给网络中所有的机器。
skynet 节点,通过 master ,认识网络中所有其它 skynet 节点。它们相互一一建立单向通讯通道。也就是说,如果一共有 100 个 skynet 节点,在它们启动完毕后,会建立起 1 万条通讯通道。
为了缩短开发时间,我利用了 zeromq 来做 harbor 间通讯,以及 master 的开发。蜗牛同学觉得更高效的做法是自己用 C 来写,并和原有的 gate 的 epoll 循环合并起来。我觉得有一定道理,但是还是先给出一个快速的实现更好。
最近工作展开后, 我们一共有 10 名程序员在目前的项目上工作。我暂时没有和其他人有依赖关系的工作,最近一周在改进以前做的一些东西,在不修改接口的前提下,争取提供更高的性能,以及完成一些之前没完成的功能,为以后的扩展做准备。
最近值得一提的东西是:关于我们的共享储存的数据结构。
最早在设计的时候,是按多进程共享的需求来设计的。希望不同的进程可以利用共享内存来共享一组结构化数据。所以 。这个东西实现的难点在于:一、共享内存不一定在不同进程间有相同的地址,所以不能在结构中用指针保持引用关系;二、不希望有太复杂的锁来保证并发读写的安全性。
后来,我们采用了 Erlang 做底层的框架。在同一台机器上,只有一个系统进程。所以,这个东西可以不必实现的这么复杂。我抽了三天实现,重新实现了一个。这次不考虑跨进程的问题,只在同一进程的不同线程中,让独立的 Lua State 可以访问同一份结构化数据。至于结构化数据支持到怎样的数据类型,我认为和 Lua 原有的 table 类型大致一致就可以了。
最后,就完成了这么一个东西。我认为到目前这个阶段,这个模块还是比较独立的,适合开源分享。以后的工作可能会和我们具体项目的模块整合在一起,还需要做一些修改,就不太适合公开了。有兴趣的同学可以在我的 github 上看到代码。 。
问题是早就提出了的。在
中,就写到一个需求:一个玩家数据的写入者,可以批量修改他的属性。但是,同时可能有其他线程在读这个玩家的数据(通过共享内存)。这可能造成,读方得到了不完整的数据。
我们可以不在乎读方得到某个时间的旧数据,但不可以读到一份不完整的版本。就是说,对玩家数据的修改,需要成组的修改,每组修改必须是原子的。
起先,我想用读写锁来解决这个问题。方案想好了,一直没有实现。只是把读写锁的基本功能实现了。
这几天这个问题被重提出来。因为,前段我们都采用了鸵鸟政策,当问题不存在(事实上我们也没有发现实际中出现可观测到的问题)。
反正探讨了好几个解决方案,一开始都是围绕怎么加锁,锁的粒度有多大来展开的。甚至,我们把其中的一种方案都实现出来了,并写了压力测试程序测试。不过,这些方案都不太令人满意。大家担心锁的开销,以及逻辑代码编写者所需求关心的问题太多,导致有死锁的可能性。
昨天差一点决定用一个地图锁来解决这个问题,就是用牺牲同一个地图进程上,玩家间并行的可能性为代价的。这个方案也不无不可。但昨晚躺在床上一直睡不安稳。因为这样做,就失去了一开始我期望用并行方案来设计游戏服务器的初衷。如果这样,还不如全部退化到单地图单进程来编写程序。那么一定有方法是可以避开锁以及避免让写逻辑的程序员去关心数据共享的读写冲突问题的。
我们项目的第一个里程碑杯已经比较顺利的完成了。主要是完成最基本的用户注册登陆,场景漫游,走跑跳,远程进程法术等技能攻击,怪物及 npc 的简单行为,等等。尝试了几十个玩家与上百个 npc 的混战,还比较流畅。
中间小作休整,然后开始做第二里程碑的计划安排。这次预计要三个月,完成几乎所有的 MMO 必须的游戏功能了。
其中一个大的任务就是要完成背包、物品、装备、货币、掉落、交易这些相互关联的模块。
下面简单记录一下背包系统的设计实现思路:
物品交易和掉落系统不是一期里程碑的内容,手头事情不多,就还是先做设计了。
物品掉落和交易,本质上是一件事情。区别在于玩家之间的物品交换于玩家和系统之间的物品交换。这部分的设计,我在去年初曾经写过一篇
这次重新设计实现,做了少量的改动。主要是一些简化。设计原则是,交易服务器独立,简洁,健壮,容易回溯交易历史做数据恢复和分析。
交易服务,我认为是一个后备系统。在数据不出问题时,不大用理会它的存在。它仅仅是用来保证物品不被复制,确定每件物品可以有唯一的所有人。在出现争议的时候,可以作为仲裁机构。
在游戏服务中,玩家或其他实体储存的物品,同样会保存在玩家数据中, 。 每件物品记录在对应的包裹格类,直接记录物品名称或指代名称的 id 。即,同样的物品用同样的标识。之外,再额外记录一个(或若干个)物品唯一 id (用 64bit 或 128bit ) 。这样做,避免在访问物品的功能时,额外去查一张表。
交易认证服务,其实只关心一件事情。即,某个物品 id 归属于谁。它不关心物品 id 代表的是什么含义。当然,可以有另一个数据库记录这个关系。在物品的所有者改变时,交易服务来保证其原子性。除了玩家间的转换,还有大量的和系统间的交换。掉落物品其实是从系统改为玩家所有,而玩家销毁物品则是把所有者改为系统所有。
这段时间项目进展还算顺利,叮当同学在盯项目进度,我专心解决程序上的各种小问题。
最近我在协助解决 NPC (包括地图上的怪物)的行为控制以及 AI 的问题。
目前,我们的进度还处在玩家可以通过客户端登陆到服务器,可以在场景上漫游,以及做一些简单的战斗和技能动作的阶段。按最初的设计原则,我们的每个玩家是在服务器上有一个独立的
服务的。目前写到现在,和中间想过的一些实现方法有些差异,但大体上还是按这个思路进行的。
关键是在于去除大部分的回调方式的异步调用;编写的控制流程自然完整,不需要太多的去考虑 agent 行为之外的交互性。
如果丝毫不考虑性能问题,我很想把每单个怪物和 NPC 都放在独立服务中。但是估算后,觉得不太现实。在
的最后一段已经提过这个问题了。
今天展开来谈谈我的方案。
我想把怪物的移动行为独立出来做,以减少 AI 的压力。也就是说,地图上所有的怪,在设定的时候,都可以设定他们的巡逻路径,或是仅仅站立不动。我希望在没有外力干扰的时候,处理这些行为对系统压力最小。
我不想让怪在没有任何玩家看见的时候就让它静止不动,因为这样可能会增加实现的复杂性,并在怪物行为较为复杂时,无法贯彻策划的意图。
最好的方法还是把之隔离,使其对系统的负荷受控。同时也可以通过分离,减小实现的复杂性。
这个子系统是这样的:
前段时间为策划提供过一些技术上的支持,
。但随着更多策划加入团队,我觉得这个思路不能很好的贯彻下去。
策划更喜欢通过 excel 表格来表达他心中的数值关系,而不是通过代码语言。我需要找到另一种更切合实际的方案来将策划的想法转换为可以运行的代码。
研究了一下策划历史项目上的 excel 表格后,我归纳了一下其需求。我们需要把问题分步解决,excel 表格到程序可以利用的数据结构是一项工作,而从表达了数据之间联系的结构到代码又是另一项工作。
对于数值运算,有怎样的需求呢?
我更多看到的是一种声明式的表达,而不是过程式的。比如,策划会定义,“血量”这个属性,实际上是等价于“耐力 * 10”。我们把后者定义为一个公式。
许多表格其实就是在不同的位置表达了这种公式推导关系:一个属性等价于另一些属性组成的表达式。而在运行时,根据人物的一些基础属性,可以通过一系列的公式推导,得到最终的一系列属性值。满足这个需求并不难,读出表格里的对应项,做简单的解析就可以了。(这里涉及到另一个问题,表格里的对应项在哪里,今天暂且不谈)
对于这种声明式表达,程序要做的工作是进行一次拓扑排序,把需要先求值的属性排在前面,有依赖关系的属性求解放在后面。这样就可以转换为过程式的指令。
另一种表格称为查表。其实就是表达一种映射关系。如下表:
法术伤害物理伤害
这几天,安排新来的王同学做数据持久化工作。一开始他是将 sharedb 里的数据序列化为文本储存的。这步工作做完后,开始动手把数据放到 Redis 数据库中。我们的系统主干由 Lua 构建,所以需要一个 Lua 的 Redis 库。google 来的那份,王同学不满意。三下五除二自己重写了一个。据说把代码量减少到了原来的三分之一(开源计划我正在督促)。唯一的问题是,如果直接采用系统的 socket 库,不能很好的嵌入我们的整个通讯框架。我们的 skynet 全部是通过异步 IO 自己调度的,如果这个数据服务单方面阻塞了进程,会使得别的进程获得不了时间片。
蜗牛同学打算改进 skynet 增加异步 IO 的支持。
我今天在考虑现有的 API 时候,对比原有的 timer 接口和打算新增加的异步 IO 接口,发现它们其实是同一类东西。即,都是一个异步事件。由客户准备好一类请求,绑定一个 session id 。当这个事件发生后,skynet 将这个 session id 推送回来,通知这个事件已经发生。
在用户编写的代码的执行序上,异步 IO 和 RPC 调用一样,虽然底层通过消息驱动回调机制转了一大圈,但主干上的逻辑执行次序是连续的。
受历史影响,我之前在封装 Timer 的时候,受到历史经验的影响,简单的做了个 lua 内 callback 的封装。今天仔细考虑后发现,我们整个系统不应该存在任何显式的回调机制。正确的接口应该保持和异步 IO 一致:
这几天我的工作是设计未来游戏服务器的热更新系统。
这部分的工作, 。这些工作,在当时一段时间与广州网易其他项目交流时,也对网易其他项目的设计产生过一些影响,之后,也在实战中,各个项目组逐步发展出许多热更新的系统来。
我最近对之前所用到的一些方案,如修改 lua
module 的加载策略,增加一些间接层,来达到热更新代码的系统设计做了一些思考。感觉在处理热更新这个问题时,还不够严谨。经过两天的思考,我按我的构思实现了新系统的雏形。
在函数式编程语言中,热更新通常比较容易实现。erlang , lisp 都把热升级做为核心特性之一。函数副作用越小的语言,越容易做热升级:你只需要简单的把新写的函数替换回去就好了。
对于纯粹的请求回应式的服务,做热升级也相对容易。比如大多数 web server ,采用 REST 风格的协议设计的网站,重启服务器,通常对用户都是透明的。中间状态保存在数据库中,改写了代码,把旧的服务停掉,启动新版的服务,很少有出错的。
按我们现在的游戏服务器设计,大多数服务也会遵循这个结构,所以许多底层的子模块简单重启进程就可以了。但和游戏逻辑相关的一些东西却可能要考虑更多东西。
我想把我们的系统分步设

我要回帖

更多关于 陌陌争霸公会游骑兵 的文章

 

随机推荐