【问题标题】:How to delay a method in Unity and C#?如何延迟 Unity 和 C# 中的方法?
【发布时间】:2019-10-01 17:08:07
【问题描述】:

我的名字是 Laurenz,我的问题是如何使用 c# 在 Unity 中延迟我的精灵的颜色变化。

现在我有一个随机生成器,它根据一个数字选择一种颜色,但这种情况每帧都会发生。所以现在真正的挑战是如何延迟它以减少它的变化。

public class colorchange : MonoBehaviour
{
    public int color;
    public bool stop = true;
    void Start()
    {

    }

    void Update()
    {

        Debug.Log("Hello");
        color = Random.Range(1, 5);
        if (color == 2)
        {
            gameObject.GetComponent<SpriteRenderer>().color = Color.blue;
        }
        if (color == 3)
        { 
            gameObject.GetComponent<SpriteRenderer>().color = Color.red;
        }
        if (color == 4)
        {
            gameObject.GetComponent<SpriteRenderer>().color = Color.yellow;
        }
    }
}

【问题讨论】:

  • 计算自上次执行以来的时间量(例如:使用Time.deltaTime),仅在超过一定量时改变颜色
  • OP,如果您想提供自己的答案,请将其作为答案而不是对问题的编辑。编辑回滚。

标签: c# unity3d delay


【解决方案1】:

您可以将您的代码放在Coroutine 中的循环中,该循环每隔几秒迭代一次:

public class colorchange : MonoBehaviour
{
    public int color;        
    public float delaySeconds = 1f;
    IEnumerator changeColorCoroutine;

    SpriteRenderer mySprite;

    public bool doChangeColor;

    void Start()
    {
        // cache result of expensive GetComponent call
        mySprite = GetComponent<SpriteRenderer>();

        // initialize flag
        doChangeColor = true;

        // create coroutine
        changeColorCoroutine = ChangeColor();

        // start coroutine
        StartCoroutine(changeColorCoroutine);
    }

    void OnMouseDown()
    {
        // toggle doChangeColor
        doChangeColor = !doChangeColor;
    }

    IEnumerator ChangeColor()
    {
        WaitUntil waitForFlag = new WaitUntil( () => doChangeColor);

        while (true)
        {
            yield return waitForFlag;

            Debug.Log("Hello");
            color = Random.Range(1, 5);

            // switch for neater code
            switch (color)
            {
            case 2:
                mySprite.color = Color.blue;
                break;

            case 3:
                mySprite.color = Color.red;
                break;

            case 4:
                mySprite.color = Color.yellow;
                break;
            }

            yield return new WaitForSeconds(delaySeconds);
        }
    }
}

【讨论】:

  • 如果我放 onmousedown(),你可能知道如何停止颜色变化
  • @LaurenzKaml 我编辑了答案以在OnMouseDown() 中包含一个切换功能,请确保将 ColliderGUIElement 附加到相同的gameobject 作为脚本。
  • 然后您实际上可以直接将Start 设为IEnumerator ;) 并删除一个空的Update 方法.. unity 通过消息调用它只会导致不必要的开销
  • 对不起 Ruzihm,但我 1 天前才开始团结,几乎不知道你能告诉我怎么做。
  • 一个建议,创建一个WaitUntil 对象和yield return 它,而不是每次都创建new WaitUntil
【解决方案2】:

您可以使用时间计数器和 Time.deltaTime:

public class colorchange : MonoBehaviour
{
    public int color;
    public bool stop = true;
    public float delay;
    private float timer;
    void Start()
    {
        timer = delay;
    }

    void Update()
    {
        timer -= Time.deltaTime;
        if (timer <= 0) {
            timer = delay;
            Debug.Log("Hello");
            color = Random.Range(1, 5);
            if (color == 2)
            {
                gameObject.GetComponent<SpriteRenderer>().color = Color.blue;
            }
            if (color == 3)
            { 
                gameObject.GetComponent<SpriteRenderer>().color = Color.red;
            }
            if (color == 4)
            {
                gameObject.GetComponent<SpriteRenderer>().color = Color.yellow;
            }
        }
    }
}

【讨论】:

    【解决方案3】:

    这可以通过多种方式完成。在向您展示具体内容之前,这里有一个基本结构,适用于所有示例代码,我将使用它:

    public class ColorChanger : MonoBehaviour {
        //Avoid Find and GetComponent methods in performance-critical contexts like Update and FixedUpdate
        //Store the value once in the beginning. This is called 'caching'
        public SpriteRenderer _renderer;
    
        //Don't hard-code stuff like this
        public Color[] _colors;
    
        public float _colorChangeInterval = 0.5f;
    
        //Convenience property to access _renderer.color
        public Color Color {
            get => _renderer.color;
            set => _renderer.color = value;
        }
    
        private void Start() {
            //Attempts to find the SpriteRenderer in the object if it wasn't set in the inspector
            if (!_renderer)
                _renderer = GetComponent<SpriteRenderer>();
        }
    
        //This piece of code does a specific thing, so it's best to put it in a method
        public void ChangeColor() {
            if (_colors.Length < 1)
                Debug.LogError($"You forgot to set {nameof(_colors)} in the Inspector. Shame! Shame!");
            Color = _colors[Random.Range(0, _colors.Length - 1)];
        }
    }
    

    在我看来,以下是一些主要的,按照它们的直观程度排列:


    定时器模式:

    两种口味。

    1) 可以是经过时间的累加器(如下面的代码),或者相反,从区间递减到零:

    private float _elapsed;
    
    private void Update() {
        _elapsed += Time.deltaTime;
    
        if (_elapsed < _colorChangeInterval)
            return;
        ChangeColor();
        _elapsed %= _colorChangeInterval;
    }
    

    或者 2) 可以是从上一个或直到下一个(如下)时间戳的时间戳检查触发器:

    //Replaces _elapsed
    private float _timestamp;
    
    private void Start() {
        //...
        _timestamp = Time.time; //Initial timestamp
    }
    
    private void Update() {
        if (Time.time < _timestamp + _colorChangeInterval)
            return;
        ChangeColor();
        _timestamp = Time.time;
    }
    

    协程 & WaitForSeconds:

    当您需要统一延迟或排序代码时,这是推荐的过程。

    注意,Unity 还提供了其他类型的等待方法,如WaitWhileWaitUntil 等...

    //Since unlike code in Update, coroutines need to be started and stopped, we start it when the script is enabled
    private void OnEnable() {
        StartCoroutine(ChangeColorContinuously());
    }
    
    //This is automatically stopped by unity when the script is disabled
    private IEnumerator ChangeColorContinuously() {
        while (true) {
            yield return new WaitForSeconds(_colorChangeInterval);
            ChangeColor();
        }
    }
    

    不要做异步等待!

    嗯,它可以完成,但它有很多陷阱,非常推荐给初学者。

    无论如何,它并不是要取代协程。


    不要执行 InvokeRepeating!

    这是一种依赖于魔术字符串和反射的方法。对于示例代码的快速和简单的设置很有用,但如果可能的话(并且有可能,这要归功于上面的方法)应该像生产代码中的瘟疫一样避免。

    【讨论】:

      【解决方案4】:

      你可以用一个简单的协程来处理这个问题。如果您不知道如何操作,请查看 Unity 官方 API 文档中的此链接 (https://docs.unity3d.com/ScriptReference/WaitForSeconds.html)。

      如果你不想使用协程,你可以使用 Unity 的 InvokeRepating 方法。创建一个用于更改颜色的新函数并每隔 x 秒调用一次。 这里也是 InvokeRepating 方法的链接https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html

      ps:在这种情况下,使用 Switch 来确定颜色整数比对每个颜色代码使用 if 和 else if 更有效。它更高效,更容易扩大规模。

      【讨论】:

        【解决方案5】:

        Coroutinessimple timer in Update 的另一种选择是使用InvokeRepeating 并使用CancelInvoke 停止它。在您这样的用例中,我发现这种方式更易于实施和控制:

        public float Interval = 1;
        
        [SerializeField] private SpriteRenderer spriteRenderer;
        
        private void Awake()
        {
            // DO THIS ONLY ONCE
            if(!spriteRenderer) spriteRenderer = GetComponent<SpriteRenderer>();
        }
        
        // Automatically called when this component or GameObject gets enabled
        private void OnEnable ()
        {
            // Start invoking every Interval seconds
            InvokeRepeating(nameof(ChangeColor), Interval, Interval);
        }
        
        // Automatically called when this component or GameObject gets disabled
        private void OnDisable()
        {
            // Stop the repeated invoking 
            CancelInvoke();
        }
        
        private void ChangeColor()
        {
            Debug.Log("Hello");
            color = Random.Range(1, 5);
        
            // You should also use a switch case here
            switch(color)
            {
                case 2:
                    spriteRenderer.color = Color.blue;
                    break;
        
                case 3:
                    spriteRenderer.color = Color.red;
                    break;
        
                case 4:
                     spriteRenderer.color = Color.yellow;
                     break;
            }
        }
        

        然后您可以通过启用和禁用此组件来简单地启用和禁用颜色更改。

        或者简单地将相应的代码行从 OnEnableOnDisable 移动到相应的公共方法,如 public void StartColorChangepublic void StopColorChange

        【讨论】:

          【解决方案6】:

          有多种方法可以做你想做的事。

          定时器

          您可以使用计时器每隔 x 时间更改颜色。

          至少有两种方法可以做到这一点:

          1

          public class colorchange : MonoBehaviour
          {
              public int color;
              public bool stop = true;
          
              private SpriteRenderer _mySpriteRenderer;
              private float _timeBetweenChanges = 0.5F;
              private float _lastChange = 0f;
          
              void Start()
              {
                  _mySpriteRenderer = GetComponent<SpriteRenderer>();
              }
          
              void Update()
              {
                  if (Time.time - _lastChange >= _timeBetweenChanges)
                  {
                      _lastChange = Time.time;
                      color = Random.Range(1, 5);
          
                      if (color == 2)
                      {
                          _mySpriteRenderer.color = Color.blue;
                      }
                      if (color == 3)
                      { 
                          _mySpriteRenderer.color = Color.red;
                      }
                      if (color == 4)
                      {
                          _mySpriteRenderer.color = Color.yellow;
                      }
                  }
              }
          }
          

          注意:您可以缓存组件 SpriteRenderer 以避免每次调用 GetComponentSee here

          2

          public class colorchange : MonoBehaviour
          {
              public int color;
              public bool stop = true;
          
              private SpriteRenderer _mySpriteRenderer;
              private float _timeBetweenChanges = 0.5F;
          
              void Start()
              {
                  _mySpriteRenderer = GetComponent<SpriteRenderer>();
                  InvokeRepeating("ChangeColor", 0F, _timeBetweenChanges);
              }
          
              void Update()
              {
                  // You don't need Update here, you can safly remove it unless you need it for anything else
              }
          
              void ChangeColor()
              {
                  color = Random.Range(1, 5);
                  if (color == 2)
                  {
                      _mySpriteRenderer.color = Color.blue;
                  }
                  if (color == 3)
                  { 
                      _mySpriteRenderer.color = Color.red;
                  }
                  if (color == 4)
                  {
                      _mySpriteRenderer.color = Color.yellow;
                  }
              }
          }
          

          随机

          您可以使用 RNG 来确定是否要更改此帧的颜色。这意味着您无法定义每个sec / min / in general 更改颜色的次数。

          public class colorchange : MonoBehaviour
          {
              public int color;
              public bool stop = true;
          
              private SpriteRenderer _mySpriteRenderer;
              private float _randomChance = 0.2F;
          
              void Start()
              {
                  _mySpriteRenderer = GetComponent<SpriteRenderer>();
              }
          
              void Update()
              {
                  if (Random.Range(0F, 1F) < _randomChance)
                  {
                      color = Random.Range(1, 5);
          
                      if (color == 2)
                      {
                          _mySpriteRenderer.color = Color.blue;
                      }
                      if (color == 3)
                      { 
                          _mySpriteRenderer.color = Color.red;
                      }
                      if (color == 4)
                      {
                          _mySpriteRenderer.color = Color.yellow;
                      }
                  }
              }
          }
          

          这里有 20 个

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-03-03
            • 2021-12-18
            • 2019-07-18
            • 2023-02-11
            • 2012-07-22
            • 1970-01-01
            • 2013-12-25
            • 1970-01-01
            相关资源
            最近更新 更多