Как сделать так, чтобы 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));

Другие вопросы по тегам