开头很简单,最难的是坚持。|

陈侠云

园龄:2年10个月粉丝:1关注:1

游戏中的AOI

在游戏开发中,AOI通常指Area of Interest,即“兴趣区域”或“关注区域”。它是网络游戏(特别是大型多人在线游戏,MMORPG)中一个关键的优化概念,用于管理玩家和游戏对象之间的交互范围。


AOI的核心含义

AOI 是一种通过限制玩家感知范围来优化游戏性能的技术。

  • 玩家感知范围:玩家只能看到或与一定范围内的对象互动,超出这个范围的对象不会被同步到客户端。
  • 兴趣区域:为每个玩家或游戏对象定义的范围,用于决定哪些其他对象需要同步。

AOI 的主要作用

  1. 减少网络流量
    • 玩家不需要接收所有其他玩家或 NPC 的状态更新,而只需接收在其 AOI 范围内的对象。
    • 减少服务器与客户端之间的数据传输。
  2. 优化服务器性能
    • 服务器只需处理每个玩家或对象在其兴趣区域内的逻辑,大大降低计算复杂度。
  3. 提升客户端表现
    • 客户端只需要渲染和处理当前兴趣区域内的对象,减少性能压力。

AOI的实现方式

AOI通常通过以下方式实现:

  1. 基于圆形或矩形范围的 AOI
    • 定义一个固定半径的圆或矩形,范围内的对象是当前兴趣对象。
    • 优点:实现简单,适合中小型地图。
    • 缺点:范围固定,在密集区域可能导致负载过高。
  2. 基于网格划分的AOI
    • 将地图划分为多个网格,玩家或对象只关注自己所在网格及周围网格的内容。
    • 优点:适合大型地图,且可以动态调整网格大小。
    • 缺点:复杂度较高,边界处对象的处理需要特别注意。
  3. 基于兴趣列表的AOI
    • 每个玩家维护一个兴趣列表,动态更新列表中关注的对象。
    • 优点:可以更细粒度地控制感知范围。
    • 缺点:需要额外的管理逻辑。

常用案例:

在Unity或者其他引擎中,可以通过以下方式实现AOI:

  • Physics Overlap Checks:使用物理引擎的重叠检测(如 Physics.OverlapSphere)来获取范围内的对象。
  • Spatial Partitioning:利用空间分区技术(如四叉树、八叉树)高效查询范围内的对象。

具体案例:

为了更细致的了解AOI,我这边用Unity引擎写一个简单的客户端demo作为例子展示。
这个例子是基于球形范围内的AOI,将不渲染超出范围外的模型,减轻客户端渲染压力。这里采用Unity自带的CullingGroup技术,不了解CullingGroup的同学可以点击文末的链接进行学习,这里不作详细扩展。
简单来说,会以主角(黄色立方体)为圆心,观察在“5米”范围内或者在摄像机视野范围内,是否有NPC(红色立方体),有就渲染,超出范围就不会渲染。
image
效果如图所示,在NPC超出主角5米范围后,将隐藏模型显示,详细代码放在下面,有兴趣的一同学习。

CullingControl

负责裁剪事件派发,当NPC超出主角“5米”范围后或者超出摄像机视野范围外,会进行通知。

using System.Collections.Generic;
using UnityEngine;

namespace CullingGroup.Scripts
{
    public class CullingControl : MonoBehaviour
    {
        private static CullingControl _instance;

        public static CullingControl Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = FindObjectOfType(typeof(CullingControl), true) as CullingControl;
                    _instance.Init();
                    DontDestroyOnLoad(_instance);
                }
                return _instance;
            }
        }

        private static int MAX_SIZE = 1024;
        private static float[] CULLING_DISTANCE = new float[] {1f, 5f};
        private static Vector3 NO_RENDER_DIS = Vector3.one * Mathf.Infinity;   // 假如移除该角色,对应bound的position设置为超远距离,避免渲染
        private BoundingSphere[] bounds = new BoundingSphere[MAX_SIZE];
        private UnityEngine.CullingGroup cullingGroup;
        private Dictionary<int, IPlayer> index2Players = new Dictionary<int, IPlayer>();
        private Dictionary<IPlayer, int> player2Index = new Dictionary<IPlayer, int>();
        
        private void Init()
        {
            cullingGroup = new UnityEngine.CullingGroup();
            cullingGroup.targetCamera = Camera.main;
            cullingGroup.SetBoundingDistances(CULLING_DISTANCE);
            cullingGroup.SetBoundingSpheres(bounds);
            cullingGroup.SetBoundingSphereCount(index2Players.Count);
            cullingGroup.onStateChanged = OnStateChanged;
        }

        private void OnDestroy()
        {
            cullingGroup.Dispose();
            cullingGroup = null;
        }

        private void OnStateChanged(CullingGroupEvent e)
        {
            int index = e.index;
            if (index2Players.TryGetValue(index, out var player))
            {
                player.ChangeModel(e);
            }
        }

        public void SetReferencePoint(IPlayer player)
        {
            if (player != null && player.isMain)
            {
                cullingGroup.SetDistanceReferencePoint(player.transform);
            }
            else
            {
                cullingGroup.SetDistanceReferencePoint(null);
            }
        }

        public void Add(IPlayer player)
        {
            int newIndex = -1;

            for (int i = 0; i < MAX_SIZE; i++)
            {
                if (!index2Players.ContainsKey(i))
                {
                    newIndex = i;
                    break;
                }
            }

            if (newIndex == -1)
            {
                Debug.LogError("[CullingControl] 超出同屏最大显示模型数量");
                return;
            }
            
            index2Players.Add(newIndex, player);
            player2Index.Add(player, newIndex);
            bounds[newIndex].radius = player.radius;
            bounds[newIndex].position = player.pos;
            cullingGroup.SetBoundingSphereCount(index2Players.Count);
        }

        public void Remove(IPlayer player)
        {
            if (player2Index.ContainsKey(player))
            {
                int index = player2Index[player];
                
                player2Index.Remove(player);
                index2Players.Remove(index);
                bounds[index].position = NO_RENDER_DIS;
            }
        }

        private void LateUpdate()
        {
            foreach (var kvp in index2Players)
            {
                bounds[kvp.Key].position = kvp.Value.transform.position;
            }
        }
    }   
}

Player

using UnityEngine;

namespace CullingGroup.Scripts
{
    public interface IPlayer
    {
        Transform transform { get; }
        Vector3 pos { get; }
        float radius { get; }
        bool isMain { get; }
        void ChangeModel(CullingGroupEvent e);
    }
}
using UnityEngine;

namespace CullingGroup.Scripts
{
    public class Player : MonoBehaviour, IPlayer
    {
        public bool isMain => mainPlayer;
        public float radius => playerRadius;
        public Vector3 pos => transform.position;

        public bool mainPlayer;
        public float playerRadius = 0.5f;
        public GameObject model;

        private void OnEnable()
        {
            CullingControl.Instance.Add(this);
            CullingControl.Instance.SetReferencePoint(this);
            model = transform.Find("Model").gameObject;
        }

        private void OnDisable()
        {
            CullingControl.Instance.Remove(this);
        }

        public void ChangeModel(CullingGroupEvent e)
        {
            model.SetActive(e.isVisible);
        }
    }
}

CullingGroup API

本文作者:陈侠云

本文链接:https://www.cnblogs.com/chenxiayun/p/18656273

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   陈侠云  阅读(40)  评论(0编辑  收藏  举报
//雪花飘落效果
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起