plitri

[Godot] 루프에서 wait 함수 사용하기

2017.08.21 17:45학습/Godot 엔진

2.x 기준입니다.

wait 함수

OS.delay_msec 등의 함수가 있지만 이건 왠만하면 사용하지 않도록 합시다. 스레드 째로 멈춰버려서 다른 게 아예 실행이 안 될 수 있습니다. (테스트는 안 해봤음)

타이머 노드

고도 엔진에서 대기할 일이 있으면 왠만하면 타이머 노드를 사용한다고 보시면 됩니다. 에디터에서 타이머 노드의 timeout 시그널을 함수에 연결해서 해당 함수로 시간이 경과되었을 때의 처리를 작성할 수는 있는데 이건 함수 실행 도중에 함수의 수행을 일시정지하고 기다렸다 재개하고 싶다- 라는 요구사항에는 안 맞죠.

아무튼 타이머 노드에서 필요한 시그널은 timeout 입니다. 잘 기억해둡시다.

yield

다른 언어처럼 GDScript에서도 yield()(문서: GDScript 기본-코루틴 / @GDScript.yield )를 호출해 함수를 중간에서 멈추고, 나중에 멈춘 지점부터 계속 실행할 수 있습니다. yield() 를 호출하면 함수의 실행 상태를 담은 값(GDFunctionState)을 반환하게 되고, 이걸 resume() 하면 함수가 이어서 마저 실행됩니다.

func myfunc():
    print( yield() ) # yield를 호출하는 순간 반환합니다.

func _ready():
    var y = myfunc() # yield로 반환되었기에 GDFunctionState 값이 y에 할당됩니다.
    print( y.resume("world") )

이걸 잘 활용하면 wait 함수를 직접 짤 수 있으나 아직 좀 번거롭습니다.

yield + signal

yield 함수에는 추가로 인자를 넣을 수 있습니다. yield( 대상 객체, 시그널 이름 ) 으로, 이렇게 호출한 경우 특정 개체의 시그널을 받으면 함수의 수행을 그 자리부터 재개하게 됩니다.

그래서, 유니티에서는 이렇게 하는 코드를

yield return new WaitForSeconds(.1f);

고도에서는 이렇게 합니다 / 2.

yield( timer, "timeout" ) # timer 개체의 timeout 시그널이 발생하면 여기서부터 재개합니다.

유니티에서는 타이머 객체를 필요할 때 생성하는 반면 고도엔진에서는 특정 객체의 시그널이 발생하면 해당 지점으로부터 재개할 수 있도록 바로 설정할 수 있습니다. 그래서… 씬 트리에 삽입되어있는 객체인 Timer 노드를 사용하려면 timeout이 발생할 수 있도록 사전 조작을 필요로 합니다. 타이머가 없다면 만들어 씬 트리에 넣고, 타이머를 시작할 필요가 있죠.

func myfunc():
    var timer = Timer.new()
    self.add_child(timer)

    for i in range(10):
        timer.set_one_shot( true )
        timer.set_wait_time( 1.0 )
        timer.start()
        yield( timer, "timeout" )

    timer.queue_free() # free 처리를 안 해주면 myfunc()이 호출될 때마다 타이머가 쌓일겁니다.
    # 아니면 사전에 timer 노드가 트리에 있고 영속적이라면 문제가 없죠.

기억에 의하면 Timer 노드는 트리에 삽입된 상태여야지 동작하는 것 같던데 이 점은 아쉽더라구요. 노드가 아닌 타이머가 필요하다면 Reference를 상속받는 커스텀 객체 같은 걸 만들어서 yield로 호출되었을 때 별도의 코드 없이 바로 처리되게 만들수도 있을 것 같습니다.

여담인데 yield가 값이 아닌 함수의 상태를 반환하는 바람에 제너레이터 패턴을 손쉽게 만들 수 없습니다. 멤버변수 등으로 우회할 순 있겠지만요.

123456···16