接收连续输入呼叫的最佳方式?

Best way to Receive Consecutives Input calls?

我是 Unity 的初学者,虽然我没有太多编码经验,但我知道我的代码很乱。

所以,事情是这样的:当我单击鼠标 (0) 时,我插入了一个允许玩家从其位置 Lerp 到“Object1”的特定功能。单击时,时间计数器启动,在 2 秒内,如果我再次单击 de mouse(0),它会将播放器跳到第二次,现在跳到“Object2”。我应用此逻辑的方式允许用户将播放器 Lerp 4 次到 4 个不同的位置,如果每个输入都在 5 秒内。

我付出了很多努力,找到了一种让它工作的方法,它是有功能的,但是以许多布尔值和 ifs 语句为代价,一个完整而糟糕的 Spaguetti。所以我对你们的问题是,我怎样才能让这整个混乱变得更加干净和高效?在这种情况下我可以使用什么代码结构来使整个事情更具可读性,并减少大量的布尔值和条件?提前谢谢大家!

代码如下:

    [SerializeField] private Transform cube1;
    [SerializeField] private Transform cube2;
    [SerializeField] private Transform cube3;
    [SerializeField] private Transform cube4;
    private float timer;
    private bool canCount;
    private bool click1;
    private bool click2;
    private bool click3;
    private bool click4;
    private bool lerp1;
    private bool lerp2;
    private bool lerp3;
    private bool lerp4;

private void CountDown()
{
    if (canCount)
    {
        timer += Time.deltaTime;
        if (timer >= 2)
        {
            canCount = false;
            Debug.Log("Timer End");
        }
    }
}
private void Lerp()
{
    if (lerp1)
    {
        transform.position = Vector3.Lerp(transform.position, cube1.position, Time.deltaTime * 10);
    }
    if (lerp2)
    {
        transform.position = Vector3.Lerp(transform.position, cube2.position, Time.deltaTime * 10);
    }
    if (lerp3)
    {
        transform.position = Vector3.Lerp(transform.position, cube3.position, Time.deltaTime * 10);
    }
    if (lerp4)
    {
        transform.position = Vector3.Lerp(transform.position, cube4.position, Time.deltaTime * 10);
    }
}

private void Update()
{
    CountDown();
    Lerp();

    if (Input.GetMouseButtonDown(0))
    {
        if (!canCount)
        {
            click1 = true;
            click2 = false;
            click3 = false;
            click4 = false;
            lerp2 = false;
            lerp3 = false;
            lerp4 = false;

            if (click1)
            {
                click1 = false;
                click2 = true;
                lerp4 = false;
                lerp1 = true;
                timer = 0;
                canCount = true;
            }

            else if (click2 && canCount)
            {
                click2 = false;
                click3 = true;
                lerp1 = false;
                lerp2 = true;
                timer = 0;
            }
            else if (click3 && canCount)
            {
                click3 = false;
                click4 = true;
                lerp2 = false;
                lerp3 = true;
                timer = 0;
            }
            else if (click4 && canCount)
            {
                click4 = false;
                click1 = true;
                lerp3 = false;
                lerp4 = true;
                timer = 0;
            }
        }
        else
        {
            if (click1)
            {
                click1 = false;
                click2 = true;
                lerp4 = false;
                lerp1 = true;
                timer = 0;
                canCount = true;
            }

            else if (click2)
            {
                click2 = false;
                click3 = true;
                lerp1 = false;
                lerp2 = true;
                timer = 0;
            }
            else if (click3)
            {
                click3 = false;
                click4 = true;
                lerp2 = false;
                lerp3 = true;
                timer = 0;
            }
            else if (click4)
            {
                click4 = false;
                click1 = true;
                lerp3 = false;
                lerp4 = true;
                timer = 0;
            }
        }
    }

下面的代码应该允许您设置无限数量的对象以按顺序移动,每个对象都有独特的等待时间,直到玩家可以再次单击以移动到下一个对象。如果用户在给定时间内没有点击,则下一次点击再次移动到第一个对象。

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

/// <summary>
/// Holds data to a goal target and how long the player has to click to move towards it
/// </summary>
[System.Serializable]
public class CountdownLerpData
{
    public float timeToWaitForClick = 0.0f;
    public Transform destination = null;
}

public class YourScript : MonoBehaviour
{
    [SerializeField] private List<CountdownLerpData> LerpDestinations = new List<CountdownLerpData>();

    private Coroutine ExtraMouseClicks = null;
    private Coroutine MoveToDestination = null;

    // distance until our object has reached our goal point
    private const float DISTANCE_EPSILON = 0.001f;

    // the speed at which our object moves to a goal position
    private float moveSpeed = 10f;

    private void Update()
    {
        // just use update for your first click input, any successive clicks are handled by the Coroutine
        if (Input.GetMouseButtonDown(0) && ExtraMouseClicks == null)
            ExtraMouseClicks = StartCoroutine(MoveAndDetectMouseClicks(0));
    }

    /// <summary>
    /// Will start a movement coroutine and wait until the time to click is exceeded or
    /// when the player clicks, it will start a new coroutine
    /// </summary>
    /// <param name="idx"></param>
    /// <returns></returns>
    private IEnumerator MoveAndDetectMouseClicks(int idx)
    {
        // we wait for the next frame to assure we do NOT use the same mouse click event
        yield return null;

        // we exceeded our container, so exit the coroutine and set it to null
        if (idx >= LerpDestinations.Count)
        {
            ExtraMouseClicks = null;
            yield break;
        }

        float currentTimer = 0.0f;      // the time to wait for our next click
        bool hasMouseClick = false;     // flag to determine if the user clicked

        // start a new Coroutine for the motion - stop it if it is ongoing
        if (MoveToDestination != null)
            StopCoroutine(MoveToDestination);

        // start it with our current index
        MoveToDestination = StartCoroutine(MoveTowardsDestination(LerpDestinations[idx].destination.position));

        // now wait our time to determine if another mouse click occurs, if it does, then increment our counter
        while (currentTimer <= LerpDestinations[idx].timeToWaitForClick && !hasMouseClick)
        {
            // if we have a mouse click, then 
            if (Input.GetMouseButtonDown(0) && !hasMouseClick)
            {
                // the user clicked, so set our flag to true
                hasMouseClick = true;

                // assign the coroutine
                ExtraMouseClicks = StartCoroutine(MoveAndDetectMouseClicks(idx + 1));
            }

            // increase our time since the last frame
            currentTimer += Time.deltaTime;

            yield return null;
        }

        // the user missed the window to click again, so set the reference back to null
        if (!hasMouseClick)
            ExtraMouseClicks = null;
    }

    /// <summary>
    /// Moves your object to a goal location by moveSpeed speed
    /// </summary>
    /// <param name="goalPosition"></param>
    /// <returns></returns>
    private IEnumerator MoveTowardsDestination(Vector3 goalPosition)
    {
        // now lerp until we reach our destination
        while (Vector3.Distance(transform.position, goalPosition) > DISTANCE_EPSILON)
        {
            transform.position = Vector3.MoveTowards(transform.position, goalPosition, moveSpeed * Time.deltaTime);
            yield return null;
        }
    }
}

我的方法将删除单独跟踪此数据,而不是存储多次点击和 lerps,这是非常令人头疼的。相反,您将拥有 List 个名为 CountdownLerpData 的对象,其中包含两个字段。第一个字段是 timeToWaitForClick,这是程序在单击发生后等待的时间,以决定单击是移动到列表中的下一个对象,还是移回第一个对象。第二个字段是destination,就是你此时要移动到的对象的Transform

当用户第一次点击时,它会通过您的对象数据的 List 启动一个似是而非的反应链。最初的点击将启动所谓的 Coroutine,它可以简单地定义为一种特殊功能,允许在多个帧上完成较大任务的一小部分。

一旦你开始倒计时Coroutine,它就会开始MoveTowardsDestinationCoroutine,这只是将你的对象移动到索引倒计时Coroutine的目标对象是。如果用户碰巧在所需的时间间隔内单击,它将再次调用相同的 Coroutine 但现在索引增加 1 以移动到我们列表中的下一个对象。如果刚好达到或超过List,就会退出Coroutine

让我知道这对您来说如何,或者这是否不是您想要的。如果我的实施不是您想要的,我可以调整答案。如果您对它的工作原理或原因有任何疑问,也请发表评论。