• Unity 遍历敌人——使用四叉树空间分区


    最近看了《游戏编程模式》这本书,里面有一篇空间分区的文章,看了心里痒痒,决定去尝试实现一下。文章后面会给出整个学习参考的链接。

    实现的效果如下,我们有一个很大的场景,场景有许许多多的敌人。红色的点代表是玩家,黑色的点代表是敌人。在这样的一个大量敌人的情景下,我们不可能在玩家或敌人寻找身边的攻击对象时穷尽所有的对象。因为我们要建立空间分区,只遍历某个对应区的对象。在图下中,红点中遍历红框中的黑点对象,其他一律不遍历。

    接下来我直接放代码了,主要采用了四叉树,如果对于一些不懂的地方断点调试下就可以了,并没有涉及到很高深的算法,不要想得太难。

    其中也实现了红点在分区边缘遍历另外一个区的敌人的漏洞。

     1 using System.Collections;
     2 using System.Collections.Generic;
     3 using UnityEngine;
     4 
     5 /// <summary>
     6 /// Rect的扩展方法类
     7 /// </summary>
     8 public static class RectExtension
     9 {
    10     /// <summary>
    11     /// 计算点到Rect的border的距离,若点在Rect内则返回0
    12     /// </summary>
    13     /// <param name="rect"></param>
    14     /// <param name="pos"></param>
    15     /// <returns></returns>
    16     public static float PointToBorderDistance(this Rect rect, Vector2 pos)
    17     {
    18         float xdisance;
    19         float ydisance;
    20 
    21         if (rect.x <= pos.x && pos.x <= rect.xMax)
    22         {
    23             xdisance = 0;
    24         }
    25         else
    26         {
    27             xdisance = Mathf.Min((Mathf.Abs(pos.x - rect.width)), (pos.x - rect.x));
    28         }
    29 
    30         if (rect.y <= pos.y && pos.y <= rect.yMax)
    31         {
    32             ydisance = 0;
    33         }
    34         else
    35         {
    36             ydisance = Mathf.Min((Mathf.Abs(pos.y - rect.height)), (pos.y - rect.y));
    37         }
    38 
    39         return xdisance * xdisance + ydisance * ydisance;
    40     }
    41 }
    RectExtension
     1 using System.Collections;
     2 using System.Collections.Generic;
     3 using UnityEngine;
     4 
     5 /// <summary>
     6 /// 叶子节点类,负责坐标与数据的映射
     7 /// </summary>
     8 /// <typeparam name="T"></typeparam>
     9 public class QuadTreeLeaf<T>
    10 {
    11     private Vector2 pos;
    12     private T refObject;
    13 
    14     public QuadTreeLeaf(Vector2 pos,T obj)
    15     {
    16         this.pos = pos;
    17         refObject = obj;
    18     }
    19 
    20     public T LeafObject
    21     {
    22         get
    23         {
    24             return refObject;
    25         }
    26     }
    27 
    28     public Vector2 Pos
    29     {
    30         get { return pos; }
    31         set { pos = value; }
    32     }
    33 }
    QuadTreeLeaf
      1 using System;
      2 using System.Collections;
      3 using System.Collections.Generic;
      4 using UnityEngine;
      5 
      6 /// <summary>
      7 /// 四叉树节点
      8 /// </summary>
      9 /// <typeparam name="T"></typeparam>
     10 public class QuadTreeNode<T>
     11 {
     12     /// <summary>
     13     /// 节点拥有的叶子节点
     14     /// </summary>
     15     protected List<QuadTreeLeaf<T>> items;
     16     /// <summary>
     17     /// 节点拥有的分支
     18     /// </summary>
     19     protected QuadTreeNode<T>[] branch;
     20     /// <summary>
     21     /// 节点空间最大容量,受minSize影响
     22     /// </summary>
     23     protected int maxItems;
     24     /// <summary>
     25     /// 节点空间分割的最小大小(最小宽度,高度)
     26     /// </summary>
     27     protected float minSize;
     28 
     29     public const float TOLERANCE = 0.001f;
     30     /// <summary>
     31     /// 节点的空间
     32     /// </summary>
     33     public Rect bounds;
     34 
     35     public QuadTreeNode(float x, float y, float width, float height, int maximumItems, float minSize = -1)
     36     {
     37         bounds = new Rect(x, y, width, height);
     38         maxItems = maximumItems;
     39         this.minSize = minSize;
     40         items = new List<QuadTreeLeaf<T>>();
     41     }
     42 
     43     public bool HasChildren()
     44     {
     45         if (branch != null)
     46             return true;
     47         else
     48             return false;
     49     }
     50 
     51     /// <summary>
     52     /// 将节点空间分割4份
     53     /// </summary>
     54     protected void Split()
     55     {
     56         if (minSize != -1)
     57         {
     58             if (bounds.width <= minSize && bounds.height <= minSize)
     59             {
     60                 return;
     61             }
     62         }
     63 
     64         float nsHalf = bounds.height - bounds.height / 2;
     65         float ewHalf = bounds.width - bounds.width / 2;
     66 
     67         branch = new QuadTreeNode<T>[4];
     68 
     69         branch[0] = new QuadTreeNode<T>(bounds.x, bounds.y, ewHalf, nsHalf, maxItems, minSize);
     70         branch[1] = new QuadTreeNode<T>(ewHalf, bounds.y, ewHalf, nsHalf, maxItems, minSize);
     71         branch[2] = new QuadTreeNode<T>(bounds.x, nsHalf, ewHalf, nsHalf, maxItems, minSize);
     72         branch[3] = new QuadTreeNode<T>(ewHalf, nsHalf, ewHalf, nsHalf, maxItems, minSize);
     73 
     74         foreach (var item in items)
     75         {
     76             AddNode(item);
     77         }
     78 
     79         items.Clear();
     80     }
     81 
     82     /// <summary>
     83     /// 根据坐标获得相应的子空间
     84     /// </summary>
     85     /// <param name="pos"></param>
     86     /// <returns></returns>
     87     protected QuadTreeNode<T> GetChild(Vector2 pos)
     88     {
     89         if (bounds.Contains(pos))
     90         {
     91             if (branch != null)
     92             {
     93                 for (int i = 0; i < branch.Length; i++)
     94                     if (branch[i].bounds.Contains(pos))
     95                         return branch[i].GetChild(pos);
     96 
     97             }
     98             else
     99                 return this;
    100         }
    101         return null;
    102     }
    103     /// <summary>
    104     /// 增加叶子节点数据
    105     /// </summary>
    106     /// <param name="leaf"></param>
    107     /// <returns></returns>
    108     private bool AddNode(QuadTreeLeaf<T> leaf)
    109     {
    110         if (branch == null)
    111         {
    112             this.items.Add(leaf);
    113 
    114             if (this.items.Count > maxItems)
    115                 Split();
    116             return true;
    117         }
    118         else
    119         {
    120             QuadTreeNode<T> node = GetChild(leaf.Pos);
    121             if (node != null)
    122             {
    123                 return node.AddNode(leaf);
    124             }
    125         }
    126         return false;
    127     }
    128 
    129     public bool AddNode(Vector2 pos, T obj)
    130     {
    131         return AddNode(new QuadTreeLeaf<T>(pos, obj));
    132     }
    133 
    134     /// <summary>
    135     /// 
    136     /// </summary>
    137     /// <param name="pos">可以是空间任意位置,只是根据这个位置找到所在的空间去删除对象</param>
    138     /// <param name="obj"></param>
    139     /// <returns></returns>
    140     public bool RemoveNode(Vector2 pos, T obj)
    141     {
    142         if (branch == null)
    143         {
    144             for (int i = 0; i < items.Count; i++)
    145             {
    146                 QuadTreeLeaf<T> qtl = items[i];
    147                 if (qtl.LeafObject.Equals(obj))
    148                 {
    149                     items.RemoveAt(i);
    150                     return true;
    151                 }
    152             }
    153         }
    154         else
    155         {
    156             QuadTreeNode<T> node = GetChild(pos);
    157             if (node != null)
    158             {
    159                 return node.RemoveNode(pos, obj);
    160             }
    161         }
    162         return false;
    163     }
    164 
    165     public bool UpdateNode(Vector2 pos, T obj)
    166     {
    167         // TODO  参找RemoveNode
    168         return false;
    169     }
    170 
    171     /// <summary>
    172     /// 得到在一个Rect内的所有数据
    173     /// </summary>
    174     /// <param name="rect"></param>
    175     /// <param name="nodes"></param>
    176     /// <returns></returns>
    177     public int GetNode(Rect rect, ref List<T> nodes)
    178     {
    179         if (branch == null)
    180         {
    181             foreach (QuadTreeLeaf<T> item in items)
    182             {
    183                 if (rect.Contains(item.Pos))
    184                 {
    185                     nodes.Add(item.LeafObject);
    186                 }
    187             }
    188         }
    189         else
    190         {
    191             for (int i = 0; i < branch.Length; i++)
    192             {
    193                 if (branch[i].bounds.Overlaps(rect))
    194                     branch[i].GetNode(rect, ref nodes);
    195             }
    196         }
    197         return nodes.Count;
    198     }
    199 
    200     /// <summary>
    201     /// 根据坐标得到坐标附近节点的数据
    202     /// </summary>
    203     /// <param name="pos"></param>
    204     /// <param name="ShortestDistance">离坐标最短距离</param>
    205     /// <param name="list"></param>
    206     /// <returns></returns>
    207     public int GetNodeRecRange(Vector2 pos, float ShortestDistance, ref List<T> list)
    208     {
    209         float distance;
    210         if (branch == null)
    211         {
    212             foreach (QuadTreeLeaf<T> leaf in this.items)
    213             {
    214                 distance = Vector2.Distance(pos,leaf.Pos);
    215 
    216                 if (distance < ShortestDistance)
    217                 {
    218                     list.Add(leaf.LeafObject);
    219                 }
    220             }
    221         }
    222         else
    223         {
    224             for (int i = 0; i < branch.Length; i++)
    225             {
    226                 float childDistance = branch[i].bounds.PointToBorderDistance(pos);
    227                 if (childDistance < ShortestDistance * ShortestDistance)
    228                 {
    229                     branch[i].GetNodeRecRange(pos, ShortestDistance, ref list);
    230                 }
    231             }
    232         }
    233         return list.Count;
    234     }
    235 }
    QuadTreeNode

    测试代码:

    挂在红点和黑点上。

     1 using System.Collections;
     2 using System.Collections.Generic;
     3 using UnityEngine;
     4 
     5 public class TestController : MonoBehaviour
     6 {
     7     public bool IsPlayer = false;
     8 
     9     // Use this for initialization
    10     void Start()
    11     {
    12         TestObj.quadRoot.AddNode(new Vector2(transform.position.x, transform.position.z), this.gameObject);
    13     }
    14 
    15     // Update is called once per frame
    16     void Update()
    17     {
    18         if (IsPlayer && Input.GetKeyDown(KeyCode.S))
    19         {
    20             List<GameObject> list = new List<GameObject>();
    21             TestObj.quadRoot.GetNodeRecRange(new Vector2(transform.position.x, transform.position.z), 5f, ref list);
    22             foreach (var item in list)
    23             {
    24                 Debug.Log(item.name);
    25             }
    26         }
    27     }
    28 }
    TestController
    1 using System.Collections;
    2 using System.Collections.Generic;
    3 using UnityEngine;
    4 
    5 public class TestObj : MonoBehaviour
    6 {
    7     public static QuadTreeNode<GameObject> quadRoot = new QuadTreeNode<GameObject>(0, 0, 100, 100, 10, 50);
    8 }
    TestObj

    TestObj随便挂在一个物体上就可以了。

     

    《游戏编程模式》空间分区章节:http://gpp.tkchu.me/spatial-partition.html

    四叉树代码参考:http://www.oxox.work/web/recommend/quadtree-c-c/

  • 相关阅读:
    ORA16014 与 ORA00312
    ORA26687
    ORA32004问题解决
    ORA23616:执行块5失败
    Streams实践之Schemas级复制
    Streams FAQ
    ORA23622: 正在执行操作SYS.DBMS_STREAMS_ADM.MAINTAIN_GLOBAL
    为什么白帽SEO更好?
    HyperV虚拟机安装及网卡无法找到,驱动无法安装解决办法
    Memcahed分布式缓存服务替换Session解决方案——简单介绍[转]
  • 原文地址:https://www.cnblogs.com/SeaSwallow/p/6830555.html
Copyright © 2020-2023  润新知