Как я могу использовать Mathf.PingPong для масштабирования и поворота объекта между максимумом и минимумом? - PullRequest
0 голосов
/ 14 апреля 2020
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateAndScale : MonoBehaviour
{
    public Transform target; // Target to scale
    public Vector3 minScale; // Minimum scale value
    public Vector3 maxScale; // Maximum scale value
    public Vector3 maxRotate;
    public Vector3 minRotate;
    public float speed;

    private float t = 0.0f;

    void Update()
    {
        //if (Input.GetKey(KeyCode.C))
        //{
        t += speed * Time.deltaTime;
            target.localScale = Vector3.Lerp(target.localScale, maxScale, t);
            target.localRotation = Quaternion.Lerp(target.localRotation,
                Quaternion.Euler(maxRotate.x, maxRotate.y, maxRotate.z), t);
       // }
    }
}

Это позволит масштабировать и поворачивать объект до максимума один раз. но я хочу, чтобы он достиг максимума, а затем go вернулся к минимуму, а затем к максимуму и минимуму без остановок.

Затем после него я хочу использовать клавишу, например C и при однократном нажатии на C он дойдет до макс. Еще одним нажатием на C он вернется к мин.

, но сначала я не уверен, как это сделать с помощью пинг-понга без остановок, а затем как это сделать с помощью клавиши?

Я добавил новый глобальный флаг, чтобы прервать сопрограмму, поэтому, если это правда при нажатии C в середине, он изменится в реальном времени непосредственно в другом направлении мин или макс, и если флаг прерывания будет ложным, это будет использовать клавишу C, как и раньше. Но он не работает, он ничего не делает, но мне нужно подождать, пока он завершится sh, чтобы изменить его на мин / макс, даже если флаг прерывания имеет значение true.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateAndScale : MonoBehaviour
{
    public Transform target; // Target to scale
    public Vector3 minScale; // Minimum scale value
    public Vector3 maxScale; // Maximum scale value
    public Vector3 maxRotate;
    public Vector3 minRotate;
    public float speed;
    public bool interruptCoroutine = false;

    // Flag for blocking input until one routine is done
    private bool isMoving;

    // Flag for deciding in which direction to go next
    // Via the Inspector set this to the direction it shall initially go towards
    [SerializeField] bool towardsMin;

    private float t = 0.0f;

    void Update()
    {


        if(interruptCoroutine == true)
        {
            if (Input.GetKeyDown(KeyCode.C))
            {
                StopAllCoroutines();
                StartCoroutine(DoMove());
            }
        }
        else
        {
            if (!isMoving && Input.GetKeyDown(KeyCode.C))
            {
                StartCoroutine(DoMove());
            }
        }
    }

    // Once started via a StartCoroutine call this will be executed
    // every frame until the next yield command
    IEnumerator DoMove()
    {
        if (interruptCoroutine == false)
        {
            // Just in case block concurrent routines
            if (isMoving) yield break;

            // Block input
            isMoving = true;
        }

        // Decide if going to Max or min
        var targetRot = Quaternion.Euler(towardsMin ? minRotate : maxRotate);
        var targetScale = towardsMin ? minScale : maxScale;

        // Store Start values
        var startRot = target.localRotation;
        var startScale = target.localScale;

        var duration = 1 / speed;
        var timePassed = 0f;
        while (timePassed < duration)
        {
            t = timePassed / duration;
            // Optional: add ease-in and ease-out
            //t = Mathf.SmoothStep(0, 1, t);

            target.localScale = Vector3.Lerp(startScale, targetScale, t);
            target.localRotation = Quaternion.Lerp(startRot, targetRot, t);

            // Increase the time passed since last frame
            timePassed += Time.deltaTime;

            // This tells Unity to "pause" the routine here,
            // render this frame and continue from here
            // in the next frame
            yield return null;
        }

        // Just to be sure to end with exact values apply them hard once
        target.localScale = targetScale;
        target.localRotation = targetRot;

        // Invert direction
        towardsMin = !towardsMin;

        if (interruptCoroutine == false)
        {
            // Done -> unlock input
            isMoving = false;
        }
    }
}

1 Ответ

2 голосов
/ 14 апреля 2020

Прежде всего, общее примечание: это не то, как вы хотите использовать Lerp.

В настоящее время вы всегда начинаете новую интерполяцию с растущим коэффициентом, но от текущего поворота / масштаба к максимальным значениям. .

Таким образом, в следующем кадре текущие значения уже будут перемещены, поэтому при следующей интерполяции используется другое начальное значение и т. Д. c.

Как правило, для интерполяции существует два варианта использования:

  • Интерполировать текущее значение по отношению к цели, используя коэффициент constant . Это приводит к сглаженному движению, когда, например, движение камеры ожидает много дрожания. Это не то, что вы хотите сделать, так как это становится все медленнее и медленнее в конце, вероятно, никогда не достигнув цели.
  • Интерполяция между постоянным начальной и конечной точкой с коэффициентом растущим от 0 до 1. Это приводит к плавному движению в течение контролируемого времени. Это больше похоже на то, что вы хотите.

Mathf.PingPong использует ввод времени и перемещается вперед и назад между 0 и заданным максимальным параметром.

В вашем случае вы хотите go до 1, используя значение скорости в качестве множителя для ввода времени.

Как это

void Update()
{
    t = Mathf.PingPong(speed * Time.time, 1);
    target.localScale = Vector3.Lerp(target.localScale, maxScale, t);
    target.localRotation = Quaternion.Lerp(target.localRotation,
        Quaternion.Euler(maxRotate.x, maxRotate.y, maxRotate.z), t);
}

Тогда для одноразового нажатия клавиши все становится немного сложнее. Я бы предположил, что на данный момент вы всегда хотите подождать, пока одно нажатие кнопки «анимация» не будет сделано, прежде чем принять следующее нажатие клавиши для возврата.

Я бы использовал для этого Coroutine . Они работают как временные небольшие подпрограммы Update, но их проще контролировать и поддерживать:

// Flag for blocking input until one routine is done
bool isMoving;

// Flag for deciding in which direction to go next
// Via the Inspector set this to the direction it shall initially go towards
[SerializeField] bool towardsMin;

void Update()
{
    if(!isMoving && Input.GetKeyDown(KeyCode.C))
    {
        StartCoroutine(DoMove());
    }
}

// Once started via a StartCoroutine call this will be executed
// every frame until the next yield command
IEnumerator DoMove()
{
    // Just in case block concurrent routines
    if(isMoving) yield break;

    // Block input
    isMoving = true;

    // Decide if going to Max or min
    var targetRot = Quaternion.Euler(towardsMin ? minRotate : maxRotate);
    var targetScale = towardsMin ? minScale : maxScale;

    // Store Start values
    var startRot = target.localRotation;
    var startScale = target.localScale;

    var duration = 1 / speed;  
    var timePassed = 0f;

    while(timePassed < duration)
    {
        t = timePassed / duration;
        // Optional: add ease-in and ease-out
        //t = Mathf.SmoothStep(0, 1, t);

        target.localScale = Vector3.Lerp(startScale, targetScale, t);
        target.localRotation = Quaternion.Lerp(startRotation, targetRotation, t);

        // Increase the time passed since last frame
        timePassed += Time.deltaTime;

        // This tells Unity to "pause" the routine here,
        // render this frame and continue from here
        // in the next frame
        yield return null;
    }

    // Just to be sure to end with exact values apply them hard once
    target.localScale = targetScale;
    target.localRotation = targetRotation;

    // Invert direction
    towardsMin = !towardsMin;

    // Done -> unlock input
    isMoving = false;
}

Если вы хотите прервать текущую подпрограмму и сразу же go вернуться, вместо нее используйте

// Flag for deciding in which direction to go next
// Via the Inspector set this to the direction it shall initially go towards
// Since the routine inverts this you have to set the initial value to exactly the opposite
// of what you want to be the first direction!
[SerializeField] bool towardsMin;

void Update()
{
    if(Input.GetKeyDown(KeyCode.C))
    {
        StopAllCoroutines(DoMove());
        StartCoroutine(DoMove());
    }
}

// Once started via a StartCoroutine call this will be executed
// every frame until the next yield command
IEnumerator DoMove()
{
    // Invert direction for the next time
    towardsMin = !towardsMin;

    // Decide if going to Max or min
    var targetRot = Quaternion.Euler(towardsMin ? minRotate : maxRotate);
    var targetScale = towardsMin ? minScale : maxScale;

    // Store Start values
    var startRot = target.localRotation;
    var startScale = target.localScale;

    var duration = 1 / speed;  
    var timePassed = 0f;
    while(timePassed < duration)
    {
        t = timePassed / duration;
        // Optional: add ease-in and ease-out
        //t = Mathf.SmoothStep(0, 1, t);

        target.localScale = Vector3.Lerp(startScale, targetScale, t);
        target.localRotation = Quaternion.Lerp(startRotation, targetRotation, t);

        // Increase the time passed since last frame
        timePassed += Time.deltaTime;

        // This tells Unity to "pause" the routine here,
        // render this frame and continue from here
        // in the next frame
        yield return null;
    }

    // Just to be sure to end with exact values apply them hard once
    target.localScale = targetScale;
    target.localRotation = targetRotation;
}

Короче говоря: Избавьтесь от isMoving и всех вхождений полностью!

Затем вам нужно будет инвертировать флаг towardsMin перед 1058 * l oop! Таким образом, вам также нужно будет инвертировать флаг в Инспекторе, поэтому, если вы хотите, чтобы он сначала двигался в направлении максимума, установите для него значение true => Инвертировано сопрограммой => При первом перемещении в сторону значений максимума.


Напечатано на смартфоне, но я надеюсь, что идея проясняется

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...