【塔防】单位体重叠判定和查找效率问题(一)

本文介绍了在开发一款塔防游戏时遇到的单位体重叠判定和查找效率问题。通过使用四叉树实现区域划分,但在特定情况下可能导致较高的查找次数。作者指出,四叉树对于大量单位的查找可能更为有效,但存在查找顺序不合理的问题。提出了以单位为中心,基于格子坐标系统的查找方式可能更为高效,并讨论了不同方法在少量单位和频繁操作场景下的适用性。
摘要由CSDN通过智能技术生成

最近在做一个塔防类小游戏,游戏世界设置为40*80的方格,每个单位占多个格子,单位所占区域相互不能重叠(Rect与Rect的重叠计算),考虑到世界是以2d为基础,所以最初想到的是“四叉树”,深度为3,则可以把世界分为4^3=64个叶子节点,将每个单位递归保存至与之重叠的叶子节点,以此实现了区域划分。在查找某个Rect是否有与之重叠的单位时,从Root节点依次向下深度查找。下图列举出某次查找的过程。在这里插入图片描述
该图省略了Root节点的比较,直接从第二深度开始计算。第一次比较,取全图的1/4的左上角块与红色框进行重叠判定,发现有重叠,则进行第二步,第二步发现与红色无重叠,所以进行第三步…第五步时,与红色框发生了重叠,所以要进行下一个深度的查询,由于第三深度为叶子节点,且经过比较,叶子节点上所有的数据都不符合重叠判定,所以进行到第十步,开始右上1/4的节点树比较。按前面描述的过程发现第十五步时与红框且与蓝色区域发生了重叠,所以至此找到了重叠结果。
如上图所示情况,地图中只有一个数据,但是发生了15次查找,才最终查找到结果,如果使用顺序存储则只需要一次判定就可以拿到结果,当然四叉树本身就是用于大量单位的查找才能发挥其有效的作用。
以下是四叉树的代码:

using System.Collections.Generic;
using Pathfinding.Util;
using UnityEngine;

namespace Model
{
   
    public class QuadTree
    {
   
        private Node root;
        private int maxDepth = 3;

        private Dictionary<IQuadTreeUnit, Node[]> _unitPos = new Dictionary<IQuadTreeUnit,Node[]>();
        
        public QuadTree(Rect rect)
        {
   
            root = new Node(1, rect, null, this);
        }

        public void Update(IQuadTreeUnit unit)
        {
   
            var list = ListPool<Node>.Claim();
            var r = unit.GetRect();
            if (_unitPos.ContainsKey(unit))
            {
   
                var oldNodes = _unitPos[unit];
                foreach (var oldNode in oldNodes)
                {
   
                    oldNode.Remove(unit);
                }
                root.Add(unit, r, list);
            }
            else
            {
   
                root.Add(unit, r, list);
            }

            _unitPos[unit] = list.ToArray();
            ListPool<Node>.Release(list);
        }

        public bool IsOverlaps(Rect rect)
        {
   
            return root.IsOverlaps(rect);
        }

        public IQuadTreeUnit[] Overlaps(Rect rect)
        {
   
            var list = ListPool<IQuadTreeUnit>.Claim();
            root.Find(rect, list);
            var res = list.ToArray();
            ListPool<IQuadTreeUnit>.Release(list);
            return res;
        }

        public IQuadTreeUnit Contain(Vector2 point)
        {
   
            return root.Contain(point);
        }

        public void Remove(IQuadTreeUnit unit)
        {
   
            if (_unitPos.ContainsKey(unit))
            {
   
                var oldNodes = _unitPos[unit];
                foreach (var oldNode in oldNodes)
                {
   
                    oldNode.Remove(unit);
                }

                _unitPos.Remove(unit);
            }
        }
        
        public void OnDrawGizmos()
        {
   
            root.OnDrawGizmos();
        }
        
        
        public class Node
        {
   
            public int Id;
            private int _depth;
            private Rect _rect;
            private QuadTree _tree;
            private Node _parent;
            private Node[] _children;
            private UnOrderList<IQuadTreeUnit> _dataList = new UnOrderList<IQuadTreeUnit>();
            
            public Node(int depth, Rect r, Node p, QuadTree belongTree)
            {
   
                this._rect = r;
                this._tree = belongTree;
                this._parent = p;
                this._depth = depth;
                if (depth <= belongTree.maxDepth)
                {
   
                    _children = new Node[4];
                    for (int i = 0; i < 4; i++)
                    {
   
                        var xSymbol = (i == 1 || i == 2) ? 0.5f : 0;
                        var ySymbol = (i == 0 || i == 1) ? 0f : 0.5f;
                        var childRect = new Rect(_rect.position +
                            new Vector2(_rect.width * xSymbol, _rect.height * ySymbol)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值