Как сделать так, чтобы GameObjects плавно переводил по экрану в Unity?
По сути, у меня есть игра, где в верхней части экрана появляются билеты. Когда билет завершен, он уничтожается, и если между оставшимися билетами есть пробел, билеты сдвигаются вниз, чтобы заполнить пробел. У меня на самом деле все работает нормально, но они просто прыгают на позиции, и это выглядит не очень красиво.
Как я могу плавно переместить объекты в их целевое положение? Я пытался возиться с 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, метод завершается, а следующий метод продолжается.
Затем SlideRail вызывается в методе Update():
void Update()
{
if (up && ticketList.Count > 2)
SlideRail();
if (Time.time > nextTicket)
{
nextTicket = Time.time + timeBetweenTickets;
StartCoroutine(WaitToPrint());
CreateTicket();
UpdatePosition();
}
}
Я чувствую, что должен быть в состоянии сделать это, используя Translate
но я должно быть что-то упустил.
3 ответа
С таким движением вы обычно должны включать Time.deltaTime
(как фактор в переводе), чтобы связать движение с частотой кадров. [Редактировать: перевести это предложение в независимый от частоты кадров, вот что я имел в виду.]
Теперь, на самом деле, похоже, что вы делаете движение всего за один кадр, и, таким образом, вы получаете этот прыжок (вы всегда устанавливаете up
ложно в конце SlideRail
).
Возможный способ сделать это - указать целевую позицию при срабатывании триггера (задача выполнена, нужно нажать на нее), а затем продолжить вызов ticketList[i].transform.position = Vector3.MoveTowards(ticketList[i].transform.position, targetPosition, speed * Time.deltaTime);
для каждого объекта (в рамке проверки "достигнута целевая позиция", например, с использованием if(Vector3.Distance(ticketList[i].transform.position, targetPosition) > someThreshold)
, Вы можете добавить к этому еще else, чтобы напрямую установить позицию в пороговом диапазоне (сделать порог небольшим, чтобы он не был виден, например, 0.1f), хотя это обычно требуется только для нелинейных функций, таких как lerp, которые замедляются в конце и может заглохнуть. [Редактировать: из-за неточности с плавающей запятой, на самом деле, всегда лучше использовать порог.]
Я написал этот кусок кода для себя раньше, может быть, это поможет вам.
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();
}
}
Используя iTween, это может быть упрощено в вызов одной линии:
iTween.MoveTo(someGameObject, iTween.Hash("y", targetY,
"islocal", true,"easetype", iTween.EaseType.spring,
"time", 0.2f));