Unity武器与子弹碰撞检测
Unity武器与子弹碰撞检测
在想进行碰撞检测制造伤害或生成特效时,有两种常用的方法,一种是使用碰撞器和刚体,检测到碰撞后会自动调用OnCollisionEnter等函数,一种是使用Physics接口下的OverlapBox等检测函数。
一、采用碰撞器和刚体进行碰撞检测
在使用Collider进行碰撞检测时,至少要有一方是带有刚体组件。在默认设置的情况下这样就能够进行碰撞检测,但是在设置物体为静态或勾选刚体的动力学选项后会有不同。
关于这两种碰撞什么情况下可以检测到请看该链接
不关心碰撞位置的情况:
可以把碰撞检测方的碰撞器设置为IsTrigger,并在碰撞组件同级挂载碰撞检测后调用的代码。把碰撞器设置为IsTrigger后,能检测到碰撞并且调用OnTriggerEnter,OnTriggerStay,OnTriggerExit,但是不会对双方造成物理作用。
/// <summary>
/// 进入碰撞范围时自动调用
/// </summary>
/// <param name="other">碰撞检测到的物体,以下简称目标物</param>
private void OnTriggerEnter(Collider other)
{
// owner是持有武器的角色身上的碰撞器,目标物的碰撞体为拥有者的碰撞体时直接返回
if (other.GetInstanceID() == owner.GetInstanceID())
return;
// 判断目标物是否为可以被攻击的物体
Controller entity;
if (!other.TryGetComponent(out entity))
return;
// 调用目标物的受到伤害的函数
entity.TakeDamage(1);
}
关心碰撞位置的情况:
有时可能需要碰撞的位置来生成特效,比如子弹打中哪里就在哪里生成击中特效,在OnTriggerEnter中做不到这一点,当然也能在此时碰撞体所在的位置生成特效,但位置仍是不准确的。
此时不能把碰撞器设置为IsTrigger,因为需要使用OnCollisionEnter函数,同样在碰撞组件同级挂载碰撞检测后调用的代码。
/// <summary>
/// 进入碰撞范围时自动调用
/// </summary>
/// <param name="collision">碰撞检测到的物体,以下简称目标物</param>
private void OnCollisionEnter(Collision collision)
{
// 发生碰撞的位置
Vector3 hitPoint = collision.contacts[0].point;
// 实例化特效
GameObject hitEffect = Instantiate(effectPrefab);
// 将特效放置在碰撞点
hitEffect .transform.position = hitPoint;
}
如果想要更精确的模拟击中特效,可以将生成的特效的方向改为碰撞体的反方向,比如子弹击中后,血液往子弹来的方向喷。
二、使用代码检测
Physics接口中有许多碰撞检测的函数可以调用,每个函数的返回值和参数都不一样,但用法大同小异,这里就拿OverlapBox作为例子。
// 进行检测的层级,暴露在属性页面进行勾选
[SerializeField] LayerMask impactLayer;
// 碰撞检测的物体
[SerializeField] Transform colBox;
private void Update()
{
BoxCheck(colBox);
}
/// <summary>
/// 箱型检测
/// </summary>
/// <param name="colBox">箱子物体,可以空物体,作为武器的子物体跟随移动</param>
void BoxCheck(Transform colBox)
{
// 检测中心点
Vector3 center = colBox.position;
// 碰撞器的缩放,以Unity默认的正方体为标准
Vector3 checkBox= new Vector3(1, 0.1f, 0.1f);
// 碰撞检测到的碰撞体
Collider[] colliders = Physics.OverlapBox(center, checkBox, colBox.rotation, impactLayer);
foreach (var col in colliders)
{
// 碰撞体是自身
if (col.GetInstanceID() == owner.GetInstanceID())
continue;
// 判断目标物是否为可以被攻击的物体
Controller entity;
if (!col.TryGetComponent(out entity))
continue;
// 调用目标物的受到伤害的函数
entity.TakeDamage(1);
}
}
用代码检测碰撞时不需要刚体组件,但与OnTriggerEnter一样获取不到碰撞位置。
在使用武器攻击敌人时,如果不对敌人的碰撞体进行记录,会产生多段伤害,因为每一帧都在调用碰撞检测,所以上面的代码需要进行优化。
1、只在武器挥出的时候进行检测,可以在动画里设置帧事件,在攻击动作的前后设置开启和关闭
2、标记碰撞体以避免重复检测
// 已经被伤害过的碰撞体
HashSet<int> colsMarked = new HashSet<int>();
//是否开启碰撞检测
bool enableDamage;
private void Update()
{
if (!enableDamage)
return;
BoxCheck(colBox);
}
/// <summary>
/// 箱型检测
/// </summary>
/// <param name="colBox">箱子物体,可以空物体,作为武器的子物体跟随移动</param>
void BoxCheck(Transform colBox)
{
// 检测中心点
Vector3 center = colBox.position;
// 碰撞器的缩放,以Unity默认的正方体为标准
Vector3 checkBox= new Vector3(1, 0.1f, 0.1f);
// 碰撞检测到的碰撞体
Collider[] colliders = Physics.OverlapBox(center, checkBox, colBox.rotation, impactLayer);
foreach (var col in colliders)
{
// 碰撞体是自身
if (col.GetInstanceID() == owner.GetInstanceID())
continue;
// 判断目标物是否为可以被攻击的物体
Controller entity;
if (!col.TryGetComponent(out entity))
continue;
// 向HashSet添加成功时返回True,否则返回False
// 返回False时说明已经被添加过了,不能再次造成伤害
if (!colsMarked.Add(col.GetInstanceID()))
continue;
// 调用目标物的受到伤害的函数
entity.TakeDamage(1);
}
/// <summary>
/// 清空标记,可以通过动画帧事件进行调用
/// </summary>
/// <param name="enable">是否开启碰撞检测</param>
public void EnableDamage(bool enable)
{
// 清空标记过的碰撞体
colsMarked.Clear();
// 开启或关闭检测
enableDamage = enable;
}
}
上一篇: Java 实现求1+2+3+…+n
下一篇: 在未排序的数组中找到第 k 个最大的元素
推荐阅读