想要实现的效果:
例1:飞出的弹丸,碰到墙壁后,反弹。【↘️| 】——>【↙️| 】
例2:向右下方【↘️】移动的对象,碰到右侧的墙壁 【↘️| 】 后,继续沿着着墙壁向下方移动【↓ | 】
为什么要用代码实现?
确实,一般来说使用刚体和碰撞器(Dynamic模式)的话Unity会负责物理碰撞的部分。
但我的实现是用Kinematic X Static。敌人使用Kinematic 是因为需要控制他们各自不同的运动方式,所以碰到障碍物要绕行/滑行/反弹的话需要自己手动实现。
一、计算反射向量
无论要实现哪个效果都需要一个很重要的函数:根据入射向量,计算反射向量。(见下图)
用法很简单:
Vector3 reflectDir = Vector3.Reflect(OrgDir, normal);//OrgDir是原本的运动方向向量,normal是碰撞表面的法线
那么,碰撞表面的法线inNormal要怎样得到?
我只记录我有在使用的两种方式,应该可以满足大部分需求。
1.有发生实际碰撞:
在脚本中使用碰撞检测,可以得到碰撞点信息:
protected ContactPoint2D contactPoint;//用这个变量保存碰撞点信息protected void OnCollisionEnter2D(Collision2D collision)
{contactPoint = collision.GetContact(0); //这个函数用来获取发生碰撞的点。因为它可能会是一个点的集合,我只需要一个就可以了,所以取第一个。
}
contackPoint.normal 就是法线。
reflectDir = Vector2.Reflect(orgDir, contactPoint.normal);
2.未发生实际碰撞
有一些情况需要在发生实际碰撞之前,先去检测会不会发生碰撞(未雨绸缪)。
比如对象看到运动方向前面有障碍物,它会提前绕行,而不是撞到以后再改变运动方向。
这种情况可以使用Cast函数,unity提供了多种Cast函数。比如:
- Collider2D.Cast (将碰撞体形状投射到从该碰撞体位置开始的场景中,忽略该碰撞体本身)
- Rigidbody2D.Cast (附加到 Rigidbody2D 的所有 Collider2D 形状都将投射到场景中 - 忽略附加到同一个 Rigidbody2D 的碰撞体。)
- Physics2D.RaycastAll (射线投射)
关于射线的更多内容可以参考这个⬇️,不再这篇多做赘述。【Unity2D】射线·碰撞投射·方法总结_unity raycasthit2d-CSDN博客文章浏览阅读4.8k次,点赞6次,收藏24次。射线:Physics2D.Raycast( )必须参数:起点、方向可选参数:距离、Z轴深度、过滤条件(检测哪些层、是否检测触发碰撞器等)out 参数:RaycastHit2D[ ] (存放碰撞返回的结果)返回值:int (表示碰撞结果的个数)/ RaycastHit2D(射线碰撞的结果)备注:如果射线从碰撞体内部发出,可以使用collider2d.Raycast( )方法手册链接:Physics2D-Raycast - Unity 脚本 API与碰撞体相关的射线/投射:._unity raycasthit2dhttps://blog.csdn.net/yjy99yjy999/article/details/124551072?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171535726816800213068639%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=171535726816800213068639&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-124551072-null-null.nonecase&utm_term=cast&spm=1018.2226.3001.4450这里我使用了 Rigidbody2D.Cast 函数。
//用一个数组保存投射的结果,这里我设定数组长度为10个 RaycastHit2D[] hits = new RaycastHit2D[10];//检测对象运动方向上有没有障碍物,检测距离为dist,n表示检测到的碰撞数量//contactFilter_obstacle 是提前设定好的过滤条件,如何设置要根据需求而定public bool HasObstacle(Vector2 orgDir, float dist){int n = m_rigidbody.Cast(orgDir, contactFilter_obstacle, hits, dist);return n != 0;}
Cast函数将碰撞结果保存在 hits 数组中,hits[0].normal 就是我们需要的法线。
二、对象碰撞后的运动方向
有了反射向量就很简单了。
要实现例1反弹效果的话,直接使用反射向量即可。
要实现例2贴墙继续走的话,用 OrgDir + ReflectDir 即可。注意实现时,要保持同之前运动相同的速度,就需要一些细节了,比如这样:
//入射与反射向量相加后,把向量归一化。再乘以原本要移动的距离。避免对象的运动速度骤减或突然翻倍
return (orgDir + reflectDir).normalized * moveLength;