GameUnity 2.0 文档(四) 网格+四叉树 最优碰撞检测

时间:2023-03-09 17:16:21
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);

第三个参数,是否显示,如果不选,则第一个参数 不需要填写。

下面我们来看看效果

GameUnity 2.0 文档(四) 网格+四叉树 最优碰撞检测

GameUnity 2.0 文档(四) 网格+四叉树 最优碰撞检测

说明下,添加位置和disk相同。我用cube作为网格形状,我选择的范围是0和1,也就是9和25宫格,我让他显示,当然,最好建议不要选择显示,这样会影响效率。

GameUnity 2.0 文档(四) 网格+四叉树 最优碰撞检测

这张是我旋转之后的图,看的出人物方向还是面对面过来的。

有人问,为何范围一定要 9,25,49,81这类的。其实 大家仔细想下就知道,我要让 人物 永远保证在中心点,那么 奇数 *奇数才能确保这样。

网格计算是协程方式,测试在2000左右手机下,同屏显示 300人 走动碰撞检测保证在35帧以上。低端手机 也能满足同屏 100+人 不卡。

下一篇,AI。

链接:http://pan.baidu.com/s/1G5DAA 密码:v2kl

最优网格四叉法代码

前一阵子,我想想,其实给一点代码也没什么,所以就放一部分 代码大家看看。GameUnity 2.0 文档(四) 网格+四叉树 最优碰撞检测

这个是 其原理图,我自己画的,画工一般。概念就是 人物在填充里面 是 只检测 绿色填充,而走到红蓝线 开始检测不同 的临界区。

同时,为了记录和检索不要那么频繁,也优化过了。

检测精度非常高,比如人物在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 / ;
heights = Screen.height / ;
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 < )
{
w = ;
}
if (h < )
{
h = ;
}
if (w > )
{
w = ;
}
if (h > )
{
h = ;
}
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 > )
{
for (int i = ; i < arr.Count; i++)
{
if (arr[i] != this.transform.name)//如果不是自己
{ stop = false;//停止检测
return true;
}
}
}
return false;
}
//查询数据
void OnFindData(int vector)
{
//根据人物 角度 选择 从 哪个开始
switch (vector)
{
case : //中间
sortGird(w, h);
break;
case : //左上
if (sortGird(w, h))//自己
{
break;
} if (sortGird(w - , h + )) { break; }//左上对角
if (sortGird(w - , h)) { break; }//左
if (sortGird(w, h + )) { break; }//上
//找出一个 就跳转
break;
case : //右上
if (sortGird(w, h)) { break; }//自己
if (sortGird(w + , h + )) { break; }//右上对角
if (sortGird(w + , h)) { break; }//右
if (sortGird(w, h + )) { break; }//上
break;
case : //左下
if (sortGird(w, h)) { break; }//自己
if (sortGird(w - , h - )) { break; }//左下对角
if (sortGird(w - , h)) { break; }//左
if (sortGird(w, h - )) { break; }//下
break;
case : //右下
if (sortGird(w, h)) { break; }//自己
if (sortGird(w + , h - )) { break; }//右下对角
if (sortGird(w - , h)) { break; }//右
if (sortGird(w, h - )) { break; }//下
break;
case : //上
if (sortGird(w, h)) { break; }//自己
if (sortGird(w, h + )) { break; }//上
break;
case : //右
if (sortGird(w, h)) { break; }//自己
if (sortGird(w + , h)) { break; }//右
break;
case : //下
if (sortGird(w, h)) { break; }//自己
if (sortGird(w, h - )) { break; }//下
break;
case : //左
if (sortGird(w, h)) { break; }//自己
if (sortGird(w - , 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();
}
else if (!right)//右上
{
OnFindData();
}
else
{ //上
OnFindData();
} return;
} //如果在下面
if (!bottom)
{
if (!left)//左下
{
OnFindData();
}
else if (!right)//右下
{
OnFindData();
}
else
{ //下
OnFindData();
} return;
}
//如果在左边
if (!left)
{
if (!top)//左上
{
OnFindData();
}
else if (!bottom)//左下
{
OnFindData(); }
else //左边
{
OnFindData();
}
return;
}
//如果在右边
if (!right)
{
if (!top)//右上
{
OnFindData();
}
else if (!bottom)//右下
{
OnFindData();
}
else //右边
{
OnFindData();
}
return;
}
//不在临界点
if (top && bottom && left && right)
{
OnFindData();
}
}
void Update()
{
//存储坐标 和 找出临界点
Storage();
///////////////////////////
//判断临界点 的位置,找出 需要 检索的 格子。
if (stop)
{
FindGrid();
}
}
}