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

问题描述

我是 Unity 的初级编码员,虽然我没有太多编码经验,但我知道我的代码一团糟。

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

我费了很大的力气,找到了让它工作的方法,它很实用,但代价是使用了许多布尔值和 ifs 语句,这是一个完整而糟糕的意大利面。所以我对你们的问题是,我怎样才能让这整个烂摊子更干净、更高效?在这种情况下我可以使用什么代码结构来使整个事情更具可读性,并切断大量的布尔值和条件?先谢谢大家!

代码如下:

    [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,它将开始 MoveTowardsDestination Coroutine,它只会将您的对象移动到倒计时 Coroutine 所在的索引的目标对象.如果用户碰巧在所需的时间间隔内点击,它将再次调用相同的 Coroutine,但现在索引增加 1 以移动到我们列表中的下一个对象。如果它碰巧达到或超过 List,它将退出 Coroutine

让我知道这对您有什么影响,或者这是否不是您想要的。如果我的实现不是你想要的,我可以调整答案。如果您对它的工作方式或原因有任何疑问,也可以发表评论。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...