【Unity C#篇学习笔记】运用协程实现简单的被击和中毒效果

首先,协程的准备工作

关键的代码段,写在了OnGUI中,在判断按钮的if语句块下

if (GUI.Button(new Rect(40, 50, 150, 50), "被击"))
        {
            StartCoroutine(WaitBehit());
        }

然后创建新的结构,包含了协程需要执行的具体内容,固定写法 IEnumerator name()

IEnumerator WaitBehit()
    {
        // GetComponentInChildren会在该物体各个子物体的属性中查找对应的值(SkinnedMeshRenderer)
        var smr = Monster.GetComponentInChildren<SkinnedMeshRenderer>();
        //没有返回类型,所以不需要设置
        smr.sharedMaterial.SetColor("_Color", Color.white);

        yield return new WaitForSeconds(0.05f);

        smr.sharedMaterial.SetColor("_Color", Color.black);
    }

简单易懂,按下按钮后, 执行SetColor为白色,等待0.05秒后,执行SetColor为黑色

但是缺少了过渡效果,下面我们在中毒的效果中加入此项

if (GUI.Button(new Rect(40, 120, 150, 50), "中毒"))
        {
            StartCoroutine(WaitDu());
        }

同样的,协程固定写法,然后是创建协程的结构

 IEnumerator WaitDu()
    {

        // 声明一个_time为0的数值进入循环
        float _time = 0;
        // 始终为真,会一直循环,所以利用yield break打破循环,条件是当_time大于2时
        while (true)
        {
            // Time.deltaTime表示距离上一帧所用的时间,该数值较小,会在每一帧都加上这个数值
            _time += Time.deltaTime;
            Debug.Log(_time);
            // WaitForEndOfFrame表示在每一帧结束后执行一次,主要用来限制while循环的速率,避免一次性全部执行
            // yield 表示让协程在此暂停并计算条件,满足后继续
            yield return new WaitForEndOfFrame();

            // 限制条件,避免进入死循环,_time值大于2时打破循环
            if (_time > 2f)
            {
                yield break;
            }

            // else语句,_time不大于2时,执行以下颜色过渡计算
            else
            {
                // 利用lerp函数,_time为0时,_color值为Color.red,_time为1时,_color值为Color.black ,这里为了取到1要除以2
                Color _color = Color.Lerp(Color.red, Color.black, _time / 2f);
                var smr = Monster.GetComponentInChildren<SkinnedMeshRenderer>();
                smr.sharedMaterial.SetColor("_Color", _color);
            }
        }

    }

看起来复杂,我们从关键的代码中梳理一下,关键代码其实在最后三段,利用lerp函数控制从白到黑的过渡,lerp(a,b,t) a是红色(中毒后掉血的效果,也可以是绿色),b是黑色,那么关键在于t

t是一个递增的数值,从a递增到b,我们使用while循环语句,并将其设定为true,为了避免死循环,我们设定if语句判断t的值是否大于某个数,只要成立那么就用 yield break 打破循环!

递增多少呢,我们使用了_time += Time.deltaTime; 即_time会加等于每一帧之间所需要的时间,我的理解,帧数越低,所需的实际时间应该会越长,所以玩家即便掉帧,也能收到反馈,我以为会引入实际时间计算,也许是考虑到了这一点。

还有一个问题,我的电脑性能强大,一瞬间就执行完了这个循环,那么我们应该引入一个时间的概念,即限制循环语句计算的速率

就是它了

yield return new WaitForEndOfFrame();

yield 这个语句会让我们的协程进入等待,完成计算或满足条件后解除等待,WaitForEndOfFrame() 就是在每一帧结束后再进行计算

过渡的效果已经有了,接下来我们进入优化代码的阶段

不难发现,我们每次需要对这个物体的材质进行操作时,都要输入一段长长的代码

var smr = Monster.GetComponentInChildren<SkinnedMeshRenderer>();
                smr.sharedMaterial.SetColor("_Color", _color);

既然需要多次使用它,我们可以提前声明,并且在游戏开始时(void Start())就对它赋值

首先,我们要声明一个私有变量,给它命名为mat

#region [成员变量]
    public GameObject Monster;
    private Material mat;
    #endregion

并在游戏开始时,就把需要用到的参数赋给mat

 void Start()
    {
        mat = Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial;
    }

这样就省了很多冗余的代码段了

哦对,还有2f值,即消散的时间,要更改它有些麻烦,我们也可以用同一个值控制它,在这里声明

IEnumerator WaitDu(float time)

那这个time的值到底是多少呀,这里仅仅是声明一个名字为time的浮点值,其实在GUI那里

 if (GUI.Button(new Rect(40, 120, 150, 50), "中毒"))
        {
            StartCoroutine(WaitDu(2f));
        }

在这里设置time具体的值,显而易见,这里的2f会传递给 float time,最后,我们把2f换成time就好了,如果需要,也可以将time值暴露出来进行设置

还有个问题

两个协程不能同时执行,点击中毒时,再点击被击是没有打断的效果的,那么简单粗暴的思路,我们设定在执行此协程时,停用所有其他协程,只需要一句

StopAllCoroutines();

写在GUI的if语句块里就好

 if (GUI.Button(new Rect(40, 50, 150, 50), "被击"))
        {
            StopAllCoroutines();
            StartCoroutine(WaitBehit());
        }

最后就大功告成了!下面是完整代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MonsterShader : MonoBehaviour
{

    #region [成员变量]
    public GameObject Monster;
    private Material mat;
    #endregion

    #region [Start/Update]
    void Start()
    {
        mat = Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial;
    }

    void Update()
    {

    }

    #endregion

    #region [GUI]
    void OnGUI()
    {
        if (GUI.Button(new Rect(40, 50, 150, 50), "被击"))
        {
            StopAllCoroutines();
            StartCoroutine(WaitBehit());
        }

        if (GUI.Button(new Rect(40, 120, 150, 50), "中毒"))
        {
            StopAllCoroutines();
            StartCoroutine(WaitDu(2f));
        }
    }
    #endregion

    #region [被击]
    IEnumerator WaitBehit()
    {
        mat.SetColor("_Color", Color.white);
        yield return new WaitForSeconds(0.05f);
        mat.SetColor("_Color", Color.black);
    }
    #endregion

    #region [中毒]
    IEnumerator WaitDu(float time)
    {
        float _time = 0;
        while (true)
        {
            _time += Time.deltaTime;
            yield return new WaitForEndOfFrame();
            if (_time > time)
            {
                yield break;
            }
            else
            {
                Color _color = Color.Lerp(Color.red, Color.black, _time / time);
                mat.SetColor("_Color", _color);
            }
        }
    }
    #endregion
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值