본문으로 건너뛰기

Unity3D 지연 호출

무료2015-10-17#Unity3D#u3d延时调用#unity3d几秒后#unity3d return new yield#u3d return new yield#StartCoroutine#WaitForSeconds#Invoke#InvokeRepeating

u3d 는 Coroutine(코루틴) 을 사용하여 지연 호출을 구현할 수 있으며, Invoke 를 사용하거나 Time.time 으로 직접 제어할 수도 있습니다

일.코루틴

코루틴이란 무엇인가?

A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done.

코루틴은새로운 스레드가 아닙니다. u3d 가 제공하는 이터레이터 관리 메커니즘의 일종입니다. 예를 들어 일반적인 예:

void Start () {
    print (0);
    StartCoroutine (wait (3));
    print (1);
}

IEnumerator wait(float s) {
    print (2);
    yield return new WaitForSeconds (s);
    print (3);
}
// => 0 2 1 [3s later] 3

StartCoroutinewait가 반환하는 이터레이터를 전달하면, 코루틴 내부에서 이터레이터 내부의 실행을 제어할 수 있습니다. 다음과 같이:

IEnumerator iter;

void Start () {
    print (0);
    iter = wait ();
    print (1);
}

IEnumerator wait() {
    print (2);
    yield return 1;
    print (3);
}
// => 0 1

왜 2 와 3 이 출력되지 않는가?

wait 는 이터레이터를 생성했을 뿐이기 때문입니다. 이터레이터는next() 메서드 (C# 은MoveNext, js 는next) 를 호출하지 않으면 실행되지 않는다는 것을 우리는 알고 있습니다. 그럼 계속 시도해 봅시다:

IEnumerator iter;

void Start () {
    print (0);
    iter = wait ();
    print (1);
    iter.MoveNext ();
    iter.MoveNext ();
}

IEnumerator wait() {
    print (2);
    yield return 1;
    print (3);
}
// => 0 1 2 3

이터레이터의MoveNext() 메서드를 수동으로 호출하면, 우리가 원하는 대로 결과가 나옵니다. 이것이StartCoroutine 의 매개변수입니다. StartCoroutine 은 내부에서 이터레이터의 참조를 얻어 언제MoveNext 를 실행할지, 언제 멈출지 제어할 수 있습니다. 따라서 우리가 보는 효과는: 코드가 분할되어 실행되고 있다 (yield return 문에 의해 분할됨) 는 현상입니다

아직 잘 모르겠는가? 괜찮다, 계속 살펴보자:

IEnumerator iter;

void Start () {
    print (0);
    iter = wait (3);
    print (1);
    iter.MoveNext ();
    iter.MoveNext ();
}

IEnumerator wait(float s) {
    print (2);
    yield return new WaitForSeconds (s);
    print (3);
}
// => 0 1 2 3

잠깐, 왜 2 를 출력한 후 3 초를 대기하지 않는가?

우리는 현재 이터레이터를 수동으로 관리하고 있기 때문에, 첫 번째MoveNext() 이후yield return 이 반환하는WaitForSeconds 타입 객체를 적절히 처리하지 않았기 때문에 10 초를 대기하지 않습니다.

StartCoroutine 은 내부에서 다음과 같은 처리를 했을 가능성이 있습니다:

  1. 매개변수 (이터레이터) 를 얻은 후, 어떤 시점에 (즉시는 아님, 언제인지는 나중에 논의하겠습니다) 이터레이터의MoveNext() 를 호출

  2. MoveNext() 의 반환값 (즉yield return 뒤의 것) 이WaitForSeconds 객체이면, s 초 후에MoveNext() 를 다시 실행

최종적으로 우리가 직접 보는 결과는 s 초 지연되어yield return 아래의 것을 실행하는 것입니다.好了, 여기까지 거의 이해했습니다. 마지막 예:

IEnumerator iter;

void Start () {
    print (0);
    StartCoroutine (iter = wait (3));
    print (1);
    iter.MoveNext ();
}

IEnumerator wait(float s) {
    print (2);
    yield return new WaitForSeconds (s);
    print (3);
}
// => 0 2 1 3

2 를 출력한 후 3 초를 대기할지 추측해 보라?

아니다, 우리가 수동으로MoveNext () 했기 때문에, 코루틴이 첫 번째로MoveNext () 를 호출할 때 이터레이터는 이미 한 걸음 진행되었고, 코루틴은WaitForSeconds 를 보지 못했습니다. 따라서 코루틴은 이터레이터를 얻은 후 즉시 실행하는 것이 아니라, 코루틴에 속하는 어떤 시간대를 대기합니다 (소문에 따르면Update 이후라고 합니다. 관심이 있으면 u3d 함수 실행 순서도를 찾아보세요. 물론, 이는 중요하지 않습니다)

코루틴이란 무엇인가? 답은 서두에서 언급한通り입니다:

코루틴은 u3d 가 제공하는 이터레이터 관리 메커니즘의 일종입니다

P.S.선배의 블로그 에 따르면, "코루틴은 사실 IEnumerator(이터레이터) 그 자체"라고 하는데, 둘 중 어느 것이 더 맞는지 변론할 필요는 없겠죠

이.코루틴으로 구현한 지연 호출

매우 유연한 지연 호출을 보았습니다. 다음과 같습니다:

using UnityEngine;
using System.Collections;
using System;

public class Delay {
    public static IEnumerator run(Action action, float delaySeconds) {
        yield return new WaitForSeconds(delaySeconds);
        action();
    }
}

코드는 Unity 지연 실행 구현의 비교적 양호한 방식 에서 수정했습니다

정적 메서드이므로 호출이 편리하며, 도구 함수로 사용할 수 있고 어떤 오브젝트에도 바인딩할 필요가 없습니다. 호출 방식은 다소 보기 흉합니다:

void Start() {
    print (1);
    StartCoroutine(Delay.run (() => {
        print (2);
    }, 3));
}
// => 1 [3s later] 3

특별 주의: 지연 호출이 실패하는 2 가지 상황이 있습니다

  • StartCoroutine 이후에 씬 전환 (Application.LoadLevel) 이 있는 경우

    씬 전환 후 코루틴 실행이 중지되므로 지연 호출이 실패합니다

  • StartCoroutine 이후에 현재 오브젝트 또는 현재 오브젝트의 조상 오브젝트를Destroy 하는 경우

    오브젝트가 소멸되면, 해당 오브젝트에 첨부된 모든 스크립트의StartCoroutine 으로 추가된 코루틴 태스크 실행이 중지되므로 지연 호출이 실패합니다

삼.Invoke 의 지연 호출

Invoke 는 js 의setTimeout 과 유사하며, setInterval 과 유사한InvokeRepeating 도 있지만 js 만큼 강력하지는 않습니다. Invoke 시리즈는 문자열 형식의 메서드 이름 받아들일 수 있으며, 이름으로 지연 호출을 실행합니다. 예를 들어:

int arg;

void Start() {
    arg = 1;
    Invoke ("doSth", 3);
}

void doSth() {
    print (arg);
}
// => [3s later] 1

문자열 형식은 매개변수를 전달할 수 없으므로, 전역 변수를 사용하여 매개변수를 전달했습니다. 문법은 매우 간결하고 혼란을 주는 곳도 없습니다

특별 주의: 위에서 언급한 2 가지 지연 호출 실패 상황은 여전히 존재합니다

사.Time.time 으로 구현한 지연 호출

다소 어리석은 방법이지만, 씬 전환과 오브젝트 소멸로 인한 지연 호출 무효화 문제를 피할 수 있습니다. 코드는 다음과 같습니다:

using UnityEngine;
using System.Collections;
using System;

public class Wait : MonoBehaviour {
    static Action _action;
    static float time;
    static float delayTime;

    // Use this for initialization
    void Start () {
        // 씬 전환 시 이 오브젝트를 소멸하지 않음
        DontDestroyOnLoad (gameObject);

        reset ();
    }
    
    // Update is called once per frame
    void Update () {
        if (Time.time > time + delayTime) {
            _action();
            reset();
        }
    }

    void reset() {
        time = 0;
        delayTime = int.MaxValue;
    }

    public static void runAfterSec(Action action, float s) {
        _action = action;
        time = Time.time;
        delayTime = s;
    }
}

사용법에는 요령이 있습니다:

  1. 위 스크립트 생성

  2. 빈 오브젝트를 생성하고 위 스크립트를 바인딩

그런 다음 어디서나 호출할 수 있습니다. 예를 들어:

void Start() {
    int arg = 1;
    Wait.runAfterSec (() => {
        print (arg);
    }, 3);
    Application.LoadLevel("scene1");
}
// => 씬 점프 [3s later] 1

기능이 약하다고 느껴지면, 큐를 사용하여action 을 저장하도록 수정하는 등自行으로 확장하세요

오.정리

첫 번째 방식, 즉 코루틴으로 지연 호출을 구현하는 방식을 권장합니다. 제어성에 대한 요구가 높은 경우 세 번째 방식을 채택할 수 있습니다. 시나리오가 비교적 단순한 경우 두 번째 방식을 사용할 수 있습니다. 매개변수 전달과 함수 정의가 필요하므로 그리 편리하지는 않습니다

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성