程序设计基础
登录
< 返回教程列表

3.4 物理与碰撞检测

知识介绍

碰撞的三个条件: 两个物体都有 Collider,其中至少一个带 Rigidbody。

碰撞器 Collider 类型:

触发 vs 碰撞:

类型 Collider 设置 特点
Collision IsTrigger = 关 会发生物理反弹、位置挤压
Trigger IsTrigger = 开 会穿过,只触发事件

碰撞事件(Collision):

// 碰撞开始
private void OnCollisionEnter(Collision collision)
{
    Debug.Log($"撞到了:{collision.gameObject.name}");
    // 获取接触点信息
    ContactPoint[] contacts = collision.contacts;
}
// 碰撞持续中
private void OnCollisionStay(Collision collision) { }
// 碰撞结束
private void OnCollisionExit(Collision collision) { }

触发事件(Trigger):

private void OnTriggerEnter(Collider other)
{
    Debug.Log($"进入触发区:{other.name}");
    if (other.CompareTag("Coin"))   // 用 Tag 判断对象类型
    {
        Destroy(other.gameObject);  // 吃掉金币
        Debug.Log("+1金币");
    }
}
private void OnTriggerStay(Collider other) { }
private void OnTriggerExit(Collider other) { }

Rigidbody 物理控制:

private Rigidbody _rb;
private void Awake()
{
    _rb = GetComponent<Rigidbody>();
}

void MoveWithPhysics()
{
    // 推荐在 FixedUpdate 中操作 Rigidbody
    _rb.velocity = new Vector3(h * speed, _rb.velocity.y, v * speed);  // 直接设速度
    _rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);           // 加力(跳跃)
    _rb.MovePosition(transform.position + dir * speed * Time.fixedDeltaTime);  // 移动
}

例题

例题: 简单跳跃+金币拾取。

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class PlayerController : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float jumpForce = 6f;
    public LayerMask groundLayer;       // 地面层(在 Inspector 设置)
    public Transform groundCheck;       // 脚底检测点(拖一个空物体到角色脚下)
    public float checkRadius = 0.2f;

    private Rigidbody _rb;
    public int coinCount;               // 金币数

    private void Awake()
    {
        _rb = GetComponent<Rigidbody>();
    }

    private void FixedUpdate()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 dir = new Vector3(h, 0, v);
        if (dir.magnitude > 1) dir.Normalize();

        // 水平移动(保留 Y 轴速度以受重力影响)
        _rb.velocity = new Vector3(dir.x * moveSpeed, _rb.velocity.y, dir.z * moveSpeed);
    }

    private void Update()
    {
        // 检测是否在地面
        bool isGrounded = Physics.CheckSphere(groundCheck.position, checkRadius, groundLayer);

        // 跳跃
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            _rb.velocity = new Vector3(_rb.velocity.x, 0, _rb.velocity.z);
            _rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }
    }

    // 吃到金币(金币对象需要:Collider 勾 IsTrigger,Tag = Coin)
    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Coin"))
        {
            coinCount++;
            Debug.Log($"金币 +1,当前 {coinCount}");
            Destroy(other.gameObject);
        }
    }

    // 可视化脚底检测范围
    private void OnDrawGizmosSelected()
    {
        if (groundCheck != null)
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireSphere(groundCheck.position, checkRadius);
        }
    }
}

练习题

  1. 给场景搭一个简单关卡:Plane 做地面,几个 Cube 做障碍,一个 Sphere 做 Player(带 Rigidbody),写脚本让 Player 被碰撞后 Debug.Log 输出撞到了什么。

  2. 做一个"终点线":放一个 Cube 作墙,勾 IsTrigger,Tag 设为 Finish;玩家进入触发区时输出"胜利!"。

  3. 做子弹:按左键在角色前方向前发射一个小球(预制体+Rigidbody),用 AddForce 给它向前的速度;小球碰到 Tag="Enemy" 的物体时两个都销毁。

  4. 做伤害区域:在地面放一个红色 Cube 作"熔岩",勾 IsTrigger,OnTriggerStay 中每帧扣玩家血量;当血量<=0 时销毁玩家。