본문 바로가기

레퍼런스/고도엔진

고도엔진 튜토리얼 #5 스크립팅 - 이어서(Scripting (continued))

프로세싱



고도에서 몇몇 행동들은 콜백이나 가상 기능들에 의해 촉발되기 때문에, 항상 쓰기 코드를 확인할 필요가 없습니다. 또한 애니메이션 플레이어로 많은 것을 할 수 있습니다.


그러나 모든 프레임에서 스크립트 프로세스를 수행하는 것은 여전히 일반적인 경우입니다. 처리에는 유휴 처리와 고정처리의 두 가지 경우가 있습니다.


유휴 처리는 Node.set_process() 기능으로 활성화됩니다. 활성화되면, Node.process() 콜백은 모든 프레임마다 불리게 됩니다. 예 :


func _ready():
    set_process(true)

func _process(delta):
    # do something...

델타 매개 변수는 이전의 _process() 호출 이후 경과된 시간(초, 부동소수점)으로 기술합니다. 고정 처리는 유사하지만 물리적 엔진과 딩기화 하는데만 사용합니다.


이를 테스트하는 간단한 방법은 다음과 같은 스크립트를 사용하여 단일 Label(라벨) 노드를 사용하여 씬을 만드는 것입니다.


extends Label

var accum=0

func _ready():
    set_process(true)

func _process(delta):
    accum += delta
    set_text(str(accum))


이는 각각의 프레임마다 카운터가 증가하는 것을 보여줍니다.



그룹


노드는 그룹에 추가될 수 있습니다(노드 당 원하는 수만큼). 이것은 큰 씬을 정리하기 위한 간단하지만 유용한 특징입니다. 이 작업은 두 가지 방법으로 수행할 수 있습니다. 첫번째 방법은 UI에서 Node-Panel 아래의 Groups(그룹) 버튼을 클릭하는 것입니다.

../../_images/groups_in_nodes.PNG
두 번째는 코드에서 온 것입니다. 한가지 유용한 예는 적인 씬에게 태그를 다는 것입니다.

func _ready():
    add_to_group("enemies")


이렇게 하면 플레이어가 비밀 기지로 들어가는 것이 발견될 경우, SceneTree.call_group()을 통해 모든 적에게 알람소리를 이용해 알릴 수 있습니다.


func _on_discovered():
    get_tree().call_group(0, "guards", "player_was_discovered")


위 코드는 그룹의 모든 멤버에서 "guards"라 불리는 기능을 "player_was_discovered"라고 부릅니다.


선택적으로 SceneTree.get_nodes_in_group()에 호출해서 "guards" 노드의 전체 목록을 자기고 올 수 있습니다 :


var guards = get_tree().get_nodes_in_group("guards")


더 많은 것들은 SceneTree에서 나중에 볼 수 있습니다.



알림


고도는 알림 시스템을 가지고 있습니다. 이는 대부분 스크립팅은 필요하지 않은데, 대부분 너무 낮은 수준이고 가상 함수가 제공되기 때문입니다. 그냥 그들이 존재한다는 것을 알게 되어 기쁩니다. Object._notification() 기능만 추가하면 됩니다.


func _notification(what):
    if (what == NOTIFICATION_READY):
        print("This is the same as overriding _ready()...")
    elif (what == NOTIFICATION_PROCESS):
        var delta = get_process_time()
        print("This is the same as overriding _process()...")


클래스 참조의 각 클래스에 대한 문서는 수신가능한 알림을 보여줍니다. 그러나 대부분의 경우 GDScript는 간단하게 재정의할 수 있는 기능을 제공합니다.



재정의 가능한 함수들


노드는 다음과 같이 설명할 수 있는 많은 재정의 가능한 함수를 제공합니다 :


func _enter_tree():
    # When the node enters the _Scene Tree_, it becomes active
    # and  this function is called. Children nodes have not entered
    # the active scene yet. In general, it's better to use _ready()
    # for most cases.
    pass

func _ready():
    # This function is called after _enter_tree, but it ensures
    # that all children nodes have also entered the _Scene Tree_,
    # and became active.
    pass

func _exit_tree():
    # When the node exits the _Scene Tree_, this function is called.
    # Children nodes have all exited the _Scene Tree_ at this point
    # and all became inactive.
    pass

func _process(delta):
    # When set_process() is enabled, this function is called every frame.
    pass

func _fixed_process(delta):
    # When set_fixed_process() is enabled, this is called every physics
    # frame.
    pass

func _paused():
    # Called when game is paused. After this call, the node will not receive
    # any more process callbacks.
    pass

func _unpaused():
    # Called when game is unpaused.
    pass



앞서 언급했듯이, 이러한 함수들을 사용하는 것이 가장 좋습니다.



노드를 생성하기


코드를 기반으로 노드를 생성하려면, new() 메서드를 호출하기만 하면 됩니다(다른 클래스 기반 데이터 유형과 동일) 예 : 


var s
func _ready():
    s = Sprite.new() # create a new sprite!
    add_child(s) # add it as a child of this node


노드를 삭제하려면 노드가 씬의 내부 혹은 외부에 있는 경우 free()를 사용해야 합니다.


func _someaction():
    s.free() # immediately removes the node from the scene and frees it


노드가 삭제되면 하위 노드도 함께 삭제됩니다. 이로 인해 수동으로 노드를 삭제하는 것이 보이는 것보다 훨씬 간단합니다. 기저 노드(base node)를 삭제하면 그 아래에 있는 서브 트리들이 함께 삭제됩니다.


그러나 현재 신호나 함수를 호출하고 있기 때문에 현재 "blocked(차단)" 되어 있어 노드를 삭제하고 싶지만 그렇지 못하는 경우가 매우 많습니다. 이것은 결국 게임에 충돌을 발생시킬 것입니다. 고도를 실행하는 동안 디버거는 그런 경우를 알려주고 여러분에게 경고해줍니다.


노드를 삭제하는 가장 안전한 방법은 Node.queue_free()를 사용하는 것입니다. 이렇게 하면 유휴 중에 노드가 안전하게 지워집니다.


func _someaction():
    s.queue_free() # remove the node and delete it while nothing is happening



씬을 인스턴스화하기


코드에서 씬을 인스턴스화 하는 것은 매우 쉽고 두 단계로 수행됩니다. 첫번째는 디스크에서 씬을 로드하는 것입니다.


var scene = load("res://myscene.scn") # will load when the script is instanced


사전 로드는 때로 더 편리한데, 구문 분석 시간(parse time)에 발생하기 때문입니다.


var scene = preload("res://myscene.scn") # will load when parsing the script


그러나 씬은 하위 노드를 포함하는 노드가 아닙니다. PackedScene이라는 특별한 리소스로 포장되어 있습니다. 실제 노드를 생성하기 위해 PackedScene.instance()라는 함수를 호출해야 합니다. 이것은 활성 씬에 추가할 수 있는 노드 트리가 반환됩니다 :


var node = scene.instance()
add_child(node)


2단계 프로세스의 장점은 포장된 씬(packed scene)을 로드하여 사용할 수 있으므로 원하는 만큼 인스턴스를 생성하는데 사용할 수 있다는 점 입니다. 이는 특히 활성화된 씬에서 여러 적, 총알 등을 신속하게 포착하는 데 유용합니다.