如何在 Unity 中让 GameObjects 在屏幕上平滑平移?
Posted
技术标签:
【中文标题】如何在 Unity 中让 GameObjects 在屏幕上平滑平移?【英文标题】:How can I make GameObjects translate across the screen smoothly in Unity? 【发布时间】:2018-09-27 21:49:57 【问题描述】:基本上,我有一个游戏,门票出现在屏幕顶部。当一张票完成后,它会被销毁,如果剩余票之间有间隙,票会向下滑动以填补间隙。我实际上让它工作得很好,但他们只是跳到位置上,看起来不太漂亮。
如何将对象顺利移动到目标位置?我试过搞乱Lerp
和Smoothdamp
,但这两个似乎都产生了奇怪的结果(票到处都是。这是我正在使用的相关代码:
public void SlideRail()
ticketList.RemoveAll(x => x == null);
int count = ticketList.Count;
for (int i = destroyedIndex; i < ticketList.Count; i++)
if (ticketList[i] != null)
ticketList[i].transform.Translate(Vector3.left * 35);
ticketList[i].GetComponent<Ticket>().index--;
indexer = railPosition.IndexOf(ticketList.Last().GetComponent<Ticket>().item_pos);
up = false;
DestroyedIndex 是被销毁对象的索引(所以我们只在列表中移动它之后的对象) Up 是一个激活 SlideRail() 的标志,当它设置为 false 时,方法结束,下一个方法继续。
然后在 Update() 方法中调用 SlideRail:
void Update()
if (up && ticketList.Count > 2)
SlideRail();
if (Time.time > nextTicket)
nextTicket = Time.time + timeBetweenTickets;
StartCoroutine(WaitToPrint());
CreateTicket();
UpdatePosition();
我觉得我应该可以使用 Translate
来做到这一点,但我一定错过了一些东西。
【问题讨论】:
【参考方案1】:对于这样的移动,您通常应该包含Time.deltaTime
(作为转换的一个因素)以将移动绑定到帧速率。 [编辑:将这句话翻译成与帧速率无关,这就是我的意思。]
现在,实际上,看起来你只在一帧内完成了移动,因此你得到了这个跳转(你总是在 SlideRail
的末尾将 up
设置为 false)。
这样做的一种可能方法是在触发时指定目标位置(任务完成,需要推过去),然后为每个对象继续调用ticketList[i].transform.position = Vector3.MoveTowards(ticketList[i].transform.position, targetPosition, speed * Time.deltaTime);
(由“到达目标位置”检查框定,例如使用@ 987654325@. 您可以在此添加一个 else 以在阈值范围内直接设置位置(使阈值变小使其不可见,例如 0.1f),尽管您通常只需要对像 lerp 这样减慢的非线性函数使用它最后并且可以停止。[编辑:由于浮点不精确,实际上使用阈值通常总是一个好主意。]
【讨论】:
这是迄今为止我得到的最好的建议,尽管我仍然不确定如何实现它。为了测试,我制作了一个随机方形预制件,并将您建议的代码放在更新函数中,看看它是否可以工作。果然,它做到了。这是因为,我假设 Update() 正在检查每一帧的 if 条件。但是在更新期间会调用 SlideRail(),但并不是每帧都以相同的方式检查条件。我这样想对吗?如果是这样,我如何检查列表中每个元素的 if 条件?我将条件放在了一个while循环中,但这会使Unity崩溃。you should usually include Time.deltaTime (as a factor in translate) to bind the movement to the frame rate.
其实解绑吧。您希望移动帧速率独立,这就是使用 deltaTime 的好处。
@Draco18s 是的,我就是这个意思。
@Matt 好吧,Update
已经是一个循环,所以你不需要额外的循环(当然,除了对所有元素都这样做)。在Update
中检查if(up == true)
,现在,在SlideRail
的末尾,您将其设置为false,没有任何条件,因此,使用提供的代码,您只会进入SlideRail
一次。相反,您希望将 up = false
置于一个条件中,询问对象(或可能所有对象,是否需要一些计数器或类似物)到达目标位置,然后设置 up = false
。
另一种选择是使用协程而不是像 ATHellboy 的答案中那样更新。那确实需要while循环来做与更新相同的事情。【参考方案2】:
使用iTween,可以简化为一行调用:
iTween.MoveTo(someGameObject, iTween.Hash("y", targetY,
"islocal", true,"easetype", iTween.EaseType.spring,
"time", 0.2f));
【讨论】:
所以,我很感兴趣。我最近开始使用 C# 进行编码,将我的 Flash 游戏改造成 Unity。重新回到游戏中。请解释“someGameObject”和“targetY”。虽然这是一个很好的例子。我不知道如何应用这个。当我终于认识到它实际上在导入时,VSCode 现在“看到”了它......我正在将我的 someGameObject 移动到我知道的位置;我实际上知道 x/y 坐标。但这不是这个函数想要的。所以我创建了一个对象来“寻找”并将它放在舞台上。将 someGameObject 移动到这个 targetY 对象?我试过了,也没用。 似乎只适用于 3D 对象?如果是这样,有没有办法使用 2D 游戏对象将此扩展程序/插件用于 2D 游戏?【参考方案3】:这段代码我以前自己写过,也许对你有帮助。
using System;
using System.Collections;
using UnityEngine;
public class MoveController : MonoBehaviour
public float moveTime;
public AnimationCurve moveSpeedCurve;
public AnimationCurve movePositionCurve;
public void StartMoving(Vector2 destination, Action action = null)
StartCoroutine(Move(destination, action));
IEnumerator Move(Vector3 destination, Action action = null)
float currentTime = 0;
float perc = 0;
Vector3 currentPos = transform.localPosition;
while (perc != 1)
currentTime += Time.deltaTime;
if (currentTime > moveTime)
currentTime = moveTime;
perc = moveSpeedCurve.Evaluate(currentTime / moveTime);
transform.localPosition = Vector2.LerpUnclamped(currentPos, destination, movePositionCurve.Evaluate(perc));
yield return null;
if (action != null)
action();
【讨论】:
以上是关于如何在 Unity 中让 GameObjects 在屏幕上平滑平移?的主要内容,如果未能解决你的问题,请参考以下文章
循环遍历用于 AutoSpawn 的 Unity GameObjects 数组
Unity Netcode for GameObjects多人联机(源文件)
有没有一种方法将GameObjects放置在网格模式中(类似于tilemap)?