UDN
Search public documentation:

UnrealScriptReferenceKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > 언리얼스크립트 > 언리얼스크립트 언어 참고서

언리얼스크립트 언어 참고서


문서 변경내역: Tim Sweeney 원저. 홍성진 번역.

개요


이 문서는 UnrealScript 언어에 대한 기술적 설명서입니다. 튜토리얼은 아니며, 직접 쓸 수 있는 자세한 UnrealScript 코드 예제가 있지도 않습니다. UnrealScript 예제로는 엔진의 소스 코드를 보시는 것이 좋습니다. 소스 코드에는 AI, 동작, 인벤토리, 트리거 등 많은 문제들을 풀어주는, 수 만 줄의 현재 사용중인 UnrealScript 코드가 제공되어 있습니다. 처음에는 "Actor", "Object", "Controller", "Pawn", "Weapon" 스크립트를 살펴보시는 것이 좋습니다.

이 글은 C/C++/Java 관련 실용적인 지식을 갖고 있고, 객체 지향형 프로그래밍에 익숙하며, 언리얼을 플레이해 본 적이 있으며, 또 언리얼 에디터를 사용해 본 적이 있다는 것을 전제로 합니다.

UnrealWiki 의 UnrealScript Overview 에서도 UnrealScript 관련 엄청난 양의 정보를 제공하고 있습니다.

객체 지향형 프로그래밍이 처음인 독자들의 경우, Amazon.com 이나 서점에 가서 Java 프로그래밍에 대한 입문서를 구입해 보실 것을 강력히 추천합니다. Java 는 UnrealScript 와 매우 비슷하며, 접근방식이 명확하고 단순해서 배우기에 훌륭한 언어입니다.

UnrealScript 의 디자인 목표


UnrealScript 는 저희 개발팀과 제삼자 언리얼 개발자들의 게임 프로그래밍 상의 요구와 뉘앙스가 자연스럽게 반영된, 강력한 내장 프로그래밍 언어를 만들고자 개발한 것입니다.

UnrealScript 의 주요 디자인 목표는 다음과 같습니다:

  • 종래의 프로그래밍 언어들이 제시하지 않는 시간, 스테이트, 프로퍼티, 네트워킹 등과 같은 주요 개념을 지원하는 것입니다. 그 덕분에 UnrealScript 코드가 크게 간소화됩니다. 일정 기간 후 완료되는 이벤트나 오브젝트의 스테이트(상태)에 따라 달라지는 이벤트 처리를 C/C++ 로 하려면, AI 와 게임 로직 프로그래밍이 크게 복잡해 집니다. C/C++ 에서 이런 것들이 작성, 이해, 관리, 디버깅이 힘들어지는 스파게티 코드가 되는 것입니다. UnrealScript 에는 시간, 스테이트, 네트워크 리플리케이션에 대한 네이티브 지원이 포함되어 있어, 게임 프로그래밍을 크게 단순화시킬 수 있습니다.
  • Java 스타일의 프로그래밍 단순성, 객치 지향성, 컴파일 시간 에러 검사를 제공하기 위함입니다. Java 가 웹 프로그래머들에게 깔끔한 프로그래밍 환경을 조성한 것처럼, UnrealScript 는 3D 게임계에 깔끔하고 단순하며 탄탄한 프로그래밍 언어가 되고 있습니다. UnrealScript 가 Java 에서 차용한 주요 프로그래밍 개념은:
    • 포인터 없는 환경에 자동 가비지 콜렉션
    • 간단한 단일 상속 클래스 그래프
    • 강력한 컴파일 시간 형 검사
    • 안전한 클라이언트측 실행 "샌드박스"
    • C/C++/Java 코드와 비슷한 외형과 느낌
  • 이 모두 비트와 픽셀이 아닌 게임 오브젝트와 상호작용 측면에서 풍부하고 높은 레벨의 프로그래밍을 가능케 하기 위함입니다. UnrealScript 디자인상의 상충요소가 있습니다. 개발상의 단순성과 강력함을 위해 실행 속도를 희생시켰습니다. 더해지는 복잡도보다 퍼포먼스 이득이 중요한 로우 레벨 퍼포먼스 중시 코드는 C/C++ 로 작성됩니다. UnrealScript 는 그 위의 레벨, 즉 비트와 픽셀 레벨이 아닌, 오브젝트와 상호작용 레벨에서 작동하는 것입니다.

UnrealScript 의 초기 개발 단계에서, 현재의 구체적인 모습에 이르기까지 여러가지 다른 중요한 프로그래밍 패러다임이 검토되고 또 버려졌습니다. 제일 먼저, 저는 Sun 과 Microsoft 의 윈도우용 Java VM 을 Unreal 의 스크립팅 언어의 기초로 사용하여 연구했습니다. 그 결과는, Unreal 의 상황에서는 Java 가 C/C++ 보다 나은 프로그래밍의 이점을 제공하지 않으며, 필요한 언어 기능(연산자 오버로딩 등)의 부족으로 인한 실망스러운 제약이 있는 것으로 나타났습니다. 그리고 VM 태스크 교체의 비용 및 대형 오브젝트 그래프의 경우 Java 가비지 컬렉터의 비효율성 때문에 한없이 느린 것으로 나타났습니다. 두 번째로, 저는 UnrealScript 의 조기 구현 기반을 Visual Basic 의 변형에 두었습니다. 이것은 잘 되었지만 C/C++ 에 길들여진 프로그래머들에게는 익숙하지 않았습니다. UnrealScript 를 C++/Java 의 변형에 기반을 두기로 한 최종 결정은 게임 특유의 컨셉을 언어의 정의 그 자체 위에 표현하려는 열망과, 속도 및 익숙함에 대한 필요를 바탕으로 이루어졌습니다. 덕분에 언리얼 코드베이스의 많은 면을 크게 단순화시킬 수 있었습니다.

언어적 특징


언어적 특징

언리얼스크립트에 이미 익숙하신 분들을 위해, 여기 언리얼 엔진 2 이후에 있었던 언리얼스크립트 관련 주요 변화내용을 짧게 요약해 보겠습니다.

  • 동적 배열 - 동적 배열에서 요소 인덱스를 찾을 수 있는 함수 find() 가 새로 생겼습니다.
  • 동적 배열 반복처리기 - foreach 명령이 동적 배열에서도 돌아갑니다.
  • 다른 클래스에서 상수를 접근할 수 있습니다: class'SomeClass'.const.SOMECONST
  • 툴팁 지원 - 프로퍼티가 UnrealScript 의 선언 위에 /** 툴팁 내용 */ 형태의 코멘트가 있는 경우, 에디터 프로퍼티 창에서 그 프로퍼티 위에 마우스 커서를 올리면 툴팁이 표시됩니다.
  • 디폴트 프로퍼티 - defaultproperties 블럭 처리가 약간 변경/개선되었습니다.
    • 구조체 디폴트 - 구조체도 이제 디폴트 프로퍼티를 가질 수 있습니다.
    • 더이상 환경설정(config)/현지화(localized) 변수에 디폴트 값을 설정할 수 없습니다.
    • Defaultproperties 는 실행시간에 읽기전용으로, 더이상 class'MyClass'.default.variable = 1 할 수 없습니다.
  • 메타데이터 지원 - 프로퍼티에 여러가지 종류의 메타데이터를 연결시켜 게임내 에디터내 함수성을 확장시킵니다.
  • 디버깅 함수 - 디버깅 관련 함수가 새로 추가되었습니다.
  • 다중 타이머 지원
  • 함수 디폴트 인수 값 - 이제 함수의 옵션 인수에 디폴트 값을 지정할 수 있습니다.
  • 스테이트 스태킹 - 이제 스테이트를 스택에 push / pop 할 수 있습니다.
  • 언리얼스크립트 전처리기 - 매크로와 조건부 컴파일을 지원합니다.
  • 언리얼스크립트 인터페이스 - 인터페이스가 지원됩니다.
  • 언리얼스크립트 델리게이트 함수 인수 - 이제 UE3 는 델리게이트를 함수의 인수로 전달할 수 있습니다.
  • 리플리케이션 - UE3 에서는 리플리케이션 문이 변경되었습니다:
    • 이제 replication 블록은 변수에 대해서만 사용됩니다.
    • 이제 함수 리플리케이션은 (Server, Client, Reliable 등의) 함수 지정자를 통해 지정합니다.

자료

언리얼스크립트 툴과 유틸리티


스크립트 프로파일링

Gameplay Profiler 는 어느 부분에서 실행되는 스크립트가 가장 시간이 오래 걸리나 알아보는 데 좋습니다.

스크립트 디버거

자세한 정보는 언리얼의 Debugging Tools KR 페이지를 참고해 주시기 바랍니다.

고급 테크니컬 토픽


언리얼 가상 머신

언리얼 가상 머신은 서버, 클라이언트, 렌더링 엔진 그리고 엔진 지원 코드 등 여러 기본 단위로 이루어져 있습니다.

언리얼 서버는 모든 게임플레이 그리고 플레이어와 액터 사이의 상호작용을 제어합니다. 싱글 플레이어 게임에서는 언리얼 클라이언트와 언리얼 서버가 같은 컴퓨터에서 실행됩니다. 인터넷 게임에서는 전용 컴퓨터에서 운용되는 데디케이티드(dedicated, 전용) 서버가 있습니다. 이 컴퓨터에 접속되어 있는 플레이어들은 모두 클라이언트입니다.

모든 게임플레이는 "레벨", 즉 지오메트리와 액터로 구성된 독립 환경에서 벌어집니다. UnrealServer 는 레벨을 동시에 둘 이상 실행시킬 수 있기는 하지만, 각 레벨은 독립적으로 실행되며 서로간에 보호됩니다. 즉 액터가 레벨 사이를 오갈 수 없고, 한 레벨의 있는 액터는 다른 레벨에 있는 액터와 통신할 수 없습니다.

맵에 있는 각각의 액터는 (네트워크 게임에서는 여럿 있을 수도 있는) 플레이어가 제어하든가 스크립트가 제어하든가 둘 중 하나입니다. 액터가 스크립트 제어하에 있을 때는, 그 스크립트가 액터의 이동이나 다른 액터와의 상호작용 방식을 완전히 정의합니다.

월드에서 그 모든 액터가 돌아다니고, 스크립트가 실행되고, 이벤트가 발생하고 있는데, UnrealScript 내 실행 흐름을 어떻게 이해할 수 있겠느냐고 의문이 들 수 있습니다. 답은 이렇습니다:

시간 관리를 위해 언리얼은 게임플레이 시간을 "Tick" 으로 나눕니다. 틱은 레벨의 모든 액터가 업데이트되는 최소 시간 단위입니다. 틱은 보통 0.1 초에서 0.01 초가 됩니다. 틱 시간은 CPU 시간에 의해서만 제한되며, 빠를 수록 틱 주기도 빨라집니다.

UnrealScript 일부 명령은 실행하는 데 0 틱 걸리는(, 즉 게임 시간이 전혀 흐르지 않는) 것도 있고, 여러 틱 걸리는 것도 있습니다. 게임 시간이 약간 지나야 되는 함수는 "잠복성(latent) 함수"라고 합니다. 예를 들면 Sleep, FinishAnim, MoveTo 같은 것입니다. UnrealScript 의 잠복성 함수는 (state 로 선언되는) 스테이트 내 코드에서만 호출 가능하며, (state 안에서 정의되는 것도 포함해서) 함수 내 코드에서는 안됩니다.

액터가 잠복성 함수를 실행하는 동안에는 잠복성 함수가 완료되기 전까지 해당 액터의 스테이트 실행은 계속되지 않습니다. 그러나 다른 액터 또는 가상머신은 그 액터 안에서 함수를 호출해도 됩니다. 종합하면 모든 UnrealScript 함수는 언제든, 심지어 잠복성 함수가 대기중인 동안에도 호출 가능합니다.

종래의 프로그래밍 관점에서 보면, UnrealScript 는 마치 레벨 내의 각 액터가 고유의 실행 "스레드"를 가지고 있는 것처럼 작동합니다. 내부적으로 언리얼은 윈도우 스레드를 사용하지 않는데, 매우 비효율적이기 때문입니다. (윈도우 95 와 윈도우 NT 는 수천개의 스레드 동시 처리를 효율적으로 해 내지 못합니다.) 대신 UnrealScript 는 스레드를 시뮬레이트합니다. 이 사실은 UnrealScript 코드에서는 투명하지만, UnrealScript 와 상호작용하는 C++ 코드를 작성해 보면 명백히 드러납니다.

모든 UnrealScript 는 서로에게 독립적으로 실행됩니다. 레벨에 몬스터가 100 마리 돌아다니면, 그에 대한 스크립트 100 개가 매 "틱"마다 독립적으로 한꺼번에 실행되는 것입니다.

언리얼스크립트 구현

컴파일 프로세스 에서부터 실행, 그리고 바이트 코드 표현에 이르기까지, UnrealScript 내부적 작동법에 대한 정보는 UnrealScript Implementation KR 페이지를 참고하시기 바랍니다.

언리얼스크립트 바이너리 호환성 문제

UnrealScript 는 패키지 파일 내의 클래스들이 바이너리 호환성을 해치지 않는 선에서 서서히 발전하도록 디자인되었습니다. 여기서 바이너리 호환성이란 "개별적인 바이너리 파일이 오류 없이 로드되고 또 링크되는 것"을 뜻합니다. 변경한 코드가 의도대로 기능을 발휘하는지의 여부는 별도의 문제입니다. 구체적으로, 변경해도 안전한 작업은 다음과 같습니다:

  • 패키지 내의 .uc 스크립트 파일들은 다시 컴파일해도 바이너리 호환성이 깨지지 않습니다.
  • 패키지에 새 클래스 추가
  • 클래스에 새 함수 추가
  • 클래스에 새 스테이트 추가
  • 클래스에 새 변수 추가
  • 클래스에서 프라이빗 변수 제거

일반적으로 안전하지 않은 작업은 다음과 같(으며, 더 있을 수도 있)습니다:

  • 구조체에 새 멤버 추가
  • 패키지에서 클래스 제거
  • 변수, 함수 파라미터, 반환값 유형 변화
  • 함수의 파라미터 갯수 변화

테크니컬 노트

가비지 컬렉션. 언리얼의 모든 오브젝트와 액터는 Java VM 과 비슷하게 트리 구조를 따르는 가비지 컬렉터를 사용해서 가비지 컬렉팅합니다. 언리얼의 가비지 컬렉터는 UObject 클래스의 Serialize 함수성을 재귀적으로 사용해서 각 오브젝트가 다른 오브젝트를 추가로 참조하는 것이 있는지 알아봅니다. 그 덕분에 오브젝트를 일일히 삭제해 줄 필요가 없습니다. 오브젝트가 참조되지 않는 상태가 되면 가비지 컬렉터가 그것들을 찾아내어 청소해 주기 때문입니다. 여기에는 참조되지 않는 오브젝트의 삭제가 지연되는 부작용이 있지만, 삭제가 빈번하지 않을 때는 훨씬 효율적입니다. 습니다; 그러나 이것은 빈번하지 않은 삭제의 경우 참조를 일일히 계산하는 것보다 훨씬 효과적입니다. 상세한 정보는 가비지 콜렉션 페이지를 참고하십시오.

UnrealScript 는 바이트 코드에 기반을 두고 있습니다. UnrealScript 의 코드는 p-code 또는 Java 바이트코드와 비슷한 바이트 코드 시리즈로 컴파일됩니다. 덕분에 UnrealScript 는 플랫폼 중립적이 되지요. 언리얼의 클라이언트와 서버 컴포넌트를 다른 플랫폼, 즉 매킨토시나 유닉스로 간단히 이식할 수 있으며, 모든 버전이 같은 스크립트를 실행하니 상호연동도 쉽습니다.

가상 머신으로서의 언리얼. Java 언어와 내장 Java 클래스 계층구조가 웹 페이지 스크립팅용 가상 머신을 정의하는 것과 마찬가지로, 언리얼 엔진을 3D 게이밍에 대한 가상 머신으로 생각해 볼 수 있습니다. 언리얼 가상 머신은 (플랫폼 종속적인 코드를 모두 별도의 모듈에 분리했기 때문에) 천부적으로 이식이 가능하고 (확장이 가능한 클래스 계층구조 덕분에) 확장성이 있습니다. 그렇지만, 현재로서는 다른 이들이 Unreal VM 을 확장하여 독립적이고 호환성 있는 구현을 만들어내는데 필요한 정도까지 문서화할 계획은 없습니다.

UnrealScript 컴파일러는 3 패스입니다. C++ 와 달리, UnrealScript 는 3개의 뚜렷이 다른 패스에서 컴파일됩니다. 첫 패스에서는 변수, 구조체, 열거형, const, 스테이트, 함수 선언을 해석하고 기억합니다. 각 클래스의 뼈대가 구축되는 거죠. 두 번째 패스에서는 스크립트 코드가 바이트 코드로 컴파일됩니다. 순환 의존성을 가진 복잡한 스크립트 계층구조가 완전하게 컴파일되어 별도의 링크 단계 없이 두 패스에 링크되도록 해줍니다. 세 번째 패스는 .uc 파일의 defaultproperties 블록에서 지정된 값들을 사용해서 클래스에 대한 디폴트 프로퍼티를 해석하고 임포트합니다.

지속적인 액터의 스테이트. 언리얼에서는 사용자가 언제라도 게임을 저장할 수 있기 때문에, 모든 액터의 스테이트가 (그들의 스크립트 실행 스테이트를 포함하여) UnrealScript 스택의 최저 레벨에 있을 때에만 저장된다는 것을 이해하는것이 중요합니다. 이 지속성때문에 잠복성 함수는 스테이트 코드에서만 호출 가능한 것입니다. 스테이트 코드는 가능한 최저 스택 레벨에서 집행되며, 따라서 쉽게 직렬화될 수 있습니다. 함수 코드는 어떤 스택 레벨에도 존재할 수 있으며, 스택에서 그 아래에 (예를 들자면) C++ 의 네이티브 함수를 가질 수 있습니다. 이것은 분명히 디스크에 저장하고 나중에 복원할 수 상황이 아닙니다.

Unrealfile 은 언리얼 본래의 바이너리 파일 포맷입니다. Unrealfile 은 인덱스, 특정 Unreal 패키지 내 오브젝트의 직렬화된 덤프를 함유하고 있습니다. Unrealfile은 다른 Unrealfile 에 저장되어 있는 다른 오브젝트에의 참조를 가질 수 있다는 점에서 DLL 과 비슷합니다. 이 접근방식은 사전 정의된 “패키지” 내의 Unreal 컨텐츠를 인터넷에 배포하는 것이 가능하도록 해줍니다. 패키지로 배포하는 것은 특정 패키지를 한 번 이상 다운로드하지 않음으로써 다운로드 시간을 줄이기 위해서 입니다.

UnrealScript 는 왜 static 변수를 지원하지 않는가. C++ 는 (클래스 별로 처리되는) static 변수를 지원하는데, 그것은 그 언어의 기원이 낮은 레벨이라는데 이유가 있습니다. Java도static 변수를 지원하는데, 이것은 그다지 깊이 생각한 결과가 아닌 것으로 보입니다. UnrealScript 에는 이러한 변수들이 설 자리가 없습니다. 직렬화, 파생, 그리고 복수 레벨 등에 관하여 유효범위가 애매하기 때문입니다: static 변수가 모든 활성적인 Unreal 레벨에서 모든 static 변수들이 같은 값을 가지는 것을 뜻하는, "글로벌" 의미론을 가져야 할지? 그것이 레벨 단위여야 할지? 그렇다면 그것들이 어떻게 직렬화 되어야 할지 — .u 파일에 있는 클래스, 아니면 .unr 파일에 있는 레벨? 그것들은 베이스 클래스별로 독특한지, 아니면 클래스의 파생 버전이 static 변수 고유의 값을 가지고 있는지? UnrealScript 에서, 저희는 static 변수를 언어 기능의 하나로서 정의하지 않음으로써 이 문제를 비껴갑니다. 그리고 이를 프로그래머들에게 맡겨서, 그들이 클래스를 만들어 static 및 global 과 비슷한 변수들을 포함한 다음 이것들을 실제의 오브젝트에 드러냄으로써 이러한 변수들을 관리하도록 합니다. 레벨별로 접근할 수 있는 변수를 가지고 싶다면, 이러한 변수를 포함하는 클래스를 만들어 그들이 레벨에서 확실히 직렬화되도록 해두면 됩니다. 이렇게 하면 애매하지 않습니다. 이러한 종류의 목적에 부합하는 클래스의 예를 보려면 LevelInfo 와 GameInfo 를 참고하십시오.