< 返回教程列表
3.4 物理与碰撞检测
知识介绍
碰撞的三个条件: 两个物体都有 Collider,其中至少一个带 Rigidbody。
碰撞器 Collider 类型:
BoxCollider— 立方体SphereCollider— 球CapsuleCollider— 胶囊(常用在角色)MeshCollider— 网格(性能高但一般只能勾选 Convex)
触发 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);
}
}
}
练习题
给场景搭一个简单关卡:Plane 做地面,几个 Cube 做障碍,一个 Sphere 做 Player(带 Rigidbody),写脚本让 Player 被碰撞后 Debug.Log 输出撞到了什么。
做一个"终点线":放一个 Cube 作墙,勾 IsTrigger,Tag 设为
Finish;玩家进入触发区时输出"胜利!"。做子弹:按左键在角色前方向前发射一个小球(预制体+Rigidbody),用
AddForce给它向前的速度;小球碰到 Tag="Enemy" 的物体时两个都销毁。做伤害区域:在地面放一个红色 Cube 作"熔岩",勾 IsTrigger,
OnTriggerStay中每帧扣玩家血量;当血量<=0 时销毁玩家。