简体   繁体   English

如何使用C#创建一个可以在两个位置之间统一移动的对象,为什么我的代码不起作用?

[英]How do I create an object to move between two positions in unity, using C#, and why is my code not working?

I have this piece of code right here that should make the block object move between the startPos and endPos objects, but something is wrong about it and I don't know what. 我在这里有这段代码,应该可以使块对象在startPosendPos对象之间移动,但是它有些问题,我不知道是什么。

void FixedUpdate()
{
    if (block.transform.position == startPos.transform.position)
    { 
        check = false; 
    }

    if(block.transform.position == endPos.transform.position)
    { 
        check = true;  
    }

    if (check == false)
    {
        block.transform.position = Vector3.Lerp(block.transform.position, endPos.transform.position, .03f);
    }

    if (check == true)
    { 
        block.transform.position = Vector3.Lerp(block.transform.position, startPos.transform.position, .03f);     
    }
}

At some point the block will reach endPos , and then on its way back to startPos it will stop, because the functions will be executed simultaneously. 在某些时候,该块将到达endPos ,然后在返回startPos它将停止,因为这些函数将同时执行。 But how is this possible because my if's right there should not allow this to happen? 但是这怎么可能呢,因为如果我对的话应该不允许这种情况发生?

The third argument of Vector3.Lerp is a percentage of the distance between the first two arguments. Vector3.Lerp的第三个参数是前两个参数之间距离的百分比 When you pass in 0.03f , you're getting 3% closer to your end position but never actually getting exactly there (You can prove this by logging the block's position and the target's position and you'll see they're never perfectly equal). 当您在传递0.03f ,你越来越接近你的最终位置3%,但实际上从未得到确切有(您可以通过登录块的位置和目标的位置证明了这一点,你会看到他们是永远不会完全相等) 。

Instead of checking if your block's position with the == operator (which works to an accuracy of 0.00001f) to the target position, you could simply check if it's close enough with Vector3.Distance : 不用用==运算符检查块的位置(其精度为0.00001f)到目标位置,只需使用Vector3.Distance检查它是否足够 Vector3.Distance

if(Vector3.Distance(block.transform.position, endPos.transform.position) < 0.1f) {

You can make your "close enough threshold" ( 0.1f in the example) a named variable for easy adjustment until it feels right. 您可以将“足够接近的阈值”(在示例中为0.1f )命名为易于调整的变量,直到感觉正确为止。

In general you should always be using 一般来说,您应该始终使用

instead of 代替

except you are dealing with Physics somehow (which doesn't seem to be the case here). 除了您正在以某种方式处理Physics (此处似乎并非如此)。 Also see the Update and FixedUpdate Tutorial 另请参阅Update和FixedUpdate教程


The issue with Vector3.Lerp is that it doesn't behave as you expect. Vector3.Lerp的问题在于它的行为不符合您的预期。

I guess you like that it starts fast and then becomes "smooth" ... but actually this might be your problem. 我想您喜欢它启动快,然后变得“平稳”的情况,但是实际上这可能是您的问题。

It never reaches the target position really. 从未真正达到目标位置。 It just gets closer and closer and slower and slower ... 它只是越来越近越来越慢...

... until at some moment the == with a precision of 0.00001f eventually becomes true. ...直到某个时刻==精度为0.00001f最终变为真。

So it might seem that it stopped but actually it might still be moving just really really slow. 因此,似乎它停止了,但实际上它可能仍在移动,只是速度非常慢。


For the following two alternatives you have to decide a bit what you want to control: 对于以下两种选择,您必须决定要控制的内容:

  1. Option: The speed 选项:速度

    If you want to have a linear velocity for the object you should rather use 如果要使对象具有线速度 ,则应使用

     // adjust via the Inspector [SerializeField] private float moveSpeedInUnityUnitPerSecond = 1f; // you should use Update here in general void Update() { if (block.transform.position == startPos.transform.position) { check = false; } // always use else in cases where only on condition can be // true at the same time anyway else if(block.transform.position == endPos.transform.position) { check = true; } block.transform.position = Vector3.MoveTowards(block.transform.position, check ? startPos.transform.position : endPos.transform.position, Time.deltaTime * moveSpeed); } 
  2. Option: The duration If you rather want a smooth movement but control the duration it takes to reach the target you should use a Lerp but with a factor depending on the time like 选项:持续时间如果您想平稳移动控制达到目标所需的持续时间 ,则应使用Lerp但取决于时间,例如

     // adjust via the Inspector [SerializeField] private float moveDurationInSeconds = 1f; private float passedTime; // you should use Update here in general void Update() { // prevent overshooting passedTime += Mathf.Min(moveDurationInSeconds - passedTime, Time.deltaTime); if(passedTime >= moveDurationInSeconds) { check = !check; passedTime = 0; } var lerpFactor = passedTime / moveDurationInSeconds; // and now add ease-in and ease-out var smoothedLerpFactor = Mathf.SmoothStep(0, 1, lerpFactor); var fromPosition = check ? endPos.transform.position : startPos.transform.position; var toPosition = check ? startPos.transform.position : endPos.transform.position; block.transform.position = Vector3.Lerp(fromPosition, toPosition, smoothedLerpFactor); } 

    For this you could also use a Coroutine which usually is a bit easier to interpret and maintain: 为此,您还可以使用协程 ,它通常更易于解释和维护:

     // adjust via the Inspector [SerializeField] private float moveDurationInSeconds = 1f; // yes you see correctly one can directly use the Start // as a Coroutine private IEnumerator Start() { var fromPosition = startPos.transform.position; var toPosition = endPos.transform.position; // looks strange but as long as you yield somewhere inside // the loop it simply means repeat the sequence forever // just like the Update method while(true) { var passedTime = 0f; while(passedTime < moveDurationInSeconds) { var lerpFactor = passedTime / moveDurationInSeconds; // and now add ease-in and ease-out var smoothedLerpFactor = Mathf.SmoothStep(0, 1, lerpFactor); block.transform.position = Vector3.Lerp(fromPosition, toPosition, smoothedLerpFactor); passedTime += Mathf.Min(moveDurationInSeconds - passedTime, Time.deltaTime); // reads like: "pause" here, render this frame and continue // from here in the next frame yield return null; } // once reached flip the positions var temp = fromPosition; fromPosition = toPosition; toPosition = temp; } } 

    in both cases you could still add more flexibility and instead of simply using the moveDurationInSeconds use 在这两种情况下,您仍然可以增加灵活性,而不仅仅是使用moveDurationInSeconds使用

     var fixedDuration = moveDurationInSeconds * Vector3.Distance(fromPosition, toPosition); 

    this way the movement takes shorter if the positions are closer together and longer if they are further apart. 这样,如果位置靠得更近,则运动所需的时间较短,而如果位置彼此靠得很近,则所需的运动时间也较长。 This comes pretty close to the Lerp you used before regarding to the smoothness of motion but you can control very good how long the movement will take. 这与您之前使用过的Lerp有关运动的平滑度非常接近,但是您可以很好地控制运动需要多长时间。

For starters, Update() loops every frame. 对于初学者,Update()会循环播放每帧。 FixedUpdate() depends on the number of frames per second set in the time setting. FixedUpdate()取决于时间设置中每秒设置的帧数。 So, put your code into Void Update() instead. 因此,请将您的代码放入Void Update()中。

If you refer to Vector3.MoveTowards documentation, you may find a much better approach to your problem. 如果您参考Vector3.MoveTowards文档,则可能会找到一种更好的解决方法。

The script below should do the trick. 下面的脚本应该可以解决问题。 The target variable should be set to the position of your end point. 目标变量应设置为终点的位置。 Speed is how fast your object should move. 速度是对象应移动的速度。

Inside Update(), the step variable is used to determine how far the object should move based off its speed and the time elapsed since it last moved. 在Update()内部,step变量用于根据对象的速度和自上次移动以来经过的时间来确定对象应移动多远。 Finally, the last line changes the position of the object and records the new position. 最后,最后一行更改对象的位置并记录新位置。

public Transform target;
public float speed;

void Update() {
    float step = speed * Time.deltaTime;
    transform.position = Vector3.MoveTowards(transform.position, target.position, step);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM