Unity无法从计时器经过的功能中翻转精灵

Unity Can't flip sprite from timer elapsed function

我刚刚开始探索 unity 和 c# 的协同工作,不小心遇到了下一个问题: 我有一条鱼。鱼应该从左到右,然后改变方向,从右到左。我还没有完成那个移动部分,但我打算用计时器来完成。因此计时器已激活,鱼开始移动,计时器停止,改变方向,计时器重置,鱼开始移动等。 我想翻转我的精灵,所以它会面向正确的方向。它不适用于 Elapsed 函数,我不明白为什么。如果您有任何想法,请分享

using System.Timers;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class FishBehavior : MonoBehaviour
{
    public float moveSpeed;
    private int direction; //for left -1, for right 1
    private Timer timer;
    private SpriteRenderer img;
    private Rigidbody2D rb;


    void Start()
    {
        img = GetComponent<SpriteRenderer>();
        rb = GetComponent<Rigidbody2D>();
        direction = 1;
        timer = new Timer();
        timer.Elapsed += TimerElapsed;
        ChangeTimerOptions();
    }

    private void Move()
    {
        //moving
    }

    private void ChangeDirection()
    {
        direction *= -1;
        img.flipX = !img.flipX; //Doesn't work!
        //stop movement
    }

    private void ChangeTimerOptions()
    {
        System.Random rand = new System.Random();
        timer.Interval = rand.Next(3000, 8000);
        timer.Enabled = true;
        Move();
    }

    private void TimerElapsed(object source, ElapsedEventArgs e)
    {
        ChangeDirection();
        ChangeTimerOptions();
    }

}

问题是 TimerElapsed is on a different thread than that of Unity. Meaning any sort of calls you make inside of it to methods that would normally work in Unity will not. I would recommend instead using a Coroutine or an Invoke.

的回调

为了简要说明两者的含义,Coroutine 是一种特殊方法,可以在多个帧上完成少量工作。该方法也可以暂停一定时间以等待下线执行代码。 InvokeInvokeRepeating 是在一定时间后执行方法的调用,或者在设定的开始时间和设定的时间量后连续调用方法。

如果你最终也加入了一个动作并随机化你的等待时间,我会考虑使用 Coroutine 而不是 Invoke 这样你就可以处理来自同一个的所有 movement/flip 逻辑主叫。

public float moveSpeed;
private int direction; //for left -1, for right 1
private Timer timer;
private SpriteRenderer img;
private Rigidbody2D rb;

// store the coroutine in case a duplicate call occurs somehow
private Coroutine MoveAndFlip = null;

void Start()
{
    img = GetComponent<SpriteRenderer>();
    rb = GetComponent<Rigidbody2D>();
    direction = 1;

    // detect if any coroutine is already running
    CleanUpMoveAndFlipCoroutine();

    MoveAndFlip = StartCoroutine(MoveAndFlipAsset());
}

private void Move()
{
    //moving
}

private void ChangeDirection()
{
    direction *= -1;
    img.flipX = !img.flipX;
    //stop movement
}

private IEnumerator MoveAndFlipAsset()
{
    // instead of milliseconds, use seconds
    float randomTimeInterval = Random.Range(3.0f, 8.0f);

    while(true)
    {
       // can do movement here - depending on how you would like to apply motion it would change how
       // it is implemented such as directly changing velocity, using a Vector3.Lerp, AddForce, etc.

        // wait the time to flip
        yield return new WaitForSeconds(randomTimeInterval);

        // now flip
        ChangeDirection();
    }
}

private void CleanUpMoveAndFlipCoroutine()
{
    if (MoveAndFlip != null)
        StopCoroutine(MoveAndFlip);
}

如果您想要上述实现的 Invoke 示例,请按以下方法进行操作。

void Start()
{
    img = GetComponent<SpriteRenderer>();
    rb = GetComponent<Rigidbody2D>();
    direction = 1;

    Invoke("ChangeDirection", RandomTimeToFlip());
}

private float RandomTimeToFlip()
{
    return Random.Range(3.0f, 8.0f); ;
}

private void ChangeDirection()
{
    direction *= -1;
    img.flipX = !img.flipX;
    //stop movement

    // start our method again after a set amount of time
    Invoke("ChangeDirection", RandomTimeToFlip());
}

编辑: 显然,您可以通过更改计时器的 SynchronizingObject 引用来使用 TimerElapsed,但我不确定要分配什么它到。不过,我仍然建议使用上面描述的两种方法之一。