如何在移动Y轴的物体上平稳地改变X物体的位置?

时间:2019-04-09 16:48:59

标签: c# unity3d

对象沿Y轴移动,我需要平滑地移动X轴位置。

void FixedUpdate()
{
   playerRigidbody.velocity = new Vector2(0.0f, 2.0f);

    if (Input.GetKeyDown(KeyCode.RightArrow))
    {
        if (newBallPos != 2)
        {
            Vector3 pos1 = this.transform.position;
            Vector3 pos2 = this.transform.position;
            pos2.x += 1f;
            StartCoroutine(Move_Routine(this.transform, pos1, pos2));
        }
    }

   ----
}

----

private IEnumerator Move_Routine(Transform transform, Vector3 from, Vector3 to)
{
    float t = 0f;
    while (t < 1f)
    {
        t += Time.deltaTime;

        transform.position = Vector3.Lerp(from, to, Mathf.SmoothStep(0f, 1f, t));
        yield return null;
    }
}

使用此代码。 X轴发生变化,但是随着Y轴的位置变旧,它会随着X轴的移动而产生某种程度的抖动。

单击箭头时,我需要平滑地更改对象X的位置,同时仍沿Y轴移动(我需要将X位置更改1)

感谢您的帮助!

2 个答案:

答案 0 :(得分:0)

尝试用这种方法将x轴添加到Time.smoothDeltaTime;中,将值在几帧上取平均值,以获得更平滑的效果,这是一种偏爱,但有时会产生明显的不同。

 pos2.x += Time.smoothDeltaTime;

答案 1 :(得分:0)

首先,据我了解,您只想平滑头寸的X值,所以您宁可这么做

private IEnumerator Move_Routine(Transform transform, float from, float to)
{
    float t = 0f;
    while (t < 1f)
    {
        t += Time.deltaTime;

        // only lerp the single float value
        var currentX = Mathf.SmoothStep(from, to, t);

        // let the current other two values unchanged
        transform.position.x = currentX;

        yield return null;
    }

    // Just to be sure there is no under/overshooting I would
    // always set the fixed value in the end
    transform.position.x = to;
}

但是,您遇到的另一个问题是您正在处理RigidBody。

  1. 您完全不应该在刚体上使用transform.position =,而应该使用playerRigidbody.position =playerRigidbody.MovePosition(targetPosition)

  2. 协程会在每个Update调用中执行,但您只应在FixedUpdate =>中更改刚体,才能在协程中使用WaitForFixedUpdate

  3. 第三个问题是,如果在完成运动之前再次按下该按钮,则可能同时运行多个协同程序。 =>您可以通过简单的bool标志解决此问题。另外,如果您想允许输入(例如,要向另一个方向移动,则应先StopCoroutine当前例程)

因此产生了抖动等。

private bool alreadyLerping;

private void FixedUpdate()
{
    playerRigidbody.velocity = new Vector2(0.0f, 2.0f);

    if (Input.GetKeyDown(KeyCode.RightArrow))
    {
        // only do if not lerping laready otherwise input is ignored
        if (newBallPos != 2 && !alreadyLerping)
        {
            var currentX = transform.position.x;
            var targetX = currentX + 1.0f;
            StartCoroutine(Move_Routine(currentX, targetX));
        }
    }

    ...
}

// I just added an optional duration value
// if you do not pass it it will be 1 second as you had it
// but this way you can still make it faster or slower without having
// to hardcode
// you also don't have to pass in the transform or if you do you should not call it transform
private IEnumerator Move_Routine(float from, float to, float duration = 1.0f)
{
    if(alreadyLerping) yield break;

    alreadyLerping = true;

    var passedTime = 0f;

    do
    {
        yield return new WaitForFixedUpdate();

        var lerpfactor = passedTime / duration;

        var currentX = Mathf.SmoothStep(from, to, lerpfactor);

        playerRigidbody.MovePosition(new Vector3(currentX, transform.position.y, transform.position.z));

        passedTime += Time.deltaTime;

    } while (passedTime < duration);

    // Just to be sure there is no under/overshooting I would
    // always set the fixed value in the end
    playerRigidbody.MovePosition(new Vector3(to, transform.position.y, transform.position.z));

    // release the flag
    alreadyLerping = false;
}

或者如果您想要StopCoroutine变体

private IEnumerator currentRoutine;

private void FixedUpdate()
{
    playerRigidbody.velocity = new Vector2(0.0f, 2.0f);

    if (Input.GetKeyDown(KeyCode.RightArrow))
    {
        // only do if not lerping laready otherwise input is ignored
        if (newBallPos != 2)
        {
            var currentX = transform.position.x;
            var targetX = currentX + 1.0f;

            if(currentRoutine!=null) StopCoroutine(currentRoutine);

            currentRoutine = Move_Routine(currentX, targetX);

            StartCoroutine(currentRoutine);
        }
    }

    ...
}