【问题标题】:Cannot use InvokeRepeating with method parameters. How do I work around this?不能将 InvokeRepeating 与方法参数一起使用。我该如何解决这个问题?
【发布时间】:2016-01-02 23:57:45
【问题描述】:

我正在尝试实施一个随时间推移损坏的系统,但 Unity 一直说“尝试调用方法...无法调用。”我要调用的方法使用参数“Collider coll”,但根据我的研究,如果该方法有参数,则无法调用。

这是我的代码:

using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;

public class DamageOverTime : MonoBehaviour
{

    public int PHP; //PHP = Player Health from PlayerHealth.cs script.
    public int Damage; //Amount of damage.
    public int DamageOverTime; //Damage over time.
    public float DamageInterval_DOT = .25f; //Damage interval for damage over time.
    public string Level;
    PlayerHealth player;

    void Start()
    {
        player = GameObject.Find("Player").GetComponent<PlayerHealth>();
        InvokeRepeating("OnTriggerEnter", DamageInterval_DOT, DamageInterval_DOT);
    }

    void Update()
    {
        PHP = GameObject.Find("Player").GetComponent<PlayerHealth>().PlayerHP;

        if (PHP <= 0)
        {
            SceneManager.LoadScene(Level);
        }

    }

    void OnTriggerEnter(Collider coll)
    {
        if (coll.gameObject.tag == "Player")
        {
            GameObject.Find("Player").GetComponent<PlayerHealth>().PlayerHP = PHP - Damage;
        }

        if (coll.gameObject.tag == "Ball")
        {
            gameObject.SetActive(false);
            SceneManager.LoadScene(Level);
        }
    }
}

我的目标是让 OnTriggerEnter 函数循环 1/4 秒(或更低)。进入对撞机后,我的生命值在大约一秒钟内消耗了 60%,这太快了。我应该如何解决这个问题?

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    InvokeRepeating 不能与OnTriggerEnter 一起使用,因为它是一个触发器,这意味着它会在其持有者进入时触发一次。 InvokeRepeating 也意味着你想要不断地重复一个动作,而这里不是这种情况。您希望触发器发生一次,然后随着时间的推移删除 health points

    解决方案 - 协程

    Unity3D 自定义使用 IEnumerableyield 关键字 Coroutine 始终返回 IEnumerator。这个怎么运作?它将返回对我们Coroutine 中的每个yield 的控制权,然后将返回到返回控制权的确切位置,而不是从头开始执行函数。

    代码:

    void OnTriggerEnter(Collider coll)
    {
        if (coll.gameObject.tag == "Player")
        {
            StartCoroutine("DamageOverTimeCoroutine");
        }
    
        if (coll.gameObject.tag == "Ball")
        {
            gameObject.SetActive(false);
            SceneManager.LoadScene(Level);
        }
    }
    
    public IEnumerator DamageOverTimeCoroutine()
    {
        var dotHits = 0;
        while (dotHits < 4)
        {
            //Will remove 1/4 of Damage per tick
            GameObject.Find("Player").GetComponent<PlayerHealth>().PlayerHP -= Damage / 4;
            dotHits++;
            //Will return control over here
            yield return new WaitForSeconds(DamageInterval_DOT);
            //And then control is returned back here once 0.25s passes
        }
    }
    

    Coroutine 当然还有改进的余地。您可以将参数传递给它,就像您可以传递给C# 中的任何其他方法一样。您也可以实现其他非硬编码的逆向。上面的代码只是一个关于如何处理这种情况的简单示例。

    随着时间的推移持续损坏

    public IEnumerator DamageOverTimeCoroutine()
    {
        var dotHits = 0;
        var player = GameObject.Find("Player").GetComponent<PlayerHealth>();
        while (true)
        {
            //Stop removing damage, player is dead already
            if (player.PlayerHP <= 0)
                yield break;
            //Will remove 5 Damage per tick
            player.PlayerHP -= 5;
            dotHits++;
            //Will return control over here
            yield return new WaitForSeconds(DamageInterval_DOT);
            //And then control is returned back here once 0.25s passes
        }
    }
    

    要在代码的其他位置停止 Coroutine,请使用 StopCoroutine("DamageOverTimeCoroutine") 停止某些协程类型或使用 StopAllCoroutines() 停止所有当前处于活动状态的协程。

    【讨论】:

    • 我明白了。我对其进行了测试,它确实有效,但在我重新进入之前它只会移除 1/4 的玩家生命值。我希望它持续消耗生命值,直到它达到 0,除非玩家移出所述触发器。我该怎么做?
    • 您可以使用while (true) 代替并添加void OnTriggerExit(Collider other),这将在您离开触发器时停止协程:StopCoroutine(DamageOverTimeCoroutine())。如果您希望其他协程在此事件上停止,您可能需要调用StopAllCoroutines()。您还需要检查 HP 是否不为 0。您可能需要在 while() 子句中检查它,或者在循环内检查它并点击 yield break 以破坏 Coroutine
    • 我已经更新了我的答案,所以你可以明白我的意思。
    • 好的。 while (true) 对持续损坏非常有效,为了阻止它,我尝试了 StopCoroutine(DamageOverTimeCoroutine());,但这并没有阻止它,然后我尝试进行标签检查 (like so),但这并没有也不行。我只会使用 StopAllCoroutines(),但我希望它是特定于播放器的,因为我以后可能想添加更多。同样在我的更新方法中,我让它检查玩家的 HP 是否为 0,如果是,它将重新加载关卡。
    • 尝试更改为StartCoroutine("DamageOverTimeCoroutine"),然后通过带有名称的字符串而不是方法签名来停止它。如果要保留方法签名样式,则需要将协程分配给变量,然后使用StopCoroutine 访问它。我已经编辑了第一种方法(协程名称)方案的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 2020-02-14
    • 2021-04-13
    • 2016-08-26
    相关资源
    最近更新 更多