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();
        }
    }
}

 

posted on 2015-08-11 13:49  太粗难进  阅读(3176)  评论(0编辑  收藏  举报