C# 如何使用Mathf.PingPong在max和min之间缩放和旋转对象?

C# 如何使用Mathf.PingPong在max和min之间缩放和旋转对象?,c#,unity3d,C#,Unity3d,这会将对象缩放并旋转到最大值一次。但我希望它到达最大值,然后回到最小值,然后不停地到达最大值和最小值 然后在它之后,我想使用一个键,例如C,当按下一次C时,它将达到最大值,再按下一次C时,它将回到最小值 但首先我不知道如何不间断地打乒乓球,然后是如何用钥匙打乒乓球 我添加了一个新的全局标志来中断协同进程,如果在中间按下C时它是真的,它将实时地直接改变到另一个方向MIN或MAX,如果中断标志为false,它将像以前那样使用C键。但它不工作,它什么也不做,我需要等待它完成,以更改为最小/最大,即使中

这会将对象缩放并旋转到最大值一次。但我希望它到达最大值,然后回到最小值,然后不停地到达最大值和最小值

然后在它之后,我想使用一个键,例如C,当按下一次C时,它将达到最大值,再按下一次C时,它将回到最小值

但首先我不知道如何不间断地打乒乓球,然后是如何用钥匙打乒乓球

我添加了一个新的全局标志来中断协同进程,如果在中间按下C时它是真的,它将实时地直接改变到另一个方向MIN或MAX,如果中断标志为false,它将像以前那样使用C键。但它不工作,它什么也不做,我需要等待它完成,以更改为最小/最大,即使中断标志为真

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);
       // }
    }
}
使用系统集合;
使用System.Collections.Generic;
使用UnityEngine;
公共类轮换量表:单一行为
{
公共转换目标;//要缩放的目标
公共向量3分钟刻度;//最小刻度值
公共向量3 maxScale;//最大比例值
公共向量3;
公共向量3;
公众浮标速度;
公共bool interruptCoroutine=false;
//在一个例程完成之前阻止输入的标志
私人住宅;
//决定下一步走向的标志
//通过检查员将其设置为最初应朝向的方向
[Serialized Field]布尔-托沃兹明;
私人浮动t=0.0f;
无效更新()
{
if(interruptCoroutine==true)
{
if(Input.GetKeyDown(KeyCode.C))
{
StopAllCoroutines();
start例程(DoMove());
}
}
其他的
{
if(!isMoving&&Input.GetKeyDown(KeyCode.C))
{
start例程(DoMove());
}
}
}
//一旦通过Start例程调用启动,将执行此操作
//每帧直到下一个屈服命令
IEnumerator DoMove()
{
if(interruptCoroutine==false)
{
//以防万一阻止并发例程
如果(正在移动)屈服断裂;
//块输入
isMoving=真;
}
//决定是最大值还是最小值
var targetRot=四元数.Euler(towardsMin?minRotate:maxRotate);
var targetScale=towardsMin?最小刻度:最大刻度;
//存储起始值
var startRot=target.localRotation;
var startScale=target.localScale;
var持续时间=1/速度;
var timePassed=0f;
while(经过的时间<持续时间)
{
t=通过的时间/持续时间;
//可选:添加缓进和缓出
//t=数学平滑步长(0,1,t);
target.localScale=Vector3.Lerp(startScale,targetScale,t);
target.localRotation=Quaternion.Lerp(startRot,targetRot,t);
//增加自上一帧以来经过的时间
timePassed+=Time.deltaTime;
//这告诉Unity在这里“暂停”常规,
//渲染此帧并从此处继续
//在下一帧中
收益返回空;
}
//为了确保以精确的值结束,请用力应用它们一次
target.localScale=targetScale;
target.localRotation=targetRotation;
//反向
towardsMin=!towardsMin;
if(interruptCoroutine==false)
{
//完成->解锁输入
isMoving=假;
}
}
}

首先,请注意:这不是您希望如何使用
Lerp

当前,始终使用增长因子开始新插值,但从当前旋转/缩放到最大值

因此,在下一帧中,当前值将已经移动,因此下一次插值使用不同的起始值等

如何插值基本上有两个用例:

  • 使用常量因子针对目标插入当前值。这将导致在您有一个平滑的运动时,例如,相机的运动预计会有很大的抖动。这不是你想要做的,因为这会变得越来越慢,最终可能永远达不到目标
  • 常数开始点和结束点之间插入一个从
    0
    1
    的因子。这将导致在受控时间内平稳移动。这更像是你想要的

使用时间输入,在
0
和给定的最大参数之间来回移动

在您的情况下,您希望使用速度值作为时间输入的倍增器上升到
1

像这样

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;
        }
    }
}

然后,对于一次性按键,它变得有点复杂。我假设现在你总是想等到一次按下“动画”后再接受下一次按键返回

我会用一只手来做这个。它们的工作方式类似于临时的小
更新
例程,但更易于控制和维护:

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);
}
//在一个例程完成之前阻止输入的标志
布尔·伊斯莫温;
//决定下一步走向的标志
//通过检查员将其设置为最初应朝向的方向
[Serialized Field]布尔-托沃兹明;
无效更新()
{
if(!isMoving&&Input.GetKeyDown(KeyCode.C))
{
start例程(DoMove());
}
}
//一旦通过Start例程调用启动,将执行此操作
//每帧直到下一个屈服命令
IEnumerator DoMove()
{
//以防万一阻止并发例程
如果(正在移动)屈服断裂;
//块输入
isMoving=真;
//决定是最大值还是最小值
var targetRot=四元数.Euler(towardsMin?minRotate:maxRotate);
var targetScale=towardsMin?最小刻度:最大刻度;
//存储起始值
// 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;
}
// 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;
}