接收连续输入呼叫的最佳方式?
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
,它就会开始MoveTowardsDestination
Coroutine
,这只是将你的对象移动到索引倒计时Coroutine
的目标对象是。如果用户碰巧在所需的时间间隔内单击,它将再次调用相同的 Coroutine
但现在索引增加 1 以移动到我们列表中的下一个对象。如果刚好达到或超过List
,就会退出Coroutine
。
让我知道这对您来说如何,或者这是否不是您想要的。如果我的实施不是您想要的,我可以调整答案。如果您对它的工作原理或原因有任何疑问,也请发表评论。
我是 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
,它就会开始MoveTowardsDestination
Coroutine
,这只是将你的对象移动到索引倒计时Coroutine
的目标对象是。如果用户碰巧在所需的时间间隔内单击,它将再次调用相同的 Coroutine
但现在索引增加 1 以移动到我们列表中的下一个对象。如果刚好达到或超过List
,就会退出Coroutine
。
让我知道这对您来说如何,或者这是否不是您想要的。如果我的实施不是您想要的,我可以调整答案。如果您对它的工作原理或原因有任何疑问,也请发表评论。