碰撞体(Collider)

碰撞体的作用

  • 检测碰撞
  • 产生阻碍

碰撞体的类型

碰撞体确定了一个碰撞区域,这个区域的形状支持Unity自带的几个几何图元,有以下几种碰撞器:

  • BoxCollider
  • SphereCollider
  • (。。。)

添加碰撞体

  • 可以给一个物体添加多个碰撞体。当撞到任何一个碰撞体时,可以检测到这个物体被撞了。
  • 如果要检测物体的哪个部分被撞了,不能给这个物体添加多个碰撞体,而是添加多个子物体,给每个子物体添加一个碰撞体。
  • 一个非Trigger的碰撞体内部不应该有非Trigger碰撞体,这样会自己撞到自己。

阻碍型碰撞检测

  • 在MonoBehaviour中可以添加一组OnCollisionXXX函数来检测碰撞。
  • 这组函数有3个OnCollisionEnterOnCollisionStayOnCollisionExit。它们分别表示,和另一个碰撞体接触了、持续碰撞中、脱离碰撞。
  • 一个完整的碰撞过程中,OnCollisionEnterOnCollisionExit只会触发一次。OnCollisionStay会触发多次。
  • 这3个碰撞检测函数的参数都是Collision
  • 碰撞总是在两个物体间发生的,Collision参数中存有碰撞时对方的信息。

触发型碰撞检测

  • 如果将碰撞器的isTrigger属性设为true,碰撞体将不会产生阻碍,但依然可以用它来检测碰撞。
  • 和阻碍性碰撞类似,有3个函数检测非阻碍性碰撞:OnTriggerEnter,OnTriggerStay,OnTriggerExit。它们分别表示,和另一个碰撞体接触了、持续碰撞中、脱离碰撞。

碰撞检测的前提

要想检测到碰撞,需要满足以下条件:

  • 发生碰撞的双方都有碰撞体
  • 其中一方有刚体

做碰撞检测的步骤

  1. 确定写代码的位置。分析碰撞的两者中谁要检测这个碰撞,谁检测就在谁的脚本中写代码。有可能两者都可以去做这个检测,那选择其中一个就行。
  2. 确定用哪组检测函数。分析这个碰撞是否是阻碍性的,如果是选用OnCollisionXXX函数,非阻碍性的碰撞则用OnTriggerXXX函数检测。
  3. 确定检测时机。OnCollisionXXXOnTriggerXXX,其XXX部分可以替换为Enter,Stay,Exit。如果是持续检测用Stay,如果时单次检测,碰撞体接近时检测用Enter,碰撞体离开时检测用Exit
  4. 编写碰撞处理代码。在编写过程中可以使用参数获取碰撞时对方的信息。

Demo

  • Demo 推箱子

本游戏中有一个核心技术点是:人推着箱子走。这时人和箱子发生了碰撞。本案例的两个难点是:

  1. 人和箱子谁来检测碰撞。
  2. 如何推动箱子。

实现思路是:箱子来检查碰撞。在碰撞接触时记录下人和箱子间的位置偏移。在碰撞过程中保持人和箱子的相对位置偏移不变。



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