【问题标题】:Best way to Receive Consecutives Input calls?接收连续输入呼叫的最佳方式?
【发布时间】:2021-08-22 01:09:42
【问题描述】:

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

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

经过大量努力,我找到了一种让它工作的方法,它是功能性的,但代价是许多布尔值和 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;
            }
        }
    }

【问题讨论】:

    标签: c# unity3d if-statement boolean lerp


    【解决方案1】:

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

    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,这很令人头疼。相反,您将拥有一个名为CountdownLerpData 的对象List,其中包含两个字段。第一个字段是timeToWaitForClick,这是程序在单击发生后等待的时间,以决定单击是移动到列表中的下一个对象,还是移回第一个对象。第二个字段是destination,这是您此时要移动到的对象的Transform

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

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

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

    【讨论】:

    • 这正是我想要的。我知道它与协程有关!非常感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    • 1970-01-01
    相关资源
    最近更新 更多