Корутины и цикл while

Я работаю над движением объекта по пути, который я получаю от Navmesh Unity3d. Я использую сопрограмму, в которой я контролировал ее с помощью цикла while, как я могу показать.

  public void DrawPath(NavMeshPath pathParameter, GameObject go)
{

    Debug.Log("path Parameter" + pathParameter.corners.Length);
    if (agent == null || agent.path == null)
    {
        Debug.Log("Returning");
        return;
    }

    line.material = matToApplyOnLineRenderer;
    line.SetWidth(1f, 1f);

    line.SetVertexCount(pathParameter.corners.Length);

    allPoints = new Vector3[pathParameter.corners.Length];

    for (int i = 0; i < pathParameter.corners.Length; i++)
    {
        allPoints[i] = pathParameter.corners[i];
        line.SetPosition(i, pathParameter.corners[i]);

    }

   StartCoroutine(AnimateArrow(pathParameter));

    //StartCoroutine(AnimateArrowHigh(pathParameter));
}


#endregion


#region AnimateArrows


void RunAgain()
{
    StartCoroutine(AnimateArrow(Navpath));
}

IEnumerator AnimateArrow(NavMeshPath path)
{
    Vector3 start;
    Vector3 end;

    while (true)
    {


        if (index > 0)
        {
            if (index != path.corners.Length - 1)
            {
                start = allPoints[index];

                index += 1;
                end = allPoints[index];

                StopCoroutine("MoveObject");
                StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
                yield return null;

            }
            else
            {
                index = 0;
                RunAgain();
            }
        }
        else if (index == 0)
        {
            start = allPoints[index];
            arrow.transform.position = allPoints[index];

            index += 1;
            end = allPoints[index];


           StopCoroutine("MoveObject");
           StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));

            yield return null;
        }
    }


}

IEnumerator MoveObject(Transform arrow, Vector3 startPos, Vector3 endPos, float time)
{
    float i = 0.0f;
    float rate = 1.0f / time;
    journeyLength = Vector3.Distance(startPos, endPos);
            float distCovered = (Time.time - startTime) * speed;
            float fracJourney = distCovered / journeyLength;


    while (i < 1.0f)
    {


       // Debug.Log("fracJourney In While" + fracJourney);
        arrow.position = Vector3.LerpUnclamped(startPos, endPos, fracJourney);

        yield return endPos;
    }
    Debug.LogError("Outside While");
}

Но проблема в том, что я должен перемещать объект с постоянной скоростью, но мой объект набирает скорость в каждом цикле, так как мне нужно совершать движение в цикле, поэтому он имеет тенденцию двигаться, пока пользователь не захочет закончить его вводом, ребята, помогите, пожалуйста, я не понимаю что я делаю неправильно в Coroutines, что скорость моих объектов растет, я хочу, чтобы она оставалась постоянной, но почему-то это не работает, спасибо


person Syed Anwar Fahim    schedule 08.04.2016    source источник


Ответы (3)


while (i < 1.0f) будет работать вечно, потому что i это 0.0f, а 0.0f всегда < 1.0f, и внутри цикла while нет места, где вы увеличиваете i, чтобы оно было >= 1.0f. Вам нужен способ выйти из этого цикла. Это должно было выглядеть примерно так:

while (i < 1.0f){
i++ or i= Time.detaTime..... so that this loop will exist at some point.
}

Кроме того, ваша двигательная функция плохая. Функция ниже должна делать то, что вы пытаетесь сделать:

bool isMoving = false;
IEnumerator MoveObject(Transform arrow, Vector3 startPos, Vector3 endPos, float time = 3)
{
    //Make sure there is only one instance of this function running
    if (isMoving)
    {
        yield break; ///exit if this is still running
    }
    isMoving = true;

    float counter = 0;
    while (counter < time)
    {
        counter += Time.deltaTime;
        arrow.position = Vector3.Lerp(startPos, endPos, counter / time);
        yield return null;
    }

    isMoving = false;
}

Кроме того, в своей функции AnimateArrow(NavMeshPath path) замените эти три строки кода:

StopCoroutine("MoveObject");
StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
yield return null;

с

yield return StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));

Это приведет к ожиданию завершения функции MoveObject перед возвратом и повторным запуском в цикле while. Вы должны заменить их внутри if (index != path.corners.Length - 1) и else if (index == 0)

person Programmer    schedule 08.04.2016
comment
Спасибо за быстрый ответ, но это не решение, я сделал то, что вы предлагаете, но он делает то же самое, набирает скорость, что и его петли от конца пути. - person Syed Anwar Fahim; 08.04.2016
comment
Прикреплено ли твердое тело к этому игровому объекту? - person Programmer; 08.04.2016
comment
Нет, это не жесткое тело и даже не коллайдер. Я весь день искал в Google, но не нашел решения, я думаю, что это неправильный подход к этому. - person Syed Anwar Fahim; 08.04.2016
comment
@ Роберт, это не проблема. когда счетчик увеличивается, все, что он делает, это возвращает новый Vector3, который ближе к концу. Скорость не увеличивается и не должна. Я использовал эту функцию бесчисленное количество раз без проблем. - person Programmer; 08.04.2016
comment
@SyedAnwarFahim, попробуйте следующее: я думаю, что вы, вероятно, изменяете значение из другого скрипта. Используйте Debug.DrawLine(startPos,endPos,Color.red);, затем перейдите к просмотру сцены. Это нарисует линию и покажет вам, куда перемещается объект в представлении сцены. Вы можете использовать его, чтобы определить, правильно ли это местоположение. Еще можно попробовать удалить оператор while(true) из функции AnimateArrow и посмотреть, что произойдет. Я не понимаю ваш код в других ваших функциях, поэтому я больше не смогу помочь, но попробую их. - person Programmer; 08.04.2016
comment
@Programmer Прошу прощения, я думал о MoveTowards вместо Lerp :) - person Allen; 08.04.2016

В качестве альтернативы вы можете использовать класс Unity AnimationCurve, чтобы легко отображать все виды сверхгладких типов анимации:

введите здесь описание изображения

Вы можете определить кривые в инспекторе или в коде

 public AnimationCurve Linear
{
    get
    {
        return new AnimationCurve(new Keyframe(0, 0, 1, 1), new Keyframe(1, 1, 1, 1));
    }
}

И вы можете определить использование в сопрограмме как таковое:

Vector2.Lerp (startPos, targetPos, aCurve.Evaluate(percentCompleted));

Где «percentCompleted» — ваш таймер/TotalTimeToComplete.

Полный пример lerping можно увидеть из этой функции:

    IEnumerator CoTween(RectTransform aRect, float aTime, Vector2 aDistance, AnimationCurve aCurve, System.Action aCallback = null)
{
    float startTime = Time.time;
    Vector2 startPos = aRect.anchoredPosition;
    Vector2 targetPos = aRect.anchoredPosition + aDistance;
    float percentCompleted = 0;
    while(Vector2.Distance(aRect.anchoredPosition,targetPos) > .5f && percentCompleted < 1){
        percentCompleted = (Time.time - startTime) / aTime;
        aRect.anchoredPosition = Vector2.Lerp (startPos, targetPos, aCurve.Evaluate(percentCompleted));
        yield return new WaitForEndOfFrame();
        if (aRect == null)
        {
            DeregisterObject(aRect);
            yield break;
        }
    }
    DeregisterObject(aRect);
    mCallbacks.Add(aCallback);
    yield break;
}

Дополнительные примеры кода см. в этой библиотеке Tween: https://github.com/James9074/Unity-Juice-UI/blob/master/Juice.cs

person JamesTheMage    schedule 27.01.2017

Может быть, вы могли бы умножить скорость, скажем, на 0,95f. Это заставит его ускоряться, затем оставаться на постоянной скорости, а затем, когда вы захотите его остановить, он будет постепенно замедляться. Увеличение 0,95f приведет к более быстрому ускорению/замедлению.

person Zac    schedule 08.04.2016