引言

如果我们想在两秒内调用特定代码,或者规定间隔时间重复执行特定代码,我们首选想到的可能是使用协程

而这篇文章将介绍协程的替代方案:MonoBehaviour .Invoke()。

简单介绍

实现看一下方法声明:

public void Invoke (string methodName , float time );

参数介绍
string methodName:将被调用的方法名。
float time:在 time 秒内调用该方法。

注意
如果时间设置为 0,则在下一个更新周期调用该方法。在这种情况下,最好直接调用该函数。

示例

在 2 秒内发射一个射弹。

public class ExampleScript : MonoBehaviour
{
    // 射弹的刚体。
    Rigidbody projectile;

    void Start()
    {
	使用Invoke()调用发射方法。
        Invoke("LaunchProjectile", 2.0f);
    }

    // 射弹发射方法。
    void LaunchProjectile()
    {
        Rigidbody instance = Instantiate(projectile);
        instance.velocity = Random.insideUnitSphere * 5.0f;
    }
}

示例的协程版本

public class ExampleClass : MonoBehaviour {
    // 射弹的刚体。
    Rigidbody projectile;

    // 等待指定时间并执行发射方法的协程。
    IEnumerator WaitAndLaunchProjectile(float wait) {
        yield return new WaitForSeconds(wait);

        Rigidbody instance = Instantiate(projectile);
        instance.velocity = Random.insideUnitSphere * 5;
    }

    // 调用协程。
    void Start() {
        StartCoroutine(LaunchProjectile(2));
    }
}

为什么说这是协程的下位替代

当您想快速地开发出可以见到实际效果的游戏某功能的演示版本,Invoke()方法可能是一个不错的选择。从上面的例子中,可以很容易地看出,Invoke()方法的代码量,远少于使用协程。

但由于将字符串作为方法名称传递,因此如果你在后期开发中改变了那个方法名,游戏将会出错,但 IDE 不会在你写代码时给出错误信息。因此,重构错误仅在运行时可见,这通常意味着麻烦。

所以,使用协程可以获得更好的性能和可维护性。使用 Invoke() 则可以快速开发出游戏原型。

重复调用

作为协程的替代品,Invoke 方法也可以完成指定间隔时间重复调用指定方法的功能,只不过它换了一个名字,叫做:MonoBehaviour .InvokeRepeating()。

简单介绍

实现看一下方法声明:

public void InvokeRepeating (string methodName , float time , float repeatRate );

参数介绍
string methodName:将被调用的方法名。
float time:在 time 秒内调用该方法。
float repeatRate:每隔 repeatRate (秒)重复一次。

注意
如果您将时间刻度设置为 0,这将不起作用。

示例

2 秒后开始,然后每 0.3 秒发射一次射弹。

public class ExampleScript : MonoBehaviour
{
    public Rigidbody projectile;

    void Start()
    {
        InvokeRepeating("LaunchProjectile", 2.0f, 0.3f);
    }

    void LaunchProjectile()
    {
        Rigidbody instance = Instantiate(projectile);

        instance.velocity = Random.insideUnitSphere * 5;
    }
}

取消调用

CancelInvoke允许您取消使用前两种方法安排的调用。它有两个重载。

public void CancelInvoke();

取消此 MonoBehaviour 上的所有 Invoke 调用。

...
    void Update () 
    { 
        // 取消所有 Invoke 调用
        if ( Input.GetButton ("Fire1")) 
            CancelInvoke(); 
    } 
...

public void CancelInvoke (string methodName );

在此 MonoBehaviour 上取消所有具有名称 methodName 的 Invoke 调用。

...
    void Update () 
    { 
        // 仅取消 LaunchProjectile 调用
        if ( Input.GetButton ("Fire1")) 
            CancelInvoke("LaunchProjectile"); 
    } 
...

检查是否存在没有结束的 Invoke

MonoBehaviour.IsInvoking() 方法可以帮助我们检查当前 MonoBehaviour 中,是否存在没有结束的 Invoke 。它有两个重载。

public bool IsInvoking();

此 MonoBehaviour 是否有任何没有结束的调用?

public bool IsInvoking(string methodName);

此 MonoBehaviour 是否有没有结束的调用将调用名为 methodName 的方法?

示例

此示例将本文中的示例制作为具有 2 秒冷却时间,按空格键发射的大炮。

public class ExampleScript : MonoBehaviour
{
    public Rigidbody projectile;

    void Update()
    {
        // 按下空格并且没有炮弹即将发射时,调用 2 秒后发射的方法。
        if (Input.GetKeyDown(KeyCode.Space) && !IsInvoking("LaunchProjectile"))
            Invoke("LaunchProjectile", 2.0f);
    }

    void LaunchProjectile()
    {
        Rigidbody instance = Instantiate(projectile);
        instance.velocity = Random.insideUnitSphere * 5;
    }
}