Unity 分批加载玩家形象

需求背景

游戏房间上限最大可以有count个人,当用户进入房间后,需要去远程服务器获取这count个人物的服装配置, 获取成功以后进行逐个加载。
目前的方案是一次性获取所有人物的配置,然后以一种几乎同时加载的方式去做。
这种方案最坏的情况下会同时有count个换装的任务在运行,导致count个任务之间抢占CPU,结果就是看起来卡顿。
为了使游戏的表现更加流畅,需要设计一种方案进行有优先级的批量加载。

预期效果

  • 无论何时,用户自己的形象配置获取成功以后,立即优先加载。
  • 其他用户形象返回后进入队列,进行分批加载。
  • 其他用户优先加载可视区域内部的形象。
  • 如果都处于可视区域内部,那么优先加载距离摄像机近的形象。
  • 拥有动态调整能力,初始可以同时加载n个形象,如果检测到平均加载时间过长,动态降低并发任务上限,最低不少于m,如果平均时间在可接受范围,尝试增加并发数。

实现方案

用户自身的形象加载不受优先级控制,获取后立即加载。

优先级队列

分析完需求和预期效果之后,我们可以很容易地想到优先级队列这个数据结构。
优先级队列虽然是叫做队列,但是实现起来可以使用各种数据结构,我们可以采用堆,栈,队列。
对于我们的场景来说,我们高频使用的操作除了push和pop之外,最常用的应该就是一次性取出前n个,从这角度考虑,我们应该采用队列的方式去实现。
正好C#虽然没有默认实现的优先级队列,但是有个SortedList可以使用。

优先级算法

上面算法最重要的一点就是计算优先级。
基于我们的几点考虑:

  • 可视区域内部优先级一定高于不可视区域。
  • 人物之间的距离不会超过1000.
    我们可以使用int类型作为Priority,计算人物和摄像机之间的距离,然后用1000减去该距离作为初始优先级,接下来判断如果是处于可是区域内部,优先级再加上1000,否则加上0;
    计算两点之间的距离很简单,那么问题就是如何判断一个物体是否处于可视区域内。

如何判断是否处于可视区域

https://blog.csdn.net/cyf649669121/article/details/110580986
由于我们就是要判断先渲染那个形象,所以只能用坐标判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public bool IsVisableInCamera
{
get
{
Camera mCamera = Camera.main;
Vector3 pos = transform.position;
//转化为视角坐标
Vector3 viewPos = mCamera.WorldToViewportPoint(pos);
// z<0代表在相机背后
if (viewPos.z < 0) return false;
//太远了!看不到了!
if (viewPos.z > mCamera.farClipPlane)
return false;
// x,y取值在 0~1之外时代表在视角范围外;
if (viewPos.x < 0 || viewPos.y < 0 || viewPos.x > 1 || viewPos.y > 1) return false;
return true;
}
}