티스토리 뷰

728x90

고도 스크립트 개요, 예약어와 연산자, 고도 스크립트 타입과 변수 선언, 고도 스크립트 클래스와 함수, 고도 스크립트의 제어문, 고도 스크립트의 시그널에 이은 글이다.

 

tool 모드

통상 고도 스크립트는 실행 모드에서만 수행되고 된다. 그런데 스크립트를 편집기 내부에서도 수행할 수 있는데 스크립트 첫 라인에 위의 그림과 같이 tool 키워드를 기술하면 된다. 스크립트를 적용한 씬을 편집할 때 해당 스크립트도 수행한다. 고도에서는 현재 스크립트가 실행 모드인지 아니면 편집 상태인지를 위의 예제와 같이 Engine.editor_hint 로 확인할 수 있게 해 준다. 위의 예제에서는 _draw 이벤트 함수가 호출되더라도 실행 모드에서는 해당 이벤트를 무시하고 편집기 상태에서만 내보내기 한 변수 값으로 사각형을 그린다.

 

위의 예제는 경계선이 없는 영역에 대한 편집을 효과적으로 하기 위하여 tool을 적용하고 연관 변수들을 export로 내보내기 한 것으로 우측 "스크립트 변수"에서 크기나 색상을 변경하면 해당 영역을 나타내는 사각형도 즉시로 변경된다. 그런데, 실행 모드에서는 영역은 적용되지만 표진 과정에 사용했던 사각형은 표시되지 않는 것이다.

 

tool 모드는 앞선 사례처럼 제한적인 용도로만 사용한다. 편집기에서도 스크립트를 실행해야 하는 상황이라면 위의 코드처럼 시스템에 영향을 미치는 부분이 있다면 편집기 상태에서는 해당 부분을 수행하지 않도록 걸러 주는 작업이 필요하다. 코드를 잘못 실행시키는 편집기가 비정상 종료될 수 있으므로 주의해야 한다.

 

참조 카운트와 메모리 관리

수행 중에 노드 및 클래스 인스턴스가 반복적으로 생성되는 경우 메모리가 증가하면서 성능에 영향을 미치게 된다. 또한 free() 또는 queue_free()를 제대로 수행하지 않은 경우 메모리 누출이 발생하여 이 또한 성능에 영향을 미치게 된다.

 

위의 예제 코드는 장면 전환 과정에서 기존에 추가되었던 노드들을 제거하고 queue_free()로 메모리에서 제거하는 예제이다. queue_free()는 안전한 제거를 위해서 큐에 등록하는 것을 의미한다. free()를 사용할 수도 있지만 free()는 해당 노드나 인스턴스를 참조할 가능성이 전혀 없을 때는 문제가 될 것이 없지만 혹여라도 삭제된 노드나 인스턴스를 참조하게 되면 프로그램은 비정상 종료하게 되는 것이다. 이런 문제를 방지하기 위해서 queue_free()를 사용한다.  is_instance_valid()를 사용하면 특정 인스턴스가 이미 메모리에서 해제된 상태인지를 확인할 수 있다.

 

클래스가 Reference를 상속하는 경우에는 굳이 free()로 정리할 필요가 없다. 클래스 인스턴스를 추가하면 참조 카운트를 늘리고, 해제하면 카운트를 줄이는 방식으로 운용하기 때문이다. 인스턴스는 더 이상 사용되지 않을 때 해제된다.

 

여러 클래스가 서로를 참조하고 있는 경우 메모리 해제 과정에서 메모리 누출이 발생할 수 있는데, 이때 유용하게 사용할 수 있는 것이 약한 참조이다. var a_classref = weakref(a_node) 처럼 weakref 함수를 이용해서 클래스 참조를 만들 수 있으며 사용 시점에서 var a_node = a_classref.get_ref() 처럼 get_ref()로 참조 노드를 생성할 수 있다. 만약 참조하고 있던 클래스가 이미 메모리에서 해제되었다면 get_ref() 결과는 false가 된다.

 

■ 코루틴

일반적인 함수는 위의 그림과 호출하는 쪽에서 특정 함수를 호출하면 호출받은 함수가 끝날 때까지, 즉, 함수에 속한 끝 문장까지 수행하거나 return 문을 만날 때까지 수행하고 호출한 곳으로 돌아와서 다음 문장을 계속 실행을 진행한다.

 

반면에 코루틴은 대기(Suspend) 및 재시작(Resume) 과정이 추가된다. 위의 그림에서 점선으로 표시한 것이 함수 간에 yield()와 resume()으로 상호 협력하며 코드를 수행하는 과정을 나타낸다. 호출된 함수에서 인수가 없이 yield()를 수행하면 해당 시점에 함수 수행을 중단하고 호출한 함수로 돌아간다. 이때 함수 리턴 값은 return 문에서 전달되는 값이 아니라 GDScriptFunctionState 라는 타입이 전달되고 이 타입으로 대기 상태에 들어가 있는 함수를 깨울 수 있는 resume()을 호출할 수 있다. resume()을 호출하면서 값을 전달할 수 있으며 이 값은 yield()의 리턴 값이 된다.

 

함수 내에 루프를 통해서 여러 번의 yield()를 수행할 수 있으므로, 위의 예제처럼 함수 호출 및 resume()의 리턴이 GDScriptFunctionState 타입이고 is_valid() 상태면 함수가 수행을 종료하고 값을 리턴한 것이 아니라 yield()로 대기 상태에 들어간 것이므로 resume()으로 코루틴의 대기 상태를 정리해 주어야 한다.

 

코루틴과 시그널

앞서 호출받은 코루틴에서 인수 없이 yield()를 호출해서 현재 함수는 대기 상태로 들어가면서 호출한 함수로 바로 돌아갔다가 호출한 측에서 resume()을 호출하면 대기 지점 이후로 코드 수행을 이어가는 예제를 살펴보았지만 고도 데모에서 많이 사용하는 방식은 코루틴과 시그널을 연관시키는 방식이다.

 

yield(오브젝트, "시그널 이름")으로 호출하면 코루틴 자체는 대기 상태가 되고 지정한 오브젝트의 시그널이 발생하면 대기 지점 이후로 코드 수행을 이어가는 방식이다. 시그널 이벤트가 resume()의 기능을 수행하는 방식이다.

 

위의 코드는 타이머 오브젝트를 통해서 sleep() 효과를 내는 모습이다.

 

위의 예제에서는 두 가지를 파악해야 한다. 우선, resume()으로 호출하는 함수에서 코루틴으로 yield()의 리턴 값을 전달할 수 있는 것처럼, 시그널에 의해서 코루틴이 깨워지는 경우에도 시그널의 파라미터를 yield()의 리턴 값으로 받을 수 있다. 만약 파라미터가 여러 개인 경우에는 배열로 값이 전달된다.

 

또 한 가지는 코루틴은 함수가 종료될 때 "completed"라는 시그널을 발생하고 함수의 리턴 값이 시그널의 파라미터로 적용된다는 점이다. 위의 예제를 보면 choose_action이라는 코루틴을 수행하면서 함수 종료를 나타내는 "completed" 시그널을 지정했다. 코루틴 내부에서도 yield로 특정 시그널을 연관시키기는 했지만 return문을 만나면 "completed" 시그널이 발생되고 리턴 값이 시그널 파라미터로 action = yield(...)로 전달되는 것이다. 

 

onready 구문

고도에서는 자주 사용하는 노드를 변수에 할당해 놓고 코드 수행 중에 get_node() 수행함으로 인한 오버헤드를 줄이는 등의 방법으로 성능을 높이기도 하는데 변수에 특정 노드를 할당하는 시점에는 해당 노드가 준비가 안 된 상황일 수 있다. 이런 상황을 예방하기 위해서 위의 예제처럼 변수에 노드를 할당하는 문장 앞에 onready 키워드를 붙여주면 해당 노드의 _ready()가 호출되는 시점에 실제 노드 할당이 수행된다. 각 노드의 _ready()는 해당 노드가 씬 트리에 들어가는 시점에 호출된다.

 

Assert 조건 확인 구문

코드 수행 과정에서 심각한 문제로 코드를 더 이상 수행할 수 없는 조건을 확인하는 구문이다.

위의 예제를 보면 파일을 쓰기 모드로 열지 못하면 error에는 오류 코드가 들어가고, 정상이면 0이 리턴되는데 위의 assert() 문은 파일이 정상적으로 open()되었는지를 확인하는 것이다. 정상이 아니면 오류를 발생시키고 편집기에서 코드를 수행했다면 수행이 중단된다.

 

다만, 고도에서 assert() 구문은 디버그 모드에서만 영향을 미치고 릴리즈 모드로 내보내기 했다면 아무런 영향을 미치지 못한다.

 

위의 예제는 또 다른 조건 확인 구문 예제로 assert 괄호 안에는 조건을 기술해야 한다.

728x90
댓글
글 보관함
최근에 올라온 글
최근에 달린 댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31