繁体   English   中英

C#四叉树效率

[英]C# quadtree Efficiency

我对统一比较陌生,所以我决定开始在C#中编写一个小平台物理引擎,其中主要的stat结构是四叉树。 不幸的是,我的生成代码非常慢,花了4秒多时间只为10K对象构建一个四叉树(普通的C#对象不是GameObjects)。

代码与普通四叉树略有不同; 而不是存储与根节点中的区域重叠的对象,而是将引用存储在包含该对象的每个子节点中。 由于我的所有对象的大小都相对相同,并且节点的最小大小是此大小的两倍,因此以这种方式构建树可以将碰撞算法的效率从~O(N ^(3/2))提高到~O (N)。

我的简单四叉树类如下所示:

using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System;

 public class QuadTree {

     public QuadTree parent = null;
     public QuadTree[] childern = null;
     public List<PPObject> objectList = null;
     public BBox bbox = null;

     public bool leaf = true;
     public float minSize = 1.0f;
     public float maxObjs = 4;

     public float width;
     public float height;
     public Vector3 position;
     public int level;

     public QuadTree(QuadTree parent, BBox bbox, int level)
     {
         this.parent = parent;
         this.bbox = bbox;
         this.level = level;
         width = bbox.xmax - bbox.xmin;
         height = bbox.ymax - bbox.ymin;
         position = new Vector3(bbox.xmin + width * 0.5f, bbox.ymin + height * 0.5f, 0.0f);

         objectList = new List<PPObject>();
     }

     public void Subdivide()
     {
         Profiler.BeginSample("Subdivide");
         float x1 = bbox.xmin;
         float x2 = bbox.xmin + 0.5f * width;
         float x3 = bbox.xmax;
         float y1 = bbox.ymin;
         float y2 = bbox.ymin + height * 0.5f;
         float y3 = bbox.ymax;

         Profiler.BeginSample("allocate new quadtrees");
         childern = new QuadTree[4];
         QuadTree tl = new QuadTree(this, new BBox(x1, x2, y2, y3), level + 1);
         QuadTree tr = new QuadTree(this, new BBox(x2, x3, y2, y3), level + 1);
         QuadTree br = new QuadTree(this, new BBox(x2, x3, y1, y2), level + 1);
         QuadTree bl = new QuadTree(this, new BBox(x1, x2, y1, y2), level + 1);

         childern[0] = tl;
         childern[1] = tr;
         childern[2] = br;
         childern[3] = bl;
         Profiler.EndSample();

         PushToChildern();

         leaf = false;
         Profiler.EndSample();
     }

     public void PushToChildern()
     {
         Profiler.BeginSample("pushToChildern");
         foreach (QuadTree child in childern)
         {
             foreach (PPObject obj in objectList)
             {
                 child.AddObject(obj);
             }
         }

         objectList = null;
         Profiler.EndSample();
     }

     public void AddObject(PPObject obj)
     {
         Profiler.BeginSample("addObject");
         if (childern == null)
         {
             if (obj != null)
             {
                 float x1 = obj.position.x;
                 float w1 = obj.bbox.xmax - obj.bbox.xmin;

                 float y1 = obj.position.y;
                 float h1 = obj.bbox.ymax - obj.bbox.ymin;

                 float dx = Math.Abs(x1-position.x);

                 float dy = Math.Abs(y1-position.y);

                 if (dx < ((width+w1) * 0.5f) && dy < (height + h1) *0.5f )
                 {
                     objectList.Add(obj);
                 }
             }

             if (objectList.Count > maxObjs && width > minSize && height > minSize)
             {
                 Subdivide();
             }
         } else {
             foreach (QuadTree child in childern)
             {
                 child.AddObject(obj);
             }
         }
         Profiler.EndSample();
     }
 }

这里BBox和PPObject只是包含位置,界限,速度等的简单类。上述执行时间为4秒,没有详细的对象稀缺分析器,范围为0.99到0.4。 如果有人能帮助我理解为什么这么慢,那就太棒了。 可能有大约100K函数调用,20K实例化总计3.4 Mb。 也许我只是习惯于在Fortran中写作,但这对于运行~10 GFlop核心来说似乎很荒谬。

谢谢!

基本上,我首先实例化树深度而不是广度优先。 这导致我制作了相关列表的许多额外(特别未提供的)副本。 以下代码在我的机器上执行~50ms,减少了100x。 通过一些tweeking,它应该适合在游戏引擎中使用。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

public class QuadTree
{

    public QuadTree parent = null;
    public QuadTree[] childern = null;
    public List<PPObject> objectList = null;
    public BBox bbox = null;

    public bool leaf = true;
    public float minSize = 1.0f;
    public float maxObjs = 4;

    public float width;
    public float height;
    public Vector3 position;
    public int level;

    public QuadTree(QuadTree parent, BBox bbox, List<PPObject> objectList, int level)
    {
        this.parent = parent;
        this.bbox = bbox;
        this.level = level;
        width = bbox.xmax - bbox.xmin;
        height = bbox.ymax - bbox.ymin;
        position = new Vector3(bbox.xmin + width * 0.5f, bbox.ymin + height * 0.5f, 0.0f);

        this.objectList = objectList;
        BuildTree();
    }

    public QuadTree(QuadTree parent, BBox bbox, int level)
    {
        this.parent = parent;
        this.bbox = bbox;
        this.level = level;
        width = bbox.xmax - bbox.xmin;
        height = bbox.ymax - bbox.ymin;
        position = new Vector3(bbox.xmin + width * 0.5f, bbox.ymin + height * 0.5f, 0.0f);

        objectList = new List<PPObject>();
    }

    public void Subdivide()
    {
        if (objectList.Count > maxObjs && childern == null && width > minSize && height > minSize)
        {
            float x1 = bbox.xmin;
            float x2 = bbox.xmin + 0.5f * width;
            float x3 = bbox.xmax;
            float y1 = bbox.ymin;
            float y2 = bbox.ymin + height * 0.5f;
            float y3 = bbox.ymax;

            childern = new QuadTree[4];
            BBox tlBBox = new BBox(x1, x2, y2, y3);
            BBox trBBox = new BBox(x2, x3, y2, y3);
            BBox brBBox = new BBox(x2, x3, y1, y2);
            BBox blBBox = new BBox(x1, x2, y1, y2);

            QuadTree tl = new QuadTree(this, tlBBox, level + 1);
            QuadTree tr = new QuadTree(this, trBBox, level + 1);
            QuadTree br = new QuadTree(this, brBBox, level + 1);
            QuadTree bl = new QuadTree(this, blBBox, level + 1);

            foreach (PPObject obj in objectList)
            {
                tl.AddObject(obj);
                tr.AddObject(obj);
                br.AddObject(obj);
                bl.AddObject(obj);
            }

            childern[0] = tl;
            childern[1] = tr;
            childern[2] = br;
            childern[3] = bl;

            objectList = new List<PPObject>();

            leaf = false;

        }
        else if (childern != null)
        {
            PushToChildern();
        }
    }

    public void PushToChildern()
    {
        foreach (QuadTree child in childern)
        {
            foreach (PPObject obj in objectList)
            {
                child.AddObject(obj);
            }
        }

        objectList = null;
    }

    public bool CheckBounds(PPObject obj, BBox region)
    {
        float x1 = obj.position.x;
        float w1 = obj.bbox.xmax - obj.bbox.xmin;

        float y1 = obj.position.y;
        float h1 = obj.bbox.ymax - obj.bbox.ymin;

        float dx = Math.Abs(x1 - position.x);

        float dy = Math.Abs(y1 - position.y);

        if (dx < ((width + w1) * 0.5f) && dy < (height + h1) * 0.5f)
        {
            return true;
        }
        return false;
    }

    /*
     * Make this faster by building the list to push here
     */
    public void AddObject(PPObject obj)
    {
        if (obj != null)
        {
            if (CheckBounds(obj, bbox))
            {
                objectList.Add(obj);
            }
        }
    }

    public void BuildTree()
    {
        Subdivide();
        if (childern != null)
        {
            foreach(QuadTree child in childern)
            {
                child.BuildTree();
            }
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM