본문 바로가기

카테고리 없음

Unity 공간분할트리

using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

public class TreeData
{
    // 현재 영역의 Level
    public int level = 0;
    
    public int maxValue = 2;
    public int currentValue = 0;

    public Rect area;

    public List<TreeData> nodeList;

    // 다음 Level의 영역을 생성한다.
    public void NextLevelNode()
    {
        if (nodeList != null)
            return;
            
        nodeList = new List<TreeData>();

        Rect[] lRectArrer = Split();
        for (int i = 0; i < 4; ++i)
        {
            TreeData lTreeData = new TreeData();
            lTreeData.level = level + 1;
            lTreeData.area = lRectArrer[i];
            
            nodeList.Add(lTreeData);
            
        }
    }

    // 공간 분할함수
    private Rect[] Split()
    {
        float subWidth = area.width *0.5f;

        float subHeight = area.height * 0.5f;

        float x = area.x;

        float y = area.y;

        Rect[] lRectArray = new Rect[4];
        
       lRectArray[0] = new Rect( x - (subWidth * 0.5f), y + (subHeight * 0.5f), subWidth, subHeight);
       lRectArray[1] = new Rect( x + (subWidth * 0.5f), y + (subHeight * 0.5f), subWidth, subHeight);
       lRectArray[2] = new Rect( x + (subWidth * 0.5f), y - (subHeight * 0.5f), subWidth, subHeight);
       lRectArray[3] = new Rect( x - (subWidth * 0.5f), y - (subHeight * 0.5f), subWidth, subHeight);
       
        return lRectArray;
    }
}

public class QuadTree : MonoBehaviour
{
    public RectTransform quadMap;

    public List<GameObject> objectList;

    private TreeData _treeData;
    private List<Color> colorList;
    
    public void Start()
    {
        // 가장 기초적인 0Level의 영역
        _treeData = new TreeData();
        _treeData.currentValue = int.MaxValue; // 기초 영역이 사라지는걸 방지하기 위하여 현재영역이 가지고있는 object의 갯수는 int.MaxValue로 잡음
        _treeData.level = 0;
        _treeData.area = new Rect(quadMap.localPosition.y, quadMap.localPosition.y, quadMap.rect.width, quadMap.rect.height);
        
        _treeData.NextLevelNode();

        //* Gizmo 색상
        colorList = new List<Color>();
        for (int i = 0; i < 100; ++i)
        {
            colorList.Add(Random.ColorHSV());
        }
        //*/
    }

    public void Update()
    {
        NodeAreaCheck(_treeData);
    }

    // 객채가 n들어왔는지판단하는함수
    private void NodeAreaCheck(TreeData pTreeData)
    {   
        // 모든 Node를 순회하여 자기 공간안에 객체가 몇개있는지 파악
        // 나의 공간에 객채가 n개있으면 분리
        // 나와 같은 레벨의 공간들 전부 객체를 n개 가지고있지않다면 다시 합체
        
        // 현재 나의 공간에 n개의 객체를 가지고 있지 않다면 나의 아래 node들이 n개의 객체를 가지고 있지 않다는 이야기 그러므로 다 지운다.
        
        if (pTreeData == null || pTreeData.nodeList == null)
            return;

        foreach (var lNode in pTreeData.nodeList)
        {
            // lnode 객체갯수 확인
            if (AreaInObjectCount(lNode) > lNode.maxValue)
            {
                lNode.NextLevelNode();
            }

            NodeAreaCheck(lNode);
        }

        if(pTreeData.currentValue <= pTreeData.maxValue)
            pTreeData.nodeList = null;
    }

    // 자신의 영역에 있는 Object의 갯수를 반환
    private int AreaInObjectCount(TreeData pTreeData)
    {
        pTreeData.currentValue = 0;
        
        int lCount = objectList.Count;
        for (int i = 0; i < lCount; ++i)
        {
            var lDis = Vector3.Distance(pTreeData.area.position, objectList[i].transform.localPosition);
            
            if (lDis <= (pTreeData.area.height * 0.5f) + (objectList[i].transform.localScale.x * 0.5f))
            {
                pTreeData.currentValue++;
            }
        }

        return pTreeData.currentValue;
    }
    
    //* 쿼드트리로 분리된 영역을 Gizmo로 그려준다.
    void OnDrawGizmos()
    {
        Drawing(_treeData);
    }

    private void Drawing(TreeData pTreeData)
    {
        if (pTreeData == null || pTreeData.nodeList == null)
            return;

        foreach (var lNode in pTreeData.nodeList)
        {
            ColorSelect(pTreeData.level);
            DrawRect(lNode.area);

            Drawing(lNode);
        }
    }
    
    void DrawRect(Rect rect)
    {
        Gizmos.DrawWireCube(new Vector3(rect.x + 1920, rect.y + 1080, 0.01f), new Vector3(rect.size.x, rect.size.y, 0.01f));
    }

    private void ColorSelect(int pLevel)
    {
        Gizmos.color = colorList[pLevel];
    }
    //*/
}