云计算百科
云计算领域专业知识百科平台

Unity笔记(四)——Camera、碰撞检测函数、刚体加力、音频

写在前面:

写本系列(自用)的目的是回顾已经学过的知识、记录新学习的知识或是记录心得理解,方便自己以后快速复习,减少遗忘。主要是C#代码部分。

十三、Camera

1、重要静态成员

(1)获取摄像机

获取主摄像机使用:Camera.main,要注意获取主摄像机时,场景中一定至少要有一个摄像机tag为MainCamera。如果有多个主摄像机,会随机返回一个。

获取当前场景中摄像机的个数:Camera.allCamerasCount

获取所有的摄像机:Camera.allCameras

void Start()
{
print(Camera.main.name);

print(Camera.allCamerasCount);

Camera[] allCamera = Camera.allCameras;
print(allCamera.Length);
}

(2)渲染相关委托

目前这里仅做了解,知道有这几个函数,后续用到再学

Camera.onPreCull,摄像机剔除前处理的委托函数

Camera.onPreRender,摄像机渲染前处理的委托

Camera.onPostRender,摄像机渲染后处理的委托

搭配匿名函数使用

void Start()
{
Camera.onPreCull += (c) =>
{

};

Camera.onPreRender += (c) =>
{

};

Camera.onPostRender -= (c) =>
{

};
}

2、重要成员

(1)Inspector上的参数

Inspector面板上的参数都可以通过Camera获得并修改:

例如,获取并修改参数Depth:

Camera.main.depth = 10;

(2)世界(场景)坐标转为屏幕坐标

其中,屏幕坐标的x、y分别表示的是该点(场景上的点)在屏幕上的坐标,而z轴表示3D物理离摄像机有多远。使用的函数是Camera.main.WorldToScreenPoint()

void Start()
{
Vector3 v = Camera.main.WorldToScreenPoint(this.transform.position);
print(v);
}

图片上半部分是场景,下半部分是屏幕

(3)屏幕坐标转为世界坐标

可以捕捉鼠标在屏幕上的坐标,然后将其转换为场景坐标,这样可以实现通过鼠标控制场景上的物体,使用的函数是:Camera.main.ScreenToWorldPoint()

public GameObject obj;
void Update()
{
Vector3 v = Input.mousePosition;
v.z = 10;
obj.transform.position = Camera.main.ScreenToWorldPoint(v);
}

这里解释一下为什么要将鼠标的z轴坐标设置为10。上文提到,在屏幕上的坐标,z轴表示3D物理离摄像机有多远。鼠标的z轴默认为0,假如不更改鼠标的z轴,就会导致鼠标坐标转换为场景坐标时,离摄像机的距离为0。满足离摄像机距离为0的点只有1个,即摄像机本身的坐标。这样无论你怎么动,得到的场景坐标都不会变。

z轴可以设置为任意非0值,转换后的z轴坐标保持为这个固定值不变。看起来的效果是无论鼠标怎么移动,在场景上都是在一个x、y平面上移动。

十四、碰撞检测函数

1、物理碰撞检测响应函数

碰撞检测响应函数有三个,它们也是一种特殊的生命周期函数:

private void OnCollisionEnter(Collision collision):碰撞触发接触时,会自动执行

private void OnCollisionExit(Collision collision):碰撞结束分离时,会自动执行

private void OnCollisionStay(Collision collision):两个物体相互接触摩擦时,会自动执行

而参数列表中,Collision类型的参数包含了碰到自己的对象的相关信息:

1、碰撞到的对象碰撞器的信息

collision.collider

2、碰撞对象的依附对象

collision.gameObject

3、碰撞对象的依附对象的位置信息

collision.transform

4、碰撞点数相关

collision.contactCount

接触点具体的坐标:ContactPoint[] pos = collision.contacts;

只要得到了碰撞对象的任意一个信息,就可以得到它的所有信息

private void OnCollisionEnter(Collision collision)
{
print(this.name + "被" + collision.gameObject.name + "撞到了");
}

private void OnCollisionExit(Collision collision)
{
print(this.name + "被" + collision.gameObject.name + "结束碰撞了");
}

private void OnCollisionStay(Collision collision)
{
print(this.name + "一直在和" + collision.gameObject.name + "接触");
}

2、触发器检测响应函数

触发器可以模拟子弹穿过物体,它和物体会结束但是不会产生碰撞效果。在Inspector面板里勾选is Trigger即可将物体作为触发器。

触发器检测响应函数同样有三个:

private void OnTriggerEnter(Collider other):触发器接触物体时,会自动执行

private void OnTriggerExit(Collider other):触发器和物体分开时,会自动执行

private void OnTriggerStay(Collider other):触发器和物体在“融合”状态时,会自动执行,融合状态就是触发器穿过物体时:

private void OnTriggerEnter(Collider other)
{
print(this.name + "被" + other.gameObject.name + "触发了");
}

private void OnTriggerExit(Collider other)
{
print(this.name + "和" + other.gameObject.name + "结束接触了");
}

private void OnTriggerStay(Collider other)
{
print(this.name + "和" + other.gameObject.name + "正在融合状态");
}

只要挂载的对象能和别的物体产生碰撞或者触发,那么以上的6个函数就能够响应

如果是异形物体,刚体在父对象上,如果想通过子对象上挂脚本检测碰撞是不行的,必须要挂载父对象上

另外,访问修饰符不一定要写private,可以写protected,这样可以把函数写成虚函数在子类中重写。而写成public没有必要,因为我们一般不会自己调用这几个函数,都是unity来调用

十五、刚体加力

1、刚体自带添加力的方法

在对刚体添加力之前,需要先获取刚体组件:rigidbody = GetComponent<Rigidbody>();

(1)添加力

添加力可以分别相对世界坐标、相对于本地坐标,分别用:

rigidbody.AddForce()、rigidbody.AddRelativeForce(),括号里传入添加的力,力包含力的方向和力的大小。

加力后,对象是否停止移动是由阻力决定的,如果阻力为0,娜给过力后是始终不会停止移动的。

Rigidbody rigidbody;
void Start()
{
rigidbody = GetComponent<Rigidbody>();

//相对世界坐标
rigidbody.AddForce(Vector3.forward * 10);

//相对本地坐标
rigidbody.AddRelativeForce(Vector3.forward * 10);
}

(2)添加扭矩力,让其旋转

添加扭矩力同样可以分别相对世界坐标、相对于本地坐标,分别用:

rigidbody.AddTorque()、rigidbody.AddRelativeTorque()

Rigidbody rigidbody;
void Start()
{
rigidbody = GetComponent<Rigidbody>();

rigidbody.AddTorque(Vector3.up * 10);

rigidbody.AddRelativeTorque(Vector3.up * 10);
}

(3)直接改变速度

可以使用rigidbody.velocity直接改变速度。这个速度方向是相对于世界坐标系的。

Rigidbody rigidbody;
void Start()
{
//直接改变速度,速度方向相对于世界坐标系
rigidbody.velocity = Vector3.forward * 5;
}

(4)模拟爆炸效果

模拟爆炸效果采用的方法是:rigidbody.AddExplosionForce(),里面有三个参数,分别传入力的大小、爆炸中心、爆炸半径。

假如模拟爆炸需要多个对象,那么这些对象都需要添加刚体,并且挂载这个脚本,才会达到爆炸的效果。

Rigidbody rigidbody;
void Start()
{
//模拟爆炸效果
rigidbody.AddExplosionForce(10, Vector3.zero, 10);
}

2、力的几种模式

力的模式主要的作用就是计算方式不同。共有四种方法,四种方法计算方式的不同,最终的移动速度也会不同。

我们知道,由动量定理:Ft = mv。其中,F是力,t是时间,m是质量,v是速度。那么 v = Ft/m。

(1)Acceleration

当添加力,使用的模式是Acceleration时:

rigidbody.AddForce(Vector3.forward * 10, ForceMode.Acceleration);

这个模式下力会给物体增加一个持续的加速度,忽略其质量。在这个代码下F:(0, 0, 10),t:0.02(默认物理帧更新时间)、m:默认为1,那么此时的v = 0.2m/s。

(2)Force

当添加力,使用的模式是Force时:

rigidbody.AddForce(Vector3.forward * 10, ForceMode.Force);

这个模式下力会给物体添加一个持续的力,与物体的质量有关。在这个代码下F:(0, 0, 10),t:0.02(默认物理帧更新时间)、m:2kg(假设为2kg,可以修改),那么此时的v = 0.1m/s。

(3)Impulse

当添加力,使用的模式是Impulse时:

rigidbody.AddForce(Vector3.forward * 10, ForceMode.Impulse);

这个模式下力会给物体添加一个瞬间的力,与物体的质量有关,忽略时间,默认为1。在这个代码下F:(0, 0, 10),t:1(默认)、m:2kg(假设为2kg,可以修改),那么此时的v = 5m/s。

(4)VelocityChange

当添加力,使用的模式是VelocityChange时:

rigidbody.AddForce(Vector3.forward * 10, ForceMode.VelocityChange);

这个模式下力会给物体添加一个瞬间的速度,忽略质量,忽略时间。在这个代码下F:(0, 0, 10),t:1(默认)、m:1(默认),那么此时的v = 10m/s。

3、力场

可以直接在Inspector面板中Add component添加组件:

这样在某些不必要使用代码的情况下会更加方便。

4、刚体的休眠

为节省性能,Unity会在一些情况下让刚体休眠。若是不希望刚体休眠,可以使用如下代码:

if(rigidbody.IsSleeping())
{
rigidbody.WakeUp();
}

十六、音频相关

Unity常用音频格式有:wav、mp3、ogg、aiff

1、代码控制播放停止暂停

在控制播放停止暂停前,需要先获取对象上依附的音频源脚本:

AudioSource audioSource;

void Start()
{
audioSource = GetComponent<AudioSource>();
}

在获取音频源脚本后,就可以控制音频的播放、停止、暂停。

控制播放:audioSource.Play();

控制停止:audioSource.Stop();

控制暂停:audioSource.Pause();

void Update()
{
if(Input.GetKeyDown(KeyCode.P))
{
audioSource.Play();
}

if(Input.GetKeyDown(KeyCode.S))
{
audioSource.Stop();
}

if(Input.GetKeyDown(KeyCode.Space))
{
audioSource.Pause();
}
}

此外,还要一些不是很常用的,比如

控制停止暂停(和暂停后使用paly()其实一样):audioSource.UnPause();

延迟n秒后播放:audioSource.PlayDelayed(n);

void Update()
{
if (Input.GetKeyDown(KeyCode.X))
{
audioSource.UnPause();
}

if (Input.GetKeyDown(KeyCode.X))
{
//填写的是秒数
audioSource.PlayDelayed(5);
}
}

2、检测音效播放完毕

如果希望某个音效播放完毕后,想要做什么事,那么就可以在Update生命函数中不断地去检测音频是否还在播放

void Update()
{
if(audioSource.isPlaying)
{
print("播放中");
}
else
{
print("播放完毕");
}
}

3、动态控制音效播放

当我们需要做受伤音效、战败音效等,就需要动态控制音效播放。第一种动态播放音效的方法就是上文第一点,代码控制播放停止暂停,直接在要播放音效的对象上挂载脚本,控制播放。此外,还要两种方法:

(1)实例化挂载了音效源脚本的对象

这种方法用的比较少。我们可以创建一个自身挂有音效源脚本的预设体,在人物受伤时,实例化这个预设体来达到效果:

Instrantiate(obj)

(2)用一个AudioSouce控制播放不同音效

一个对象可以挂载多个AudioSouce。我们可以自己创建一个音频切片,并且将需要播放的音效拖入Inspector面板。之后就可以自行创建AudioSouce进行关联clip,达到控制播放不同音效的效果。

public AudioClip clip;

void Start()
{
audioSource = GetComponent<AudioSource>();
AudioSource aus = this.gameObject.AddComponent<AudioSource>();
aus.clip = clip;
aus.Play();
}

4、麦克风相关

(1)获取设备麦克风信息

可以使用Microphone.devices获取设备的所有麦克风设备

void Start()
{
string[] strs = Microphone.devices;
for(int i = 0; i < strs.Length; i++)
{
print(strs[i]);
}
}

(2)开始录制、结束录制

开始录制使用方法:Microphone.Start(),它会返回音频切片,可以创建音频切片接收。能传入4个参数:

参数一:设备名,传空使用默认设备

参数二:超过录制时长后,是否重头录制。填true就是重头,false不重头

参数三:录制时长(秒)

参数四:采样率

结束录制使用方法:Microphone.End(),传入一个参数:设备名

void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
clip = Microphone.Start(null, false, 10, 44100);
}

if (Input.GetKeyUp(KeyCode.Space))
{
Microphone.End(null);
AudioSource s = this.GetComponent<AudioSource>();
if(s == null)
{
s = this.gameObject.AddComponent<AudioSource>();
}
s.clip = clip;
s.Play();
}
}

(3)获取音频数据用于存储或传输

用于存储数组数据的长度,是用声道数*剪辑长度,这是固定的,使用:clip.channels*clip.samples。那么创建数组就可以使用代码:float[] f = new float[clip.channels*clip.samples];

将音频数据存入数组使用:clip.GetData(f, 0),第一个参数是存储的数组,第二个参数是偏移位置,默认为0

这里结合上文开始结束录制,来进行音频数据的获取。

void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
clip = Microphone.Start(null, false, 10, 44100);
}

if (Input.GetKeyUp(KeyCode.Space))
{
Microphone.End(null);
AudioSource s = this.GetComponent<AudioSource>();
if(s == null)
{
s = this.gameObject.AddComponent<AudioSource>();
}
s.clip = clip;
s.Play();

//创建数组
float[] f = new float[clip.channels*clip.samples];
//存储数据
clip.GetData(f, 0);

print(f.Length);
}
}

至此,入门部分结束

赞(0)
未经允许不得转载:网硕互联帮助中心 » Unity笔记(四)——Camera、碰撞检测函数、刚体加力、音频
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!