본문 바로가기

레퍼런스/고도엔진

고도엔진 튜토리얼 #4 스크립팅(Scripting)

소개


사용자가 프로그래밍 없이 비디오 게임을 만들 수 있게 해주는 도구에 대해서 많은 논의들이 있어 왔습니다. 많은 독립적인 개발자들이 코드를 쓰지 않고도 게임을 만드는 것이 꿈입니다. 이런 필요성은 오랫 동안, 심지어 게임 디자이너들이 게임의 흐름을 더 잘 제어하고 싶어하는 회사 내부에서조차 존재해 왔습니다.


많은 제품이 프로그래밍이 필요없는 환경을 약속하며 출시되었지만 결과적으로 기존 코드에 비해 종종 불완전하거나 복잡하고 또는 비효율적입니다. 결과적으로, 프로그래밍은 앞으로도 지속될 것입니다. 사실, 게임 엔진의 일반적인 방향은 특정 작업을 위해 작성되어야 하는 코드를 줄이고 개발 속도를 높이기 위해 도구들을 추가하는 것입니다.


그런 의미에서 고도엔진은 그 목표를 향애 유용한 디자인 결정을 내렸습니다. 첫번째이자 가장 중요한 것은 장면 시스템입니다. 이 목표는 처음에는 명확하지 않았지만 나중에는 잘 작동했습니다. 즉, 프로그래머들이 코드를 설계하는 책임에서 벗어나게 하기 위함이죠.


씬 시스템을 이용해 게임을 디자인할 때, 전체 프로젝트는 개별 씬이 아닌 보완 씬(complementary scene)으로 분할됩니다. 씬들은 서로 분리되지 않고 보완관게에 있습니다. 앞으로 이에 대한 많은 예가 나옵니다. 이것을 기억하는 것이 매우 중요합니다.


프로그래밍 전문 지식이 풍부한 사용자에게 이는 MVC와 다른 설계 패턴을 의미합니다. 고도는 MVC의 습관을 없애는 비용으로 효율성을 약속하며, 이는 보완적인 패턴인 씬으로 대체됩니다.


고도는 스크립팅을 위한 확장 패턴을 사용하는데, 이는 스크립트가 이용 가능한 모든 엔진 등급에서 확장된다는 것을 의미합니다.



GDScript


GDScript는 고도엔진에 맞게 동적으로 입력되는 스크립트 언어입니다. 다음과 같은 목표를 가지고 설계되었습니다 :


  • 첫번째이자 가장 중요한 것은, 가능한 간단하고 친숙하며 배우기 쉽게 만드는 것입니다.
  • 코드를 읽기 쉽고 오류를 안전하게 처리합니다. 구문은 대부분 Python의 것을 빌렸습니다.
프로그래머들은 이것을 배우는 데 며칠이 걸리고, 2주안에 편안함을 느낍니다.

대부분의 동적 입력 언어와 마찬가지로, 더 높은 생산성(코드 학습이 더 쉽고 빠르게 쓰기 가능하며, 컴파일을 거치지 않는 등)은 성능 저하 없이 균형을 이룹니다. 그러나 대부분의 중요한 코드는 이미 엔진에 있는 C++로 작성되어(벡터 연산, 물리학, 수학, 색인화 등) 대부분의 게임 유형에서 충분한 성능을 발휘합니다.

어떠한 경우에도 더 좋은 성능이 필요하다면 C++로 중요한 섹션을 다시 작성하고 스크립트에 투명하게 누출시킬 수 있습니다. 이는 나머지 게임을 변경하지 않고 GDScript 클래스를 C++ 클래스로 교체하는 것을 허용합니다.



씬과 스크립팅


계속하기 전에 GDScript 참조를 읽어야 합니다. 굉장히 간단한 언어이고 참조 문헌은 짧습니다. 개념의 개요를 이해하는 데는 몇 분 이상 걸리지 않으실 겁니다.



씬 설정


이 튜토리얼은 간단한 GUI 씬을 스크립팅 하는 것으로 시작합니다. 노드 추가 대화 상자를 사용하여 다음과 같은 노드로 다음 계층을 생성합니다 :


  • Panel
  1. Label
  2. Button

씬 트리에서 다음과 같이 표시됩니다 :


../../_images/scripting_scene_tree.png

2D 편집기를 사용하여 버튼과 레이블의 위치를 지정하고 크기를 조정하여 아래 이미지와 같이 보이도록 합니다. Inspector 창에서 텍스트를 설정할 수 있습니다.


../../_images/label_button_example.png

마지막으로 씬을 저장합니다. 적합한 이름은 "sayhello.scn"이 될 수 있습니다.



스크립트 추가


패널 노드에서 오른쪽 버튼으로 클릭하고 컨텍스트 메뉴에서 "스크립트 추가"를 선택합니다.


../../_images/add_script.png

스크립트 생성 대화 상자가 나타납니다. 이 대화 상자에서는 언어, 클래스 이름 등 GDScript가 스크립트 파일에 클래스 이름을 사용하지 않도록 선택할 수 있으므로, 필드를 편집할 수 없습니다. 스크립트는 "Panel"에서 상속되어야 합니다(패널 유형의 노드인 노드를 확장하도록 되어 있으므로 자동으로 채워짐).


스크립트의 경로 이름을 입력하고 "Create"를 선택합니다.


../../_images/script_create.png

이 작업이 완료되면 스크립트가 생성되어 노드에 추가됩니다. 이 아이콘은 노드에서 뿐만 아니라 스크립트 속성에서도 볼 수 있습니다 :


../../_images/script_added.png

스크립트를 편집하려면 강조 표시된 버튼 중 하나를 선택합니다. 그러면 기본적으로 기존 템플릿이 포함될 스크립트 편집기로 이동합니다.


../../_images/script_template.png

그 안에는 많지 않습니다. "_ready()" 기능은 노드(및 모든 하위 자손)가 활성화된 씬에 들어갔을 때 호출됩니다(기억하세요, 생성자가 아니며 생성자는 "_init()"의 형태입니다).



스크립트의 역할


스크립트가 노드에 동작을 추가해줍니다. 다른 노드(하위, 상위, 형제 등)와 마찬가지로 노드 기능을 제어하는 데 사용됩니다. 스크립트의 로컬범위는 노드(일반적인 상속에서와 같이)와 스크립트에 종속된 노드의 가상함수입니다.(The local scope of the script is the node (just like in regular inheritance) and the virtual functions of the node are captured by the script.)


../../_images/brainslug.jpg


시그널(신호) 다루기


신호는 대부분 GUI 노드에서 사용되지만 다른 노드에도 있습니다. 신호는 특정한 종류의 작업이 발생할 때 "emit(방출)"되며, 모든 스크립트 인스턴스 기능에 연결할 수 있습니다. 이 단계에서는 버튼의 "pressed" 신호가 사용자 지정 기능에 연결됩니다.


스크립트에 신호를 연결하기 위한 인터페이스가 편집기에 있습니다. 씬 트리에서 노드를 고름으로써 그리고 "Node" 탭을 누름으로써 여러분은 신호에 접근할 수 있습니다. "Signals"가 선택되었는지 확인해주세요.


../../_images/signals.png\

어떠한 경우에도, 이 지점에서 확실히 해야합니다. 우리는 "pressed" 신호에 관십이 있는 것입니다. 눈에 보이는 인터페이스 대신에, 우리가 코드 연결화를 선택할 겁니다.


이를 위해 고도 프로그래머가 가장 많이 사용하는 함수 중에 하나인 Node.get_node()가 있습니다. 이 함수는 스크립트를 가진 연관된 노드들에 대해서는 현재 트리 또는 씬의 어느 곳이던지 접근할 수 있습니다.


버튼을 가져오기 위해 다음을 사용합니다 :


get_node("Button")

다음으로, 버튼을 누르면 라벨의 텍스트가 변경되는 콜백이 추가됩니다.


func _on_button_pressed():
    get_node("Label").set_text("HELLO!")


마지막으로, 버튼의 "pressed" 신호는 Object.connect()를 이용하면 콜백 안의 _ready()와 연결될 것입니다.


func _ready(): get_node("Button").connect("pressed",self,"_on_button_pressed")


최종 스크립트는 다음과 같습니다 :


extends Panel

# member variables here, example:

# var a=2
# var b="textvar"

func _on_button_pressed():
    get_node("Label").set_text("HELLO!")

func _ready():
    get_node("Button").connect("pressed",self,"_on_button_pressed")


실행되는 씬은 버튼을 눌렀을 때 다음과 같은 결과가 됩니다.


../../_images/scripting_hello.png

참고 : 이 튜토리얼에서는 일반적으로 오해되는 내용인, get_node(path)가 위의 스크립트로 제어되는 노드의 직계 하위를 패널로 반환하여 작동한다는 점을 다시 한 번 분명히 합니다(이 경우에는 패널이 반드시 해당). 그래서 버튼은 위와 같은 코드에서 반드시 패널의 자식입니다. 명확한 설명을 위해 버튼이 라벨의 자식일 경우 획득할 코드는 다음과 같습니다 :


Note: As it is a common misunderstanding in this tutorial, let’s clarify again that get_node(path) works by returning the immediate children of the node controlled by the script (in this case, Panel), so Button must be a child of Panel for the above code to work. To give this clarification more context, if Button were a child of Label, the code to obtain it would be:

# not for this case
# but just in case
get_node("Label/Button")


또한, 노드는 타입이 아닌 이름으로 참조된다는 점을 기억하세요.





- 번역 외에 원문을 그대로 옮겨 놓은 것은 제가 정확하게 이해하지 못한 문장들입니다.