首先我们看到Weapon_01的一些内容:注意层级关系
Ryun_WeaponController 脚本制作
给出子弹连发的具体脚本,新建一个Ryun_WeaponController 脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ryun_WeaponController : MonoBehaviour
{
public Transform bulletstartpoint;
public GameObject bullet;//首先在hierarchy窗口新建一个bullet_001,设置好Material材质,调整大小后将其生成预制体。后面将该预制体拖给bullet变量
public float bulletstartspeed = 100;//设置子弹初始的发射速度为100
public float fireinterval = 0.1f;//设置子弹开火间隔时间为0.1f
public bool isFire = false;//设置布尔类型的isFire初始化为false
public Transform dufalutpoint;
public Transform Backpoint;//为子弹后坐力动画设置两个Transform类型的位置,分别代表默认位置和后坐力位置,调整这两个Transform的位置(一前一后)
public float lerpRatio = 0.2f;//设置插值ratio为0.2f.
public AudioSource shootaudio;//定义一个AudioSource 类型的音频变量,首先要在Weapon_01上挂载一个AudioSource组件,并加载开火音效。设置Play On Awake为false
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
OpenFire();
}
private void OpenFire()//控制动画开火的函数
{
if (Input.GetMouseButtonDown(0))//按下鼠标左键瞬间
{
isFire = true;
StartCoroutine("Fire");//开火时调用协程函数
}
if (Input.GetMouseButton(0))
{
}
if (Input.GetMouseButtonUp(0))
{
isFire = false;
StopCoroutine("Fire");//如果鼠标松开,isFire设置为false,停止开火,停止调用协程函数
}
}
IEnumerator Fire()//定义协程函数 Fire() ---协程并不会在Unity中开辟新的线程来执行,其执行仍然发生在主线程中。当我们有较为耗时的操作时,可以将该操作分散到几帧或者几秒内完成,而不用在一帧内等这个操作完成后再执行其他操作。(互不影响)
{
while (isFire == true)//大前提
{
if (bulletstartpoint != null || bullet != null)//bulletstartpoint 即为子弹发射的初始位置
{
GameObject newbullet = Instantiate(bullet, bulletstartpoint.position, bulletstartpoint.rotation);
//GameObject :Unity 场景中所有实体的基类。这里定义一个GameObject 类型的变量newbullet,并将其实例化
newbullet.GetComponent<Rigidbody>().velocity = newbullet.transform.forward * -bulletstartspeed;
//将该变量获取一个Rigidbody的Component,将子弹的速度velocity给一个bulletstartspeed
shootaudio.Play();//开火时调用音频播放函数
StopCoroutine("Weaponbackanimation");//每帧调用时将上一帧的播放动画停止以防连击发生冲突
StartCoroutine("Weaponbackanimation");//开火时调用Weaponbackanimation协程函数
Destroy(newbullet, 5);//每间隔5秒销毁一个子弹
}
yield return new WaitForSeconds(fireinterval);//中断函数,间隔0.2秒重新执行IEnumerator Fire(),这里将开火的函数调用改变为0.2秒的响应时间。在这中间可能有多帧在调用后坐力动画的效果。关于这个yield return new WaitForSeconds的设置就很有必要
}
}
IEnumerator Weaponbackanimation()//利用协程函数控制子弹后坐力动画
{
if (dufalutpoint != null && Backpoint != null)//大前提
{
while (this.transform.localPosition != Backpoint.localPosition)
{
this.transform.localPosition = Vector3.Lerp(this.transform.localPosition, Backpoint.localPosition, lerpRatio*4);
//Lerp函数在unity中可以用于物体到达另外一个目标物体之间进行平滑过渡运动效果。
//函数原型为Lerp(a,b,t);内部实现为y = b+(1-t)*a;其中t被clamp在了[0,1],也就是说最小为0,最大为1.
yield return null;
// yield return null表示暂缓一帧,在下一帧接着往下处理,即等待下一帧(Update函数执行后)继续执行,这恰恰做到了单帧所做不到的事!大家可以自行思考。
}
while (this.transform.localPosition != dufalutpoint.localPosition)//反向过渡
{
this.transform.localPosition = Vector3.Lerp(this.transform.localPosition, dufalutpoint.localPosition, lerpRatio);
yield return null;
}
}
}
private void Playshootaudio()//定义一个播放开火音频的函数,在开火瞬间调用
{
if (shootaudio)
{
shootaudio.Play();
}
}
}
自此,可实现子弹连击和后坐力动画的播放。
关于IEnumerator函数
Ryun_WeaponController 脚本里的协程:
IEnumerator Fire()//定义协程函数 Fire() ---协程并不会在Unity中开辟新的线程来执行,其执行仍然发生在主线程中。当我们有较为耗时的操作时,可以将该操作分散到几帧或者几秒内完成,而不用在一帧内等这个操作完成后再执行其他操作。
IEnumerator Weaponbackanimation()//利用协程函数控制子弹后坐力动画
Update调用函数时,函数将运行到完成状态,然后返回。这实际上意味着在函数中发生的任何动作都必须在单帧更新内发生;函数调用不能用于包含程序性动画或随时间推移的一系列事件。
引入协程:(操作分散到几帧或者几秒内完成)
协程:协程是一个分部执行,遇到条件(yield return 语句)时才会挂起,直到条件满足才会被唤醒继续执行后面的代码。
此协程本质上是一个用返回类型 IEnumerator 声明的函数,并在主体中的某个位置包含 yield return 语句。yield return null 行是暂停执行并随后在下一帧恢复的点(单帧频率执行)。要将协程设置为运行状态,必须使用 StartCoroutine 函数:
StopCoroutine("Weaponbackanimation");//每帧调用时将上一帧的播放动画停止以防连击发生冲突
StartCoroutine("Weaponbackanimation");//开火时调用Weaponbackanimation协程函数
默认情况下,协程将在执行 yield 后的帧上恢复,但也可以使用 WaitForSeconds 来引入时间延迟
IEnumerator Fade()
{
for (float ft = 1f; ft >= 0; ft -= 0.1f)
{
Color c = renderer.material.color;
c.a = ft;
renderer.material.color = c;
yield return new WaitForSeconds(0.1f);
}
}
可通过这样的协程在一段时间内传播效果,但也可将其作为一种有用的优化。游戏中的许多任务需要定期执行,最容易想到的方法是将任务包含在 Update 函数中。但是,通常情况下,每帧将多次调用该函数。不需要以这样的频繁程度重复任务时,可以将其放在协程中来进行定期更新,而不是每一帧都更新。
这方面的一个示例可能是在附近有敌人时向玩家发出的警报。此代码可能如下所示:
function ProximityCheck()
{
for (int i = 0; i < enemies.Length; i++)
{
if (Vector3.Distance(transform.position, enemies[i].transform.position)< dangerDistance)
{
return true;
}
}
return false;
}
如果有很多敌人,那么每帧都调用此函数可能会带来很大开销。但是,可以使用协程,每十分之一秒调用一次:
IEnumerator DoCheck()
{
for(;;)
{
ProximityCheck();
yield return new WaitForSeconds(.1f);
}
}
这将大大减少所进行的检查次数,而不会对游戏运行过程产生任何明显影响。
补充内容来源于:
(122条消息) unity笔记--协程StartCoroutine和yield return_协程与yield return_babywang0的博客-CSDN博客
具体可参考下面博主的文章:
版权声明:本文为CSDN博主「做一只会飞的猪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/beihuanlihe130/article/details/76098844