碰撞体(Collider)
碰撞体的作用
- 检测碰撞
- 产生阻碍
碰撞体的类型
碰撞体确定了一个碰撞区域,这个区域的形状支持Unity自带的几个几何图元,有以下几种碰撞器:
- BoxCollider
- SphereCollider
- (。。。)
添加碰撞体
- 可以给一个物体添加多个碰撞体。当撞到任何一个碰撞体时,可以检测到这个物体被撞了。
- 如果要检测物体的哪个部分被撞了,不能给这个物体添加多个碰撞体,而是添加多个子物体,给每个子物体添加一个碰撞体。
- 一个非Trigger的碰撞体内部不应该有非Trigger碰撞体,这样会自己撞到自己。
阻碍型碰撞检测
- 在MonoBehaviour中可以添加一组
OnCollisionXXX
函数来检测碰撞。 - 这组函数有3个
OnCollisionEnter
、OnCollisionStay
、OnCollisionExit
。它们分别表示,和另一个碰撞体接触了、持续碰撞中、脱离碰撞。 - 一个完整的碰撞过程中,
OnCollisionEnter
和OnCollisionExit
只会触发一次。OnCollisionStay
会触发多次。 - 这3个碰撞检测函数的参数都是
Collision
。 - 碰撞总是在两个物体间发生的,
Collision
参数中存有碰撞时对方的信息。
触发型碰撞检测
- 如果将碰撞器的isTrigger属性设为true,碰撞体将不会产生阻碍,但依然可以用它来检测碰撞。
- 和阻碍性碰撞类似,有3个函数检测非阻碍性碰撞:
OnTriggerEnter
,OnTriggerStay
,OnTriggerExit
。它们分别表示,和另一个碰撞体接触了、持续碰撞中、脱离碰撞。
碰撞检测的前提
要想检测到碰撞,需要满足以下条件:
- 发生碰撞的双方都有碰撞体
- 其中一方有刚体
做碰撞检测的步骤
- 确定写代码的位置。分析碰撞的两者中谁要检测这个碰撞,谁检测就在谁的脚本中写代码。有可能两者都可以去做这个检测,那选择其中一个就行。
- 确定用哪组检测函数。分析这个碰撞是否是阻碍性的,如果是选用
OnCollisionXXX
函数,非阻碍性的碰撞则用OnTriggerXXX
函数检测。 - 确定检测时机。
OnCollisionXXX
和OnTriggerXXX
,其XXX
部分可以替换为Enter
,Stay
,Exit
。如果是持续检测用Stay
,如果时单次检测,碰撞体接近时检测用Enter
,碰撞体离开时检测用Exit
。 - 编写碰撞处理代码。在编写过程中可以使用参数获取碰撞时对方的信息。
Demo
- Demo 推箱子
本游戏中有一个核心技术点是:人推着箱子走。这时人和箱子发生了碰撞。本案例的两个难点是:
- 人和箱子谁来检测碰撞。
- 如何推动箱子。
实现思路是:箱子来检查碰撞。在碰撞接触时记录下人和箱子间的位置偏移。在碰撞过程中保持人和箱子的相对位置偏移不变。
- Demo 子弹反射
当子弹撞到墙时反弹。
using UnityEngine;
using System.Collections;
public class Bullet : MonoBehaviour {
// 要移动方向
Vector3 direction;
// Use this for initialization
void Start () {
// 初始化移动方向
direction = transform.forward;
}
// Update is called once per frame
void Update () {
// 朝着要移动方向移动
transform.Translate (direction.normalized * 5 * Time.deltaTime, Space.World);
}
void OnCollisionEnter(Collision other) {
// 计算法线
Vector3 normal = Vector3.zero;
switch (other.gameObject.name) {
case "Left":
case "Right":
normal = Vector3.right;
break;
case "Up":
case "Down":
normal = Vector3.forward;
break;
}
// 计算反射向量,并修改要移动的方向。
direction = Vector3.Reflect (direction, normal);
}
}
- Demo 打砖块
当小球撞到墙或者玩家的挡板时反弹。
using UnityEngine;
using System.Collections;
public class Ball : MonoBehaviour {
Vector3 direction;
// Use this for initialization
void Start () {
direction = transform.up;
}
// Update is called once per frame
void Update () {
transform.Translate (direction.normalized * 4 * Time.deltaTime, Space.World);
}
void OnCollisionEnter(Collision collision) {
Vector3 inNormal = Vector3.zero;
switch (collision.gameObject.name) {
case "Left":
case "Right": inNormal = collision.transform.right; break;
case "Up":
case "Player":inNormal = collision.transform.up; break;
}
direction = Vector3.Reflect (direction, inNormal);
}
}