30.核心系统-物理系统-碰撞检测函数
30.1 知识点
知识点回顾
- 如何让两个游戏物体之间产生碰撞:两个对象至少有一个刚体和两个碰撞器
- 如何让两个物体之间碰撞时表现出不同效果:使用不同的物理材质
- 触发器的作用是什么:让两个物体碰撞没有物理效果,只进行碰撞触发处理
- 注意:碰撞和触发响应函数属于特殊的生命周期函数也是通过反射调用
- 每次物理帧更新就会检测碰撞和触发响应函数
物理碰撞检测响应函数
代码
// OnCollisionEnter方法 碰撞触发接触时会自动执行这个函数
private void OnCollisionEnter(Collision collision)
{
// 获取碰撞到的对象的相关信息
Collider collider = collision.collider; // 碰撞到的对象的碰撞器信息
GameObject gameObject = collision.gameObject; // 碰撞到的对象
Transform transform = collision.transform; // 碰撞到的对象的位置信息
// 获取接触点数
int contactCount = collision.contactCount;
ContactPoint[] pos = collision.contacts; // 返回接触点类数组,可以查看每个接触点的具体的坐标
print(this.name + "被" + collision.gameObject.name + "撞到了");
}
// OnCollisionExit方法 碰撞结束分离时会自动执行的函数
private void OnCollisionExit(Collision collision)
{
print(this.name + "被" + collision.gameObject.name + "结束碰撞了");
}
// OnCollisionStay方法 两个物体相互接触摩擦时会不停的调用该函数 接触但静止时不会调用
private void OnCollisionStay(Collision collision)
{
print(this.name + "一直在和" + collision.gameObject.name + "接触");
}
图示
触发器检测响应函数
代码
// OnTriggerEnter方法 触发开始的函数 当第一次接触时会自动调用
protected virtual void OnTriggerEnter(Collider other)
{
print(this.name + "被" + other.gameObject.name + "触发了");
}
// OnTriggerExit方法 触发结束的函数 当水乳相融的状态结束时会调用一次
private void OnTriggerExit(Collider other)
{
print(this.name + "被" + other.gameObject.name + "结束水乳相融的状态了");
}
// OnTriggerStay方法 当两个对象水乳相融的时候会不停调用
private void OnTriggerStay(Collider other)
{
print(this.name + "和" + other.gameObject.name + "正在水乳相融");
}
图示
要明确什么时候会响应函数
- 只要挂载的对象能和别的物体产生碰撞或者触发,对应的这6个函数就能够被响应。前提是物理碰撞触发的是物理碰撞检测的函数,触发器触发的是触发检测的函数。
- 6个函数不是说都得写,我们一般是根据需求来进行选择书写。
- 如果是一个异形物体,刚体在父对象上,如果你想通过子对象上挂脚本检测碰撞是不行的,必须挂载到这个刚体父对象上才行。
- 要明确物理碰撞和触发器响应的区别。
碰撞和触发器函数都可以写成虚函数,在子类去重写逻辑
- 一般会把想要重写的碰撞和触发检测函数写成protected保护类型的,加上virtual变成虚函数。
- 没有必要写成public,因为不会自己手动调用碰撞和触发检测函数,都是Unity通过反射帮助我们自动调用的。
30.2 知识点代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson30_物理系统之碰撞检测_碰撞检测函数 : MonoBehaviour
{
#region 知识点回顾
//1.如何让两个游戏物体之间产生碰撞(至少1个刚体 和 两个碰撞器)
//2.如何让两个物体之间碰撞时表现出不同效果(物理材质)
//3.触发器的作用是什么(让两个物体碰撞没有物理效果,只进行碰撞处理)
#endregion
#region 注意:碰撞和触发响应函数 属于 特殊的生命周期函数 也是通过反射调用
#endregion
#region 知识点一 物理碰撞检测响应函数
//OnCollisionEnter方法 碰撞触发接触时会 自动执行这个函数
//当该碰撞体/刚体已开始接触另一个刚体/碰撞体时,调用 OnCollisionEnter。
private void OnCollisionEnter(Collision collision)
{
//Collision类型的 参数 包含了 碰到自己的对象的相关信息
//Collision类关键参数
//collider变量 碰撞到的对象碰撞器的信息
//我们撞击的 Collider(只读)。
Collider collider = collision.collider;
//gameObject变量 碰撞对象的依附对象(GameObject)
//您正在碰撞其碰撞体的 GameObject。(只读)。
GameObject gameObject = collision.gameObject;
//transform变量 碰撞对象的依附对象的位置信息
//我们撞击的对象的 Transform(只读)。
Transform transform = collision.transform;
//触碰点数相关
//contactCount变量
//获取此碰撞的接触点数。
int contactCount = collision.contactCount;
//contacts变量 返回接触点类数组 可以查看每个接触点的具体的坐标
//物理引擎生成的接触点。应避免使用它,因为它会产生内存垃圾。改用 GetContact 或 GetContacts。
ContactPoint[] pos = collision.contacts;
//只要得到了 碰撞到的对象的 任意一个信息 就可以得到它的所有信息
print(this.name + "被" + collision.gameObject.name + "撞到了");
}
//OnCollisionExit方法 碰撞结束分离时 会自动执行的函数
//当该碰撞体/刚体已停止接触另一个刚体/碰撞体时,调用 OnCollisionExit。
private void OnCollisionExit(Collision collision)
{
print(this.name + "被" + collision.gameObject.name + "结束碰撞了");
}
//OnCollisionStay方法 两个物体相互接触摩擦时 会不停的调用该函数 接触但静止时不会调用
//对应正在接触刚体/碰撞体的每一个碰撞体/刚体,每帧调用一次 :ref::OnCollisionStay。
private void OnCollisionStay(Collision collision)
{
print(this.name + "一直在和" + collision.gameObject.name + "接触");
}
#endregion
#region 知识点二 触发器检测响应函数
//OnTriggerEnter方法 触发开始的函数 当第一次接触时 会自动调用
//GameObject 与另一个 GameObject 碰撞时,Unity 会调用 OnTriggerEnter。
protected virtual void OnTriggerEnter(Collider other)
{
print(this.name + "被" + other.gameObject.name + "触发了");
}
//OnTriggerExit方法 触发结束的函数 当水乳相融的状态结束时 会调用一次
//当 Collider other 已停止接触该触发器时调用 OnTriggerExit。
private void OnTriggerExit(Collider other)
{
print(this.name + "被" + other.gameObject.name + "结束水乳相融的状态了");
}
//OnTriggerStay方法 当两个对象 水乳相融的时候 会不停调用
//对于接触触发器的每一个 Collider /other/,每次物理更新调用一次 OnTriggerStay。
private void OnTriggerStay(Collider other)
{
print(this.name + "和" + other.gameObject.name + "正在水乳相融");
}
#endregion
#region 知识点三 要明确什么时候会响应函数
//1.只要挂载的对象 能和别的物体产生碰撞或者触发 那么对应的这6个函数 就能够被响应 前提是物理碰撞触发的是物理碰撞检测的函数 触发器触发的是触发检测的函数
//2.6个函数不是说 我都得写 我们一般是根据需求来进行选择书写
//3.如果是一个异形物体,刚体在父对象上,如果你想通过子对象上挂脚本检测碰撞是不行的 必须挂载到这个刚体父对象上才行
//4.要明确 物理碰撞和触发器响应的区别
#endregion
#region 知识点四 碰撞和触发器函数都可以写成虚函数 在子类去重写逻辑
//一般会把想要重写的 碰撞和触发检测函数 写成protected保护类型的 加上virtual变成虚函数 没有必要写成public
//因为不会自己手动调用碰撞和触发检测函数 都是Unity通过反射帮助我们自动调用的
#endregion
}
30.3 练习题
在之前Input和Screen中的练习题基础上,加入一个点击鼠标左键可以发射一颗子弹飞出的功能
给坦克添加一个炮口空物体当做子弹飞出来的位置
添加点击飞出子弹代码,创建子弹预设体,拖拽赋值炮口空物体和子弹预设体
//Update内
if (Input.GetMouseButtonDown(0))
{
//实例化一个子弹对象
GameObject obj = Instantiate(bulletObj);
//设置对象的位置
obj.transform.position = bulletPos.position;
//设置对象的角度
obj.transform.eulerAngles = bulletPos.eulerAngles;
}
添加子弹飞出去的脚本,附加给子弹预设体
//Update内
//子弹一直往前飞
this.transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
在上一题的基础上,加入子弹触碰到地面会自动消失的功能
给子弹预设体添加刚体,让它受到重力的作用,添加平面
发射子弹时如果和坦克自身的碰撞和重合了,可能一开始就会被移除,所以给平面添加一个Ground标签,方便碰撞检测判断是什么对象,判断自己碰撞到的对象的标签是什么,一定是特定对象才移除自己依附的子弹对象。
假如在OnCollisionEnter写碰撞逻辑,坦克本身就带有碰撞盒 ,当子弹和坦克自身的碰撞盒碰撞可能会产生力的作用,出现一些意想不到的效果,所以改在OnTriggerEnter写逻辑
private void OnTriggerEnter(Collider other)
{
//问题一:如果发射子弹时 和坦克自身的碰撞和重合了 可能一开始 就会被移除
//解决方案:判断自己碰撞到的对象 是什么 一定是特定对象 才移除自己
if (other.gameObject.CompareTag("Ground") ||
other.gameObject.CompareTag("Monster"))
{
//碰撞到别的东西 就让子弹小时
//一定是移除自己依附的GameObject对象 而不是脚本自己
Destroy(this.gameObject);
}
//问题二:坦克本身就带有碰撞盒 当子弹和坦克自身的碰撞盒碰撞可能会产生力的作用 出现一些意想不到的效果
//解决方案:把子弹做成触发器 这样就没有了力的作用
}
在上一题的基础上,在场景加入一些立方体,每个立方体被子弹打3下就会消失
给立方体添加OnTriggerEnter碰撞检测函数逻辑,血量小于等于0就销毁自身,把立方体的标签修改为Monster,让他子弹判断触发到Monster标签的对象也销毁自身
public int HP = 3;
//当子弹碰到我时 就减血 血量为0了 就移除
private void OnTriggerEnter(Collider other)
{
//由于场景上 只有子弹时触发器 所以我们可以不用进行任何判断 就可以完成这个功能
//减血
HP -= 1;
//为0就移除自己
if (HP <= 0)
{
Destroy(this.gameObject);
}
}
30.4 练习题代码
Lesson30_物理系统之碰撞检测_练习题
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson30_物理系统之碰撞检测_练习题 : MonoBehaviour
{
#region Lesson21 练习题
public float moveSpeed = 50;
public float rotateSpeed = 50;
public Transform head;
public float headRotateSpeed = 50;
#endregion
#region Lesson22 练习题
//声明炮管尾部Transform和炮管移动速度 炮管尾部Transform在Inspector拖拽赋值
//class内
public Transform gunTail;
public float gunTailRotateSpeed = 50;
//声明摄像机看向的对象Transform和摄像机旋转的速度 摄像机看向的对象Transform在Inspector拖拽赋值
//class内
public GameObject mainCamera;
//摄像机看向的对象
public Transform target;
//摄像机旋转的速度
public float roundSpeed = 50;
#endregion
#region Lesson30 练习题一
//子弹预设体
public GameObject bulletObj;
//炮管 子弹将要出现的位置
public Transform barrelHead;
#endregion
void Update()
{
#region Lesson21 练习题
//1.使用之前的坦克预设体,用WASD键控制坦克的前景后退,左右转向
//会用到
//Transform当中的 位移 自转 相关知识点
//键盘输入 相关知识点
//可以利用默认轴向
//Input.GetAxis("Horizontal"); 水平方向 -1到1之间的值 0就是没有按下
//Input.GetAxis("Vertical"); 竖直方向 -1到1之间的值 0就是没有按下
//ws键 控制位移
// 这公式 是 : 前进方向 * 速度 * 时间 * 当前是否移动(-1~1 相当于 正向还是反向的感觉 不按就不动 0)
this.transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime * Input.GetAxis("Vertical"));
//ad键 控制 左右转向
// 这公式 是 : 转动的轴 * 速度 * 时间 * 当前是否移动(-1~1 相当于 正向还是反向的感觉 不按就不动 0)
this.transform.Rotate(Vector3.up * rotateSpeed * Time.deltaTime * Input.GetAxis("Horizontal"));
//2.在上一题的基础上,鼠标左右移动控制炮台的转向
//会用到
//鼠标输入相关
//Input.GetAxis("Mouse X");
// 这公式 是 : 转动的轴 * 速度 * 时间 * 当前是否移动(-1~1 相当于 正向还是反向的感觉 不按就不动 0)
head.Rotate(Vector3.up * headRotateSpeed * Time.deltaTime * Input.GetAxis("Mouse X"));
#endregion
#region Lesson22 练习题
//1.在输入习题的基础上,鼠标滚轮控制控制炮管的抬起放下
//使用鼠标滚轮滚动Api Input.mouseScrollDelta.y 调整炮管
//Update内
gunTail.Rotate(Vector3.right * gunTailRotateSpeed * Time.deltaTime * Input.mouseScrollDelta.y);
//2.在上一题的基础上,加入长按鼠标右键移动鼠标
// 可以让摄像机围着坦克旋转,改变观察坦克的视角
//脚本挂载到摄像机上
//让摄像机一直看向目标 检测鼠标长按后检测鼠标水平移动旋转摄像机
//Update内
//每一帧实时让摄像机看向目标对象
mainCamera.transform.LookAt(target);
//使用 绕着某一个点 的某一个轴 旋转的知识点 进行处理
//鼠标 右键长按 知识点
if (Input.GetMouseButton(1))
{
mainCamera.transform.RotateAround(target.position, //围绕的点
Vector3.up, //哪个轴
roundSpeed * Time.deltaTime * Input.GetAxis("Mouse X"));//速度 Mouse X -1到1
}
#endregion
#region Lesson30 练习题一
//在之前Input和Screen中的练习题基础上,加入一个点击鼠标左键可以发射一颗子弹飞出的功能
//左键按下
if (Input.GetMouseButtonDown(0))
{
//实例化一个子弹对象
GameObject obj = Instantiate(bulletObj);
//设置对象的位置
obj.transform.position = barrelHead.position;
//角度
//sphere.transform.rotation = barrelHead.rotation;
//角度
obj.transform.eulerAngles = barrelHead.eulerAngles;
}
#endregion
#region Lesson30 练习题二
//在上一题的基础上,加入子弹触碰到地面会自动消失的功能
#endregion
#region Lesson30 练习题三
//在上一题的基础上,在场景加入一些立方体,每个立方体被子弹打3下就会消失
#endregion
}
}
Lesson30_物理系统之碰撞检测_练习题子弹
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson30_物理系统之碰撞检测_练习题子弹 : MonoBehaviour
{
#region Lesson30 练习题一
public float moveSpeed = 20;
#endregion
void Update()
{
#region Lesson30 练习题一
//Update内
//子弹一直往前飞
this.transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
#endregion
}
#region Lesson30 练习题二
//在上一题的基础上,加入子弹触碰到地面会自动消失的功能
//private void OnCollisionEnter(Collision collision)
//{
// //问题一:如果发射子弹时 和坦克自身的碰撞和重合了 可能一开始 就会被移除
// //解决方案:判断自己碰撞到的对象 是什么 一定是特定对象 才移除自己
// if (collision.gameObject.CompareTag("Ground"))
// {
// //碰撞到别的东西 就让子弹小时
// //一定是移除自己依附的GameObject对象 而不是脚本自己
// Destroy(this.gameObject);
// }
// //问题二:坦克本身就带有碰撞盒 当子弹和坦克自身的碰撞盒碰撞可能会产生力的作用 出现一些意想不到的效果
// //解决方案:把子弹做成触发器 这样就没有了力的作用
//}
private void OnTriggerEnter(Collider other)
{
//问题一:如果发射子弹时 和坦克自身的碰撞和重合了 可能一开始 就会被移除
//解决方案:判断自己碰撞到的对象 是什么 一定是特定对象 才移除自己
if (other.gameObject.CompareTag("Ground") ||
other.gameObject.CompareTag("Monster"))
{
//碰撞到别的东西 就让子弹小时
//一定是移除自己依附的GameObject对象 而不是脚本自己
Destroy(this.gameObject);
}
//问题二:坦克本身就带有碰撞盒 当子弹和坦克自身的碰撞盒碰撞可能会产生力的作用 出现一些意想不到的效果
//解决方案:把子弹做成触发器 这样就没有了力的作用
}
#endregion
}
Lesson30_物理系统之碰撞检测_练习题立方体
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson30_物理系统之碰撞检测_练习题立方体 : MonoBehaviour
{
#region Lesson30 练习题三
//在上一题的基础上,在场景加入一些立方体,每个立方体被子弹打3下就会消失
public int HP = 3;
//当子弹碰到我时 就减血 血量为0了 就移除
private void OnTriggerEnter(Collider other)
{
//由于场景上 只有子弹时触发器 所以我们可以不用进行任何判断 就可以完成这个功能
//减血
HP -= 1;
//为0就移除自己
if (HP <= 0)
{
Destroy(this.gameObject);
}
}
#endregion
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com