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