游戏大世界场景(胡诌篇)
科比:“你知道凌晨4点的洛杉矶是什么样子吗?”
IT男:”哦,那个点我还在加班“
科比:“。。。哦,那没事了“
这个段子反应了国内程序员们说不出的苦逼,我也正是因为昨天在公司看到了凌晨4点的景象,所以今天才能在家腾出时间来整理些平时没空整理的东西,也算是在不清醒的状态下胡诌一下吧
早晨像狗一样爬起来后,漫无目的地浏览着硬盘,在抵挡住一个个神秘文件夹的诱惑之后,居然翻出了几年前珍藏的wow的视频,一打开就是看了一个多小时,看完之后不免世俗地感叹了一番时光流逝之快,以前颓废于wow的景象历历在目,那真是一段特殊的经历,虽然沉溺,但回想起来却没有一丝罪恶,也不后悔,反而更多的是温馨和怀念,也许真的是工作时间久了,反而对虚拟世界有了眷恋。自己也曾思考为什么会对wow如此有情节,抛开游戏产品本身的优劣,也许是因为当时国内的游戏环境还没有太多的利益冲突,史玉柱的免费网游模式还未铺天盖地地席卷中国大陆,在相对公平的时间消费的基础上,大家都是在互帮互助,一起体验游戏,那真是游戏人生中的一段高光了,少了真实生活中的尔虞我诈,略显不真实,但却快乐,可惜今后可能永远不会体验到了
========================== 扯淡分割线 =============================
标题提醒着我不能再扯淡下去,要不然观众就会埋怨还不进入正题。关于游戏大世界场景这个话题,我一直在思考,不同的公司,不同的项目,我都在思考倘若要做一个如wow般的大世界应该如何着手,这也是我上面谈到wow的理由,从技术上来讲,wow也带给中国市场太多新技术了 – 真正的大世界感觉的场景,云风曾经提到过的相位技术,以及lua的普及
我个人目前的经验来说,做一个大世界场景,有这么几个难点需要克服:
1)附近实体的搜索
2)AI地图寻路
3)不同地图上计算实体的负载
难点1:
对于一般的2D平面游戏,可以将地图按某一定大小的块进行划分(partition),寻找附近实体的附近转变为寻找附近单元,这里可以用到的数据结构可以是四叉树(quad-tree),如果是3D场景,那么将是一颗八叉树(octree),在寻找到附近单元后,再在各个单元中找需要的实体(例如按照一定的半径距离)
难点2:
一些早期的2D平面游戏,将一张地图划分为一个个正方形的方格,类似此类地图,直接利用A*算法运用到其上即可得到路径。
而一些地图单元粒度略细,坐标范围略广的场景(例如许多MMO RPG),就不能如上做法了,因为如果作为一台游戏服务器,大量大范围的寻路请求,会占用掉整个CPU资源,而导致整个服务的对外能力呈现为0。我们的解决方法是采用waypoint,在地图上设立关键点,每次运算A*之前,需要find anchor,来作为A*算法的起始。
对于3D场景waypoint也将成为瓶颈,因为3D场景地形会比2D地形复杂的多,且坐标系也多了一维,意味着范围更大,waypoint还有一个弊端是必须人工设置,且寻路所直接得到的路径非常曲折,需要通过一定的平滑处理,3D场景一般采用NavigationMesh技术来解决这个问题,关于NavigationMesh的详细介绍我也不多作描述,只是可以简单地说:
它可以利用一种自动生成的算法,来自动将一张地图划分为一个个的凸多边形
在凸多边形的基础上,寻路变得简单,首先凸多边形内的两个任意点之间,都可以无障碍通行,有了这个基础,我们的寻路可以先从凸多边形为单位开始快速计算,得到凸多边形节点的链接之后,再计算凸多边形内部的路径。
PS.假设你是第一次听到NavigationMesh技术,有可能会对我以上所讲无法理解,那么请自行google,值得一提的是,假设有动态障碍的介入(例如一块滚动的石头),那么就要进行实时的动态多边形切割,而关于A*算法,已经普遍被认为是最适合的游戏地图寻路算法了,其优化方案也有很多,也有许多尽量让服务器尽快响应客户端的方法(例如经典的生成三步骤:快捷路径-完整路径-矫正路径),这些大家网上皆可查阅。
难点3:
一般的游戏服务器的主逻辑服务,都会按照游戏的功能来划分,例如按照不同的地图,当数据量达到一定的规模,这些服务器会成为瓶颈,且难以扩展。
一种理论上可行的方案是:将某一张地图的某一块划分为一个抽象单位(例如上面提到的多边形),所有逻辑服务器在运行时动态负载地图上的这些单位。
例如以下场景:
Host A – Map A
Host B – Map B
Host A 上起先跑着Map A
Host B 上起先跑着Map B
但Map A上负载的实例(玩家)可能过多,导致Host A的负载到达瓶颈,而Map B上的负载不大,那么此时可以进行动态调整
调整过程为:对Map A进行一些凸多边形合并,合并之后仍是一个凸多边形(Map Aa),将这个凸多边形(Map Aa)的所有信息和上面的实力信息,同步到Host B,然后Host B将其进行本地合并 Map B + Map Aa = Map(B + Aa),然后两边的Host都需要对各自的Map进行重新切割,以保证NavigationMesh结构
这一过程需要一定的性能代价,不过是网络上(数据同步),还是CPU计算上(重新切割合并),所以何时需要动态调整,需要仔细斟酌,需要特别说明的是,判定动态调整的进程是一个独立的观察者,它需要相对公平的准则来监督每一个game/map server
另一个问题是地图边界的处理,在地图边界上玩家之间的交互(交易,战斗,等),需要将其中一个玩家的数据同步到另一个玩家所在的host进行处理,然后同步回去,这个同步过程可能会造成延时的提升,当然客户端应该尽量进行仿真效果的处理,而不应同步等待服务响应。
对于大世界场景的游戏,或许现在已经不是主流的游戏类型,但却一直是技术上的挑战,也许未来,我个人兴许会着手开发一个这样的demo,来完成多年的梦想。
好了,就先胡诌到这吧,虽然按照标题的节奏,可能会有续篇,但本人也不排除只有这么一篇的可能,毕竟只是今天一时兴起的胡诌。。。
另外欢迎大家到我的个人博客一起讨论:www.cppthinker.com