游戏里数据和表现的分离

  大家好,我是阿赵
  之前遇到有个朋友做游戏,遇到一个这样的问题:在一个即使战斗的游戏里面,客户端的表现经常会出现一些问题,比如血量不同步、角色位置不同步、瞬移、角色表现状态不对等。但游戏服务端非常肯定,所有的状态数据都是发送成功的。
  于是我也分析了一下问题,给大家分享一下。

一、观察问题

  我看了一下这个游戏的做法,发现是这样的,为了简化问题,只列举了比较重要的一些内容:

1、服务端

服务端发送给客户端的数据有以下这些:
1.创建角色
发送了角色的id和外观,还有创建的坐标
2.角色的初始数据,包括血量、魔法、buff
3.角色的移动
发送角色id,开始移动的时间、开始移动的方向、速度
4.角色停止移动
发送角色id,停止移动的时间和坐标
5.角色属性变化,包括血量等
发送角色id、改变的属性、变化的值
6.角色释放技能
施法角色id、受击角色id、技能id

2、客户端

  客户端在收到服务器的数据之后,开始创建角色、加载模型,然后在模型身上挂了个脚本,把服务器下发的数据存到这个脚本里面,然后角色身上挂刚体。然后保存这个角色的GameObject到列表。
  当角色移动的时候,获取角色模型的GameObject,获取刚体,然后给刚体力来移动。
  当角色属性发生改变时,获取GameObject上的脚本,修改属性。
  当角色施法的时候,找到受击角色的GameObject,然后把施法角色的GameObject朝向受击者,然后播放动作。

  看完做法之后,问题已经很明显了。看来这位游戏客户端程序员,应该是看着Unity的官方demo来学习怎样做游戏的。
  于是我问他,假如角色模型没有加载出来的时候,他已经开始移动,怎么办呢?假如角色还没加载出来的时候,各种属性已经改变了,怎么办呢?假如某个被攻击的角色的模型没有加载出来,它还会不会飘血呢?如果技能的受击角色模型没加载出来,那么施法的角色应该朝向哪里呢?
  这位朋友挺迷茫的说,模型都没加载显示,这些数据发生改变的时候,肯定不能正确处理到。

二、分析问题

  有经验的朋友们,应该已经看出这个问题出现的原因了。
  问题很简单,因为客户端把数据的载体,放在了用于表现的模型对象身上。
这也是我一直觉得很多官方教程比较容易误导人的原因。很多官方的Demo,只是为了展示某个引擎的具体功能而做,所以都是偏向于表现的,挂一个脚本在模型对象身上,是为了便于控制表现。
  但这种思路只适合用来做单机小游戏。当我们需要做前后端交互的网络游戏时,我认为数据应该是一种单独存在的层面,不应该和具体表现挂钩的。

三、解决问题

  知道了问题之后,其实要解决就非常的简单了。
  我们先不去考虑表现的问题,先处理好数据

1、建立数据层

  建立一个数据层,存放服务器下发的角色的各种数据
  通过角色id作为索引,可以把角色的数据简单分为几种类型。像上面的例子,可以分为
1.Transform数据,包括角色的位移旋转缩放
2.行为状态数据,包括现在角色是站立、移动中、技能中、受击中、死亡,等等
3.数据状态,比如血量、魔法之类
4.buff列表
  有了这个数据层,我们随时可以知道角色在什么坐标,应该做些什么表现

2、更新角色数据

  当服务器下发新数据,只考虑更新数据层的数据。只保证当前如果我们去数据层拿数据,是角色最正确的当前状态,就可以了。

3、模拟数据

  有些数据是需要模拟的,比如移动。
  移动也分很多种,比如知道开始时间、开始坐标、结束时间、结束坐标的,或者是知道开始时间、开始坐标、移动速度、移动方向的。
  我们不需要真的开一个Update然后随时的更新坐标数据,我们可以用时间插值的公式去计算,计算方式大概是这样的,先获取当前的时间点,然后和开始时间作对比,算出已经移动了多久,然后用简单的距离=速度X时间,就可以算出当前的时间点角色应该在的坐标了。
  到这里,数据层已经处理好了。原则上来说,就算我们一个模型都不加载到场景里面,游戏都可以正常的跑起来了。
  剩下的事情就很简单了。在角色模型加载完成之后,根据自己的角色id,去数据层取当前的角色数据,然后更新自己的表现。然后当服务器下发数据改变时,数据层抛事件告诉哪个角色需要更新表现。然后对应的角色监听了数据改变事件,自己更新一下自己的表现就行了。

四、总结

  上面这个简单的例子,明显是我自己编出来的,但现实中还真的会有很多人会遇到这样的问题。
  数据和表现分离,我认为是一个很必要的思想,表现层的东西它只负责表现,而不应该对实际的游戏逻辑产生影响。
  当然,由于表现层是玩家唯一看到的东西,如果表现层出错了,玩家肯定也是会认为是游戏异常的。但是,如果我们可以做到数据层脱离表现层单独的运行,那么如果出了表现上的Bug,我们可以集中精力的去解决表现问题。而且由于表现层本身之间没有关联性,比如不会因为一个角色模型加载出错了,导致影响到别的角色不能放技能,也不会导致该飘扣血的地方不飘了,那么表现上的错误也能减少很多。

猜你喜欢

转载自blog.csdn.net/liweizhao/article/details/131545471