Как отложить метод в Unity и C #? - PullRequest
2 голосов
/ 01 октября 2019

Меня зовут Лоренц, и мой вопрос заключается в том, как задержать изменение цвета моих спрайтов в Unity с помощью c #.

Сейчас у меня есть генератор случайных чисел, который выбирает цвет на основе числа, но это происходиткаждый кадр. Так что теперь реальная проблема заключается в том, как отложить его, чтобы он менялся реже.

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;
        }
    }
}

Ответы [ 6 ]

1 голос
/ 02 октября 2019

Это можно сделать несколькими способами. Прежде чем показать вам конкретный материал, вот базовая структура, общая для всех примеров кода, которую я буду использовать:

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;
}

Coroutine & WaitForSeconds:

Это рекомендуемая процедура для случаев, когда вам нужно задержать или упорядочить код в единицу.

Обратите внимание, что существуют другие типы методов ожиданияобеспечивается единством, например WaitWhile, WaitUntil и т. д. *

//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();
    }
}

Не выполняйте Async Await!

Ну, это может сделано, но в нем много подводных камней и очень много НЕ рекомендуется для начинающих.

И яВ любом случае, он не предназначен для замены сопрограмм.


Не выполняйте InvokeRepeating!

Это метод, основанный на магических строках и отражении. Полезно для быстрой и простой установки, например, кода, но его следует избегать, если это вообще возможно (и это возможно, благодаря вышеописанным методам), как чума в рабочем коде.

1 голос
/ 01 октября 2019

Вы можете поместить свой код в цикл в 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);
        }
    }
}
0 голосов
/ 01 октября 2019

Другой альтернативой сопрограммам или простой таймер в 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;
    }
}

Тогда вы можете просто включить и отключить изменение цвета, включив и отключив этот компонент.

Илив качестве альтернативы просто переместите соответствующие строки кода из OnEnable и OnDisable в соответствующие открытые методы, такие как public void StartColorChange и public void StopColorChange.

0 голосов
/ 01 октября 2019

Есть несколько способов сделать то, что вы хотите.

Таймер

Вы можете использовать таймер для изменения цвета каждый x промежуток времени.

Существует как минимум 2 способа сделать это:

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 визбегайте звонка GetComponent каждый раз. См. Здесь

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;
        }
    }
}

Случайный

Вы можете использовать ГСЧ для определения дермы, если хотите изменить цветэтот кадр или нет. Это означает, что вы не можете определить, сколько раз за 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

0 голосов
/ 01 октября 2019

Вы можете справиться с этим с помощью простой сопрограммы. Если вы не знаете, как, посмотрите на эту ссылку (https://docs.unity3d.com/ScriptReference/WaitForSeconds.html) в официальной документации API Unity.

Если вы не хотите работать с сопрограммами, вы можете использовать метод Unvo's InvokeRepating. Создайте новую функцию для изменения цвета и вызывайте ее каждые x секунд. Вот ссылка и на метод InvokeRepating https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html

ps: в этом случае использование Switch для определения целого числа цвета более эффективно, а также использование if и else if для каждого цветового кода. Его эффективнее и легче масштабировать.

0 голосов
/ 01 октября 2019

Вы можете использовать счетчик времени и 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;
            }
        }
    }
}
...