砰砰叫,谁动了她的奶酪让你的小鹿乱撞了。基于此,亦即碰撞与触发的过程。
碰撞器与触发器的区别
通俗点讲,碰撞器检测碰撞,触发器检测触发,讲了跟没讲似的。碰撞器是用来检测碰撞事件的,在unity中,两个物体能检测到碰撞的前提是两个物体都有碰撞器,且至少有一个是刚体,在引擎中,很多时候都用到了碰撞与交互。 而触发器是碰撞器的一种,一旦勾选了触发器就检测不到碰撞了,这就是碰撞器与触发器的主要区别,触发器可以实现很多物体之间产生的交互效果,而不想用碰撞检测即两物体是穿透的。就比如你跟你的npy擦肩而过,念念不忘是不是一种触发,你跟ta刚好碰到的那一刻脸红了是不是一种碰撞。物体间触发过程能产生一些事件,而碰撞过程就不只是会产生一些事件,还有物体间的接触。
碰撞小球与触发小球
实验内容:
-
新建工程,导入附件中的资源包"Physics.unitypackage",打开场景physics,场景可以实现通过键盘控制绿色小球左右移动。尝试理解脚本GroundMove和BallController的逻辑,并给脚本加上注释。
-
在Unity的资源商店中下载一个免费的音频资源并导入到工程中。
-
在场景中的地板增加若干球体、立方体,通过调整球体和立方体的属性,使得部分物体可碰撞,部分物体可触发。移动绿色小球来拦截这些物体,使得碰撞时通过OnCollisionEnter播放一种音效,并销毁被碰撞的物体;经过可触发对象时通过OnTriggerEnter播放另一种音效,并将该物体的颜色改为红色。
-
选做:给地板增加贴图、给游戏增加UI来记录碰撞的次数和触发的次数。
提示:
1)给小球增加声音组件,component->audio->audio source,并实现音频播放功能。
2)将两个不同的声音片段分别拖到Inspector面板中脚本的SC_Sound和E_Sound处。
3)后面增加的球体、立方体等根据需要增加刚体组件并调整isTrigger属性、重力属性等相关属性。
4)给小球的脚本增加相关的事件处理函数。
先导入资源包,然后观察刚体与碰撞体。注意一下,碰撞检测中的至少一个是刚体指的是普通刚体碰撞体。
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GroundMove : MonoBehaviour
{
public float moveSpeed = 5f;//控制地板移动的速度
private Vector3 startPos;
void Start()
{
startPos = transform.position;//地板的初始位置
}
void FixedUpdate()
{
// 地板向摄像机方向移动
transform.Translate(Vector3.back * moveSpeed * Time.deltaTime);//往后移,即z轴负方向
// 如果地板移出摄像机视野范围,则重新生成在起始位置
if (transform.position.z < Camera.main.transform.position.z)//检查地板是否移出了摄像机的视野范围
{
transform.position = startPos;//循环移动
}
}
}
cs
using UnityEngine;
public class BallController : MonoBehaviour
{
public float moveSpeed = 5f;//控制球体的移动速度,并设置默认值为5
public float maxDistance = 2.4f;//限制球体移动的最大距离,并设置默认值为2.4
void FixedUpdate()
{
float moveInput = Input.GetAxis("Horizontal");//获取水平方向上的输入,表示左右方向键的按下情况
//根据水平输入、移动速度和时间间隔计算出球体应该移动到的目标位置。
Vector3 targetPosition = transform.position + Vector3.right * moveInput * moveSpeed * Time.deltaTime;
//限制目标位置的x轴坐标在 - maxDistance到maxDistance之间,以确保球体不会超出指定的最大移动距离。
targetPosition.x = Mathf.Clamp(targetPosition.x, -maxDistance, maxDistance);
transform.position = targetPosition;//球体的位置设置为目标位置,实现球体的水平移动。
}
}
通过这些代码可以控制小球在一个地面运动,到达一定距离时回到原来位置重新移动。然后在场景中加多几个方块与球体对象作障碍物实现碰撞检测与触发检测。
这里记得要选好预制体,要不调参时就得点到手疼了。 选合适的纹理进行颜色贴图,然后找音频资源导入,给运动的小球加脚本等相关的配置。
你可能会遇到物体被撞飞发生形变的状况,那要是想碰撞使方块改变颜色而不产生形变怎么办,这里可以用刚体约束。
把它定在原地。
这里主要编写的碰撞触发代码,这里用了碰撞去销毁,触发去改变颜色。然后勾选触发器时,刚体默认是启用重力的,想一下如果没有碰撞盒是不是就会往下掉了,因此在触发器的物体把启用重力去掉即可。
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ent : MonoBehaviour
{
public AudioClip collisionSound;
public AudioClip triggerSound;
private int collisioncnt=0;
private int triggercnt=0;
private AudioSource audioSource;
private void Start()
{
// 获取 AudioSource 组件
audioSource = GetComponent<AudioSource>();//this.GetComponent
}
IEnumerator UI_Update()
{
yield return new WaitForSeconds(0.5f);//让程序等待 0.5 秒钟,然后再继续执行协程中的下一条语句
}
private void OnCollisionEnter(Collision collision)
{
Debug.Log("与"+collision.gameObject.name+"发生碰撞");
audioSource.clip = collisionSound;
audioSource.Play();
if (collision.gameObject.CompareTag("Cube"))
{
//调用父类中的销毁函数销毁触碰到的对象物体
collisioncnt++;
Destroy(collision.gameObject);
StartCoroutine("UI_Update");//启动UI_Update协程
UIEnt._instance.SetText(collisioncnt, triggercnt);
}
}
private void OnCollisionStay(Collision collision)
{
collision.gameObject.GetComponent<MeshRenderer>().material.color = Color.red;
Debug.Log("与" + collision.gameObject.name + "处于碰撞");
}
private void OnCollisionExit(Collision collision)
{
Debug.Log("与" + collision.gameObject.name + "碰撞解除");
this.gameObject.GetComponent<MeshRenderer>().material.color = Color.green;
}
private void OnTriggerEnter(Collider other)
{
Debug.Log(other.gameObject.name + "进入触发区域");
this.gameObject.GetComponent<MeshRenderer>().material.color = Color.blue;
audioSource.clip = triggerSound;
audioSource.Play();
//调用父类中的销毁函数销毁触碰到的对象物体
triggercnt++;
StartCoroutine("UI_Update");//启动UI_Update协程
UIEnt._instance.SetText(collisioncnt,triggercnt);
}
private void OnTriggerStay(Collider other)
{
Debug.Log(other.gameObject.name + "处于碰撞区域");
other.gameObject.GetComponent<MeshRenderer>().material.color = Color.red;
}
private void OnTriggerExit(Collider other)
{
Debug.Log(other.gameObject.name + "离开触发区域"+ triggercnt);
other.gameObject.GetComponent<MeshRenderer>().material.color = Color.yellow;
this.gameObject.GetComponent<MeshRenderer>().material.color = Color.green;
}
}
这里的碰撞与触发检测的方法还是比较重要的,然后写脚本要记得找准对象的组件元素。接着写一个ui界面来记录碰撞的次数和触发的次数。
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIEnt : MonoBehaviour
{
//单例模式,保证实例的唯一性。
public static UIEnt _instance;
public Text countText;
public int initCount;
//在游戏对象被实例化时调用,将当前实例赋值给私有变量 _instance
private void Awake()
{
_instance = this;
}
//在脚本启动时调用,初始化了几个文本框的文本内容
void Start()
{
//初始化设置Text文本框
countText.text = "Count:" + initCount.ToString();
}
//当玩家触碰到Cube对象时可以被调用
public void SetText(int colcnt,int tricnt)
{
countText.text = "碰撞次数: " + colcnt.ToString()+" 触发次数: " + tricnt.ToString();
}
}
多调试几次,你就会明白碰撞与触发的区别了。
实验心得
假期了,你负责玩,我负责写,安全感还是要有的。