HuldaGnodim 发表于 2022-1-5 19:46

关于Unity的“等待一段时间”功能的小笔记

前言

在游戏中“等待一段时间”和“持续一段时间”是非常常见的功能,然而我在自学的过程中一直没有找到什么用起来比较舒适的实现方案。
目前我找到的方案有三种:
①协程,使用WaitForSecods和StartCoroutine来实现,但是我一直没太搞懂协程这个东西本身,按查到的方式使用感觉也很不顺手;
②Invoke(),调用Invoke就可以在指定时间后执行指定函数,但实际用起来相比协程感觉更局限;
③deltaTime,在Update里给一个float持续加上或减少deltaTime,然后判断是否达到指定值。
但这三种方案在实际使用中始终都觉得很麻烦,也不知道是我太菜了还是确实就麻烦。

我本身没啥基础,同时一直也找不到一个比较好的案例来学习这种功能的实现,头疼了挺长一段时间。
在我大学那会,虚幻4刚发布,当时把玩了一两年蓝图,里面有一个节点叫“Delay”,作用是延迟指定时间后再执行后面的节点,使用起来相当便捷。
所以在Unity中我也希望能有同样便捷的方式去实现类似的效果。
解决方案

然后在今天,我一如既往的在洗澡的时候突然冒出灵感,我脑海里冒出了一个实现方案,我感觉似乎可行。
于是我立刻尝试了一下,代码的整个执行过程完全符合我的预期。
先简单说一下这个方案的思路,这是一个模拟“技能冷却”的方案,非常简单:
①使用一个Action对所有“冷却时间”进行运算;
②冷却函数用deltaTime在Update里计算;
③被使用而进入冷却的技能,将对应的冷却函数添加到Action里;
④结束冷却的技能,从Action中移除(某些情况可能也可以一直留在Action里);



测试效果截图

下面直接放代码,完全是为了效果测试而写,但原理很容易看懂:
using UnityEngine;
using System;

public class Actions : MonoBehaviour
{
    public static Action CoolDown;
}
放置Action的代码,里面有一个Action用来管理和计算所有冷却;

using UnityEngine;

public class TestCoolDownManager : MonoBehaviour
{

    void Update()
    {
      Actions.CoolDown();
    }
}
执行Action的代码,看情况挂在需要的GameObject上,然后在Update里执行Action就完了;

using UnityEngine;
using UnityEngine.UI;

public class TestSkill : MonoBehaviour
{
    public GameObject testText;
    public Transform layout;

    public Text CD1Text;
    public Text CD2Text;
    public Text CD3Text;
    public Button CD1Button;
    public Button CD2Button;
    public Button CD3Button;

    public float skill1CD;
    public float skill1CDCurrent;
    public float skill2CD;
    public float skill2CDCurrent;
    public float skill3CD;
    public float skill3CDCurrent;

    void Start()
    {
      skill1CDCurrent = 0;
      skill2CDCurrent = 0;
      skill3CDCurrent = 0;

      Actions.CoolDown += BaseTime;//因为我会移除不在冷却的技能,Action为空要给我报错,我也不知道咋处理

    }

    public void UseSkill1()
    {
      GameObject clone = Instantiate(testText, layout);
      clone.GetComponent<Text>().text = "使用了技能1";
      Destroy(clone, 2);
      skill1CDCurrent = skill1CD;
      CD1Button.interactable = false;

      Actions.CoolDown += Skill1CD;
    }

    public void UseSkill2()
    {
      //和上边一样,换成技能2的
    }

    public void UseSkill3()
    {
      //和上边一样,换成技能3的
    }

    public void Skill1CD()
    {
      if (skill1CDCurrent > 0)
      {
            skill1CDCurrent -= Time.deltaTime;
            CD1Text.text = "CD1: " + skill1CDCurrent.ToString("f3");
      }
      else
      {
            GameObject clone = Instantiate(testText, layout);
            clone.GetComponent<Text>().text = "技能1冷却结束";
            Destroy(clone, 2);
            CD1Text.text = "CD1: 0.000";
            CD1Button.interactable = true;

            Actions.CoolDown -= Skill1CD;
      }
    }

    public void Skill2CD()
    {
      //和上面一样,换成技能2的冷却
    }

    public void Skill3CD()
    {
      //和上面一样,换成技能3的冷却
    }

    void BaseTime()
    {
      //拿来填充Action的,其实也可以顺便拿来给游戏计时之类的,反正都是Update持续计算时间
    }
}
技能使用部分,点了界面上的按钮就会执行对应技能的使用函数,然后把对应的冷却函数添加到Action里计算
我这里是把进入冷却的按钮变成禁用,冷却结束后再启用。根据实际情况可以改成任何一种需要的反馈方式,比如点击后弹字提示“正在冷却中”之类的。
后记

现在这些冷却函数都是分别列出来的,等于是有多少技能就要写多少个函数做冷却,这样还是有点太麻烦了。
最好是可以复用同一个函数,还能分别计算不同的技能的冷却。
理论上给Action加上输入参数,每个技能添加冷却的时候给不同的参数,可能可以实现,我没试过不太确定,回头再测试一下。
还有就是,都是同一个函数复用的话,如果要移除特定一个该怎么移除?这个也得做下测试,要是没法移除特定一个可能就有点蛋疼了。

zifa2003293 发表于 2022-1-5 19:56

游戏里面的冷却概念可以考虑用全局管理器,内部可以用<委托, 浮点数>这样的字典,提供加入方法(返回一个消息回执,是一个类,里面有完成回调和中断操作),计时器就可以用MonoBehaviour或者另外开一个长效的计时用线程。
一般意义上的“延迟执行”可以考虑C#的异步方法或者UniRx插件(资源商店下载或者查询github)提供的异步消息流。
页: [1]
查看完整版本: 关于Unity的“等待一段时间”功能的小笔记