首个里程碑顺利结束

到今天为止,第一个里程碑算是顺利结束了。

其实周三就已经完成所有功能提交验收了,周四把GameLogic的执行顺序修正了一下,保证Component的开关是可用的。之前的做法保证勉强可用,这下应该是确保正确了。只是还遗留了两个小问题:1. 不能保证Update一定在Initialize、Enable、Start之后调用,因为Initialize和Start都有异步方法;2. 第一个问题的衍生问题,我在纠结要不要让两个异步方法不再等待,一个脚本在异步加载的时候,所有脚本都要等待加载完成,虽然并不会卡住主线程,但是总归是有点奇怪的。一时半会还没想通,先放着吧。

这两周代码总归还是写得挺开心的,状态很好以至于直接通宵了几天,上周心动48小时也是玩心大起也跟玩了两天,跟叶斌前辈以及邢大哥聊的收获颇丰,真的很感谢前辈们的分享。现在来看基本上框架算是完善一些了,也算是可以缓一口气。

网络那边交给诸谦去研究了,最后还真是觉得Skynet是比较靠谱的解决方案,现在已经(排除万坑XD)走通了基本的连接和加密方面,下周就是协议方面了,最后整个流程走通了,就可以准备进行整理和并入框架了。诸谦真的还是厉害!

放松了两天,可以过个闲适一些的周末。下周开始进入里程碑 2 的开发。

.Net的Task写起来太TM爽了

上周二拿到PvE的需求,开始搞起来了。

地形用新的TileMap搞了一下,先用着再管美术问题。

摇杆就用EasyTouch的,结果还是发现确实没法满足右摇杆的需求,于是就开始纠结怎么办了。

结果纠结了一下午之后,完美解决了。

获取实际点击位置,分辨率无关。顺便再重构了一下InputController,逻辑非常清晰,很满意。

主角状态有意没有用很复杂的结构,先简单写了一些行为,然后对输入做绑定。前期需求还不明确的情况下设计逻辑架构感觉其实还是挺不合算的。子弹也是一样,简单写了个实现。

上周基本就是这样。

其实整个框架一直都有一个严重的问题,就是基于协程的异步接口一直令人非常难受。主要的点在于,协程不能返回值啊!!!一溜烟写一串那确实是挺爽的,但是一旦需要返回值那就直接火葬场。框架服务启动的时候会涉及AssetBundle加载,肯定涉及异步,代码写起来的感觉啊,真的是拆东墙补西墙,一个地方改改一堆地方都要跪。今天一早来,改到中午,好不容易把View的加载改好了(本来是Resources.Load),结果好了,这里不阻塞了,直接服务启动速度快了很多,导致整个服务都启动好了,主角还没被加载出来,然后一加载敌人,找不到主角,跪了。

虽然这个问题可以简单的靠敌人做一下null判断就可以解决的事情,但是总是心里非常的不爽。明明弄了这么一套服务框架,出发点就是让执行顺序可以得到保证,而且明明大部分情况都是可以的,但是最后的一点问题总是不好解决,非常的难受。

而主要的问题,就是协程不能返回值啊!!!要让我把所有代码都写成一个Request形式的,也太难受了,而且这样,其实说到底还是得在一个协程里完成,只是可以把这个协程往上推,根本不解决实际问题。

崩溃,这个问题想了很久很久,感觉实在没法想出什么写法可以完美解决这个问题的。

最后,我也不记得是怎么想起来的,用.Net 4.6 Framework试一下Task。

随便Google了一下,找到了个以及作者的博客。Asset Store也有,但是不是最新提交,于是git clone了一份下来。在经历了导进去删掉再导进去再删掉最后还是导了进去之后,终于搞清楚是怎么回事了。

于是我在办公室里喊了一下午:“太爽了!!!”

所以就不能怪大家今天一直用奇怪的眼神看我了……

大家直接理解为,新的语法可以把任何一个函数变成异步的(只需要加上async关键字),然后,可以随意返回值!!!

写出来类似这样:

这是这个库的官方样例,再次感叹拓展方法真的太神奇,可以和协程完美互相结合。

我只能说,谁用谁知道。

很快把项目所有异步接口都改写成Task,服务和游戏逻辑完全保证执行顺序,完美达成目的不说,关键写起来是真的优雅,真的爽。

开心,晚饭汉堡王30-20都没能令我更开心。

卓越的企业,伟大的公司

在关电脑前,随意整理了一下收藏夹,结果看到了这个很早以前肖哥分享的视频。

http://v.youku.com/v_show/id_XNjkwMDg4Mzg0.html

说的是The Golden Circle理论。核心是下面这句:

“People don’t buy what you do; they buy why you do it. And what you do simply proves what you believe”

― Simon Sinek, Start with Why: How Great Leaders Inspire Everyone to Take Action

记得当时肖哥是非常兴奋地向我们推荐这个视频,我当时看了应该也是有些感触。多年之后,重看一遍,依然还是激发了我一些思考。

很明显,这个视频的理论并不是我构建世界观的一部分,至少不属于根本的那部分。而对肖哥,我相信他是Native得实践这个理论,而且成功了,“It do work”。

因为视频中举了Martin Luther King的例子,我也去找除了Dr. King的演讲视频。这个著名的演讲——感谢语文教材编委会——我看过中文版,实际看视频,感觉比翻译后的文字更加振奋人心,稿子写得确实好。

然后就是很自然的功利主义思想了——思考一下,对现状有什么帮助吗?首先就是意识到当前的直接方向并不符合这个理论,现在还真的是利益,或者更加直接的,利润导向嘛。现在大家热炒所谓“独立游戏”,反倒是挺符合理论的。再从根本上来说,我,以及我所在的组织,这个终极的Why是什么呢?这个问题倒是很好回答,我和肖哥各给出了一个答案:

成为一家卓越的企业(我)
成为一家伟大的游戏公司(肖哥)

偏向重点不同,但是目标一致。

卓越这个词的来源是吉姆·柯林斯那本著名的《从优秀到卓越》,这两本书(另一本是指《基业长青》)的内容对我倒没有多大影响,现在也几乎记不清了,只是一直以来都模糊得感觉“卓越”是一家企业所应该达到的终极。

企,踮着脚看的意思,引申为期盼、期待的意思。所以企鹅这个名字起得真的是绝了,掂着脚走路的鹅,萌萌哒。所谓企业,所期盼的事业也,相对于公司,看上去好像用语更加正式,实际上又更有人味儿一些。这种“看上去”又“实际上”的事情是我所喜欢的,故用是词。

对这两个答案简单的取个最大公约数吧。

成为一家伟大的企业。

卓越和伟大,这两个词粗看之下意思差不多,但是细细品味好像又不太一样。

百度阿里腾讯,说他们是卓越的公司,我想不会有人反对,但说他们是伟大的企业,那就可能会迟疑一下。

什么是我心中伟大的企业呢?脱口而出的,Google、Apple、Microsoft、Elon Musk的三家公司。

果然是

People don’t buy what you do; they buy why you do it

I have a dream,然后有一家公司正在替我实现,所以他是伟大的。

Google的收入主要来自广告,导致它大部分的产品和项目都可以不那么看中钱(有些情况下这也是个问题……),而Google又网罗了大量的优秀人才,最终导致Google一直在做“酷”的事情,而酷的事情,很多时候就是伟大的。

Apple的伟大那么的理所当然,反倒是感觉不需要说了。顺便吐一槽,现在看屁苹果的发布会,每次发布会的每一个点都还是挺兴奋的,确实是“why you do it”,但是看完之后,又回忆不起什么重要的内容。这已经跟老罗的发布会相声一个水平了嘛……

Microsoft现在每次打动我的,竟然是硬件产品,像是Surface系列,以及,当然,HoloLens。

Elon Musk的三家公司就更不用说了,反倒是说这些公司是否“卓越”我还得谨慎一些,说“伟大”,毫无问题。

所以,在我的感觉上,卓越是一个更加理性的标准,可以量化,基本来说,就直接反应在了股票上了。而伟大,则是一件更有想象力的事情。做到卓越,更像是拼命想考高分的好学生,按照一个大家公认的标准而做到最好。成为伟大,听上去才更像是人生的意义。

虽然,我依然还是选择“卓越”,因为这已经是一件非常非常非常非常非常困难的事情了,而“伟大”,还是作为行事的最终标杆吧。

作为一个终极的意义,成为一家伟大的企业,没毛病。

在这条路上,想要做出一款真正的3A游戏,也是一个明确的想象。

剩下的,还没想清楚,现在也没能力想清楚,就算想清楚了也没有用。

时常,确认一下方向是对的,然后的精力就继续回到“解决问题”上了,解决眼前的问题们,就已经是足以令人兴奋激动得要疯掉的事情了。


最后说一个个人想法,确实感觉,现在中国还没有一家可以称为“伟大”的企业。当然这有很多原因,这里还是不要多嘴了。但是呢,很明显,终于他们开始向着伟大迈步了。不管是百度对AI的All in,还是阿里的达摩院,都是明显的标志。而腾讯现在还稍稍有些停留在“有钱是可以为所欲为”的阶段。确实,我相信现在它的力量已经达到十分可怕的程度了,在BAT中,也已经是相对最不作恶,最“卓越”的一个了。所以,这也是我最期待的——当它开始变得“伟大”,将会是怎样一番景象呢?

长话短说

广东之行,吃得不错。

有幸见了云风大大,果然还是一头飘逸的秀发。不好意思耽误过多时间,就简单的交流了一些。比较值得记录的主要是两点:

  1. 询问了一下关于网络方面的需求到底该由哪边提出哪边负责的问题,总结下来确实不应该由产品组关心,产品只需要提出具体需求,是否网络,怎样网络这都可以作为具体技术实现方式来考虑。安全问题也是一样,因为理论上只能提高破解难度而不可能完全防止,所以,除了关键方面(比如付费),其他方面的安全问题,按照现阶段的情况,也只能依然是依赖程序的素养了。具体问题具体分析,这次自己负责,这个问题还是确实需要实际经验才敢说话的。
  2. 关于网络通讯,之前一直纠结在RPC上,云风大大倒是点醒了我。重点还是在于“协议”这个概念和协议尽量设计得“无状态”上。通讯本质上只是相互发消息或者说是通知而已。

这样一来,网络方面具体实现之前概念方面的问题就基本解决了,接下来的主要精力当然是在前端方面,甚至感觉在一段时间内都是可以完全不涉及网络的。这段时间正好可以好好补一补一些基础,目前的计划是C→Lua→C++。先从The C programming Language开始,特别是C99的部分需要好好了解一下。哈哈,11年后“重学”C语言,还是空杯心态。知道了为什么要去学,再去学,感觉、效果,都真的很好。

最后是跟云风大大合影>。<

回到前端

花了几天时间对“网络游戏”有了些许概念性的了解后,就还是开始攻克前端这个真正摆在眼前的问题了。

插句题外话,前些日子脂评版石头记看完后,这些日子公车途中在看汪曾祺先生的散文。特别是偶得一本说吃的合集,特长,特爽。心得有二。其一,终于意识到散文是什么了,从小一直说散文散文啊,都没想通到底什么是散文,原来这种便是;其二,我真是妄称热爱文学,博客都写得烂如这般,文字洗练还是要下功夫。一名作家,看小说会让你喜欢上他,于是想了解他。故此,散文便是最好的途径了。如汪老一般写到这般“信手拈来无不是”的境界,也就“字如其人”了(搜狗给我打了个信手拈来无不适……我很震惊自己一直理解错了吗?一查发现用“是”是对的,而且这句还真出自脂批)。

前两天状态很不错,一直有点担心是不是打折能量饮料喝多了,后面就“萎了”。至少现在看来并不是。我也不完全信是褪黑素的功劳,但是解决了“贪睡”这个老大难问题,还是挺好的,印证了一句老话:办法总比困难多。

正题

回到前端的问题上。

升了 Unity 2017.2,一是新项目用最新版的习惯(长痛不如短痛),二是有些新功能(比如原生的Tile Map)想试试看情况。

改了下.gitignore。排除了/.vs、Resharper相关路径,还有AssetBundles的导出路径

重新整理了一下Directory,从之前几个项目中,把混乱的“Framework”(自己写的框架,仗着重构namespace方便就先乱起了个名字)切出来,导入插件。

框架和插件选择这两点,等以后稳定下来,肯定需要专门系统的写几篇说明。

然后就是解决了几个问题。首先是AssetBundle,AssetBundle Manager这个插件本身没问题,不过2015年之后在Asset Store上就没更新了。按照我对Unity的理解,这么重要的东西肯定还是在做的,不说别的,就看这几年光官方就出了那么多AssetBundle插件就可能看出官方还是挺重视的,而AssetBundle Manager在这一堆插件中还算是很不错的了。于是Google一下就发现BitBucket上官方有个项目叫AssetBundleDemo,点进去一看果然最近还在更新。Clone下来导入项目一款,嗯不错,Asset Store版本中注释掉的Inspector面板,这个版本是实现了的,应该就是同一个项目的演化版本无疑了,就用它了!

某次还看到了Asset Bundle Browser Tools,Asset Store就有,也是官方出品,看上去比较靠谱,也是果断Github之(比较新),果然,很好用。

接下里的问题就花了几个通宵了,AssetBundle Manager有一些bug和一些兼容性问题,这个好修,修好以后想给提个Pull Request。随后没提真不是我懒,是我一看,好家伙,已经一堆人提过了,官方目前还没管呢。然后呢,这接口是异步的,按照Node.js的经验,异步接口无外乎写成回调和Task(或者Promise,这个我还没完全了解一堆这种名字到底是不是同一个概念的东西,哪天得好好研究研究),对于Task形式的接口,我希望是可以写成

其中,DoSomeJob函数返回值应该是个IEnumerator。

一开始我可犯了难,yield return 一个对象。 这是什么东西?这时才发现原来其实Unity用迭代器实现的这个协程自己理解的还不是那么透彻的。憋了很久算是憋出来了,结果发现嗨,不是和AssetBundle Manager原生接口返回的东西基本一样嘛。那么这个实际上就只是个适配器而已了,充其量可以打两个Log。在使用var的情况下,两种方式的代码甚至可以做到基本无差别。于是……这代码就被我怒删了。

当然……现在后悔了,因为发现其实后续一些框架接口也是要写成异步形式,所以这个类还是有用的,用到了再补回来,知道怎么写了就快了,代码很短,就是个IEnumerator的实现而已。代码后续补上。

研究了AssetBundle Manager与AssetBundle Browser的搭配工作流程,在Editor下和手机上都跑通了,我也困得不行,就拿衣服边上先睡了。

应该淘宝一床被子放公司里。

第二天一早精神依然挺好,继续整框架,发现一个新需求,我必须要让所有逻辑代码在框架启动完毕之后启动。

插播一下,现在框架启动已经是通过[RuntimeInitializeOnLoadMethod]了,真的爽飞。

于是开始改写之前临时写的GameLogic类,开始很顺利,后面发现自己想错了一大堆东西,其实并不可能写成自己想象中优雅的方式,结果还是大改特改,目前算是能跑了,先用着,在自己使用中越来越完善嘛。

这种方式,让服务启动也是使用协程顺序启动成为可能,也算给服务启动时异步加载提供了可行方案(不然脚本肯定比服务先启动的),这个根据需求后期修改。

具体实现依然等稳定了再慢慢写说明……

然后看了新的TileMap,这个很难说,因为我至今还没搞清楚怎么用,所以还是不说了吧,继续研究研究……

所以就去了。

对“网络游戏”的理解

这个周末很神奇的状态很好,只睡了10个小时,完全沉迷学习无法自拔。

深刻的自我检讨了一番,技术方面的能力还是太浅了,之前的一些想法和行为都很羞耻,啼笑大方。

一天多看完了《网络游戏核心技术与实战》 (以下简称《网》),大为叹服。清晰说明了网络游戏的主要架构不说,关键是这是第一次看到有完整的网络游戏整个研发运营过程的完整介绍,甚至包括商业部分,而且说得还很对……

6个月前就在考虑,万一最后还是决定需要强联网的话,至少应该先把这本看完,现在看完也不晚,感觉正是时候。

网游开发其实最主要的问题感觉是难入门。市面上能找到的资料其实我也看了,但总有种盲人摸象之感,难以串起来。偶有几篇讲述“帧同步”与“状态同步”的文章,都已经是极好的了。这点上,总有点感觉只要是国外不用的技术,就别想指望在互联网上能学到了,至少是要花费大量精力。如此,就更令我敬佩那些具有分享精神的前辈们了,这些经验的分享,真的是无价之宝。

下面还是具体的转述一下书中的一小部分概念,至少可以系统性的归纳一下“网络游戏”的架构方案。

当前国内的说法

我在阅读这本书之前,综合之前看的一些分享,是把网络游戏的架构分成这么几种情况:

  • 状态同步
    • 服务器计算
    • 客户端计算
  •  帧同步
    • 客户端计算

先说帧同步,原理很简单,主要是基于一个确定性假设:初始状态一致,帧率一致、每帧输入一致的情况下,理论上可以保证一致。也就是说所谓帧“同步”,其实根本没有保证什么同步,只是“碰巧”罢了。用象棋举个例子好了,帧同步就是说,假设我们有两张棋盘,两个人下棋,但是你们俩都不许碰棋子,只能由第三个人(充当网络)来进行。来我们开始,只要遵守规则,那么初始的状态肯定是一致的。你先开始,想来一个当头炮,所以吼了一句——炮二平五,这时候,第三个人听到了,就分别给你们两摆上个当头炮。轮到对方了,对方也不甘示弱,也来了个——当头炮——开玩笑怎么可能,当然是要来个马来跳,于是他说:马8进7,第三个人也负责给摆上了,就这么下去,就是个“帧同步网络游戏”啦。

听上去挺简单,但是感觉有点悬么不是?对方万一瞎走直接将军那怎么办?第三个人你是要假设他可不会下棋的(网络部分只负责同步,没有逻辑判断),直接给你老车贴脸。再来,那万一那个人忙中出错,给你和对方棋盘上摆得不一样,那可糟了,后面就全乱了。现代游戏基本都很依赖物理引擎,受限于精度,这每次运算的结果可都不一样。虽然可能只是差了0.000000000000001,但是根据Chaos(混沌,好像有人会读作【敲死】,应该是读【K奥斯】,我也觉得有点奇怪)原理,或者你喜欢说“蝴蝶效应”,一点点的误差慢慢积累,都会导致结果的巨大不同。最后的一击打没打中,很可能直接导致了生死只差呀,一次团灭与否,很可能就是胜负之差,这可怎么玩呢。具体当然是有做法,主要就是随机数全部在开始约定种子,自己实现定点数库,消灭一切不确定因素,理论上,就可以保证一致的同步了。现实中,很多RTS都是使用这种方案,像魔兽争霸、星际争霸,当然还是大家喜闻乐见的——王者荣耀。

状态同步呢,实际上是要分两种的,当然这是我自己这么分的,大多数资料上面好像一般都只说了一种情况,我也是因为用了Photon以及UNET后,感觉他们的方案明显是状态同步,但是又明显和一般说的状态同步不太一样,所以还是分开表述。

状态同步嘛,同步的当然就是状态。我觉得最好的理解方式其实就是,其实就是无线手柄分屏本地多人嘛,想象一下,用四块屏幕,大家在房间的四个角玩。游戏主机其实就是服务器,手柄无线连接上去可以当做网络,显示器也假设是网络连接上游戏机的(或者直接理解为远程桌面)。好了,搞定。现实中的MMORPG也大多确实就是这样的原理,整个游戏实际上都是在服务器上跑的,只是不需要渲染,所以可以节省大量的资源。然后你的客户端其实就是个显示器,只是从服务器获取你需要看到的内容,并渲染出来给你看。你的一切操作,都是直接被转发到服务器作为输入的。当然这也就会有很大的延迟,所以一般客户端都会做一定的“预测”,预测对了皆大欢喜,如果不对,那还是会被强制和服务器的情况保持同步,也就是一般大家经常在网络不好的时候见到的“鬼跳”了。

另一种呢,其实说白了,就是有一名玩家作为服务器,其他人都同步他的结果。这种情况,其实可以简单的当做在一台电脑上同时运行了服务端和客户端两个软件,本机的客户端自己实际上还是使用和其他客户端一样的方式(比如Socket通信)与本地的服务端连接的。这么来看,实际上也就把这种情况和上面说的服务端计算类型划了等号了。又或者每个客户端都控制由自己创造的物体,比如自己的角色等等,自己控制的物体,就是自己控制状态并同步给其他人,这样来分担运算压力,以及保证每个人自己主观的流畅度(当然还有其他原因,就不在此赘述了)。

好了,这就是目前国内能找到的资料对于同步问题的表述。大体上这么分类我觉得是没有问题的,《网》这本书,则是更加完整得给了一个回答。

《网》中对同步问题的分类

书中首先先介绍了一下基本网络拓扑结构。而具体的架构分类,则从两个角度描述,物理架构和逻辑架构。

基本网络拓扑结构

首先基本的网络拓扑结构,计算机网络基础我就不赘述了,结果上来说,网络游戏中使用一般只有:

  • 星形(和作为应用的总线型)
  • 全网状

解释一下,因为总线型可以理解为不存在中央节点的星形,所以被包含在了星形的应用中。

物理架构

  • C/S架构
    • 纯服务器型
    • 反射型
  • P2P架构
    • 同步方式
    • 异步方式
  • C/S + P2P混合架构
  • ad-hoc模式

我现在了解的情况,似乎国内是有人用C/S架构代指状态同步,不准确,而且误导性极强。

一般的时候提到C/S架构(客户端 / 服务器架构),主要是对比B/S架构(浏览器/服务器架构),强调用户端是浏览器还是一个Native的客户端。而到了现代的网页前端,基本都接受了RESTful的API规范,实际上完全是把前端项目完全当做一个独立端了,与服务器也是通过Ajax传递数据的形式更新。所以实际上,我也已经很多年没听过这个词了。而在这里用到,是用来和P2P的客户端到客户端区分,强调的是“服务器”的有无。

纯服务器型和反射型的区别,其实也只是是否对数据包的合法性进行校验(反射型不校验)。至于为什么要专门强调乃至于可以据此分类,我目前还理解不能。

P2P,顾名思义,就是没有服务器,数据完全是客户端之间自己交换的。这里不禁怀念一下童年,红警、帝国时代、CS。忽然想起,大学刚报到的时候,大家还没办网,于是几个已经非法带电脑来了的同学(没错,我校规定大一不许带电脑)就把电脑搬到同一个寝室,建了个无线热点,痛快地打了个通宵CS。当然现在看来,这种“局域网游戏”是不能称作“网络游戏”的(各种对战平台实际上是自己做了一个大局域网,现在想想还真的挺有想法的),真正想通过P2P的方式做一款网络游戏,就得考虑蛋疼的NAT穿透问题了……暂且不论。

同步和异步方式的问题,暂且留到逻辑架构中描述(因为书中就是这个奇怪的顺序啊,所以也是我觉得有必要自己总结一下的原因……也确实是网络同步方案之间关系密切,很难有一个方法清晰的分类)。简单理解,P2P同步基本等同于帧同步,P2P异步,则可以理解为客户端计算的状态同步。

至于C/S + P2P混合架构,书中没有详细介绍,我印象里好像确实有这样用的,在有中心服务器保证可用性的前提下,在可行的情况下(可以NAT穿透,并且玩家物理网络连接良好),使用P2P直传数据降低服务器压力,提升玩家体验。

ad-hoc 我看书中的意思也没有很清楚,大概是说所有设备都是无线连接的移动设备。感觉上面我说的无线热点打CS就算,当然明显的例子应该是用3DS面连打怪物猎人。因为是通过热点直连。而因为某台设备掉线的概率很大,所以比较适合异步方式的P2P架构,都不是主机,大家随便掉(当然想连回来也相对不容易……)

 

逻辑架构

好了逻辑架构书中的分类就更迷了,竟然分为了MMO和MO架构……区别还真的就是是否Massively……按照规模分的……虽然我很能理解不同规模会直接导致方案的决定,但是规模本身不该是核心分类依据吧……

吐槽归吐槽,这章其实主要还是在论述

  • 星形结构
  • 全网状结构

  • 同步方式
  • 异步方式

的各种搭配情况。

前面有提到,同步方式就可以理解为帧同步,异步方式就可以理解为状态同步。但是不论怎么同步,总归大家还是要交换数据的吧,这个肯定跑不了。那么怎么交换呢?我们来想象大家上课穿小纸条吧。一般都是想传给谁,就直接说给谁对吧,也就是说,每两个人之间都是直接传递过去的,有一条“专线”,这就是全网状结构了。但是如果你想专递给隔壁班暗恋的可爱的蓝孩子(老脸一红),那就不好办了。结果你机智的发现,走廊上竟然有个值日生在打扫卫生(不要问我为什么他不用上课,我高中还真的每学期每个班有两天不上课全校打扫卫生的……),于是你机智的让他帮忙传递过去。也就是说,所有纸条,都必须先传递给这位勤劳的值日生,然后再由他传递给收件方。这就是星型结构。在这个问题上,那个值日生自己是否参与传纸条就不是核心问题了,也就是说,星型结构的中心节点可以是一台专门的服务器,也可以由其中一台客户端担任。甚至可以在当做中心节点的客户端网络条件突然下降的时候,自动由另外的客户端接任。

来想想这两种方式各有什么优劣吧,全网状结构,首先的问题就是线路过多,某条线搞不好就从老师眼皮底下过呢?在有15个人传纸条的情况下,就需要105条直连的线路了,如果说在10分钟不出问题的概率是90%的话,那么105条线路10分钟都不出问题的概率……已经基本为0了。所以参与人数增加,网状结构靠谱度指数级下降,不适合玩家过多的情况。而星型结构呢?倒没有这个问题,多一个人也就多一条线而已,这个时候,最大的风险就是,这一切传递都依赖那位勤劳的值日生啊,万一他傲娇一下不传了,那整个网络就崩坏了。

那么怎么选择呢?

全网状结构,只适合玩家人数很少,可以确保网络条件良好(如局域网内),而对延迟又非常敏感的情况使用。

星型结构的主要问题是响应慢(必须经过转发)、节点一旦中断,游戏就无法恢复、需要转发,逻辑上稍微麻烦一些、作为节点的玩家传输负荷比其他玩家高,不甚公平。

所以总的来说,主要影响因素是玩家规模,次要因素是对延迟的容忍程度。

至于同步方式与异步方式,或者说帧同步与状态同步,只要抓住一个重点就好理解了。同步方式同步的是输入,事实上不保证状态一致。异步方式同步的是状态,输入是客户端自己处理的。

好啦,方案基本都说完了,但是实际上具体还是有非常多的细节在里面的,十分推荐真的有兴趣的话还是仔细阅读一遍《网》,只需要一个周末的时间,真的是收获非常大。

RPC与共享内存

还有一点想特别提到的就是这个问题。之前一直知道网络游戏的通讯方式嘛,肯定是Socket了,至于TCP和UDP的问题,书上说有一小部分(1%)路由器不允许UDP通过,所以全部使用TCP。目前我了解的情况是国内主流还是推荐使用UDP的,快嘛。还有一些传说中的有人自己定的一些更厉害的协议,这个有兴趣的话还是可以研究一下的。

Socket实际上只是发送byte[]而已,具体使用的时候主流做法还是依靠RPC(远程过程调用)封装。我们把“进程之间”调用的函数接口(RPC接口)称作“协议”。

协议设计的原则主要有这几点:

  1. 后端实现基本的、通用的功能,前端实现专用功能
  2. 前端依赖后端架构
  3. 协议是无状态和简单操作的集合
  4. 在一个地方接受外部的异常状态

这我不一一解释了,有兴趣可以看书。

我觉得特别重要的一个思想是,函数接口应该明确的分为

  • 单向消息 (one-way message)
  • 查询 (query)
  • 查询结果 (query result)
  • 通知 (notification)

单向消息就是一个无返回的函数,例如使用鼠标移动角色的时候,调用:

需要返回值的接口,称作“查询”,比如打开物品栏时,需要获取物品列表:

inventory是个游戏术语,代表所持物品的列表,这个词真的有用,感谢作者,虽然你自己在书上还拼错了(虽然更有可能是译者的锅……)
此外,肯定有人发现了,明明是“需要返回值的接口”,为什么是void的?
这是因为RPC中所有的接口都应该是异步的,所以“需要返回值”的函数都明确的以get作为前缀,而且会有一个对应的查询结果接口:

最后一类,有可能需要把一些信息从服务端推送到客户端,这就是通知了。书上建议是否一定需要通知是需要仔细思考的,因为一旦需要通知,接口实际是写在客户端的,但是调用方是服务端,这样会极大增加debug难度,毕竟如果这里出现问题,因为实际调用这个函数的代码在网络的另一头,你是无法追踪下去的。
通知的例子,服务器向客户端通知敌人的行动:

接口命名的时候以notify作为前缀,就很清楚这是服务端发的通知了。

如果你问,那如果通知需要返回值怎么办呢?

好问题。这种纯RPC方式一般用于纯服务器型的C/S架构,也就是说,所有的资料其实都是服务器计算出来的,客户端只是一个“显示器”或者“浏览器”而已,当然不可能有什么事情是需要服务端向客户端请求的啦。

那么对于反射型的C/S架构呢?换句话说就是我所谓的“客户端计算的状态同步”,有这种实际上是数据双向共享需求的情况下,就很合适使用这种称为“共享内存”的方式了。

刚开始看到这个词的时候我还吓了一跳,我们在谈网络通讯的问题,跟共享内存有什么关系?结果仔细看了看,还确实……没关系……作者也明确表达了和POSIX的shm_函数完全没关系,只是为了和RPC比较。

根本区别是:在游戏逻辑中,游戏进度数据的保存处理(覆盖或者不覆盖数据)所发生的时间和地点不同。换言之,RPC传递的是变化的请求,而共享内存传递的是变化的结果。从表现上看,RPC是可以像调用本地函数一样调用远端程序的函数,共享内存则是允许某个变量在端与端之间永远保持同步,这么看还确实有点“共享内存”的意思。

看到这里,UNET使用中一直困扰我的ClientRpc Calls、Commands和SyncVars到底该如何使用的问题就迎刃而解了,原来UNET只是提供了所有需要的功能任君选择罢了,原来我一直很纠结实现一个需求的方式太多到底哪个好……再次感谢作者,并且感叹一下UNET库比我想象中更加优雅。

到目前为止的新想法

上面说到现在我实在觉得UNET不错了,昨天也仔细查了查 .NET Core的情况,也安装了SDK试了试,但是毕竟和Unity实现的标准还是不一致的,这么一想,其实继续使用Unity也是个不错的选择,实际上Unity自己做服务端是可以实现上述全部情况的,而且还都挺方便。只是感觉服务端代码和客户端代码肯定是要分两个项目了,不然岂不是打包的时候全部暴露了?但是如果分成两个项目,目前我还不清楚UNET是怎么处理跨项目的RPC调用的,同一个项目中是自动生成的列表并通过Attribute直接调用函数,这个可能还得思考并尝试一下。

此外,对于现在这个项目的架构也有了比较明确的认识,首先肯定不是P2P,而且需要服务器,所以物理架构肯定是算C/S架构,逻辑架构上,星形没得说,也不做同步,肯定是异步。之前因为觉得没有同步,但是有很多数据肯定是要存在服务端的,所以非常纠结这到底算什么。其实很简单,就是个不M不M但是O的RPG嘛,除了不用考虑玩家同步的问题,其他按照MMO的思路就好了。

今天来本来的计划是,因为昨天查了登陆以及社交等服务器Asset Store里面是有看上去很靠谱的包卖,所以想看一下,还有几个网络库也想看一下,毕竟如果不用UNET,还是需要实现RPC的,还是需要找找靠谱的库。结果这一写就花了5个小时(再次感谢乐于分享的大大们)……当然总结一下对自己还是很有帮助的XD

于是现在就去实现计划吧~

P.S. 《网》里面还是有很多对我启发很大的方面,以后有机会遇到实际问题肯定要再多聊聊的!

一个新的可能

昨天,或者说今天早些时候,写完之前那篇博客之后,没想到竟然通了个宵。

主要在做的还是完整的看完了云风的两个系列文章,开发笔记 和 那些日子

这种感觉确实很久没有了啊,想起大学的时候,在期末考试前期,花了4天看完了Matrix67的所有博客。

终于,我也是到了可以左45°角望天长叹:“唉,大学的时候啊……”的年纪了。

看完到大概7点,囫囵睡了一觉,中午醒来去对面吃了个豆浆油条,哎,油条还是好吃。

回来就正式开始看网络编程部分。

系统性的方法还是很久以前就感觉如果还是要自己搞定网络部分,不得不看的《网络游戏核心技术与实战》,确实很少有这种针对游戏的网络编程书,日本人写的,粗看还是靠谱的。

看一看就深感自己还是实在欠缺,套接字通讯基本只有计算机网络课上学的一点概念而已。书上的代码是类似C的伪代码,于是我边看边用Python实现,接口当然是比较一致,但是具体的问题还是令人非常困扰,把底层完全搞懂看来还是需要花一大把精力和时间——正是我现在缺的。不得已还是用老办法,先搞清楚概念,然后硬怼一个上层方案来。

本来还是想先用Python的,原因前面也说了,但是突然灵光一闪,我其实还有另一个可行的选择。

.Net Core 2.0

前后端统一,跨平台,完美的语言,完美的IDE。

除了略微担心不好招人,主要还是这坑实在是太新了,新的技术一般都是理论上美好,实际上……要命的是这还是微软的新技术……又不是第一次坑了。(但是万一出问题还有后备方案可以用Mono应急……)

所以,继续验证方案。

新的开始

写于2017年的双十一前夜

翻翻上一篇日志,已经过去两年了。期间虽然发生了太多事情——以至于差点就要动笔记下来——却又因为种种原因从未写下一行。

还是沿袭旧例,先说说服务器的事情吧

服务器

这么算来,Vultr也是用了2年多了,这么看来还真没想到竟然有这么久,总的来说真的挺满意的。第一次用Vultr的时候发现需要给主机设置一个Tag,于是很自然的想到按照字母表的顺序命名(Android代号的影响嘛)

他们是……

  • Apple(显而易见……)
  • Birthday(生日当天建立的,其实根本就是生日当天才开始用的Vultr,所以好像是为了比较什么,所以直接建立了两台,上面那台当天就Destroy了……)
  • Crash(Birthday在一次机房断电后……就再也启动不了了,所以才有的这台)
  • Delete(忘记了……)
  • Expensive (Vultr出了一个2.5刀的套餐!于是换了……)
  • fqSou(嗯……IP被墙,所以换了一台,并且也停止了fqSou的服务,为了纪念……)

这些名字非常深刻的暴露了我的小学水平英语。

(写到这本来是准备说心累了不想再自己买VPS,结果说干就干去阿里云买了个空间迁移备案花了一小时……)

起因

写了这些,总结一下自己这么久都没写一行,主要还是思维跳跃太快了,每次想到什么,等临了动笔,早就跳跃到不知哪里去了。提笔总恨不得能开10个8个线程,一口气把几条思路全部铺陈纸上。

(说着,我又去查了一圈托管博客想看看有没有合适的,结果就已经第二天了,然后就想到唉双十一我得去看看,于是又停笔了)

说到为什么这次想起来回来写了,是这样。ICEY 之后,新的项目最终还是不得不下定决心技术方面也还是自己负责。有些事吧,想法很美好,但是终究证明了自己搞不定的事情是不可能靠招人搞定的,于是,最近开始有意识的考虑一些技术问题。

首要的问题很明显就是服务器方面了。项目管理和前端技术方面,情况大体上已经可以说很明确了,有一些还不能确定的部分,按照习惯也是在做的时候就可以思考解决了的,大概的计划也基本确定了。但是服务器这个问题毕竟还是没有实际做过,想来还是尽量多了解一些当前主流的方案比较安心。这个问题就麻烦在这里了。具体时间个什么问题,方案是肯定能想出个几种的,但是实际上哪种比较好,是肯定不知道的。这样一来,很容易就陷入一个总觉得自己想得不够完善的境地,导致步履蹒跚。

一直以来最头疼的经验缺乏就是框架和架构了,这方面的经验分享实在是少之又少,ICEY结束之后痛定思痛趁着新开项目Demo,花了2个月时间整理了一下前端的框架,大体上还是满意的,但是明显还没有完全完成。这次自己又能重新主刀,也有完成它的残念在的。这服务器可就完了,首先资料找不到,能找到的开源服务器一般也都是MMO的。然后语言也要了命,看上去是啥都能写,那总得挑一种吧,好了选择恐惧症患者表示很要命。又因为改做PvE,现在直观来说,到底哪些逻辑需要服务器我都实在是无解了。不考虑安全因素的话,这不明摆着根本就是个单机游戏嘛。完全没有经验的情况下,不看到实际的需求真的是没有办法设计,放着完全不管也静不下新来,那干脆还是查查资料吧,心想总归还是有一些的吧。

还真找到了,偶然看到一个链接云风的开发笔记,之前一直听说云风大神,也关注了他的博客,但是没想到这么早以前他还记录过这么一段完整的开发经过,这对我来说实在太重要了,再一看,哎,“简悦”,原来云风大神还是简悦创始人之一,下周正好要去拜访,不知道有没有机会可以有幸认识一下,也真是巧了。

看了一部分笔记,感觉,这种方式真的很好。当做项目日志的补充甚至替代,过程的记录实在还是很有必要。

于是这就是起因了。

计划

这次是下定决心一定要把这个项目做出来了,明确的说这是决定了生死存亡的。所以我提出了希望封闭开发,排开外界一切干扰,全身心投入到项目中来。目前团队还在进行里程碑1的开发,从各种角度来说都还是有必要完成的。产品部门也是在思考和整理新版本的策划案,需要时间。我和肖哥这边主要是20号的广东之行,作为最后的一次对外交流(FGF就只能当做突发事件了)。所以正式开始预期也就是12月的第一周。下周末会进行一次团建,也是在紧张之前,能有一个放松的过程。下下周,也就是从广东回来的那周安排了一次全体的体检,也是拖了挺久的事情。基本上来说,能想到的事情,应该都在12月之前解决了。我这边,当然是要趁着还有几周难得的时间,抓紧抱抱佛脚,好好把能想到的问题尽量想想清楚了。

项目上来说,我预期需要负责的部分,还是分为项目管理与技术负责。

项目管理方面因为有了上次的实践经验,总不是那么慌了。而且这次自己也是实际参与,随时有什么问题可以及时纠正。而为了防止自己可能在深入技术的时候可能会忽略这些,这次还想尝试带争争试试项目助理的工作。这对我们两都是一次实习经历,希望可以合作愉快。

技术方面就是前端和服务器啦,前端如上面所说,首先肯定是重新整理整理自己的框架,根据具体需求来就是了,可以有机会完善它,确实还是挺开心的,很期待。服务器部分还没什么特别明确的计划,还是准备先趁这段时间多看资料,应该在这期间是会慢慢明确方案的。语言的话,就目前的情况来看,可能Python还是一个总的来说不错的选择。不然的话,Node(选Typescript还是ES6就能纠结死我)、Lua、Go、Erlang的优势都不明显(主要是我自己都不那么熟啊)如果一定是要我在语言层面还要深入一下的,讲道理我宁愿选择C++,Java好歹还有“我很熟”和“好招人”的重大优势23333。所以前期阶段,在“快速开发”“我比较熟”“好招人”“未来好换”各方面比较平衡的,还真的就是想到Python了,至少值得一试。


不知道这次是不是能一直地写下去呢:P

程序员三大浪漫之一

我写博客吧,好像就正经不起来。总会夹杂点杂七杂八的东西。

先说正题吧。

所谓程序员三大浪漫嘛,编译原理,图形学,操作系统(不是我说的,逃

这次说的是图形学。

我好想有个坏毛病,发现个什么好玩的,自己其实根本不懂就喜欢拽着人乱说。

最过分的一次是表彰大会上大礼堂给全学院人讲信息安全(当时到底怎么想的简直不要脸

所以还是继续不要脸一次。

如果现在有人问我,怎样开始喜欢上编程,写点什么项目可以获得最大的成就感。答案肯定是写光线追踪。

唉,自己过去对图像以及显卡的了解真是太 Naive 了……简直等于什么都没有啊。

先上文

用 JavaScript 玩转计算机图形学(一)光线追踪入门

光线追踪的js实现,总共500行代码不到,循序渐进。

最神奇的地方在于,超简单的原理带来了超乎想象的效果。

如果有一点 3D 软件的使用经验和游戏开发经验的话,原理非常直白(当然还要懂点数学和物理,我这种渣水平就够)

但是效果实在是,出人意料。光看图感觉不到,自己敲完代码跑出来,太神奇了(或者是我太无知了 = =)

上图,为了帮助理解,自己改了 3x SSAA ,抗锯齿效果杠杠。

(下图分别是 1x,2x,3x, 反射都是3次追踪。为什么没有 4x ?因为 4x 跑了5分钟然后浏览器跪了……)

附上改的渣代码,确实就是 super-simpling 而已。

接下来是希望实现正交相机,融合光线,用其他语言(比如C井)实现玩玩这样……

期待什么年代游戏可以 real-time ray tracing ,那会是怎样一番光景啊……(看看现在 tone tracing 的效果都已经如此惊艳了)

说到游戏,不得不提下一个话题。

Ori And The Blind Forest

这个游戏跟 ray tracing 没什么关系,这是一个2D游戏。

与其说游戏,不如说是艺术。第一眼看上去以为是个设计作品,类似可交互动画这样。

结果分分钟打脸,手残党被虐哭……

分分钟成为自己最喜欢的游戏。

和我一起呐喊,软人希!

(Steam平台有售,强烈推荐体验。这个主题只说了这么几句不是没话说,恰是因为实在有太多话说了)

这就是本周的Sean Talk,我们下周见!(设么鬼……什么时候有这个系列了,不要说得好像要开每周专栏了一样好吧><)

完了,书单又多了一本,PBRT……

再写这些你让我怎么好意思再说这是个技术博客啊

说来也奇怪,听听歌就像写日志了。

下午在K最后点了 君の知らない物語

#Region 我是强行插入的一段
君の知らない物語这个词好难打……物語 这个词用微软日语输入法一直打不出来……怒换,本来准备换搜狗但是发现……好像根本没这个东西,只好换了百度,结果还是打不出来……最后才发现是自己把读音一直弄错了,物是もの(mono) 語是がたり(gatari)= = (微软日语输入法:怪我咯)
#EndRegion

到底还是没放,回来在随便看看(比如达音科出了DN2000J 口水|||* *|||)好久没听歌了,就想放一下君の知らない物語(已经可以熟练的打出来了= =)结果发现有MV就好奇的点进去了,本来以为应该是动画ED,结果竟然是真人……还是剧情……还是狗血……真是吓到了。太毁了!星空约会是多么美好的桥段,就这么毁了你让我以后怎么直视,哦不,直听这首啊啊啊!!!

附带最新花式虐狗大赛冠军问题

今天本来想去看速度与激情7啊,结果约了一天都没人约 – – 不想自己去(傲娇脸)所以就不去啦。自己看! 开了三个页面:十冷、霍比特人3:五军之战、星际穿越。下了星际穿越。现在是时候看看了……我为什么一直有等一个东西火过去了再看的习惯呢……真奇怪。

PS4挺好玩的……虽然坑啊坑,每天高呼索尼大法好然后用姨夫当桌面应该保佑一下没有Bug吧……

哦,代码方面啊…… 这方面的内容我总觉得自己看看就行了一直懒得写啊……

要说么,最近就是被函数式各种虐成狗(咦,从各方面说不本来就是狗么)从Clojure到Haskell(搜狗竟然有这个词……)各种被虐,感觉各门函数式之间的关联部分都很多,比如来自于Prolog的Erlang,你说有各种模式匹配列表推断生成也就算了吧,为什么Haskell里面也是各种列表推断生成,Currying还算好理解,但是真写起代码来,真是一片凌乱,好无力啊……我还是去看图形学吧……

唔……十点了……感觉电影看不了了

家里终于重新有网了T T

Optimized by WPJAM Basic