GameUnity 2.0 文檔(四) 網格+四叉樹 最優碰撞檢測


在寫之前,必須對 前一篇文檔補充一下。

Camera2DAngle類是 攝像機旋轉 2d人物360度,PlayerMove是人物移動。

這兩個類 都可以 360 °  場景旋轉人物也跟着旋轉。

但不能同時用。 前者更傾向於 不移動的 人物。后者傾向於 移動的人物。 但精度 前者 高於 后者。具體根據項目需求 選擇。

 

今天 介紹的類 CollisionDetection  碰撞檢測類

    public GameObject GridPrefab;//網格塊材料 
    public int RangeLength;//網格塊范圍
    public bool showGrid;//是否顯示

第一個參數,是網格的圖形,你可以用 任何模型 作為一個網格的形狀。當然這不是必須要顯示的

第二個參數,取值為0--max,當0的時候,就是九宮格,1的時候是 25宮格,2的時候49宮格,3的時候81宮格,規律是 (2n+1)(2n+1);

第三個參數,是否顯示,如果不選,則第一個參數 不需要填寫。

下面我們來看看效果

說明下,添加位置和disk相同。我用cube作為網格形狀,我選擇的范圍是0和1,也就是9和25宮格,我讓他顯示,當然,最好建議不要選擇顯示,這樣會影響效率。

這張是我旋轉之后的圖,看的出人物方向還是面對面過來的。

有人問,為何范圍一定要 9,25,49,81這類的。其實 大家仔細想下就知道,我要讓 人物 永遠保證在中心點,那么 奇數 *奇數才能確保這樣。

網格計算是協程方式,測試在2000左右手機下,同屏顯示 300人 走動碰撞檢測保證在35幀以上。低端手機 也能滿足同屏 100+人 不卡。

 下一篇,AI。

鏈接:http://pan.baidu.com/s/1G5DAA 密碼:v2kl

 

最優網格四叉法代碼

前一陣子,我想想,其實給一點代碼也沒什么,所以就放一部分 代碼大家看看。

這個是 其原理圖,我自己畫的,畫工一般。概念就是 人物在填充里面 是 只檢測 綠色填充,而走到紅藍線 開始檢測不同 的臨界區。

同時,為了記錄和檢索不要那么頻繁,也優化過了。

檢測精度非常高,比如人物在2 這個點,圖上有,那么他檢測的 首先是 0,然后是 2的對角。然后2的兩邊。

如果人物在0 那么他只檢測0;同時還有方向的約束,可以優先檢索順序。

測試下來,比觸發碰撞 效率高2倍。如果做游戲,優化最大,幾乎達到 一半的 無碰撞性能。

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class ceshi : MonoBehaviour
{
    float widths, heights;
    public float x1;
    public float x2;
    public float y1;
    public float y2;

    private int w;
    private int h;
    public bool stop;
    void Start()
    {

    }
    void OnEnable()
    {
        stop = true;
        Vector2 v = Camera.main.WorldToScreenPoint(this.transform.position);
        widths = Screen.width / 6;
        heights = Screen.height / 6;
        w = (int)(v.x / widths);
        h = (int)(v.y / heights);
        //初始化 人物 坐標 對應的 網格 位置,並保存到 網格內 
        OnInstet(w, h);
    }
    //插入數據
    void OnInstet(int w, int h)
    {
        Vector2 VectorGrid = new Vector2(w, h);
        ArrayList arrlist = GameModel.getInstance().GridList[VectorGrid];

        if (!arrlist.Contains(transform.name))
        {
            GameModel.getInstance().GridList[VectorGrid].Add(transform.name);
        }
        else
        {
            int Indexs = GameModel.getInstance().GridList[VectorGrid].IndexOf(transform.name);
            GameModel.getInstance().GridList[VectorGrid].RemoveAt(Indexs);
        }
    }

    bool sortGird(int w, int h)
    {
        ArrayList arr = new ArrayList();
        if (w < 0)
        {
            w = 0;
        }
        if (h < 0)
        {
            h = 0;
        }
        if (w > 5)
        {
            w = 5;
        }
        if (h > 5)
        {
            h = 5;
        }
        Vector2 VectorGrid = new Vector2(w, h);
        arr = GameModel.getInstance().GridList[VectorGrid];
        if (OnSetArmy(arr))
        {
            return true;
        }
        return false;
    }


    bool OnSetArmy(ArrayList arr)
    {
        if (arr.Count > 0)
        {
            for (int i = 0; i < arr.Count; i++)
            {
                if (arr[i] != this.transform.name)//如果不是自己
                {

                    stop = false;//停止檢測
                    return true;
                }
            }
        }
        return false;
    }
    //查詢數據
    void OnFindData(int vector)
    {
        //根據人物 角度 選擇 從 哪個開始
        switch (vector)
        {
            case 0:  //中間 
                sortGird(w, h);
                break;
            case 1:  //左上
                if (sortGird(w, h))//自己
                {
                    break;
                }

                if (sortGird(w - 1, h + 1)) { break; }//左上對角 
                if (sortGird(w - 1, h)) { break; }//
                if (sortGird(w, h + 1)) { break; }////找出一個 就跳轉
                break;
            case 2: //右上
                if (sortGird(w, h)) { break; }//自己
                if (sortGird(w + 1, h + 1)) { break; }//右上對角
                if (sortGird(w + 1, h)) { break; }//
                if (sortGird(w, h + 1)) { break; }//
                break;
            case 3:  //左下
                if (sortGird(w, h)) { break; }//自己
                if (sortGird(w - 1, h - 1)) { break; }//左下對角
                if (sortGird(w - 1, h)) { break; }//
                if (sortGird(w, h - 1)) { break; }//
                break;
            case 4:  //右下
                if (sortGird(w, h)) { break; }//自己
                if (sortGird(w + 1, h - 1)) { break; }//右下對角
                if (sortGird(w - 1, h)) { break; }//
                if (sortGird(w, h - 1)) { break; }//
                break;
            case 5:  //
                if (sortGird(w, h)) { break; }//自己
                if (sortGird(w, h + 1)) { break; }//
                break;
            case 6:  //
                if (sortGird(w, h)) { break; }//自己
                if (sortGird(w + 1, h)) { break; }//
                break;
            case 7: //
                if (sortGird(w, h)) { break; }//自己
                if (sortGird(w, h - 1)) { break; }//
                break;
            case 8: //
                if (sortGird(w, h)) { break; }//自己
                if (sortGird(w - 1, h)) { break; }//
                break;
            default: break;
        }

    }

    private bool top;
    private bool bottom;
    private bool left;
    private bool right;

    //判斷是否在臨界點
    void OnRectPostion(bool tops = true, bool bottoms = true, bool lefts = true, bool rights = true)
    {
        top = tops;
        bottom = bottoms;
        left = lefts;
        right = rights;
    }


    //存儲網格數據
    void Storage()
    {
        Vector2 v = Camera.main.WorldToScreenPoint(this.transform.position);
        int w_new = (int)(v.x / widths);
        int h_new = (int)(v.y / heights);
        int ww = w, hh = h;
        if (w_new != w)
        {
            if (w_new < w)
            {
                int w1 = (int)(v.x) % (int)(widths);
                if (w1 < x2)
                {
                    //進入了下一個格子  存儲數據
                    OnRectPostion();//判斷是否在臨界點
                    ww = w_new;
                    //    print("111");
                }
                else
                {
                    //偏左 進入臨界點
                    left = false;
                }
            }
            else
            {
                int w1 = (int)(v.x) % (int)(widths);
                if (w1 > x1)
                { //進入了下一個格子
                    OnRectPostion();
                    ww = w_new;
                    //     print("222");
                }
                else
                {
                    // 偏右 進入臨界點
                    right = false;
                }
            }
        }
        /////////////////////////////////////////////////////////////
        if (h_new != h)
        {
            if (h_new < h)
            {
                int h1 = (int)(v.y) % (int)(heights);
                if (h1 < y2)
                {
                    //進入了下一個格子
                    OnRectPostion();
                    hh = h_new;
                    //   print("333");
                }
                else
                {
                    //偏上 進入臨界點
                    top = false;
                }
            }
            else
            {
                int h1 = (int)(v.y) % (int)(heights);
                if (h1 > y1)
                { //進入了下一個格子
                    OnRectPostion();
                    hh = h_new;
                    //    print("444");
                }
                else
                {
                    //偏下 進入臨界點
                    bottom = false;
                }
            }
        }
        ///////////////
        OnInstet(w, h);//刪除老數據
        OnInstet(ww, hh);//添加新數據
        w = ww;
        h = hh; //換新

    }
    //查詢人物網格內的碰撞
    void FindGrid()
    {
        //如果在上面
        if (!top)
        {
            if (!left)//左上  
            {
                OnFindData(1);
            }
            else if (!right)//右上
            {
                OnFindData(2);
            }
            else
            {   //
                OnFindData(5);
            }

            return;
        }

        //如果在下面
        if (!bottom)
        {
            if (!left)//左下
            {
                OnFindData(3);
            }
            else if (!right)//右下
            {
                OnFindData(4);
            }
            else
            {   //
                OnFindData(7);
            }

            return;
        }
        //如果在左邊
        if (!left)
        {
            if (!top)//左上
            {
                OnFindData(1);
            }
            else if (!bottom)//左下
            {
                OnFindData(3);

            }
            else  //左邊
            {
                OnFindData(8);
            }
            return;
        }
        //如果在右邊
        if (!right)
        {
            if (!top)//右上
            {
                OnFindData(2);
            }
            else if (!bottom)//右下
            {
                OnFindData(4);
            }
            else  //右邊
            {
                OnFindData(6);
            }
            return;
        }
        //不在臨界點
        if (top && bottom && left && right)
        {
            OnFindData(0);
        }
    }
    void Update()
    {
        //存儲坐標 和 找出臨界點
        Storage();
        ///////////////////////////
        //判斷臨界點 的位置,找出 需要 檢索的 格子。
        if (stop)
        {
            FindGrid();
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM