在协程中使用Mathf.MoveTowards以及WaitUntill的正确使用方法

问题描述 投票:0回答:1

我正在编写(尝试)一个协程来处理游戏中房间的加载。一旦玩家输入触发器,它就会启动一个协程来处理加载下一个房间的不同元素。这里的问题是,我在使用材质中的着色器和浮动从透明过渡到黑色的屏幕过渡时遇到了麻烦,但是当我使用 Mathf.MoveTowards 在协程中调用它时,仅更改了浮动的一小部分时间,几乎就像被打断了一样。

public class RoomManager : MonoBehaviour {

    public static RoomManager Instance { get; private set; }

    public Room currentRoom;

    private void Awake() {
        if (Instance != null && Instance != this) {
            Destroy(this);
        } else {
            Instance = this;
        }
    }

    public IEnumerator LoadRoom(Room nextRoom) {

        Debug.Log("Started Coroutine : Room loading is " + nextRoom);

        StartCoroutine(RoomTransitionController.Instance.RoomTransitionHide());

        yield return new WaitUntil(RoomTransitionController.Instance.TransitionHasLoaded);

        Debug.Log("waiting done");

        currentRoom = nextRoom;

        CameraController.Instance.UpdateCameraPos(currentRoom);

        StartCoroutine(RoomTransitionController.Instance.RoomTransitionShow());

        yield return null;
    }
}

public class RoomTransitionController : MonoBehaviour {

    public static RoomTransitionController Instance { get; private set; }

    private Image image;

    public float transitionSpeed = 1.25f;

    private bool loadedTransition;

    private void Awake() {
        if(Instance != null && Instance != this) {
            Destroy(this);
        }
        else {
            Instance = this;
        }
    }

    private void Start() {
        image = GetComponent<Image>();

        if(image.material.GetFloat("_Cutoff") != 1f ) {
            image.material.SetFloat("_Cutoff", 1f);
        }
        loadedTransition = false;
    }

    public bool TransitionHasLoaded() {
        return image.material.GetFloat("_Cutoff") == -0.1f - image.material.GetFloat("_EdgeSmoothing");
    }

    public IEnumerator RoomTransitionHide() {
        loadedTransition = false;

        while(!loadedTransition) {

            image.material.SetFloat("_Cutoff", Mathf.MoveTowards(image.material.GetFloat("_Cutoff"), -0.1f - image.material.GetFloat("_EdgeSmoothing"), transitionSpeed * Time.deltaTime));

            yield return new WaitUntil( TransitionHasLoaded );
            loadedTransition = true;
        }
        yield return null;
    }

    public IEnumerator RoomTransitionShow() {

        while( loadedTransition ) {
            image.material.SetFloat("_Cutoff", Mathf.MoveTowards(image.material.GetFloat("_Cutoff"), 1f, transitionSpeed * Time.deltaTime));

            loadedTransition = false;
        }
        yield return null;
    }

我知道着色器工作正常,因为它可以使用更新方法使其工作,但我更喜欢使用协程并更好地理解它。

c# unity-game-engine coroutine
1个回答
0
投票

好吧,你拨打电话

 image.material.SetFloat("_Cutoff", Mathf.MoveTowards(image.material.GetFloat("_Cutoff"), -0.1f - image.material.GetFloat("_EdgeSmoothing"), transitionSpeed * Time.deltaTime));

恰好一次,然后等到

TransitionHasLoaded
返回
true
- 或者在第二个例程中,您只需设置
loadedTransition = false;
,这意味着您的循环也将迭代最多一次。


要立即降低复杂性:您可以直接从主要的

yield return
另一个
IEnumerator

所以根本不需要暴露

TransitionHasLoaded
因为你已经知道内部
IEnumerator
何时完成:

public IEnumerator RoomTransitionHide() 
{
    var initialCutoff = image.material.GetFloat("_Cutoff");
    var target = image.material.GetFloat("_EdgeSmoothing");

    var current = initialCutoff;

    while(!Mathf.Approximately(current, target)) 
    {
        current = Mathf.MoveTowards(current, target, transitionSpeed * Time.deltaTime);

        image.material.SetFloat("_Cutoff", current);

        yield return null;
    }

    image.material.SetFloat("_Cutoff", target);
}

public IEnumerator RoomTransitionShow()
{
    var initialCutoff = image.material.GetFloat("_Cutoff");
    var target = 1f;

    // personally I like more deterministic loops so while the above should behave the same
    // I usually prefer the following approach just to give you an alternative way
    var duration = (target - initialCutoff) / transitionSpeed;

    for(var timePassed = 0f; timePassed < duration; timePassed += Time.deltaTime)
    {
        var factor = timePassed / duration;

        image.material.SetFloat("_Cutoff", Mathf.Lerp(initialCutoff, target));

        yield return null;
    }

    image.material.SetFloat("_Cutoff", target);
}

然后你实际上可以从内部屈服并等待这些

 public IEnumerator LoadRoom(Room nextRoom) 
 {

    Debug.Log("Started Coroutine : Room loading is " + nextRoom);

    // This already executes the routine and wait until it  is finished
    yield return RoomTransitionController.Instance.RoomTransitionHide();

    Debug.Log("waiting done");

    currentRoom = nextRoom;

    CameraController.Instance.UpdateCameraPos(currentRoom);

    // and here accordingly
    yield return RoomTransitionController.Instance.RoomTransitionShow();
}
© www.soinside.com 2019 - 2024. All rights reserved.