简体   繁体   English

如何在IEnumerator中等待或等待,然后继续协程?

[英]How can I waitwhile or waituntil in IEnumerator and then continue the coroutine?

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

public class NaviConversations : MonoBehaviour
{
    public ObjectsManipulation op;
    public Scaling scaling;
    public ConversationTrigger conversationTrigger;

    private void Update()
    {
        if (DOFControl.hasFinished == true)
        {
            ConversationTrigger.conversationsToPlay.Add(0);
            StartCoroutine(NaviScaling());
            DOFControl.hasFinished = false;
        }
    }

    public IEnumerator NaviScaling()
    {
        // Scaling Up
        if (scaling.objectToScale.transform.localScale == scaling.minSize)
        {
            op.Scaling();
        }
        yield return new WaitUntil

        op.Scaling();
    }
}

At this part : 在这一部分:

yield return new WaitUntil

I want to wait for the scaled object to finish scaling : 我要等待缩放的对象完成缩放:

scaling.objectToScale.transform.localScale == scaling.maxSize

When this is true then continue the continue and do : 如果是这样,则继续继续并执行以下操作:

conversationTrigger.PlayConversations();

Then wait again until : 然后再次等待,直到:

conversationTrigger.conversationEnd

Is true. 是真的。

If it's true scale down back : 如果确实如此,请缩减:

op.Scaling();

The steps should be : 步骤应为:

  1. Add the conversation index to play. 添加对话索引以播放。

  2. Scale up. 放大。 And wait for the scaling up to finish. 并等待放大完成。

  3. Start conversations. 开始对话。 And wait for the conversation/s to end. 然后等待对话结束。

  4. Scale down. 缩小。

I'm trying to find the easiest wait to do it and a way that I can call the method public IEnumerator NaviScaling() from anywhere. 我正在尝试找到最简单的等待方法,以及一种可以从任何地方调用public IEnumerator NaviScaling()方法的方法。

The part in the update : 更新中的部分:

if (DOFControl.hasFinished == true)

Is happens once when the game start. 游戏开始时发生一次。

This is the script that the op.Scaling is using : 这是op.Scaling使用的脚本:

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

public class ObjectsManipulation : UnityEngine.MonoBehaviour
{
    //Camera
    public Camera playerCamera;

    //Scaling
    public bool canScale = true;
    private Scaling scaling;

    //Lights
    public DimLights dimlights;
    private Coroutine lightCoroutine;

    //Colors
    private Colors colors;

    //Rotating
    public bool stopRotation = false;
    private Rotating rotating;

    private void Start()
    {
        scaling = GetComponent<Scaling>();
        scaling.Inits();

        colors = GetComponent<Colors>();
        colors.Start();

        rotating = GetComponent<Rotating>();
    }

    // Use this for initialization
    void Update()
    {
        if (playerCamera != null)
        {
            //Scaling
            if (Input.GetKeyDown(KeyCode.F) && canScale == true)
            {
                Scaling();
            }
        }

        //Rotate
        if (Input.GetKey(KeyCode.R) && !scaling.scaleUp)
        {
            rotating.x += Time.deltaTime * rotating.rotationSpeed;
            scaling.objectToScale.transform.localRotation = Quaternion.Euler(0, 0, rotating.x);
            rotating.keyPressed = true;
        }
        if (Input.GetKeyUp(KeyCode.R))
        {
            rotating.keyPressed = false;
        }

        if (!rotating.keyPressed && !scaling.scaleUp && rotating.rotateBack == false
            && DetectInteractable.detected == false)
        {
            scaling.objectToScale.transform.rotation = Quaternion.LookRotation(playerCamera.transform.forward);
        }

        if (DetectInteractable.detected == true && !scaling.scaleUp && stopRotation == false)
        {
            rotating.x += Time.deltaTime * rotating.rotationSpeed;
            scaling.objectToScale.transform.localRotation = Quaternion.Euler(0, 0, rotating.x);
        }
    }

    public void Scaling()
    {
        //Flip the scale direction when F key is pressed
        scaling.scaleUp = !scaling.scaleUp;

        //Stop old coroutine
        if (scaling.scaleCoroutine != null)
            StopCoroutine(scaling.scaleCoroutine);

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


        //Scale  up
        if (scaling.scaleUp)
        {
            //Start new coroutine and scale up within 5 seconds and return the coroutine reference
            rotating.rotateBack = false;
            scaling.scaleCoroutine = StartCoroutine(scaling.scaleOverTime(scaling.objectToScale, scaling.maxSize, scaling.duration, playerCamera));
            if (dimlights.lightsOnOff == false)
                lightCoroutine = StartCoroutine(dimlights.dimLightOverTime(1, scaling.duration));
        }

        //Scale Down
        else
        {
            //Start new coroutine and scale up within 5 seconds and return the coroutine reference
            rotating.rotateBack = true;
            scaling.scaleCoroutine = StartCoroutine(scaling.scaleOverTime(scaling.objectToScale, scaling.minSize, scaling.duration, playerCamera));
            if (dimlights.lightsOnOff == false)
                lightCoroutine = StartCoroutine(dimlights.dimLightOverTime(0, scaling.duration)); ;
        }
    }
}

To use WaitUntil , you will need to provide a way for it to check the condition that it is waiting for. 要使用WaitUntil ,您将需要为其提供一种检查其等待条件的方法。 This is done by passing a delegate into its constructor. 这是通过将委托传递给其构造函数来完成的。

So to wait while your object is still scaling, you would do something like this: 因此,要等到对象仍在缩放时,您将执行以下操作:

yield return new WaitUntil(() => scaling.objectToScale.transform.localScale == scaling.minSize);

Note the () => before the condition, this turns your expression into an anonymous function which allows the WaitUntil to reevaluate the condition on each frame. 注意条件之前的() => ,这会将您的表达式转换为匿名函数,该函数允许WaitUntil在每个帧上重新评估条件。

You can also pass a method as the delegate which reads pretty nicely! 您还可以传递一个方法作为委托,它看起来很漂亮!

private bool IsFinishedScaling () {
    return scaling.objectToScale.transform.localScale == scaling.minSize;
}

public IEnumerator Example () {
    yield return new WaitUntil(IsFinishedScaling);
    Debug.Log("Scaling has finished!");
}

This should do what you're looking for: 这应该可以满足您的需求:

public IEnumerator NaviScaling()
{
    if (scaling.objectToScale.transform.localScale == scaling.minSize)
    {
        op.Scaling();
    }

    while (scaling.objectToScale.transform.localScale != scaling.maxSize)
    {
        yield return null;
    }

    conversationTrigger.PlayConversations();

    while (!conversationTrigger.conversationEnd)
    {
        yield return null;
    }

    op.Scaling();
}

I'm not quite happy with the so far given answers. 我对到目前为止给出的答案不太满意。

Basically the question was how to use WaitUntil which wasn't even used by the accepted answer (which by the way provided a really messi code..) 基本上,问题是如何使用WaitUntil ,它甚至没有被接受的答案使用 (顺便说一句,它提供了一个真正的梅西代码。)

The reason why the other still could be extended is that both answer are based on using == for checking the equality. 另一个仍然可以扩展的原因是,两个答案都基于使用==来检查相等性。 For Vector3 however this simply assumes that 但是对于Vector3这只是假设

Vector3.Distance(vectorA, vectorB) <= 0.00001f

This is usually great for distances between positions but for scales you might rather want more exact values. 通常,这对于位置之间的距离非常有用,但对于比例尺,您可能更希望更精确的值。

If that's the goal and you don't need it to be more exact than stick with it. 如果那是目标,并且您不需要比坚持下去更精确。

Otherwise I would make the check rather using Mathf.Approximately eg as an extension method like 否则,我宁愿使用Mathf.Approximately进行检查,例如将其作为扩展方法,例如

public static class Vector3Extensio s
{
    public static bool IsSameValue(this Vector3 a, Vector3 b)
    {
        return Mathf.Approximately(Vector3.Distance(a,b), 0f);
    }
}

And than use 比使用

yield return new WaitUntil(() => scaling.objectToScale.transform.localScale.IsSameValue(scaling.maxSize));

Ofcourse this depends a lot on how op.Scaling works which you don't show. 当然,这很大程度上取决于op.Scaling工作方式,而您并未展示。


Alternatively: (and I would prefer that) 或者:(我更愿意)

Unfortunately you didn't show what op.Scaling() does. 不幸的是,您没有显示op.Scaling()功能。 The easiest way would actually be to make it an IEnumerator as well since you can simply yield return another IEnumerator which makes it execute and automatically wait until it finishes in only one line. 最简单的方法实际上是使其也成为IEnumerator ,因为您可以简单地让它yield return另一个IEnumerator ,这使它执行并自动等待直到仅一行完成。

So let's say you make the op.Scaling something like eg 假设您进行op.Scaling例如

public IEnumerator Scaling(Vector3 targetScale, float duration)
{
    // Get current scale
    var startScale = transform.localScale;

    var timePassed = 0f;
    do
    {
        transform.localScale = Vector3.Lerp(startScale, targetScale, timePassed / duration);

        timePassed += Time.deltaTime;

        yield return null;
    } while(timePassed <= duration);

    transform.localScale = targetScale;
}

You could simply do 你可以做

public IEnumerator NaviScaling()
{
    // Scale up in 1 second and wait
    yield return op.Scaling(scaling.maxSize, 1f);

    // As you can see again this could be a Coroutine so you could directly yield it
    // instead of having to wait for the bool value to turn true
    conversationTrigger.PlayConversations();

    yield return new WaitUntil(() => conversationTrigger.conversationEnd);

    // Scale down in 1 second and wait
    yield return op.Scaling(scaling.minSize, 1f);
}

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

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