UnrealScript 언어의 참조
문서 요약: UnrealScript 에 대한 소개 및 간단한 참조로, 원 저자는 Tim Sweeney (EpicGames) 입니다.
문서 변경 내역: 작성 후 정기적으로 유지 관리.
서론
빠른 링크
UnrealScript CheatSheet 과 MasteringUnrealScriptTOC 튜토리얼을 반드시 확인하십시오.
이 문서의 목적
이것은 UnrealScript 언어를 설명하는 기술적 문서입니다. 이 문서는 튜토리얼이 아니며 UnrealScript 코드의 자세하고 유용한 예를 제공하지도 않습니다. UnrealScript 의 예에 대해서는 독자에게 엔진의 소스 코드를 소개합니다. 소스 코드에는 AI, 동작, 인벤토리, 트리거 등 많은 문제들을 풀어주는, 수 만 줄의 현재 사용중인 UnrealScript 코드가 제공되어 있습니다. 처음 시작하기에 좋은 방법은 "Actor", "Object", "Controller", "Pawn", 그리고 "Weapon" 스크립트를 살펴보는 것입니다 .
이 문서는 독자가 C/C++ 또는 Java 에 대한 실용적인 지식을 가지고 있고, 객체 지향의 프로그래밍에 익숙하고, Unreal 을 플레이해 본 적이 있으며, 또 UnrealEd 편집 환경을 사용해 본 적이 있다는 것을 전제로 합니다.
객체 지향의 프로그래밍이 처음인 독자들을 위해, 저는 Amazon.com 이나 서점에 가서 Java 프로그래밍에 대한 입문서를 구입할 것을 강력히 추천합니다. Java 는 UnrealScript 와 매우 비슷하며, 접근방식이 명확하고 단순해서 배우기에 훌륭한 언어입니다.
UnrealScript 의 디자인 목표
UnrealScript 는 개발 팀 및 제3자 Unreal 개발자들에게 자연스럽게 게임 프로그래밍의 요구와 뉘앙스를 표시하는, 강력한 내장 프로그래밍 언어를 제공하기 위해 창조되었습니다.
UnrealScript 의 주요 디자인 목표는 다음과 같습니다:
- 종래의 프로그래밍 언어들이 제시하지 않는 시간, 상태, 속성 및 네트워킹의 주요 개념을 지원하는 것입니다. 이것은 UnrealScript 코드를 크게 간소화 합니다. C/C++ 기반의 AI와 게임 논리 프로그래밍에 있어서의 커다란 복잡성은 완성에 상당한 게임 시간을 차지하는 이벤트, 그리고 객체 상태의 양상에 의존적인 이벤트를 처리하는 데에 있습니다. C/C++ 에서는 이것이 쓰기, 이해하기, 관리하기 그리고 디버그하기 힘든 스파케티 코드라는 결과를 가져옵니다. UnrealScript 에는 게임 프로그래밍을 크게 간소화하는 시간, 상태 그리고 네트워크의 복제를 위한 원시 지원이 포함되어 있습니다.
- Java 스타일의 평이한 프로그래밍, 객체 지향, 그리고 컴파일시의 오류 점검을 제공하는 것입니다. Java 가 웹 프로그래머들에게 깔끔한 프로그래밍 플랫폼을 가져다주는 것처럼, UnrealScript 는 3D 게이밍에 똑같이 깔끔하고 단순하며 강력한 프로그래밍 언어를 제공합니다. Java 로부터 파생된 주요 UnrealScript 프로그래밍 컨셉은 다음과 같습니다:
- 자동으로 가비지를 수집하는, 포인터를 사용하지 않는 환경;
- 간단한 단일 상속 클래스 그래프;
- 강력한 컴파일 시의 타입 점검;
- 클라이언트측에서 집행하는 안전한 "샌드박스";
- 그리고 친숙한 C/C++/Java 코드의 보고 느끼는 방식.
- 비트와 픽셀보다는 게임의 객체와 상호작용의 견지에서 풍부한, 고급 수준의 프로그래밍이 가능하도록 하는 것입니다. UnrealScript 에서 디자인의 취사선택이 이루어져야 할 때, 저는 개발의 단순함과 파워를 위해 집행 속도를 희생했습니다. 결국 Unreal 에서 낮은 수준의, 성능에 중대한 영향을 주는 코드는 성능의 증대가 부가적인 복잡성을 능가하는 C/C++ 로 씌여졌습니다. UnrealScript 는 비트나 픽셀 수준보다는 높은 수준, 즉 객체와 상호작용 수준에서 작용합니다.
UnrealScript 의 초기 개발 단계에서, 현재의 구체적인 모습에 이르기까지 여러가지 다른 중요한 프로그래밍 패러다임이 검토되고 또 버려졌습니다. 제일 먼저, 저는 Sun 과 Microsoft 의 윈도우용 Java VM 을 Unreal 의 스크립팅 언어의 기초로 사용하여 연구했습니다. 그 결과는, Unreal 의 상황에서는 Java 가 C/C++ 보다 나은 프로그래밍의 이점을 제공하지 않으며, 필요한 언어 기능의 (연산자 오버로딩 등의) 부족으로 인한 실망스러운 제약이 있는 것으로 나타났습니다. 그리고 VM 태스크 교체의 비용 및 대형 객체 그래프의 경우 Java 가비지 수집자의 비효율성 때문에 한없이 느린 것으로 나타났습니다. 두 번째로, 저는 UnrealScript 의 조기 구현 기반을 Visual Basic 의 변형에 두었습니다. 이것은 잘 되었지만 C/C++ 에 길들여진 프로그래머들에게는 덜 친근했습니다. UnrealScript 를 C++/Java 의 변형에 기반을 두기로 한 최종 결정은 게임 특유의 컨셉을 언어의 정의 그 자체 위에 표현하려는 열망과, 속도 및 친근감에 대한 필요를 바탕으로 이루어졌습니다. 이것은 Unreal 코드베이스의 많은 국면들을 크게 단순화한, 훌륭한 결정이었습니다.
Unreal Engine 3에서 달라진 것들
다음은 이미 UnrealScript 에 익숙한 분들을 위한, UnrealEngine2 이래 UnrealScript 에 행해진 주요 변경사항들에 대한 간략한 개요입니다 .
- Replication (복제) - UE3 에서는 replication 문이 변경되었습니다 :
- 이제는 replication 블록이 변수에 대해서만 사용됩니다.
- 함수의 복제는 이제 함수 지정자 ( Server, Client, Reliable )에 의하여 정의됩니다.
- 상태의 스택 – 상태를 스택에 추가하거나 빼낼 수 있게 되었습니다.
- UnrealScript 전처리기 – 매크로 및 조건부 컴파일 지원.
- 디버깅 함수 – 새 디버깅 관련 함수들이 추가되었습니다.
- 기본 속성 - defaultproperties 블록의 처리가 약간 변경\향상되었습니다.
- 구조체의 기본값 – struct (구조체) 들도 기본 속성들을 가질 수 있게 되었습니다.
- 이제는 config 또는 현지화된 변수들에 대해서는 기본 값을 설정하는 것이 허용되지 않습니다.
- Defaultproperties 는 런타임에 읽기 전용입니다. 이를
class'MyClass'.default.variable = 1
로 하는 것은 더 이상 허용되지 않습니다.
- 동적 배열 – 동적 배열이 요소의 인덱스를 찾기 위한 새 함수 find() 를 가지게 되었습니다.
- 동적 배열의 반복 – 이제 foreach 연산자가 동적 배열에서 기능합니다.
- 델리게이트 함수의 인수 - UE3 는 이제 델리게이트가 함수의 인수로서 전달되는 것을 허용합니다 .
- 인터페이스 – 인터페이스에 대한 지원이 추가되었습니다.
- 다른 클래스로부터 constant 에 접근하기:
class'SomeClass'.const.SOMECONST
- 복수 타이머 지원
- 함수 인수의 기본 값 – 선택적 함수의 인수에 대한 기본 값을 지정할 수 있게 되었습니다.
- 툴팁 지원- 속성에 마우스를 올리면, UnrealScript에서의 그 속성 선언 위에
/** tooltip text */
형태의 코멘트를 가지고 있는 경우, 에디터의 속성 창이 툴팁을 표시하게 되었습니다. - 메타데이터 지원 – 속성들을 다양한 타입의 메타데이터와 연합함으로써 게임 내 및 에디터 내의 기능성을 확장했습니다.
프로그램 구조의 예
다음의 예는 전형적이고 간단한 UnrealScript 클래스를 보여주며, UnrealScript 의 구문과 특징을 강조하고 있습니다. 이 문서는 코드와 동기화되어 있지 않으므로, 이 코드가 현재의 Unreal 소스코드와 다를 수 있다는 것을 유념하십시오.
//===================================================================== // TriggerLight. // 켜거나 끌 수 있는 광원. //===================================================================== class TriggerLight extends Light; //--------------------------------------------------------------------- // Variables. var() float ChangeTime; // 켜기에서 끄기로 바꾸는데 걸리는 시간. var() bool bInitiallyOn; // 켜기로 초기화되어 있는지. var() bool bDelayFullOn; // 지연했다가 완전히 켜기. var ELightType InitialType; // 광원의 초기 타입. var float InitialBrightness; // 처음의 밝기. var float Alpha, Direction; var actor Trigger; //--------------------------------------------------------------------- // 엔진 함수들. // gameplay를 시작할 때 호출함. function BeginPlay() { // 광원의 초기 타입을 기억하고 새 타입을 설정함. Disable( 'Tick' ); InitialType = LightType; InitialBrightness = LightBrightness; if( bInitiallyOn ) { Alpha = 1.0; Direction = 1.0; } else { LightType = LT_None; Alpha = 0.0; Direction = -1.0; } } // 시간이 경과할 때마다 호출함. function Tick( float DeltaTime ) { LightType = InitialType; Alpha += Direction * DeltaTime / ChangeTime; if( Alpha > 1.0 ) { Alpha = 1.0; Disable( 'Tick' ); if( Trigger != None ) Trigger.ResetTrigger(); } else if( Alpha < 0.0 ) { Alpha = 0.0; Disable( 'Tick' ); LightType = LT_None; if( Trigger != None ) Trigger.ResetTrigger(); } if( !bDelayFullOn ) LightBrightness = Alpha * InitialBrightness; else if( (Direction>0 &amp;amp;&amp;amp; Alpha!=1) || Alpha==0 ) LightBrightness = 0; else LightBrightness = InitialBrightness; } //--------------------------------------------------------------------- // Public states. // 트리거가 광원을 켬. state() TriggerTurnsOn { function Trigger( actor Other, pawn EventInstigator ) { Trigger = None; Direction = 1.0; Enable( 'Tick' ); } } //트리거가 광원을 끔. state() TriggerTurnsOff { function Trigger( actor Other, pawn EventInstigator ) { Trigger = None; Direction = -1.0; Enable( 'Tick' ); } } //트리거가 광원을 토글함. state() TriggerToggle { function Trigger( actor Other, pawn EventInstigator ) { log("Toggle"); Trigger = Other; Direction *= -1; Enable( 'Tick' ); } } // 트리거가 광원을 제어함. state() TriggerControl { function Trigger( actor Other, pawn EventInstigator ) { Trigger = Other; if( bInitiallyOn ) Direction = -1.0; else Direction = 1.0; Enable( 'Tick' ); } function UnTrigger( actor Other, pawn EventInstigator ) { Trigger = Other; if( bInitiallyOn ) Direction = 1.0; else Direction = -1.0; Enable( 'Tick' ); } }
이 스크립트에서 살펴 볼 주요 요소는 다음과 같습니다:
- 클래스 선언. 각 클래스는 하나의 부모 클래스를 "extends" (..로부터 파생) 하며, 함께 분포되는 객체들의 콜렉션인 "package" 에 속해 있습니다. 모든 함수 및 변수들은 클래스에 속하며, 그 클래스에 속해 있는 액터를 통해서만 접근할 수 있습니다. 시스템 전체의 글로벌 함수나 변수는 없습니다. 자세한 설명.
- 변수의 선언. UnrealScript는 가장 기초적인 C/Java의 타입, 객체 참조, 구조체 그리고 배열을 포함하여 매우 다양한 변수 타입의 세트를 지원합니다. 게다가 변수들을 디자이너들이 전혀 프로그래밍 하지 않고 UnrealEd 에서 접근할 수 있는, 편집이 가능한 속성으로 변경할 수 있습니다. 이 속성들은
var
대신var()
구문을 사용해서 지정됩니다. 자세한 설명.
- 함수들. 함수는 매개변수의 목록을 취할 수 있으며, 선택적으로 값을 반환합니다. 함수는 로컬 변수를 가질 수 있습니다. 어떤 함수들은 Unreal 엔진 자체에 의해 호출되며 (예: BeginPlay), 어떤 함수들은 다른 곳에서 다른 스크립트 코드로부터 호출됩니다 (예:Trigger). 자세한 설명.
- 코드.
for
,while
,break
,switch
,if
등 C 와 Java 의 표준 키워드가 모두 지원됩니다. UnrealScript 에서도 괄호와 세미콜론이 C, C++ 및 Java 에서처럼 사용됩니다.
- 액터 및 객체 참조. 함수가 객체 참조를 사용하여 다른 객체 내에서 호출되는 여러 경우를 볼 수 있습니다. 자세한 설명
- 키워드 "state". 이 스크립트는 다수의 "state" 를 정의하고 있습니다. State 는 액터가 그 상태에 있을 때만 집행되는 함수, 변수 그리고 코드의 그룹입니다. 자세한 설명
- UnrealScript 에서는 키워드, 변수의 이름, 함수 그리고 객체의 이름에 대소문자를 구분하지 않는다는 점에 유의하십시오. UnrealScript 에서는
Demon
,demON
과demon
이 모두 같은 것을 가리킵니다.
Unreal 가상 머신
Unreal 가상 머신은 서버, 클라이언트, 렌더링 엔진 그리고 엔진 지원 코드 등 여러 기본 단위로 이루어져 있습니다.
Unreal 서버는 모든 gameplay 및 플레이어들과 액터들간의 상호작용을 제어합니다. 단독 플레이어 게임에서는 Unreal 클라이언트와 Unreal 서버가 같은 컴퓨터에서 실행됩니다; 인터넷 게임에서는 한 컴퓨터에서 운용되는 전용 서버가 있습니다; 이 컴퓨터에 접속되어 있는 플레이어들은 모두 클라이언트입니다.
모든 gameplay 는 도형과 액터들을 가지고 있는 자체 환경인 "level" 의 내부에서 행해집니다. UnrealServer 가 하나 이상의 레벨을 동시에 실행할 수 있는 역량을 가지고 있기는 하지만, 각 레벨은 독립적으로 작용하며 서로에게서 가려져 있습니다: 액터는 레벨 사이를 이동할 수 없으며, 한 레벨의 액터가 다른 레벨의 액터와 교신할 수 없습니다.
한 맵에서의 각 액터는 플레이어의 콘트롤 아래 있거나 스크립트의 콘트롤 아래 있습니다 (네트웍 게임에서는 많은 플레이어가 있을 수 있습니다). 액터가 스크립트의 콘트롤 아래 있을 경우에는, 그 스크립트에서 액터가 이동하고 다른 액터와 상호작용하는 방법을 완전히 정의합니다.
세계 내에서 이 모든 액터들이 돌아다니고, 스크립트가 집행되며, 이벤트가 일어나는 것을 보고, 여러분은 한 UnrealScript 에서의 집행의 흐름을 어떻게 이해해야 할지에 대한 의문을 갖게 될 지 모릅니다. 이에 대한 대답은 다음과 같습니다:
Unreal 은 시간 관리를 위해 gameplay 의 매 초를 "Tick" 으로 나눕니다. Tick 은 레벨 내의 액터들이 업데이트 되는 시간의 최소 단위 입니다. 한 tick 은 대체로 100 분의 1 초에서 10 분의 1 초 사이입니다. Tick 시간은 CPU 의 파워에 의해서만 제한됩니다; 빠른 컴퓨터일수록 틱의 기간이 짧습니다.
UnrealScript 의 일부 명령은 집행에 0의 tick 이 소요되며 (즉, 게임 시간 경과가 전혀 없이 집행되며), 다른 것들에는 여러 tick 이 소요됩니다. 게임 시간의 경과가 요구되는 함수들은 "잠재적 함수" 라고 불립니다. 잠재적 함수의 몇 가지 예에는 Sleep, FinishAnim, 그리고 MoveTo 가 있습니다. UnrealScript 에서 잠재적 함수는 오직 상태 내의 코드 (소위 “상태 코드”) 로부터만 호출되며, 함수 (상태 내에서 정의된 함수 포함) 내의 코드로부터는 호출되지 않습니다.
액터가 잠재적 함수를 집행하는 동안, 그 액터의 상태 집행은 잠재적 함수의 집행이 끝날 때까지는 계속되지 않습니다. 그러나 다른 액터 또는 VM 은 그 액터 내의 함수를 호출할 수 있습니다. 최종 결과는 모든 UnrealScript 함수는 언제든지, 잠재적 함수가 계류중일 동안에도, 호출될 수 있다는 것입니다.
종래의 프로그래밍 관점에서 보면, UnrealScript 는 마치 레벨 내의 각 액터가 고유의 집행 “스레드”를 가지고 있는 것처럼 작용합니다. Unreal 은 내부적으로는 Windows 스레드를 사용하지 않습니다. 이것이 매우 비능률적일 것이기 때문입니다 (Windows 95 와 Windows NT 는 수천개의 동시 스레드를 능률적으로 처리하지 않습니다). UnrealScript 는 그 대신 스레드를 시뮬레이트 합니다. 이 사실은 UnrealScript 코드에서 명백하지만, UnrealScript 와 상호작용하는 C++ 코드를 작성할 경우에는 매우 분명해집니다.
모든 UnrealScript 는 서로에게 독립적으로 집행됩니다. 레벨 내에서 100 개의 괴물들이 걸어 돌아다니고 있다면, 이 100 개의 괴물들에 대한 스크립트들이 매 "Tick" 마다 독자적으로 동시에 집행됩니다.
객체의 계층구조
UnrealScript 로 작업을 시작하기 전에, Unreal 내 객체들의 고급 수준 관계를 이해하는 것이 중요합니다. Unreal 이 대부분의 다른 게임들과 가장 두드러지게 다른 점은 이 아키텍처입니다: Unreal 은 객체 그래프, 직렬화, 객체의 수명 그리고 polymorphism (다형성) 등 고급 수준 객체 지향 개념들의 지원으로 잘 정의된 객체 모델을 가지고 있다는 점에서 (COM/ActiveX 와 매우 비슷하게) 순수하게 객체 지향적입니다. 역사적으로 모든 게임들은 주요 기능이 하드코드되고 객체 레벨에서 확장이 불가능하도록, 획일적으로 디자인 되었습니다. Doom 과 Quake 같은 많은 게임들이 콘텐츠 레벨에서 매우 확장성이 있는 것으로 판명되기는 했지만 말입니다. Unreal 의 객체 지향 형식에는 중요한 이점이 한 가지 있습니다: 이는 주요 새 기능성 및 객체 타입이 런타임에 Unreal 에 추가될 수 있다는 점입니다. 그리고 이 확장은 (예를 들자면) 한 꾸러미의 기존 코드를 변경하는 대신 하위클래스화의 형태를 취합니다. 이 확장성의 형태는 Unreal 커뮤니티로 하여금 모두 상호연동하는 Unreal 의 강화 기능을 작성하는 것을 장려하는, 지극히 강력한 것입니다.
Object 는 Unreal 내 모든 객체의 부모 클래스입니다. 모든 것들이 Object 에서 파생되기 때문에, 어디에서나 Object 클래스 내에 있는 모든 함수들에 접근할 수 있습니다. Object 는 abstract 기초 클래스이므로, 그 자체는 전혀 유용한 일을 하지 않습니다. 모든 기능성은 Texture (텍스처 맵), TextBuffer (텍스트의 덩어리), 그리고 Class (다른 객체의 클래스를 설명하는) 등의 하위클래스에 의해 제공됩니다.
Actor (extends Object) 는 Unreal 내 모든 자립형 게임 객체들의 부모 클래스입니다. Actor 클래스는 액터가 돌아다니고, 다른 액터들과 상호작용하고, 환경에 영향을 주고, 게임에 관련된 다른 유용한 일들을 하는데 필요한 모든 기능성을 함유하고 있습니다.
Pawn (extends Actor) 은 고급 수준의 AI 및 플레이어 콘트롤을 감당하는 Unreal 내 모든 창조물 및 플레이어들의 부모 클래스입니다.
Class (extends Object) 는 객체의 클래스를 설명하는 특별한 종류의 객체입니다. 클래스가 객체이고, 객체가 어떤 객체를 설명한다는 것이 처음에는 혼동스러워 보일지 모릅니다. 그러나 이 개념은 논리적인 것이며 여러분이 Class 객체를 다루어야 할 경우가 많습니다. 예를 들어 UnrealScript 에서 새 액터를 스폰할 때, 그 새 액터의 클래스를 Class 객체로 지정할 수 있습니다.
UnrealScript 를 사용해서 어떠한 Object 클래스에 대한 코드라도 작성할 수 있지만, 99% 의 경우 Actor 에서 파생된 클래스에 대한 코드를 쓰게 될 것입니다. 유용한 UnrealScript 기능성의 대부분은 게임과 관련된 것이며 액터들을 취급하는 것입니다.
클래스
각 스크립트는 정확히 하나의 클래스에 해당되며, 스크립트는 클래스, 클래스의 부모 그리고 그 클래스와 관계있는 모든 추가 정보를 선언하는 것으로 시작됩니다. 가장 간단한 클래스의 형태는 다음과 같습니다:
class MyClass extends MyParentClass;
여기서 저는 "MyParentClass" 의 기능성을 물려받는, "MyClass" 라는 이름의 새 클래스를 선언하고 있습니다. 또, 이 클래스는 "MyPackage" 라는 이름의 패키지 안에 들어 있습니다.
각 클래스는 그 부모 클래스의 변수, 함수 그리고 상태를 모두 물려 받습니다. 클래스는 그 다음에 새 변수의 선언 추가, 새 함수 추가 (또는 기존 함수 오버라이드) 그리고 새 상태를 추가 (또는 기존의 상태에 기능 추가) 할 수 있습니다.
UnrealScript 에서 클래스 디자인의 전형적인 접근방식은, 필요한 모든 기능성을 가지고 있는 기존의 클래스 (예: 괴물들의 베이스 클래스인 Pawn 클래스) 를 확장하는 새 클래스(예: Minotaur 괴물) 을 만드는 것입니다. 이 방식을 따르면 절대로 시간과 노력을 낭비할 필요가 없습니다 – 커스터마이즈 할 필요가 없는 기존의 기능을 모두 유지하면서 커스터마이즈 하고 싶은 새 기능을 간단히 추가할 수 있습니다. 이 접근방식은 Unreal 에서 AI를 구현하는데 특히 효과적입니다. 내장된 AI 시스템이 여러분 고유의 창조물에 대해 구성 요소로서 사용할 수 있는, 놀라운 양의 기본적인 기능성을 제공하기 때문입니다.
클래스 선언은 그 클래스에 영향을 미치는 다수의 선택적 지정자를 취할 수 있습니다:
- Native(PackageName)
- "이 클래스는 보이지 않는 곳에서 C++ 지원을 사용한다" 는 것을 나타냅니다. Unreal은 native 클래스들이 .EXE 에 C++ 구현을 포함할 것을 기대합니다. Native 클래스만이 native 함수를 선언하거나 native 인터페이스를 구현할 수 있습니다. Native 클래스들은 언제나 다른 native 클래스로부터 파생되어야 합니다. Native 클래스들은 스크립트의 변수들 및 지정한 함수들과 상호작용 하는데 필요한 glue 와 함께, 자동 생성 C++ 헤더 파일을 만들어냅니다. 기본으로, PackageName 은 해당 스크립트가 들어있는 패키지를 말합니다. 예를 들어 클래스가 Engine 패키지 내에 있다면, 결과물인 자동 생성 헤더의 이름은 EngineClasses.h 이 됩니다.
- NativeReplication
- 이 클래스에 대한 변수 값의 복제가 C++ 구현으로 처리된다는 것을 나타냅니다. Native 클래스에서만 유효합니다.
- DependsOn(ClassName[,ClassName,...])
- ClassName이 이 클래스에 앞서 컴파일된다는 것을 나타냅니다. ClassName으로는 반드시 같은 패키지 (또는 이전의 패키지) 내의 클래스를 지정해야 합니다. 한
DependsOn
줄에서 콤마로 분리하거나, 각 클래스마다 별도의DependsOn
줄을 사용하여 여러 개의 종속 클래스를 지정할 수 있습니다. - Abstract
- 클래스를 "abstract 기본 클래스" 로서 선언합니다. 이 클래스는 그 자체로는 아무런 의미가 없기 때문에, 사용자들이 UnrealEd 에서 이 클래스의 액터를 세계에 추가하거나, 게임을 하는 동안 이 클래스의 인스턴스를 만드는 일을 못하게 합니다. 예를 들어 기본 클래스 "Actor" 는 abstract 인 반면, 하위클래스 "Ladder" 는 abstract 이 아닙니다 — 여러분은 Ladder 를 세계에 배치할 수 있지만 Actor 를 세계에 배치할 수는 없습니다. 이 키워드는 본연의 자식 클래스에는 전파되지만 스크립트 자식 클래스에는 전파되지 않습니다.
- Deprecated
- 이 클래스의 모든 객체들이 로드되지만 저장되지는 않게 합니다. 레벨 디자이너들이 에디터에서 맵을 로드할 때 폐기된 액터의 인스턴스가 배치되면 경고를 내보냅니다. 이 키워드는 자식 클래스들에 전파됩니다.
- Transient
- "이 클래스에 속하는 객체들은 절대로 디스크에 저장되면 안됨”을 말합니다. players 또는 windows 처럼 원래 지속적이지 않은 특정한 종류의 native 클래스와 함께일 때만 유용합니다. 이 키워드는 자식 클래스들에 전파됩니다; 자식 클래스들은
NotTransient
키워드를 사용하여 이 플래그를 오버라이드 할 수 있습니다. - NonTransient
- 기본 클래스로부터 물려받은
Transient
키워드를 무효로 합니다. - Config(IniName)
- 이 클래스가 .ini 에 데이터를 저장하는 것이 허용된다는 것을 가리킵니다. 클래스에 구성이 가능한 변수 ("config" 또는 "globalconfig" 와 함께 선언된)가 있으면 이 변수들이 지정한 구성 파일에 저장되도록 합니다. 이 플래그는 모든 자식 클래스에 전파되며, 무효화할 수 없습니다. 그렇지만 자식 클래스는
Config
키워드를 재선언하고 다른 IniName 을 지정함으로써 .ini 파일을 변경할 수 있습니다. 보통 IniName 은 데이터를 저장해 넣을 .ini 파일의 이름을 명기하지만, 여러가지 이름에는 다음과 같이 특별한 의미가 있습니다:- Config(Engine): 게임의 이름 뒤에 "Engine.ini" 가 이어지는 Engine 구성 파일을 사용합니다. 예를 들어 ExampleGame 의 엔진 구성 파일 이름은 ExampleEngine.ini가 됩니다.
- Config(Editor): 게임의 이름 뒤에 "Editor.ini"가 이어지는 Editor 구성 파일을 사용합니다. 예를 들어 ExampleGame 의 에디터 구성 파일 이름은 ExampleEditor.ini 가 됩니다.
- Config(Game): 게임의 이름 뒤에 "Game.ini" 가 이어지는 Game 구성 파일을 사용합니다. 예를 들어 ExampleGame 의 게임 구성 파일 이름은 ExampleGame.ini 가 됩니다.
- Config(Input): 게임의 이름 뒤에 "Input.ini" 가 이어지는 Input 구성 파일을 사용합니다. 예를 들어 ExampleGame 의 입력 구성 파일 이름은 ExampleInput.ini 가 됩니다.
- PerObjectConfig
- 이 클래스에 대한 구성 정보는 객체별로 저장되고, 각 객체는 [ObjectName ClassName] 의 포맷으로 객체의 이름을 따른 .ini 파일 내에 섹션을 가지고 있습니다. 이 키워드는 자식 클래스에 전파됩니다.
- PerObjectLocalized
- 이 클래스의 현지화된 데이터는 각 객체마다 정의되며, 각 객체는 [ObjectName ClassName] 의 포맷으로 객체의 이름을 따른 현지화 파일 내에 섹션을 가지고 있습니다. 이 키워드는 자식 클래스에 전파됩니다.
- EditInlineNew
- 에디터. 이 클래스의 객체들이 UnrealEd 속성 창으로부터 작성될 수 있음을 나타냅니다 (기존의 객체에 대한 참조만이 속성창을 통해 배정되는 것이 기본 행태입니다). 이 플래그는 모든 자식 클래스에 전파됩니다; 자식 클래스는
NotEditInlineNew
키워드를 사용하여 이 플래그를 오버라이드 할 수 있습니다. - NotEditInlineNew
- 에디터. 기본 클래스로부터 물려받은
EditInlineNew
키워드를 무효로 합니다.EditInlineNew
를 사용하는 부모 클래스가 없는 경우에는 효과가 없습니다. - Placeable
- 에디터. 이 클래스가 UnrealEd 에서 작성되어 레벨, UI 의 장면, 또는 키스멧 창에 배치될 수 있다는 것을 나타냅니다 (클래스의 타입에 따라). 이 플래그는 모든 자식 클래스에 전파됩니다; 자식 클래스는
NotPlaceable
키워드를 사용하여 이 플래그를 오버라이드 할 수 있습니다. - NotPlaceable
- 에디터. 기본 클래스로부터 물려받은
Placeable
키워드를 무효로 합니다. 이 클래스가 UnrealEd 에서 레벨 등에 배치될 수 없음을 나타냅니다. - HideDropDown
- 에디터. 이 클래스가 UnrealEd 속성 창의 콤보 박스에 나타나지 못하도록 합니다.
- HideCategories(Category[,Category,...])
- 에디터. 이 클래스의 객체들에 대해 UnrealEd 의 속성 창에서 숨겨져야 할 하나 또는 그 이상의 카테고리를 명시합니다. 카테고리가 없이 선언된 변수들을 숨기려면, 그 변수를 선언한 클래스의 이름을 사용합니다.
- ShowCategories(Category[,Category,...])
- 에디터. 기본 클래스로부터 물려받은
HideCategories
키워드를 무효로 합니다. - AutoExpandCategories(Category[,Category,...])
- 에디터. 이 클래스의 객체들에 대해 UnrealEd 의 속성 창에서 자동으로 확장되어야 할 하나 또는 그 이상의 카테고리를 명시합니다. 카테고리가 없이 선언된 변수들을 자동 확장하려면, 그 변수를 선언한 클래스의 이름을 사용합니다.
- Collapsecategories
- 에디터. 이 클래스의 속성들이 UnrealEd 의 속성 창에서 카테고리로 그룹지어져서는 안된다는 나타냅니다. 이 키워드는 자식 클래스에 전파됩니다; 자식 클래스는
DontCollapseCategories
키워드를 사용하여 이 플래그를 오버라이드 할 수 있습니다. - DontCollapseCategories
- 에디터. 기본 클래스로부터 물려받은
CollapseCatogories
키워드를 무효로 합니다. - Within ClassName
- 고급. 이 클래스의 객체들은 ClassName의 인스턴스 없이는 존재할 수 없다는 것을 나타냅니다. 이 클래스의 객체를 만들려면 반드시 ClassName의 인스턴스를
Outer
객체로서 지정해야 합니다. 이 키워드는 반드시 클래스 자체의 선언 뒤에 첫번째로 와야 합니다. - Inherits(ClassName[,ClassName,...])
- 고급. 복수의 상속에 대해 사용되며 추가의 기본 클래스를 지정합니다. 한
Inherits
줄에서 콤마로 분리하거나, 각 기본 클래스마다 별도의nherits
줄을 사용하여 복수의 기본 클래스를 지정할 수 있습니다. Native 클래스에 대해서만 유효합니다. UObject 에서 파생된 두 개의 클래스로부터의 복수 상속은 지원되지 않습니다. - Implements(ClassName[,ClassName,...])
- 고급. 이 클래스에서 구현할 하나 또는 그 이상의 인터페이스 클래스를 명시합니다. 한
Implements
줄에서 콤마로 분리하거나, 각 인터페이스 클래스마다 별도의Implements
줄을 사용하여 복수의 인터페이스 클래스를 지정할 수 있습니다. Native 클래스만이 native 인터페이스를 구현할 수 있습니다. - NoExport
- 고급. 이 클래스의 C++ 선언이 스크립트 컴파일러에 의해 자동생성된 C++ 헤더 파일에 포함되면 안된다는 것을 나타냅니다. C++ 클래스 선언은 반드시 별도의 헤더 파일에 수동으로 정의되어야 합니다. native 클래스에 대해서만 유효합니다.
변수
변수의 타입
내장된 타입들
다음은UnrealScript에서 인스턴스 변수를 선언하는 몇 가지 예입니다 :
var int a; // "A" 라는integer 변수 선언. var byte Table[64]; // "Table" 이라는 64 바이트의 정적 배열 선언. var string PlayerName; // "PlayerName" 이라는 문자열 변수 선언. var actor Other; // Actor 인스턴스에 참조로 배정될 수 있는 변수 선언. var() float MaxTargetDist; // "MaxTargetDist" 라는 float 변수를 선언하고, 그 값이 // UnrealEd 속성창에서 변경되는 것을 허용함.
UnrealScript 에서 변수들은 두 종류의 장소에 나타날 수 있습니다: 객체 전체에 적용되는 인스턴스 변수들은 클래스 선언이나 구조체의 선언 바로 뒤에 나타압니다. 로컬 변수들은 함수 내에 나타나며, 그 함수가 집행되는 동안에만 활성적입니다. 인스턴스 변수들은 키워드 var
와 함께 선언됩니다. 로컬 변수들은 아래와 같이 키워드 local
과 함께 선언됩니다:
function int Foo() { local int Count; Count = 1; return Count; }
다음은 UnrealScript에서 지원되는 내장된 변수 타입들입니다:
- byte:
0
에서255
범위의 1바이트 값. - int: 32-비트 integer 값.
- bool: boolean 값:
true
아니면false
. - float: 32-비트부동 소수.
- string: 캐릭터 문자열. (Unreal의 문자열 참고)
- constant: 변경될 수 없는 변수.
- enumeration: 사전 정의된 여러개의 명명된 integer 값들 중 하나를 취할 수 있는 변수. 예를 들면, Actor 스크립트에서 정의된 ELightType 열거형은 동적 광원을 설명하며,
LT_None
,LT_Pulse
,LT_Strobe
등의 값 중 하나를 취합니다 .
집합적 데이터 타입
- array<Type>:
Type
의 가변 길이 배열. - struct: C의 structure 와 비슷한 UnrealScript 의 struct 은 서브 변수를 함유하는 새 변수 타입을 작성할 수 있도록 해줍니다.예를 들어, 두 개의 흔히 사용되는 Unreal 의 struct 들은 X, Y, 및 Z 컴포넌트로 이루어진
vector
, 그리고 pitch, yaw, 및 roll 컴포넌트로 이루어진rotator
입니다.(Script Structs 를 참고하십시오)
Unreal 타입
- Name: Unreal 아이템의 이름(함수, 상태, 클래스 등의 이름). Name 들은 글로벌 name 테이블에 인덱스로서 보존됩니다. Name 들은 최대 64 캐릭터의 단순한 문자열에 해당됩니다. Name 들은 일단 작성되면 변경되지 않는다는 점에서 문자열과 다릅니다 (자세한 정보는 Unreal 의 문자열 을 참고하십시오).
- Object와 Actor의 참조: 세계에서 다른 객체 또는 액터를 참조하는 변수. 예를 들면, Pawn 클래스는 그 폰이 어느 액터의 공격을 시도할 것인지 지정하는 "Enemy" 액터 참조를 가지고 있습니다. 객체 및 액터 참조는 다른 액터의 변수 및 함수에 접근하는 것을 가능하게 해주는, 매우 강력한 도구입니다. 예를 들면, Pawn 스크립트에서 Enemy.Damage(123) 를 작성, 적의 Damage 함수를 호출하여 적에게 피해를 줄 수 있습니다. 객체의 참조는 또한
None
이라는 특별한 값을 가질 수 있습니다. 이것은 C에서의NULL
포인터와 같은 것입니다: 이는 "이 변수는 어떠한 객체도 참조하지 않는다" 는 의미입니다. - Delegate: UnrealScript 함수에의 참조를 보유합니다.
변수 지정자
변수들은 또한 그 변수를 좀 더 설명하는, const
와 같은 지정자를 추가로 가질 수 있습니다. 사실 범용 프로그래밍 언어에서는 보기 힘든 지정자들이 많이 있습니다. 이는 주로 UnrealScript 가 많은 게임 및 환경 특정의 개념을 자연스럽게 지원할 수 있기를 원한 결과입니다:
- config
- 이 변수는 구성 가능한 것으로 만들어집니다. 현재의 값은 ini 파일에 저장되고 작성되었을 때 로드됩니다. 기본 속성에서는 값이 주어지지 않습니다. const를 내포합니다.
- globalconfig
- 하위 클래스에서 이를 오버라이드 할 수 없다는 점을 제외하고config와 똑같은 작용을 합니다. 기본 속성에서는 값이 주어지지 않습니다. const를 내포합니다.
- localized
- 이 변수의 값은 정의된 현지화 값을 가지게 됩니다. 대개 문자열에 대해 사용됩니다. const 를 내포합니다. 이것에 대한 더 많은 정보를 원하시면 현지화 참조 와 Unreal의 문자열 을 읽어 보십시오.
- const
- 변수의 컨텐츠를 constant 로 취급합니다. UnrealScript 에서는, const 변수의 값을 읽을 수 있지만, 거기에 쓰기는 할 수 없습니다. "Const" 는, 액터의 (MoveActor 함수를 호출함으로서만 설정되는) Location 같은, 엔진에 업데이트 책임이 있는 변수들과 UnrealScript 에서는 안전하게 업데이트 할 수 없는 변수들에 대해서만 사용됩니다.
- private
- 이 변수는 private 이며, 오직 클래스의 스크립트에 의해서만 접근됩니다. 어떤 다른 클래스 (하위 클래스 포함) 도 이에 접근할 수 없습니다.
- protected
- 클래스 및 그 하위 클래스만이 이 변수에 접근할 수 있으며, 다른 클래스는 접근할 수 없습니다.
- repnotify
- 이 속성의 값이 복제를 통해 수신되었을 경우, Actor 는 (ReplicatedEvent 함수를 통해) 통지를 받아야 합니다.
- deprecated
- 이 변수는 가까운 장래에 폐기될 것이며, 에디터에서 더 이상 이용할 수 없게 되어야 한다는 것을 나타냅니다 . Deprecated 속성들은 로드되지만 저장되지는 않습니다.
- instanced
- 객체 속성에 한함. 이 클래스의 인스턴스가 만들어지면, 이 변수에 배정된 객체의 고유 복사본이 기본으로 주어집니다. 클래스의 기본 속성에서 정의된 하위 객체를 인스턴스화 하는데 사용됩니다.
- databinding
- 이 속성은 데이터 저장소 시스템에 의해 조작될 수 있습니다.
- editoronly
- 이 속성의 값은 UnrealEd 또는 커맨드렛을 실행할 때만 로드됩니다. 게임 도중에는 이 속성의 값이 버려집니다.
- notforconsole
- 이 속성의 값은 PC 에서 실행할 때만 로드됩니다. 콘솔에서는 이 속성의 값이 버려집니다.
- editconst
- 에디터. 이 변수는 UnrealEd 에 표시되지만 편집된 것이 아닙니다. editconst 인 변수는 내재적으로는 "const" 가 아닙니다.
- editfixedsize
- 에디터. 동적 배열에 대해서만 유용합니다. 이는 사용자가 UnrealEd 속성 창에서 배열의 길이를 변경하는 것을 방지합니다.
- editinline
- 에디터. 사용자가 이 변수에 의해 참조된 객체의 속성을 UnrealEd 의 속성 검사기 내에서 편집하는 것을 허용합니다 (객체 참조의 배열을 포함, 객체 참조에 대해서만 유용함).
- editinlineuse
- 에디터. editinline 과 관련된 행태에 덧붙여, 에디터에서 이 객체 참조 옆에 "Use" 버튼을 추가합니다.
- noclear
- 에디터. 이 객체 참조가 에디터에서 None 으로 설정되는 것을 허용합니다.
- interp
- 에디터. 이 값이 Matinee 에서 Float 또는 Vector 속성 트랙에 의해 수시로 끌어내어진다는 것을 나타냅니다.
- input
- 고급. 변수가 Unreal 의 입력 시스템에 접근할 수 있도록 하여, 입력 (버튼 누르기 및 조이스틱 동작 등) 이 직접 그 변수에 맵되도록 합니다. "byte" 와 "float" 타입의 변수에만 해당됩니다.
- transient
- 고급. 이 변수는 임시로 사용하기 위한 것이며 객체의 지속적인 상태의 일부가 아니라는 것을 선언합니다. Transient 변수들은 디스크에 저장되지 않습니다. Transient 변수들은 객체가 로드되었을 때 그 변수에 대한 클래스의 기본 설정값으로 초기화 됩니다.
- duplicatetransient
- 고급. (StaticDuplicateObject 를 통해) 객체의 바이너리 복제를 만들 때는 이 변수의 값이 클래스의 기본 설정값으로 재설정되어야 한다는 것을 나타냅니다.
- noimport
- 고급. T3D 텍스트를 임포트할 때는 이 변수를 건너 뛰어야 한다는 것을 나타냅니다. 다시 말해, 객체를 임포트 또는 복사하기/붙여넣기 할 때 이 변수의 값이 새 객체 인스턴스에 건네지지 않습니다.
- native
- 고급. 이 변수는 UnrealScript 가 아니라 C++ 코드에 의해 로드되고 저장되었다는 것을 선언합니다.
- export
- 고급. 객체의 속성 (또는 객체의 배열)에 대해서만 유용합니다. 객체가 복사될 때나 (복사하기/붙여넣기를 위해) T3D로 익스포트 될 때, 단지 객체 참조 자체를 출력하는 것과는 대조적으로, 이 속성에 배정된 객체가 하위 객체 블록으로서 온전히 익스포트되어야 한다는 것을 나타냅니다.
- noexport
- 고급. native 클래스에 대해서만 유용합니다. 이 변수가 자동 생성된 클래스 선언에 포함되어서는 안됩니다.
- nontransactional
- 고급. 이 변수 값의 변경이 에디터의 실행취소/재실행 내역에 포함되지 않는다는 것을 나타냅니다.
- pointer{type}
- 고급. 이 변수는 type에의 포인터입니다( type은 선택적임). 구문은: pointer varname{type}입니다.
- init
- 고급. 이 속성은 FStringNoInit 이나 TArrayNoInit 이 아니라 FString 또는 TArray 로서 헤더 파일에 익스포트 되어야 합니다. native 클래스에서 선언된 문자열이나 동적 배열에만 적용됩니다. 'Init' 속성에는 기본 값이 주어져서는 안됩니다. 객체가 만들어질 때 그 기본값이 지워질 것이기 때문입니다.(Unreal 의 문자열 과 Native 문자열 참고)
- repretry
- 고급. 구조체의 속성에 한함. 속성의 완전 전송에 실패하는 경우(예를 들어, 네트워크에 걸쳐 객체의 참조를 직렬화 하는 것이 아직 가능하지 않을 경우) 그 속성의 복제를 재시도합니다. 간단한 참조에 대해서는 이것이 기본으로 설정되어 있지만, 구조체의 경우 이는 대역폭의 비용 때문에 바람직하지 않을 때가 많으므로, 이 플래그가 지정되지 않은 한 무효화 됩니다.
- out
- 이 지정자는 함수의 매개변수에 대해서만 유효합니다. 자세한 내용은 함수 를 참고하십시오.
- coerce
- 이 지정자는 함수의 매개변수에 대해서만 유효합니다. 자세한 내용은 함수 를 참고하십시오.
- optional
- 이 지정자는 함수의 매개변수에 대해서만 유효합니다. 자세한 내용은 함수 를 참고하십시오.
- skip
- 이 지정자는 연산자 함수의 매개변수에 대해서만 유효합니다. && 및 || 같은 논리 연산자들에 대해서만 사용됩니다. 식의 결과가 이미 결정된 경우에는 평가를 금지합니다. 예: FALSE && ++b==10 (자세한 정보).
편집 기능
UnrealScript 에서는 인스턴스 변수를 "편집 가능" 으로 만들어, 사용자가 UnrealEd 에서 변수의 값을 편집할 수 있도록 할 수 있습니다. 이 방법은 UnrealEd 의 "Actor Properties" 대화상자에 있는 콘텐츠 전부에 대해 적용됩니다: 이 대화상자에 있는 것들은 모두 editable 로 선언된 UnrealScript 변수들입니다.
editable 변수는 다음과 같은 구문으로 선언합니다:
var() int MyInteger; // 기본 카테고리에서 편집 가능한 integer 를 선언. var(MyCategory) bool MyBool; // "MyCategory" 에서 편집 가능한 integer 를 선언.
변수를 editconst
로서 선언할 수도 있습니다. 이는 이 변수가 UnrealEd 에서 보여야 하지만 편집은 할 수 없음 을 의미합니다. 이것은 오직 에디터에서 변수가 변경되는 것을 금지하며, 스크립트에서의 변경을 금지하는 것은 아니라는 점을 유념하십시오. 참으로 const
이지만 그래도 에디터에서 보이는 변수를 원한다면, 그 변수를 const editconst
로 선언해야 합니다:
// MyBool 은 UnrealEd 에서 보이지만 편집할 수 없음 var(MyCategory) editconst bool MyBool; // MyBool 은 UnrealEd 에서 보이지만 편집할 수 없으며 // 스크립트에서 변경할 수 없음 var(MyCategory) const editconst bool MyBool; // MyBool 은 UnrealEd 에서 보이고 설정될 수 있지만 //스크립트에서 변경할 수 없음 var(MyCategory) const bool MyBool;
배열
배열은 다음과 같은 구문을 사용해서 선언됩니다:
var int MyArray[20]; // 20개의 int를 가진 배열을 선언.
UnrealScript 는 오직 1차원 배열만을 지원합니다. 그렇지만 여러분이 직접 행/열을 계산함으로써 다차원 배열을 시뮬레이트 할 수 있습니다. 동적 배열에 관한 정보는 아래의 고급 언어 기능 섹션을 참고하십시오.
Structs (구조체)
UnrealScript 의 구조체는 한 웅큼의 변수들을 구조체라고 불리우는 새로운 종류의 수퍼 변수 안으로 한데 꾸려넣는 수단입니다. UnrealScript 의 구조체는 변수, 배열 및 다른 구조체를 함유할 수 있다는 점에서 C 의 구조체와 매우 흡사합니다. 그러나 UnrealScript 의 구조체는 함수를 함유할 수 없습니다.
구조체는 다음과 같이 선언됩니다:
// 3D 공간에서의 포인트 또는 방위 벡터. struct Vector { var float X; var float Y; var float Z; };
일단 구조체가 선언되면, 그 구조체 타입의 특정 변수들을 선언할 수 있습니다:
// Vector 타입의 여러 변수들을 선언. var Vector Position; var Vector Destination;
구조체의 컴포넌트에 접근하려면 다음과 같은 코드를 사용합니다.
function MyFunction() { Local Vector A, B, C; // Vector 들을 합산함. C = A + B; // Vector의 x 컴포넌트들만 합산. C.X = A.X + B.X; // Vector C를 함수에 전달. SomeFunction( C ); // 특정 Vector 컴포넌트들을 함수에 전달. OtherFunction( A.X, C.Z ); }
Struct 변수로 다른 변수들로 할 수 있는 것을 무엇이든지 할 수 있습니다: 구조체에 변수를 배당할 수 있으며, 이를 함수에 전달할 수 있고, 그 컴포넌트에 접근할 수 있습니다.
Object 클래스에는 Unreal 전반에 걸쳐 사용되는 여러 개의 구조체가 정의되어 있습니다. 이것들은 스크립트의 기본적인 기본 단위이므로, 그 작용에 대해 잘 알아두어야 합니다:
- Vector
- 공간에서의 독특한 3D 포인트 또는 벡터입니다. X, Y, 그리고 Z 컴포넌트를 가지고 있습니다.
- Plane
- 3D 공간에서의 독특한 평면을 정의합니다. Plane 은 그 X, Y, 그리고 Z 컴포넌트 (표준화되었다는 것을 전제로) 에 더하여 W 컴포넌트에 의해 정의됩니다. W 컴포넌트는 (평면에서 원점까지의 최단거리인) 평면의 노멀과 함께 원점으로부터 평면까지의 거리를 나타냅니다.
- Rotator
- 독특한 직각 좌표 시스템을 정의하는 회전. rotator 에는 Pitch, Yaw, 그리고 Roll 컴포넌트가 들어 있습니다.
- Coords
- 3D 공간에서의 임의 좌표 시스템.
- Color
- RGB 색채의 값.
- Region
- 레벨 내의 독특한 볼록 지점을 정의합니다.
구조체 지정자
구조체에는 또한 그 구조체의 모든 인스턴스에 영향을 주는 몇가지 지정자가 있습니다.
- atomic
- 해당 구조체가 항상 하나의 단위로서 직렬화되어야 한다는 것을 가리킵니다; 이 구조체의 어느 속성이 그 기본값과 다를 경우, 구조체 내의 모든 요소들이 직렬화됩니다.
- atomicwhencooked
- 쿡된 패키지 데이터로 작업할 때만 'atomic' 플래그를 적용합니다.
- immutable
- 해당 구조체가 (디스크 공간을 줄이고 직렬화 성능을 향상하는) 바이너리 직렬화를 사용한다는 것을 나타냅니다; 패키지 버전을 올리지 않고 이 구조체의 멤버를 추가/제거하는 것은 안전하지 않습니다.
- immutablewhencooked
- 쿡된 패키지 데이터로 작업할 때만 'immutable' 플래그를 적용합니다.
- strictconfig
- 구조체의 속성에 'config/globalconfig'가 있을 경우, 이 구조체에서 'config/globalconfig' 로 표시된 속성들만 .ini 에서 읽혀진다는 것을 나타냅니다 (이 플래그가 없이는, 해당 속성이 구성 가능한 것이면 구조체 내의 모든 속성들이 구성 가능한 것이 됩니다).
Enumerations (열거형)
UnrealScript 에서 열거형은 한 무리의 키워드 “가운데 하나”를 가질 수 있는 변수를 선언하는 편리한 방법입니다. 예를 들면, actor 클래스에는 Unreal 이 액터에 적용해야 하는 물리를 설명하는 열거형 EPhysics
이 있습니다. 이 열거형은 PHYS_None
, PHYS_Walking
, PHYS_Falling
등등의, 사전 정의된 값 가운데 하나로 설정될 수 있습니다.
내부적으로 열거형은 byte 변수로서 저장됩니다. UnrealScript 의 디자인에서는 열거형이 필요한 것으로 여겨지지 않았지만, 액터의 물리 모드가 (예를 들자면) 3
으로 설정되어 있는 것 보다는 PHYS_Swimming
으로 되어 있는 것이 코드 읽기를 훨씬 더 쉽게 해줍니다.
다음은 열거형을 선언하는 견본 코드입니다.
// 3개의 값을 가지는 열거형 EColor 선언. enum EColor { CO_Red, CO_Green, CO_Blue }; // 이제 두 개의 EColor 타입 변수를 선언. var EColor ShirtColor, HatColor; // 다른 방법으로는, 다음과 같이 // 변수와 열거형을 함께 선언할 수도 있음: var enum EFruit { FRUIT_Apple, FRUIT_Orange, FRUIT_Bannana } FirstFruit, SecondFruit;
Unreal 소스에서, 저희는 항상 열거형의 값을 단순히 "Steady" 또는 "Falling" 으로 하기보다는 LT_Steady
, PHYS_Falling
등으로 선언합니다. 이는 다만 프로그래밍 스타일의 문제이며 언어의 요구사항은 아닙니다.
UnrealScript 는 그 열거형이 정의된 클래스 및 그 하위 클래스에서만 불완전한 형식의 열거형 태그 ( 예: FRUIT_Apple
)를 인식합니다. 클래스 계층의 다른어딘가에서 정의된 열거형을 참조할 필요가 있을 경우에는, 그 “완전한 이름을 사용” 해야 합니다:
FRUIT_Apple // Unreal이 이 열거형 태그를 찾을 수 없을 경우에는... EFruit.FRUIT_Apple // 이렇게 완전한 이름을 사용.
Constants (불변값)
UnrealScript에서는 거의 모든 데이터 타입에 대해 불변 문자 값을 지정할 수 있습니다:
- Integer 와 byte 의 불변값은
123
같이 간단한 숫자로 지정됩니다. Integer나 byte의 불변값을 16진수로 지정해야 한다면0x123
의 포맷을 사용하십시오. - Floating point 의 불변값은
456.789
처럼 소수로 지정됩니다. - String 의 불변값은 반드시 큰 따옴표에 둘러 싸여 있어야 합니다. 예:
"MyString"
- Name 의 불변값은 반드시 작은 따옴표에 둘러 싸여 있어야 합니다 예:
'MyName'
- Vector 의 불변값은
vect(1.0,2.0,4.0)
같이 X, Y, 그리고 Z 값을 가집니다. - Rotator 의 불변값은
Rot(0x8000,0x4000,0)
같이 Pitch, Yaw, 그리고 Roll의 값을 가집니다. None
불변값은 "객체 없음" 을 나타냅니다 ("액터 없음" 과 동등).Self
불변값은 "이 객체"("이 액터" 와 동등), 즉 스크립트가 집행되고 있는 객체를 나타냅니다.- 일반 객체의 불변값은 객체의 타입에 작은 따옴표에 둘러싸인 객체의 이름을 이어 붙임으로써 지정합니다. 예:
texture'Default'
EnumCount
는 열거형 내 요소의 수를 제공합니다. 사용 예:ELightType.EnumCount
ArrayCount
는 정적 배열 내 요소의 수를 제공합니다. 사용 예:ArrayCount(Touching)
나중에 이름으로 참조할 수 있는 불변값을 선언할 때는 키워드 "const" 를 사용합니다. 예:
const LargeNumber=123456; const PI=3.14159; const MyName="Tim"; const Northeast=Vect(1.0,1.0,0.0);
불변값은 클래스 또는 구조체 내에서 정의할 수 있습니다.
다른 클래스에서 선언된 불변값에 접근하려면, 아래의 예와 같이 "class'클래스 이름'.const. 불변값 이름" 구문을 사용합니다.
class'Pawn'.const.LargeNumber
객체 및 액터 참조 변수
액터 또는 객체를 참조하는 변수는 다음과 같이 선언합니다:
var actor A; // actor에의 참조. var pawn P; // Pawn 클래스에 있는 액터에의 참조. var texture T; // texture 객체에의 참조.
위에서 변수 "P" 는 Pawn 클래스에 있는 액터에의 참조입니다. 이러한 변수는 Pawn 의 하위 클래스에 속해있는 어떠한 액터라도 참조할 수 있습니다. 예를 들면, P 는 Brute, 또는 Skaarj, 또는 Manta 를 참조할 수 있습니다. 그것은 어떤 종류의 Pawn 이라도 상관 없습니다. 그러나 P 는 절대로 Trigger 액터를 참조할 수 없습니다 (Trigger 는 Pawn 의 하위 클래스가 아니기 때문입니다).
액터를 참조하는 변수를 가지는 것이 편리한 한 가지 예는 Pawn 이 공격하고자 하는 액터를 가리키는 Pawn 클래스의 변수 Enemy 입니다.
액터를 참조하는 변수를 가지고 있으면 그 액터의 변수에 접근할 수 있으며, 그 함수를 호출할 수도 있습니다. 다음은 그 예입니다:
// 하나의 pawn 을 참조하는 두 개의 변수 선언. var pawn P, Q; // P 를 사용하는 함수. // P 에 관한 다소의 정보를 표시함. function MyFunction() { // P 의 적을 Q 로 설정함. P.Enemy = Q; // P 가 running 애니메이션을 재생할 것을 지시. P.PlayRunning(); }
액터를 참조하는 변수들은 항상 유효한 액터 (레벨에 실제로 존재하는 액터) 를 참조하거나, 아니면 None
의 값을 가집니다. None 은 C/C++ 의 NULL
포인터와 같은 것입니다. 그러나, UnrealScript 에서는 None
참조를 사용해서 변수에 접근하고 함수를 호출하는 것이 안전합니다; 그 결과는 언제나 0입니다.
객체나 액터의 참조는 다른 액터 또는 객체를 "가리키는" 것이지, 액터 또는 객체를 “함유하는” 것이 아니라는 점을 명심하십시오. C 에서 이 객체 참조에 상당하는 것은 AActor 클래스에 있는 객체를 향하는 포인터입니다 (C 에서는 AActor* 로 표기함). 예를 들어, 세계에 Bob 과 Fred 라는, 서로 싸우고 있는 두 괴물이 있습니다. Bob 의 "Enemy" 변수는 Fred 를 "가리키고", Fred 의 "Enemy" 변수는 Bob 을 “가리킬” 것입니다.
C 의 포인터와는 달리, UnrealScript 의 객체 참조는 언제나 안전하고 절대 확실합니다. 객체 참조가 존재하지 않거나 유효하지 않은 객체를 참조한다는 것은 불가능합니다 (특별한 경우의 None
값 이외에는). UnrealScript 에서는 액터가 파괴되면 자동적으로 그 액터에 대한 모든 참조들이 None
으로 설정됩니다.
클래스 참조 변수
Unreal 에서는 액터, 텍스처 그리고 사운드가 객체인 것과 마찬가지로 클래스도 객체입니다. 클래스 객체는 "class" 라는 이름의 클래스에 속해 있습니다. 그 클래스에 속한 액터를 스폰할 수 있도록 클래스 객체에의 참조를 저장하고 싶은 경우가 흔히 있을 것입니다 (컴파일시에 어느 클래스인지 모르는 채로). 예:
var() class C; var actor A; A = Spawn( C ); // 임의의 클래스 C에 속하는 액터를 스폰.
여기서 클래스 C, 그리고 클래스 C에 속하는 객체 O (클래스 C 의 “인스턴스” 라고 일컫는)의 역할을 혼동하지 않도록 하십시오. 좀 억지스럽게 유추하자면, 클래스는 후추 분쇄기, 그 클래스의 인스턴스는 후추와 같습니다. 후추 (클래스의 객체) 를 만들기 위해 크랭크를 돌림으로써 (Spawn 함수의 호출) 후추 분쇄기(클래스)를 사용합니다...그러나 후추 분쇄기 (클래스) 는 후추 (그 클래스에 속하는 객체) 가 아니므로, 먹으려고 해서는 안됩니다!
클래스 객체를 참조하는 변수를 선언할 때, 선택적으로 class<metaclass> 구문을 사용하여 변수에 의해 참조될 수 있는 클래스를 metaclass 타입의 클래스 (그리고 그 자식 클래스) 로 제한할 수 있습니다. 예를 들어, 다음의 선언에서:
var class<actor> ActorClass;
변수 ActorClass 는 오직 "actor" 클래스를 확장하는 클래스만을 참조할 것입니다. 이것은 컴파일 타임의 타입 점검을 개선하는데 도움이 됩니다. 예를 들면, Spawn 함수는 클래스를 매개변수로서 취하지만, 이것은 주어진 클래스가 Actor 의 하위 클래스이고 class<classlimitor> 구문이 컴파일러가 요구조건을 실시하도록 하는 경우에만 의미가 있습니다.
동적 객체 캐스팅처럼, 클래스를 다음과 같이 동적으로 캐스트할 수 있습니다:
// SomeFunctionCall()의 결과를 Actor 타입의 클래스 (또는 Actor 의 하위 클래스)로 캐스트 class<actor>( SomeFunctionCall() )
표현식
배정
변수에 값을 배정하기 위해서는 다음과 같이 "=" 를 사용합니다:
function Test() { local int i; local string s; local vector v, q; i = 10; // integer 변수 i 에 값 배정. s = "Hello!"; // string 변수 s 에 값 배정. v = q; // vector q 의 값을 v 에 복사. }
UnrealScript 에서는, 함수나 그밖의 표현식에 특정 타입의 데이터 (예를 들어 "float") 가 요구되는데 사용자가 다른 데이터 타입 (예를 들어 "int)을 명시하는 경우, 컴파일러는 자동적으로 사용자가 제시한 값을 알맞은 타입으로 바꾸는 일을 시도합니다. 모든 수치 데이터 타입 (byte, int, 및 float) 의 변환은 사용자측의 수고가 전혀 없이 자동적으로 이루어집니다.
UnrealScript 는 또한 코드에서 명시적으로 타입을 변환하는 경우, 다른 여러 종류의 내장 데이터 타입을 다른 타입으로 변환할 수 있습니다.이를 위한 구문은 다음과 같습니다:
function Test() { local int i; local string s; local vector v, q; local rotator r; s = string(i); // integer i 를 string 으로 바꾸고, 이를 s 에 배정. s = string(v); // vector v 를 string 으로 바꾸고, 이를 s 에 배정. v = q + vector(r); // rotator r 을 vector 로 바꾸고, q 를 더함. }
다음은 UnrealScript 에서 사용할 수 있는, 비자동 변환의 전체 목록입니다:
- String 에서 Byte, Int, Float으로:
"123"
과 같은 string 을123
과 같은 값으로 바꾸기를 시도 합니다. String 이 값을 표시하지 않는 경우에는 결과가0
입니다. - Byte, Int, Float, Vector, Rotator 에서 String 으로: 숫자를 그 문자적 표현으로 바꿉니다.
- String 에서 Vector, Rotator 로: vector 또는 rotator 의 문자적 표현을 분석하려고 합니다.
- String 에서 Bool 로: 대소문자를 구분하지 않는 단어
"True"
또는"False"
를True
와False
로 바꿉니다; 0이 아닌 값들은 모두True
로 바꿉니다; 그밖의 것들은 모두False
입니다. - Bool 에서 String 으로: 결과는
"True"
아니면"False"
입니다. - Byte, Int, Float, Vector, Rotator 에서 Bool 로: 0이 아닌 값은
True
로, 0인 값은False
로 바꿉니다. - Bool 에서 Byte, Int, Float 으로:
True
를1
로,False
를0
으로 바꿉니다. - Name 에서 String 으로: name 을 그에 상응하는 텍스트로 바꿉니다.
- Rotator 에서 Vector 로: rotator 에 따라 “앞을” 향하고 있는 vector 를 반환합니다.
- Vector 에서 Rotator 로 : rotator 의 pitch 와 yaw 를 vector 의 방향으로 반환합니다; roll 은 0입니다.
- Object (또는 Actor) 에서 Int 로: 해당 object 에 대해 독특한 것임이 보장된 integer 를 반환합니다.
- Object (또는 Actor) 에서 Bool 로: object 가
None
이면False
를, 아니면True
를 반환합니다. - Object (또는 Actor) 에서 String으로: object의 문자적 표현을 반환합니다.
클래스 사이의 객체 참조 변환
단순 데이터 타입 사이에서 변환하는 위의 변환 기능들과 마찬가지로, UnrealScript 에서는 액터 및 객체 참조를 여러 가지 타입 사이에서 서로 변환할 수 있습니다. 예를 들면, 모든 액터들은 다른 액터를 나타내는 "Target" 이라는 이름의 변수를 가지고 있습니다. 여러분이 Target 이 "Pawn" 액터 클래스에 속해 있는지 점검할 필요가 있는 스크립트를 쓰고 있으며, 그 target 이 pawn 일 때에만 이치에 맞는 뭔가 특별한 일을 할 필요가 있다고 가정해 보십시오—예를 들어, Pawn 함수 가운데 하나를 호출할 필요가 있습니다. 이런 경우 액터 캐스트 연산자를 사용합니다. 다음은 그 예입니다:
var actor Target; //... function TestActorConversions() { local Pawn P; // Target 을 Pawn 으로 캐스트하고 그 결과를 P에 배정.Target 이 Pawn (또는 Pawn 의 하위 클래스) 이 아닌 경우, P에 배정되는 값은 None 이 됨. P = Pawn(Target); if( P != None ) { // Target이 pawn 이므로 이의 Enemy 를 Self 로 설정함. P.Enemy = Self; } else { // Target 이 pawn 이 아님. } }
액터의 변환을 수행하려면, 클래스의 이름에 이어 괄호 안에 변환하기 원하는 액터의 표현을 타이프합니다. 변환은 그것이 사리에 맞는지 아닌지에 따라 성공하거나 실패합니다. 위의 예에서 만일 Target 이 pawn 이 아니라 Trigger 객체를 참조하고 있다면, Pawn(Target) 은 "None" 을 반환할 것입니다. Trigger 가 Pawn 으로 변환될 수는 없기 때문입니다. 그러나 Target 이 Brute 객체를 참조하고 있다면, Brute 는 Pawn 의 하위 클래스이므로 이 변환은 성공적으로 Brute 를 반환할 것입니다.
그러므로, 액터 변환에는 두 가지의 목적이 있습니다: 첫째, 이것을 특정 액터 참조가 특정 클래스에 속해있는지 확인하기 위해 사용할 수 있습니다.둘째, 액터 참조를 한 클래스에서 보다 구체적인 다른 클래스로 변환하기 위해 사용할 수 있습니다. 이러한 변환은 변환하고 있는 액터에게 전혀 영향을 주지 않는다는 점을 주시하십시오— 이것들은 다만 UnrealScript 가 액터 참조를 마치 그것이 더 구체적인 타입인 것처럼 취급할 수 있도록 해줍니다. 그리고 더 파생된 클래스에서 선언된 속성 및 메소드에 접근할 수 있도록 해줍니다.
변환에 관한 또 하나의 예는 Inventory 스크립트입니다. Inventory 액터의 Owner 변수는 어떠한 Actor 라도 참조할 수 있지만 (Actor.Owner 는 Actor 타입의 변수이기 때문), 각 Inventory 액터는 Pawn 에 의해 소유되어 있습니다. 따라서 Inventory 코드에서의 공통 주제는 Owner 를 Pawn 으로 캐스트하는 것입니다. 다음은 그 예입니다:
// 파괴될 때 엔진에 의해 호출. function Destroyed() { // owner 의 재고에서 제거. if( Pawn(Owner)!=None ) Pawn(Owner).DeleteInventory( Self ); }
함수
함수의 선언
UnrealScript 에서는 새 함수를 선언하고 기존 함수의 새 버전을 작성할 수 있습니다 (함수 겹쳐쓰기). 함수는 (UnrealScript 가 지원하는 어떠한 변수 타입의) 한 개 또는 그 이상의 매개변수를 취할 수 있으며, 선택적으로 값을 반환할 수 있습니다. 대부분의 함수는 직접 UnrealScript 로 집니다. 그러나 UnrealScript 에서 호출할 수 있지만 C++ 로 구현되어 DLL 에 들어있는 함수를 선언할 수도 있습니다. Unreal 테크놀로지는 모든 가능한 조합의 함수 호출을 지원합니다: C++ 엔진이 스크립트 함수를 호출할 수 있습니다; 스크립트가 C++ 함수를 호출할 수 있습니다;그리고 스크립트가 스트립트를 호출할 수 있습니다.
다음은 간단한 함수 선언입니다. 이 함수는 vector 를 매개변수로 취해 부동 소수의 값을 반환합니다:
// vector 의 사이즈를 계산하기 위한 함수. function float VectorSize( vector V ) { return sqrt( V.X * V.X + V.Y * V.Y + V.Z * V.Z ); }
function
이라는 단어가 항상 함수 선언 앞에 옵니다. 이어서 선택적인 반환 값의 타입 (이 경우에는 float
) 이 뒤따르고, 그 다음에 함수의 매개변수 목록이 괄호 안에 들어 있습니다.
함수가 호출되면 중괄호 안에 있는 코드가 집행됩니다. 함수 내에서 로컬 변수를 선언하고(local
키워드를 사용), 어떠한 UnrealScript 코드라도 집행할 수 있습니다. 선택적인 return
키워드는 함수가 즉각 값을 반환하도록 합니다.
UnrealScript 타입은 무엇이든지(배열을 포함해서) 함수에 전달할 수 있으며, 함수는 모든 타입을 반환할 수 있습니다.
기본으로, 함수에서 선언하는 로컬 변수들은 모두 0으로 초기화됩니다.
함수의 호출은 재귀적일 수 있습니다. 예를 들어, 다음의 함수는 한 수의 계승을 계산합니다:
// 수의 계승을 계산하기 위한 함수. function int Factorial( int Number ) { if( Number <= 0 ) return 1; else return Number * Factorial( Number - 1 ); }
일부 UnrealScript 함수들은 특정 이벤트가 발생할 때마다 엔진에 의해 호출됩니다. 예를 들어, 한 액터가 다른 액터에 의해 건드려지면, 엔진은 그 액터의 Touch 함수를 호출하여 누가 그것을 건드리는지 알려줍니다. 커스텀 Touch 함수를 작성함으로써, touch 발생의 결과로서 특별한 액션을 취할 수 있습니다:
// 무언가가 이 액터를 건드릴 때 호출됨. function Touch( actor Other ) { Log( "누가 날 건드렸어!") Other.Message( "네가 나를 건드렸구나!"); }
위의 함수는 여러가지를 설명합니다. 제일 먼저, 이 함수는 Log 명령 (포맷 규칙을 제외하고는 Basic 의 "print" 명령 및 C의 "printf" 에 상당하는)을 사용해서 로그 파일에 메시지를 기록합니다. 둘째, 이 함수는 액터 Other 에 있는 "Message" 함수를 호출합니다. 다른 액터에서 함수를 호출하는 것은 액터들이 서로 소통하는 간단한 수단을 제공하기 때문에, UnrealScript, 그리고 일반적으로 Java 같은 객체 지향 언어에서는 흔히 있는 액션입니다.
함수 매개변수의 지정자
정상적으로 함수를 호출하면, UnrealScript 는 그 함수에 전달된 매개변수들의 로컬 복사본을 만듭니다. 함수가 매개변수의 일부를 변경하는 경우, 이 변경은 전달된 함수에 아무런 영향을 주지 않습니다. 다음 프로그램의 예를 보십시오:
function int DoSomething( int x ) { x = x * 2; return x; } function int DoSomethingElse() { local int a, b; a = 2; log( "a의 값은" $ a ); b = DoSomething( a ); log( "a의 값은" $ a ); log( "b의 값은" $ b ); }
DoSomethingElse 가 호출되었을 때 나오는 결과는 다음과 같습니다:
a의 값은 2 a의 값은 2 b의 값은 4
다시 말해, 함수 DoSomething 은 이에 전달되어 온 변수 "a" 의 로컬 복사본을 가지고 작업을 했으며, 실제의 변수 "a" 에는 영향을 미치지 않았습니다.
지정자 out
은 함수가 전달되어 온 변수의 로컬 복사본을 만들기 보다는 실제로 그 변수를 변경해야 한다는 것을 지시합니다. 이것은, 예를 들어, 호출자에게 여러 개의 값을 돌려줄 필요가 있는 함수를 가지고 있는 경우에 유용합니다. 호출자가 out
값인 여러 개의 변수를 함수에 전달하도록 하기만 하면 됩니다. 예:
// vector 의 최소 및 최대 컴포넌트를 계산함. function VectorRange( vector V, out float Min, out float Max ) { // 최소 값을 계산. if ( V.X<V.Y && V.X<V.Z ) Min = V.X; else if( V.Y<V.Z ) Min = V.Y; else Min = V.Z; // 최대 값을 계산. if ( V.X>V.Y && V.X>V.Z ) Max = V.X; else if( V.Y>V.Z ) Max = V.Y; else Max = V.Z; }
out
키워드가 없이 하나 이상의 값을 반환해야 하는 함수를 작성하려 한다면 고통스러울 것입니다. Out 매개변수들은 참조에 의해 전달되므로, 함수 내에서 매개변수의 값을 변경하면 즉시 원래의 변수에 영향을 미칩니다. 이것은 또한, C++ 와 비슷하게, "const out" 을 명기함으로써 큰 값에 대한 최적화로도 사용될 수 있습니다.
optional
키워드를 사용해서, 특정 함수 매개변수를 호출자에게 편리하도록 선택적인 것으로 할 수 있습니다. UnrealScript 의 함수에서, 호출자가 명기하지 않은 선택적 매개변수는 함수의 선언에서 주어진 기본값으로 설정되며, 함수의 시그내처에서 값이 명기되지 않은 경우에는 제로로 (즉, 0, false, "", none) 설정됩니다. native 함수에 대해서는,선택적 매개변수의 기본값은 함수에 따라 좌우됩니다. 예를 들어 Spawn 함수는 스폰하는 액터의 위치 및 회전을 기본값으로 하는, 선택적인 location 및 rotation 을 취합니다. 선택적 인수의 기본값은 = value
를 추가함으로써 지정할 수 있습니다. 예: function myFunc(optional int x = -1)
.
coerce
키워드는 호출자의 매개변수가 지정된 타입으로 변환되도록 강제합니다 (UnrealScript 가 보통 자동으로 변환을 수행하지 않을지라도). 이것은 문자열을 취급하는 함수에 유용하여, 매개변수들이 문자열로 자동 변환됩니다. (Unreal의 문자열 참고)
함수의 오버라이드
"함수의 오버라이드" 는 하위 클래스에서 함수의 새 버전을 작성하는 것을 의미합니다. 예를 들어, 여러분이 Demon 이라는 새로운 종류의 괴물을 위한 스크립트를 작성하고 있다고 가정합니다. 방금 만들어진 Demon 클래스는 Pawn 클래스를 확장합니다. 이제 pawn 이 처음으로 플레이어를 보면 pawn 의 "SeePlayer" 함수가 호출되어, pawn 이 그 플레이어를 공격하기 시작할 수 있습니다. 이것은 멋진 컨셉이지만, 여러분이 새 Demon 클래스에서 "SeePlayer" 를 다르게 처리하고 싶다고 가정해 보십시오. 이것을 어떻게 하시겠습니까? 함수의 오버라이드가 그 해답입니다.
함수를 오버라이드 하려면, 부모 클래스에서 함수의 정의를 잘라내어 새 클래스에 붙여넣으면 됩니다. 예를 들면, SeePlayer 를 위해서 Demon 클래스에 다음을 추가할 수 있습니다.
// 새 Demon 클래스 버전의 Touch 함수. function SeePlayer( actor SeenPlayer ) { log( "데몬이 플레이어를 봤습니다"); // 여기에 새로운 커스텀 기능성 추가... }
함수의 오버라이드는 새 UnrealScript 클래스들을 효과적으로 작성하는 열쇠입니다. 기존의 클래스를 확장하는 새 클래스를 만듭니다. 그 다음 해야할 일은 다르게 처리되기 원하는 함수들을 오버라이드 하는 것이 전부입니다. 이는 엄청난 양의 코드를 쓰지 않고 새로운 종류의 객체를 작성하는 것을 가능하게 해줍니다.
UnrealScript 에서 많은 함수들이 final
로서 선언됩니다. final
키워드 (단어 function
의 바로 앞에 나타나는) 는 "이 함수는 자식 클래스에 의해 오버라이드 될 수 없음" 을 말합니다. 이것은 아무도 오버라이드하기를 원치 않을 것이 확실한 함수에서 사용되어야 합니다. 그래야 스크립트 코드의 속도가 빨라집니다. 예를 들어, 벡터의 사이즈를 계산하는 VectorSize 함수가 있다고 가정해 보십시오. 누군가 이것을 오버라이드 할 이유가 전혀 없습니다. 그러므로 이를 final
로 선언합니다. 반면에, Touch 같은 함수는 매우 컨텍스트 의존적이어서 final 이 되면 안됩니다.
고급 함수 지정자
- Static
- static 함수는 클래스 객체에의 참조 없이 호출될 수 있다는 점에서, C의 글로벌 함수처럼 작용합니다. Static 함수는 다른 static 함수를 호출할 수 있으며, 변수의 기본값에 접근할 수 있습니다. Static 함수는 static 이 아닌 함수를 호출할 수 없으며 인스턴스 변수에 접근할 수 없습니다 (이들은 객체의 인스턴스에에 대해 집행되지 않기 때문입니다). C++ 같은 언어들과는 달리, static 함수는 virtual 이며 자식 클래스에서 오버라이드될 수 있습니다. 이 특성은 변동할 수 있는 클래스(컴파일시에는 알 수 없지만, 변수나 표현식에 의해 참조되는 클래스)에서 static 함수를 호출하기 원하는 경우에 유용합니다.
- Singular
- 함수의 선언 바로 앞에 나타나는
singular
키워드는 함수가 그 자신을 재귀적으로 호출하는 일을 방지합니다. 이 종류의 함수를 사용하는 규칙은 이렇습니다: 어떤 액터가 이미 singular 함수의 도중에 있는 경우에는, 그 뒤에 이어지는 singular 함수 호출은 건너 뛰어집니다. 이것은 몇몇 경우의 무한 재귀 버그를 예방하는데 도움이 됩니다. 예를 들어, Bump 함수 내에서 액터를 이동시키려고 하는 경우, 액터가 이동하는 중에 다른 액터와 부닥쳐서 또 하나의 Bump 함수의 호출이 일어나게 되고, 또 그 액터가 또 다른 액터와 부닥쳐서 또 Bump 함수의 호출이 일어나고… 이것이 끝없이 계속될 가능성이 많습니다. 이러한 일을 방지하기 위해 매우 조심해야 합니다. 그렇지만 이러한 잠재적인 무한 재귀 상태가 일어나지 않게 하는 코드를 작성할 완벽한 자신이 없다면singular
키워드를 사용하십시오.
- Native
- UnrealScript 함수를
native
로 선언할 수 있습니다. 이것은 이 함수를 UnrealScript에서 호출할 수 있지만 실제로는 C++로 (다른 곳에) 구현된 것이라는 것을 의미합니다. 예를 들어, Actor 클래스는 아주 많은 native 함수의 정의를 보유하고 있습니다. 다음은 그 한 예입니다:
native(266) final function bool Move( vector Delta );
native
키워드 다음의 괄호 안에 있는 숫자는 (AUTOREGISTER_NATIVE
매크로를 사용하여) C++에서 선언된 함수의 수에 해당되며, 연산자 함수에 대해서만 필요합니다. Native 함수는 UnrealScript 정의가 들어있는 클래스의 패키지와 똑같은 이름의 DLL 에 저장되도록 되어 있습니다.
- NoExport
- native 함수에 대해서만 사용됩니다. 이 native 함수에 대한 C++ 함수 선언이 익스포트 되어서는 안된다는 것을 선언하는 것입니다. 해당 함수의 glue 버전에 대한 선언만이 익스포트 됩니다.
- Exec
- 이 함수는 콘솔에 그 이름을 타이프해 넣음으로써 집행될 수 있다는 것을 가리킵니다. 특정 클래스에서만 유효합니다.
- Latent
- native 함수가 잠복해 있다는 선언입니다. 이는 그 함수가 상태 코드에서만 호출될 수 있다는 것을 뜻하며, 게임 시간이 다소 경과한 후 되돌려집니다.
- Iterator
- native 함수가 반복자라는 선언입니다.
foreach
명령을 사용하여 액터의 목록을 루프하는데 사용됩니다.
- Simulated
- 이 함수는 액터가 시뮬레이트된 프록시나 자율적인 프록시일 때 클라이언트 쪽에서 집행될 것이라는 것을 선언합니다. 모든 native 및 final 함수들 또한 자동적으로 시뮬레이트 됩니다.
- Server
- 함수가 로컬 클라이언트에서 실행되는 대신 집행을 위해 서버로 보내져야 한다는 것을 선언합니다.
- Client
- 함수가 서버에서 실행되는 대신 집행을 위해 소유주 클라이언트에게 보내져야 한다는 것을 선언합니다. 이 플래그는 또한 내재적으로 그 함수에 대해 simulated 플래그를 설정합니다.
- Reliable
- (server 또는 client로 표시된) 복제 함수가 확실하게 보내져야 한다는 선언입니다. 이 함수가 항상 해당 Actor 에서의 다른 복제와의 상대적인 순서대로 상대방에 도달할 것이라는 것을 뜻합니다.
- Unreliable
- (server 또는 client로 표시된) 복제 함수가 신뢰할 수 없이 보내져야 한다는 선언입니다. 이것은 이 함수가 특정 순서대로 보내지거나 아니면 아예 보내지는 것을 보장할 수 없으며, 사용할 수 있는 대역폭이 충분하지 않을 경우에는 완전히 무시될수도 있다는 것을 뜻합니다.
- Private, Protected
- 이 키워드들은 이에 상응하는 변수의 키워드들과 같은 의미를 가집니다.
- Operator, PreOperator, PostOperator
- 이 키워드들은 operator (C++ 의 operators 와 동격)라는, 특별한 종류의 함수를 선언하기 위한 것입니다. 이것은 UnrealScript 가 "+", "-", "==", 그리고 "||" 같은 모든 내장 연산자들에 대해 이해하는 방법입니다. 이 문서에서는 연산자들의 작용 방법에 대해 자세히 설명하지는 않겠지만, 연산자의 개념은 C++에서와 비슷합니다. 그리고 새 연산자 함수 및 키워드를 UnrealScript 함수 또는 native 함수와 마찬가지로 선언할 수 있습니다.
- Event
event
키워드는 UnrealScript 에서function
과 같은 의미를 가지고 있습니다. 그러나 Unreal 에서unreal -make -h
를 사용하여 C++ 헤더 파일을 익스포트할 경우, UnrealEd 는 각 "이벤트" 에 대해 자동적으로 C++ -> UnrealScript 호출을 생성합니다. 이는 자동적으로 C++ 코드가 UnrealScript 함수와 동기화 되어 있게 하고 UnrealScript 함수에 적법하지 않은 매개변수가 전달될 가능성을 제거합니다. 한 예로, 아래의 UnrealScript 코드를 보십시오:
event Touch( Actor Other ) { ... }
위의 코드는 EngineClasses.h 에 다음과 비슷한 코드를 생성합니다:
void eventTouch(class AActor* Other) { FName N("Touch",FNAME_Intrinsic); struct {class AActor* Other; } Parms; Parms.Other=Other; ProcessEvent(N, &Parms); }
따라서 C++ 에서 다음과 같이 UnrealScript 함수를 호출하는 것이 가능해집니다:
AActor *SomeActor, *OtherActor; SomeActor->eventTouch(OtherActor);
- Const
- 오직 native 로 선언된 함수와 사용할 수 있으며, 이 지정자는 함수의 선언 뒤에 추가됩니다. 이 지정자는 생성된 헤더에서 이 함수가 'const' 로서 익스포트될 것인지의 여부를 결정합니다. 사용 예:
native function int doSomething(string myData) const;
제어 구조
UnrealScript 는 C/C++/Java 의 표준 흐름 제어문을 모두 지원합니다:
반복 구조
For 루프
"For" 루프는 일정 조건이 충족되는 한 루프를 순환합니다. 예:
// "for" 루프의 예. function ForExample() { local int i; log( "for 루프 데모"); for( i=0; i<4; i++ ) { log( "i의 값은 " $ i ); } log( "종료시 i=" $ i); }
다음은 이 루프의 출력입니다:
for 루프 데모 i의 값은 0 i의 값은 1 i의 값은 2 i의 값은 3 종료시 i=4
for 루프에서는 반드시 세미콜론으로 분리된 3개의 표현을 명기해야 합니다. 첫 번째 표현은 변수를 그 시작값으로 초기화하기 위한 것입니다. 두 번째 것은 루프 집행의 각 반복에 앞서 확인되는 조건을 제시합니다; 이 표현이 true 이면, 루프가 집행됩니다. 이것이 false 이면 루프가 종료됩니다. 세 번째 조건은 루프의 카운터를 증분하는 표현입니다.
대부분의 "for" 루프 표현이 단지 카운터를 업데이트하는 것이지만, 적절한 초기화, 끝내기 그리고 증분을 사용함으로써 linked list 를 순회하는 등 "for" 루프로 보다 고도의 일들을 처리할 수도 있습니다.
모든 흐름 제어문에서, 한 줄로 이루어진 단독문은 아래처럼 중괄호 없이 집행할 수 있습니다:
for( i=0; i<4; i++ ) log( "i의 값은" $ i );
또는 복수의 표현문을 아래와 같이 중괄호로 둘러싸 집행할 수 있습니다:
for( i=0; i<4; i++ ) { log( "i의 값은"); log( i ); }
Do 루프
"Do" 루프는 종료 조건이 true 일 동안 루프를 순환합니다. Unreal 에서는 do-until
구문을 사용한다는 것을 유념하십시오. 이는 (do-while
을 사용하는) C/Java 와 다른점입니다.
// "do" 루프의 예. function DoExample() { local int i; log( "do 루프 데모"); do { log( "i의 값은" $ i ); i = i + 1; } until( i == 4 ); log( "종료시 i=" $ i); }
다음은 이 루프의 출력입니다:
do 루프 데모 i의 값은 0 i의 값은 1 i의 값은 2 i의 값은 3 종료시 i=4
While 루프
"While" 루프는 일정 시작 조건이 true 인 동안 루프를 순환합니다.
// "while" 루프의 예. function WhileExample() { local int i; log( "while 루프 데모"); while( i < 4 ) { log( "I의 값은" $ i ); i = i + 1; } log( "종료시 i=" $ i); }
다음은 이 루프의 출력입니다:
while 루프 데모 i의 값은 0 i의 값은 1 i의 값은 2 i의 값은 3 종료시 i=4
Continue
"continue" 명령은 루프의 집행을 처음으로 되돌아가게 합니다. 따라서 continue 명령 다음의 것들은 집행되지 않습니다. 이것은 특정 경우 루프 코드를 건너뛰는데 사용됩니다.
function ContinueExample() { local int i; log( "continue 데모"); for( i=0; i<4; i++ ) { if( i == 2 ) continue; log( "i의 값은" $ i ); } log( "종료시 i=" $ i ); }
이 루프의 출력은 다음과 같습니다:
continue 데모 i의 값은 0 i의 값은 1 i의 값은 3 종료시 i=4
Break
"break" 명령은 가장 가까운 루프에서 ("For", "Do", 또는 "While") 나오도록 합니다.
function BreakExample() { local int i; log( "break 데모"); for( i=0; i<10; i++ ) { if( i == 3 ) break; log( "i의 값은" $ i ); } log( "종료시 i=" $ i ); }
다음은 이 루프의 출력입니다:
Break 데모 i의 값은 0 i의 값은 1 i의 값은 2 종료시 i=3
"break" 명령은 조건문 ("switch") 의 나머지를 건너뛰는데도 사용될 수 있습니다.
선택 구조
If-Then-Else 문
"If", "Else If", 그리고 "Else" 는 특정 조건이 충족되었을 경우에 코드를 집행합니다.// 간단한 "if"의 예. if( LightBrightness < 20 ) log( "불빛이 흐릿합니다"); // "if-else"의 예. if( LightBrightness < 20 ) log( "불빛이 흐릿합니다"); else log( "불빛이 밝습니다"); // "if-else if-else"의 예. if( LightBrightness < 20 ) log( "불빛이 흐릿합니다"); else if( LightBrightness < 40 ) log( "불빛이 중간정도로 밝습니다"); else if( LightBrightness < 60 ) log( "불빛이 밝은 편입니다"); else log( "불빛이 아주 밝습니다"); // 중괄호 사용 "if"의 예. if( LightType == LT_Steady ) { log( "불빛이 안정적입니다"); } else { log( "불빛이 안정적이지 않습니다"); }
Case 문
"Switch", "Case", "Default", 그리고 "Break" 는 여러가지 조건을 쉽게 처리하도록 해줍니다.
// switch-case의 예. function TestSwitch() { // LightType 의 값에 따라 // 아래의 case 문 가운데 하나를 집행. switch( LightType ) { case LT_None: log( "조명이 없습니다"); break; case LT_Steady: log( "안정적인 조명이 있습니다"); break; case LT_Backdrop: log( "배경 조명이 있습니다"); break; default: log( "다이나믹한 조명이 있습니다"); break; } }
"switch" 문은 하나 또는 그 이상의 "case" 문과 선택적인 "default" 문으로 이루어져 있습니다. Switch 문 다음에, 일치하는 "case" 문이 있을 경우 그것이 집행됩니다; 그렇지 않으면 "default" 문이 집행됩니다; 아니면 집행은 "switch" 문의 끝을 지나 계속됩니다.
"case" 라벨에 이어 코드를 작성한 후에는 반드시 "break" 문을 사용해 집행이 "switch" 문의 끝을 지나도록 해야 합니다. "break" 를 사용하지 않으면, 집행은 그 다음의 "case" 로 "넘어갑니다".
// switch-case 의 예. function TestSwitch2() { switch( LightType ) { case LT_None: log( "조명이 없습니다"); break; case LT_Steady: // LT_Backdrop case로 “넘어감” case LT_Backdrop: log( "조명이 있습니다"); break; default: log( "기타"); break; } }
Goto
"Goto" 명령은 현재의 함수나 상태 어딘가에 있는 라벨로 가도록 합니다.
// "goto" 의 예. function GotoExample() { log( "GotoExample을 시작합니다"); goto Hither; Yon: log( "Yon에 있음"); goto Elsewhere; Hither: log( "Hither에 있음"); goto Yon; Elsewhere: log( "Elsewhere에 있음"); }
출력은 다음과 같습니다:
GotoExample을 시작합니다 Hither에 있음 Yon에 있음 Elsewhere에 있음
언어 기능
내장 연산자 및 그우선 순위
UnrealScript 는숫자들의 합산, 값의 비교, 변수의 증분 등의 실시를 위한, 다양하고 폭넓은 C/C++/Java 스타일의 연산자들을 제공합니다. 이 연산자들의 완전한 세트는 Object.u 에 정의되어 있습니다만 여기서 간단히 요약하겠습니다. 이것들은 표준 연산자들이며 우선 순위대로 소개되어 있습니다. C 스타일의 연산자들은 모두 C에서와 같은 우선 순위를 가집니다.
연산자 | 적용되는 타입 | 뜻 |
---|---|---|
@ | string | 두 string을 사이에 여백을 두고 연결함. "string1"@"string2"= "string1 string2" |
@= | string | 두 string을 사이에 여백을 두고 연결함.연결 및 배정 (v3323 및 그 이상) |
$ | string | String 연결 |
$= | string | String 연결. 연결 및 배정(v3323 및 그 이상) |
*= | byte, int, float, vector, rotator | 곱셈 및 배정 |
/= | byte, int, float, vector, rotator | 나누기 및 배정 |
+= | byte, int, float, vector | 덧셈 및 배정 |
-= | byte, int, float, vector | 뺄셈 및 배정 |
|| | bool | 논리적 or |
&& | bool | 논리적 and |
^^ | bool | 배타적 or |
& | int | 비트의 and |
| | int | 비트의 or |
^ | int | 비트의 배타적 or (XOR) |
!= | All | 같지 않음에 대한 비교 |
== | All | 같음에 대한 비교 |
< | byte, int, float, string | ~보다 적은 |
> | byte, int, float, string | ~보다 큰 |
<= | byte, int, float, string | ~보다 적거나 같음 |
>= | byte, int, float, string | ~보다 크거나 같음 |
~= | float, string | 대략 같음 (0.0001이내의 차이로), 대소문자 구별 없이 같음. |
<< | int, vector | 왼쪽 비트 시프트(int), 전진 벡터 변환(vector) |
>> | int, vector | 오른쪽 비트 시프트(int), 후진 벡터 변환(vector) |
>>> | >> 와 같음 | |
+ | byte, int, float, vector | 덧셈 |
- | byte, int, float, vector | 뺄셈 |
% | float, int, byte | 모듈로 (나눗셈 후의 나머지) |
* | byte, int, float, vector, rotator | 곱셈 |
/ | byte, int, float, vector, rotator | 나눗셈 |
Dot | vector | 벡터의 내적 |
Cross | vector | 벡터의 외적 |
** |
float | 누승 |
ClockwiseFrom |
int (rotator 의 요소) | 첫 번재 인수가 두 번째 인수로부터 시계방향에 있으면 true 를 반환 |
위의 테이블에서는 연산자들이 우선순위대로 나열되어 있습니다 (우선 순위가 같은 것은 같이 그룹지어져 있습니다). "1*2+3*4" 와 같은 복잡한 표현을 입력하면, UnrealScript 는 자동적으로 연산자들을 우선 순위에 따라 그룹짓습니다. 곱셈이 덧셈보다 우선순위가 높으므로, 이 표현은 "(1*2)+(3*4)" 로 평가됩니다.
"&&"(논리적 and)와 "||"(논리적 or) 연산자들은 단축형입니다: 첫 번째 표현에서 표현식의 결과가 결정될 수 있는 경우에는 (예를 들어, &&의 첫 번째 인수가 false 인 경우), 두 번째 것은 평가되지 않습니다.
전진 및 후진 벡터 변환에 대해서는, revers >>는 로컬 공간에서 세계 공간으로 변환하며, forward <<는 그 변환을 다시 되돌립니다.
예를 들어, 플레이어로부터 64단위 앞을 향하고있는 벡터가 있다면, vect(64,0,0) 는 로컬 플레이어 공간에 있는 것입니다. 이것을 세계 공간에 있게 하고 싶으면, 이를 플레이어의 회전을 사용해서 세계 공간으로 변환해야 합니다. 이것을 다음과 같이 계산할 수 있습니다:
myWorldSpaceVect = my64Vect >> playerPawn.rotation;
세계 공간에 벡터를 가지고 있고 이를 로컬 플레이어 공간에 있게 하고 싶은 경우에는 전진 회전을 사용하면 됩니다. 한 예로, 자동차 액터의 세계 공간 속도를 로컬 공간으로 바꾸면, X (전진 속도) 를 구해 이를 HUD 에 프린트할 수 있습니다.
표준 연산자들 외에, UnrealScript 는 다음의 1진 연산자들을 지원합니다:
- ! (bool) 논리적 not.
- - (int, float) 부정.
- ~ (int) 비트의 부정.
- ++, -- 감소 (변수의 앞 또는 뒤).
이따금 새 연산자가 엔진에 추가됩니다. 연산자의 완전 목록을 보려면 최신의 UnrealScript 소스 – 특히 Object 클래스를 확인해 보십시오.
다목적 함수
객체 만들기
UnrealScript 에서 새 객체 인스턴스를 만들기 위해서는, 그 객체가 Actor 인지 아닌지에 따라 두 개의 함수 가운데 하나를 사용합니다. 객체가 Actor라면, Actor.uc에서 정의된 Spawn 함수를 사용해야 합니다. Actor 로부터 파생된 것이 아닌 클래스에 대해서는 반드시 new
연산자를 사용해야 합니다. new 연산자에 대한 구문은 다른 어느 함수와도 다릅니다. 선택적 매개변수 목록 외에 새 객체의 클래스, 그리고 선택적으로 템플릿 객체를 지정해야 합니다. new 연산자에 대한 UnrealScript 선언은 없지만, 함수의 시그내처는 아래와 같습니다:
native final operator function coerce Object new ( object InOuter, name InName, int InFlags, class InClass, object InTemplate );
- InOuter
- (선택) 새로 만들어진 객체에 대해 Outer 로서 배정할 객체. 명기하지 않은 경우 그 객체의 Outer는 게임이 실행되고 있는 동안에만 존재하는, "transient package(일시적인 패키지)" 라는 특별 패키지로 설정됩니다 .
- InName
- (선택) 새 객체에 주어질 이름. 명기하지 않은 경우, 그 객체에 ClassName_## 형식의 독특한 이름이 주어집니다. 이 ##는 이 클래스의 인스턴스가 만들어질 때마다 증가합니다.
- InFlags
- (선택, 이제 객체의 플래그가 64비트이기 때문에 현재는 깨져 있음) 객체를 만들 때 사용하기 위한 객체 플래그. 다음의 값들이 유효합니다:
- 0x0000000100000000: 에디터의 실행취소/재실행 지원. (RF_Transactional)
- 0x0000000400000000: 외부 파일에 의해 참조될 수 있음. (RF_Public)
- 0x0000400000000000: 디스크에 저장될 수 없음. (RF_Transient)
- 0x0010000000000000: 객체를 게임 클라이언트에 로드하지 않음. (RF_NotForClient)
- 0x0020000000000000: 객체를 게임 서버에 로드하지 않음. (RF_NotForServer)
- 0x0040000000000000: 객체를 에디터에 로드하지 않음. (RF_NotForEdit)
- 0x0008000000000000: 객체가 참조되지 않았더라도 편집을 위해 유지해 둠. (RF_Standalone)
- InClass
- 인스턴스를 작성하기 위한 클래스.
- InTemplate
- 새 객체의 속성값을 초기화하기 위해 사용할 객체.
새 연산자를 위한 실제 구문은 다음과 같습니다:
ObjectVar = new[(InOuter, InName, InFlags)] <class'InClass'>[(InTemplate)];
LightFunction 클래스의 객체를 만듭니다:
function CreateALight() { local LightFunction NewObj; NewObj = new class'Engine.LightFunction'; }
"NewLight" 라는 이름의 새 LightFunction 객체를 만들어, 이 객체를 그 Outer 에 배정합니다.
function CreateALight() { local LightFunction NewObj; NewObj = new(Self,'NewLight') class'Engine.LightFunction'; }
새 객체의 속성을 초기화하기 위한 LightFunctionTemplate 변수의 값으로 배정된 객체를 사용하여, 일시적인 패키지에 "NewLight" 라는 이름의 새 LightFunction 객체를 만듭니다:
var LightFunction LightFunctionTemplate; function CreateALight() { local LightFunction NewObj; NewObj = new(None,'NewLight') class'Engine.LightFunction' (LightFunctionTemplate); } defaultproperties { Begin Object Class=LightFunction Name=MyLightFunctionArchetype End Object LightFunctionTemplate=MyLightFunctionArchetype }
Integer 함수
- int Rand( int Max ); 0 에서 Max-1 사이의 무작위 수를 반환합니다.
- int Min( int A, int B ); 두 개의 수 가운데 최소의 것을 반환합니다.
- int Max( int A, int B ); 두 개의 수 가운데 최대의 것을 반환합니다.
- int Clamp( int V, int A, int B ); 첫 번째 수가 A 에서 B의 간격으로 클램프된 값을 반환합니다.
경고 - C 나 C++ 와 달리 Min 과 Max 는 integer 에서 작용합니다. 이 함수들을 float 에 사용해도 경고는 나오지 않습니다 – 수의 소수점 이하가 잘려질 뿐입니다! Float 에 대해서는, FMin 과 FMax 를 사용해야 합니다.
Floating point 함수
- float Abs( float A ); 해당 수의 절대값을 반환합니다.
- float Sin( float A ); 라디안으로 표시된 수의 사인을 반환합니다.
- float Cos( float A ); 라디안으로 표시된 수의 코사인을 반환합니다.
- float Tan( float A ); 라디안으로 표시된 수의 탄젠트를 반환합니다.
- float ASin( float A ); 라디안으로 표시된 수의 역사인을 반환합니다.
- float ACos( float A ); 라디안으로 표시된 수의 역코사인을 반환합니다.
- float Atan( float A ); 라디안으로 표시된 수의 역탄젠트를 반환합니다.
- float Exp( float A ); constant(불변수) "e"의 A의 지수로 누승된 값을 반환합니다 .
- float Loge( float A ); (“e”를 밑값으로 하는) A의 로그를 반환합니다.
- float Sqrt( float A ); A의 제곱근을 반환합니다.
- float Square( float A ); A의 제곱 A*A를 반환합니다.
- float FRand(); 0.0에서 1.0 사이의 무작위 수를 반환합니다.
- float FMin( float A, float B ); 두 개의 수 가운데 최소의 것을 반환합니다.
- float FMax( float A, float B ); 두 개의 수 가운데 최대의 것을 반환합니다.
- float FClamp( float V, float A, float B ); 첫 번째 수가 A 에서 B의 간격으로 클램프된 값을 반환합니다.
- float Lerp( float A, float B, float Alpha ); A와 B 사이의 선형 보간을 반환합니다.
- float Smerp( float Alpha, float A, float B ); A와 B 사이의 알파-스무스 비선형 보간을 반환합니다.
- float Ceil ( float A ); 소수점 올림.
- float Round ( float A ); 정상적으로 반올림.
String 함수
- int Len( coerce string S ); 문자열의 길이를 반환합니다.
- int InStr( coerce string S, coerce string t); 첫 번째 문자열 내에서의 두 번째 문자열의 오프셋을 반환합니다. 찾는 문자열이 없을 경우 -1을 반환합니다.
- string Mid ( coerce string S, int i, optional int j ); 지정 문자 위치 i에서 시작하여 j 개의 문자를 포함하는, 문자열 S의 중간 부분을 반환합니다(j가 지정되지 않은 경우에는 i 이후의 문자를 모두 반환합니다).
- string Left ( coerce string S, int i ); 문자열 S의 i 위치에서 가장 왼편에 있는 문자를 반환합니다.
- string Right ( coerce string] S, int i ); 문자열 S의 i 위치에서 가장 오른편에 있는 문자를 반환합니다.
- string Caps ( coerce string S ); S를 대문자로 바꾸어 반환합니다.
- string Locs ( coerce string S); S의 소문자 표현을 반환합니다 (v3323 및 그 이상)
- string Chr ( int i ); ASCII 로부터 캐릭터를 반환합니다.
- int Asc ( string S ); 해당 캐릭터의 ASCII 값을 반환합니다 (문자열의 첫 문자만 사용됩니다).
- string Repl ( coerce string Src, coerce string Match, coerce string With, optional bool bCaseSensitive ); Src에서
Match
를With
로 대체합니다. (v3323 및 그 이상) - string Split(coerce string Text, coerce string SplitStr, optional bool bOmitSplitStr);
Text
에서 가장 처음 나타나는SplitStr
를 잘라내고Text
의 나머지 부분을 반환합니다.bOmitSplitStr
이 true이면, 반환된 문자열에서SplitStr
이 생략됩니다. - array
SplitString( string Source, optional string Delimiter=",", optional bool bCullEmpty ); 하나의 표현식을 사용해서 문자열을 배열로 분할하는 래퍼입니다. - JoinArray(array
StringArray, out string out_Result, optional string delim = ",", optional bool bIgnoreBlanks = true); 지정된 구분 기호를 사용해서 문자열 배열로부터 하나의 문자열을 만들어 냅니다. 선택적으로 여백인 멤버들을 무시합니다. - ParseStringIntoArray(string BaseString, out array
Pieces, string Delim, bool bCullEmpty); 구분된 문자열을 문자열 배열의 요소로 나눕니다. - A == B; 두 문자열이 같을 경우 true 를 반환하는 비교입니다 (대소문자 구분).
- A ~= B; 두 문자열이 같을 경우 true 를 반환하는 비교입니다(대소문자 구분 안함).
- A != B; 두 문자열이 다를 경우 true 를 반환하는 비교입니다 (대소문자 구분).
더 상세한 정보는 Unreal의 문자열 을 참고하십시오.
Vector 함수
- vector vect( float X, float Y, float Z ); 주어진 컴포넌트로 새 벡터를 만듭니다.
- float VSize( vector A ); 벡터의 유클리드 사이즈 (컴포넌트들을 제곱한 값을 합한 것의 제곱근)를 반환합니다.
- vector Normal( vector A ); 특별한 벡터의 방향을 향하고 있는, 사이즈가 1.0인 벡터를 반환합니다.
- Invert ( out vector X, out vector Y, out vector Z ); 3축 벡터에 의해 지정된 좌표 시스템을 반대로 합니다.
- vector VRand ( ); 균등하게 분산된 무작위 벡터를 반환합니다.
- vector MirrorVectorByNormal( vector Vect, vector Normal ); 지정된 노멀 벡터에 대한 벡터를 반영합니다.
Timer 함수
Timer 함수는 Actor 하위 클래스에서만 이용할 수 있습니다.
각각 다른 진도의 timer 를 여러 개 만들 수 있습니다. 각 타이머는 고유의 타겟 함수를 가집니다 (기본은 Timer()).
- function SetTimer(float inRate, optional bool inbLoop, optional Name inTimerFunc); inRate 초 후에 트리거되는 타이머를 시작합니다. inbLoop 이 true 이면 타이머가 루프합니다. inTimerFunc 는 호출할 함수를 정의하는데, 이는 함수 Timer() 가 기본으로 설정되어 있습니다. 이 값은 복수 타이머를 식별하는데도 사용됩니다.
- ClearTimer(optional Name inTimerFunc); 실행중인 타이머를 멈춥니다.
- bool IsTimerActive(optional Name inTimerFunc); 해당 타이머가 활성화 상태인 경우 true 를 반환합니다.
- float GetTimerCount(optional Name inTimerFunc); 타이머의 계수 값을 반환합니다. 즉, 타이머가 마지막으로 집행된 이래의 시간을 초로 나타낸 수 입니다. 타이머가 활성화 상태가 아닌 경우 -1을 반환합니다.
- float GetTimerRate(optional name TimerFuncName = 'Timer'); 타이머의 진도를 반환합니다.
GetTimerRate('SomeTimer') - GetTimerCount('SomeTimer')
는 타이머의 남아있는 시간을 반환합니다.
디버깅 함수
다음은 코드의 디버깅에 도움이 되는 함수들입니다.
- LogEx( ELoggingSeverity Severity, name Category, coerce string Msg ); 주어진 엄격도와 범주로 메시지를 로그합니다. 이 함수는 표준 log() 함수보다 많은 콘트롤을 가집니다. 이는 런타임에 엄격도와 범주에 따라 메시지를 가려낼 수 있도록 해줍니다.
- LogFatal( name Category, coerce string Msg ); LogEx(LOG_FATAL, Category, Msg) 를 호출하는 간단한 방법입니다.
- LogError( name Category, coerce string Msg );
- function LogWarn( name Category, coerce string Msg );
- LogInfo( name Category, coerce string Msg );
- LogDebug( name Category, coerce string Msg );
- LogTrace( name Category, coerce string Msg );
변경목록 134102에서부터 위의 로깅 함수들을 더 이상 이용할 수 없게 되었다는 사실을 양지하십시오. 이것들은 UnrealScript 전처리기 에 의해 처리되는 로깅 매크로로 대체되었습니다.
- ScriptTrace(); 현재의 스크립트 콜스택을 로그 파일에 비웁니다.
- Name GetFuncName(); 현재 호출하고 있는 함수의 이름을 반환합니다.
- DumpStateStack(); 현재의 상태 스택을 로그합니다.
UnrealScript 전처리기
상세한 내용은 Unrealscript 전처리기 페이지를 참고하십시오.
UnrealScript 도구 및 유틸리티
Script Profiler (스크립트 프로파일러)
스크립트 프로파일러 는 스크립트의 어느 부분이 집행에 가장 많은 시간을 차지하고 있는지 이해하는데 도움이 됩니다.
Script Debugger (스크립트 디버거)
자세한 정보는 Unreal 디버깅 도구 페이지를 참고하십시오.
Unreal 개발 환경 (UDE)
UnrealScript 의 개발 및 디버깅을 위해 UDE 사용하기에 대한 정보는 Unreal 개발 환경 페이지를 참고하십시오.
고급 언어 기능
Timers
타이머는 이벤트의 발생 또는 재발생을 정기적으로 예정하기 위한 방법으로 사용됩니다. 이에 따라, Actor 가 설정된 만큼의 시간이 경과한 후 게임 엔진이 Timer()
함수를 한 번 또는 반복해서 호출하도록 자신을 등록하기 위한 타이머를 설정할 수 있습니다.
UnrealScript 의 타이머는 각 Actor 의 내부에 구조체의 배열로서 구현되어 있습니다 (한 Actor 가 계류중인 여러 개의 타이머를 가질 수 있습니다). 이 구조체에는 타이머가 만기되기까지의 남은 시간, 만기시 호출할 함수 등이 포함되어 있습니다.
게임 루프는 대개 프레임당 한 번씩 각 Actor 를 틱 하며, 각 Actor 의 Tick()
함수 일부에는 만기된 타이머가 있는지 확인하여 알맞은 UnrealScript 함수를 호출하는 UpdateTimers()
가 포함되어 있습니다.
입도는 프레임의 델타 타임에 따라 한정되지만, 하드웨어나 OS 리소스가 요구되지 않습니다. 이것은 모두 C++ 로 구현되었으므로, 수 백개의 UnrealScript 타이머들을 문제없이 업데이트 할 수 있습니다. 물론 이 타이머들이 동시에 또는 모든 프레임에서 만기되는 일은 없습니다. 왜냐하면 이것들은 활성화 상태일 때 스크립트 코드를 집행 (느림) 하기 때문입니다.
States (상태)
상태의 개요
게임 프로그래머들은 게임이 "pong" 단계를 지나 발전하기 시작한 이래 상태라는 개념을 사용하고 있습니다. 상태 (그리고 "state machine 프로그래밍" 으로 알려진 것)은 복잡한 객체의 행태를 다루기 쉬운 것으로 만드는 자연스러운 방법입니다. 그렇지만 UnrealScript 이전에는 상태가 언어 수준에서 지원되지 않았기 때문에, 개발자들은 객체의 상태에 따라 C/C++ 의 "switch" 문을 작성해야 했습니다. 이러한 코드는 작성 및 업데이트가 어렵습니다.
UnrealScript 는 상태를 언어 수준에서 지원합니다.
UnrealScript 에서는, 세계의 각 액터가 항상 오직 하나의 상태에 있습니다. 액터의 상태는 액터가 수행하고자 하는 액션을 반영합니다. 예를 들어, 움직이는 브러쉬는 "StandOpenTimed" 및 "BumpOpenTimed" 처럼 여러 개의 상태를 가지고 있습니다. Pawn 은 "Dying", "Attacking", 그리고 "Wandering" 같은 여러 상태를 가지고 있습니다.
UnrealScript 에서는, 특정 상태에 있는 함수 및 코드를 작성할 수 있습니다. 이러한 함수들은 오직 해당 액터가 그 상태에 있을 때만 호출됩니다. 예를 들어, 여러분이 괴물 스크립트를 작성하고 있는데 "SeePlayer" 함수를 어떻게 처리할 것인지를 곰곰히 생각중이라고 가정해 보십시오. 돌아다니다가 보게 되는 플레이어를 공격하려고 합니다. 벌써 플레이어를 공격하고 있는 중이라면 이를 방해 받지 않고 계속하고 싶습니다.
이를 성취하는 가장 쉬운 방법은 여러 개의 상태 (돌아다니기, 그리고 공격하기) 를 정의하고, 각 상태에서 각기 다른 버전의 "Touch" 를 작성하는 것입니다. UnrealScript 는 이것을 지원합니다.
상태에 대해 더 깊이 파고들기에 앞서, 상태에는 두 가지 큰 이점과 한 가지 성가신 문제가 있다는 것을 이해해 둘 필요가 있습니다:
- 이점: 상태는 상태 특정의 함수를 작성하는 간단한 방법을 제공하므로, 액터가 무엇을 하고 있는지에 따라 같은 함수를 다른 방법으로 처리할 수 있습니다.
- 이점: UnrealScript 의 모든 명령들과 “잠재적 함수” 로 알려진 여러 특별 함수를 사용하여 특별한 “상태 코드” 를 작성할 수 있습니다. 잠재적 함수는 “천천히” 집행되는 (즉, 비블로킹) 함수이며, 일정한 “게임 시간” 이 경과한 후에 되돌려집니다. 이것은 시간 기반의 프로그래밍이 가능하도록 해줍니다— 이것은 C, C++ 나 Java 가 제공하지 않는 커다란 이점입니다. 다시 말해, 개념화 하는대로 코드를 작성할 수 있습니다; 예를 들어, “이 문을 연다; 2초동안 기다린다; 이 음향 효과를 재생한다; 저 문을 연다; 괴물을 풀어주어 플레이어를 공격하게 한다” 고 하는 스크립트를 쓸 수 있습니다. 이것을 단순한 선형 코드로 쓰면 Unreal Engine 이 시간 기반의 코드 집행에 대한 상세한 것들을 알아서 처리합니다.
- 성가신 문제: 여러 개의 상태 및 자식 클래스에서 (Touch 같은) 함수들이 오버라이드 되었습니다. 이제 특정 상황에서 정확히 어느 “Touch” 함수가 호출될 것인지 알아내야 하는 부담이 생겼습니다. UnrealScript 가 이 과정을 명백히 묘사하는 규칙을 제공하더라도, 복잡한 클래스와 상태의 계층구조를 만드는 경우에는 여러분이 알아두어야 할 일입니다.
다음은 TriggerLight 스크립트에서의 상태의 예입니다:
// Trigger 가 불을 켬. state() TriggerTurnsOn { function Trigger( actor Other, pawn EventInstigator ) { Trigger = None; Direction = 1.0; Enable( 'Tick' ); } } // Trigger 가 불을 끔. state() TriggerTurnsOff { function Trigger( actor Other, pawn EventInstigator ) { Trigger = None; Direction = -1.0; Enable( 'Tick' ); } }
여기서는 두 개의 다른 상태가 선언되어 있으며 (TriggerTurnsOn 과 TriggerTurnsOff), 각 상태에서 각기 다른 버전의 Trigger 함수가 작성되어 있습니다. 이것을 상태 없이 구현할 수도 있겠지만,상태를 사용하면 훨씬 더 모듈식이고 확장이 가능한 코드가 됩니다: UnrealScript 에서는, 기존 클래스의 하위 클래스화, 새 상태의 추가, 그리고 새 함수의 추가를 쉽게 할 수 있습니다. 위의 코드를 상태를 사용하지 않고 작성한다면, 나중에 확장하기가 꽤 어려울 것입니다.
상태를 편집할 수 있는 것으로 선언할 수 있습니다. 이는 사용자가 UnrealEd 에서 액터의 상태를 설정하거나 하지 않을 수 있다는 것을 뜻합니다. 편집할 수 있는 상태는 다음과 같이 선언합니다:
state() MyState { //... }
편집을 금지하는 상태는 다음과 같이 선언합니다:
state MyState { //... }
"auto" 키워드를 사용하여 액터가 있어야 할 자동 상태 또는 초기 상태를 지정할 수도 있습니다. 이것은 모든 새 액터가 처음 활성화 될 때 지정된 상태에 배치되도록 합니다:
auto state MyState { //... }
상태의 라벨 및 잠재적 함수
상태는 함수 외에 하나 또는 더 많은 수의 라벨을 UnrealScript 코드에 앞서 가질 수 있습니다. 예:
auto state MyState { Begin: Log( "MyState가 막 시작됐습니다!"); Sleep( 2.0 ); Log( "MyState가 잠에서 깨어났습니다"); goto('Begin'); }
위의 상태 코드는 "MyState가 막 시작됐습니다!"
라는 메시지를 프린트 하고, 2초 동안 정지한 다음, "MyState가 잠에서 깨어났습니다"
라는 메시지를 프린트 합니다. 이 예에서 재미있는 것은 "Sleep" 이라는 잠재적 함수의 호출입니다: 이 함수의 호출은 즉시 되돌아오지 않고, 일정한 게임 시간이 흐른 뒤에 되돌아옵니다. 잠재적 함수는 오직 상태 코드 내에서만 호출될 수 있으며, 함수에서는 호출될 수 없습니다. 잠재적 함수는 시간의 경과를 포함하여 복잡한 이벤트의 사슬을 관리할 수 있도록 해줍니다.
모든 상태 코드는 라벨을 정의하는 것으로 시작됩니다; 위의 예에서는 라벨이 "Begin" 이라는 이름입니다. 라벨은 상태 코드로의 편리한 진입점을 제공합니다. 상태 코드에서는 어떤 것이라도 라벨의 이름으로 사용할 수 있지만, "Begin" 라벨은 특별합니다: 이것은 해당 상태 코드의 기본 시작 지점입니다.
모든 액터들이 이용할 수 있는 3가지 주요 잠재적 함수가 있습니다:
- Sleep( float Seconds )은 상태의 집행이 특정 시간 동안 일시 정지한 다음 계속되도록 합니다.
- FinishAnim()은 재생중인 현재의 애니메이션 시퀀스가 끝날 대까지 기다린 다음 계속합니다. 이 함수는 애니메이션 주도적 스크립트, 메쉬 애니메이션에 의해 집행이 지배되는 스크립트의 작성을 쉽게 해줍니다. 예를 들면, 매끄러운 애니메이션이 AI 시스템의 핵심 목표이기 때문에, AI 스크립트의 대부분은 애니메이션 주도적(시간 주도적과 달리)입니다.
- FinishInterpolation()은 현재의 InterpolationPoint 움직임이 끝날 때까지 기다린 다음 계속합니다.
Pawn 클래스는 세계를 탐색한다거나 단기 이동 등의 액션에 대한 다수의 중요한 잠재적 함수를 정의하고 있습니다. 이들의 사용법에 대한 설명은 별도의 AI 문서를 참고하십시오.
다음의 세 native UnrealScript 함수들은 상태 코드를 작성할 때 특히 유용합니다:
- 상태 내의 "Goto('LabelName')" 함수 (C/C++/Basic 의 goto 와 비슷)는 상태 코드가 지정된 라벨에서 계속 집행되도록 합니다.
- 상태 내의 특별 Goto('') 명령은 상태 코드의 집행을 멈추게 합니다. 상태 코드의 집행은 새 상태에 가거나 현 상태 내의 새 라벨에 갈 때까지는 계속되지 않습니다.
- "GotoState" 함수는 액터가 새 상태에 가도록 하며, 선택적으로 지정된 라벨에서 계속되도록 합니다 (라벨을 지정해주지 않으면, 기본은 "Begin" 라벨입니다). 상태 코드 내에서 GotoState 를 호출할 수 있으며, 이는 즉시 목적지로 갑니다. 액터 내의 어느 함수에서나 GotoState 를 호출할 수도 있지만, 이것은 효과가 즉시 나타나지 않습니다: 이것은 집행이 다시 해당 상태 코드로 되돌아올 때까지는 효과가 나타나지 않습니다.
다음의 여태까지 논의해온 상태 개념의 예입니다:
// 집행할 자동 상태. auto state Idle { // 다른 액체가 건드린 경우... function Touch( actor Other ) { log( "누가 날 건드렸어. 공격해야겠어"); GotoState( 'Attacking' ); Log( "Attacking 상태에 갔었어"); } Begin: log( "쉬고 있는 중..."); sleep( 10 ); goto 'Begin'; } // Attacking state. state Attacking { Begin: Log( "attacking 상태의 코드를 집행하고 있어"); //... }
이 프로그램을 실행한 다음 액터를 touch 하러 가면, 다음과 같은 것이 표시됩니다:
쉬고 있는 중... 쉬고 있는 중... 쉬고 있는 중... 누가 날 건드렸어. 공격해야겠어 Attacking 상태에 갔었어 attacking 상태의 코드를 집행하고 있어
GotoState 에 대한 이 중요한 점을 반드시 이해하도록 하십시오: 함수 내에서 GotoState 를 호출하면, 이것은 즉시 목적지로 가지 않고, 집행이 상태 코드로 되돌아 온 다음에라야 목적지로 갑니다.
상태의 상속 및 유효범위에 대한 규칙
UnrealScript 에서는, 기존의 클래스를 하위 클래스화 하면 부모 클래스의 변수, 함수 및 상태들이 모두 그 새 클래스에 상속됩니다. 이 점은 잘 이해하실 것입니다.
그러나 UnrealScript 프로그래밍 모델에 상태라는 추상적인 개념이 추가됨으로써, 상속 및 유효범위에 대한 규칙에 가외의 변경이 더해졌습니다. 전체적인 상속 규칙은 다음과 같습니다:
- 새 클래스는 부모 클래스로부터 모든 변수를 상속한다.
- 새 클래스는 부모 클래스의 비상태 함수를 모두 상속한다. 상속한 비상태 함수중의 어떤 것이라도 오버라이드 할 수 있다. 완전히 새로운 비상태 함수를 추가할 수 있다.
- 새 클래스는 상태 내의 함수 및 라벨을 포함하여 부모 클래스의 상태를 모두 상속한다. 상속한 상태 함수중의 어떤 것이라도 오버라이드 할 수 있으며, 새 상태 함수 및 새 상태 라벨을 추가할 수 있다.
다음은 모든 오버라이드 규칙에 대한 예입니다:
// 부모 클래스의 예. class MyParentClass extends Actor; // 비상태 함수. function MyInstanceFunction() { log( "MyInstanceFunction을 집행함"); } // 상태. state MyState { // 상태 함수. function MyStateFunction() { Log( "MyStateFunction을 집행함"); } // "Begin"라벨. Begin: Log("MyState를 시작함"); } // 자식 클래스의 예. class MyChildClass extends MyParentClass; // 비상태 함수를 오버라이드 함. function MyInstanceFunction() { Log( "자식 클래스에서 MyInstanceFunction을 집행함"); } // MyStateFunction 을 오버라이드 할 수 있도록 MyState를 재선언. state MyState { // MyStateFunction 오버라이드. function MyStateFunction() { Log( "MyStateFunction을 집행함"); } // "Begin"라벨을 오버라이드. Begin: Log( "MyChildClass에서 MyState를 시작함 "); }
글로벌로, 하나 또는그 이상의 상태에서 그리고 하나 또는 그 이상의 부모 클래스에서 구현된 함수를 가지고 있는 경우에는 주어진 컨텍스트에서 어느 버전의 함수가 호출되어야 하는지 이해해야 합니다. 이러한 복잡한 상황들을 해결하는 유효범위 규칙은 다음과 같습니다:
- 객체가 상태 내에 있고 함수의 구현이 그 상태 내 어딘가에(액터의 클래스 또는 어느 부모 클래스) 있을 경우에는 가장 많이 파생된 함수의 상태 버전이 호출됨.
- 그렇지 않으면, 함수의 가장 많이 파생된 비상태 버전이 호출됨.
고급 상태 프로그래밍
상태가 같은 이름의 부모 클래스 상태를 오버라이드 하지 않는 경우, 선택적으로 "extends" 키워드를 사용해서 상태가 현재 클래스 내의 기존 상태를 확장하도록 할 수 있습니다.이것은, 예를 들어, 많은 공통 기능을 가지고 있는 한 그룹의 비슷한 상태( MeleeAttacking과 RangeAttacking 처럼)를 가지고 있는 경우에 유용합니다. 이같은 경우에는 기초 Attacking 상태를 아래와 같이 선언할 수 있습니다:
// 기초 Attacking 상태. state Attacking { // 기초 함수... } // 밀착 공격. state MeleeAttacking extends Attacking { // 밀착 공격을 위한 함수... } // 멀리서 공격. state RangeAttacking extends Attacking { // 멀리서 공격하기 위한 함수... }
상태에서 선택적으로 ignores
지정자를 사용, 함수가 상태 내에 있는 동안 이를 무시하도록 할 수 있습니다. 구문은 다음과 같습니다:
// 상태 선언. state Retreating { // 다음의 메시지들을 무시함... ignores Touch, UnTouch, MyFunction; // 함수의 본체... }
액터가 어느 특정 상태에 있는지, 그 액터의 "name" 타입 변수 "state" 를 사용해서 알릴 수 있습니다.
GotoState('') 를 사용함으로써 액터를 “무상태” 로 하는 것도 가능합니다. 액터가 “무상태”일 때는 오직 그 글로벌(비상태) 함수만이 호출됩니다.
액터의 상태를 설정하기 위해 GotoState 명령을 사용할 때마다, 엔진은 두 개의 특별한 통지 함수, EndState() 와 BeginState() 를 호출합니다 (정의되어 있는 경우). EndState 는 현재의 상태에서 새 상태가 시작되기 직전에 호출되고, BeginState 는 새 상태가 시작된 직후에 호출됩니다. 이 함수들은 상태가 요구하는, 상태 특정의 초기화 및 뒷처리를 위한 편리한 장소를 제공합니다.
State Stacking(상태의 스택)
정상적으로 상태를 바꿀 때는 한 상태에서 다른 상태로 간 다음, 이전 그대로의 상태로 돌아갈 수 없습니다. 상태의 스택을 사용하면 이것이 가능합니다. PushState 함수를 호출하면 새 상태로 바뀌고 이것이 스택의 맨 위에 놓이게 됩니다. 현재의 상태는 동결됩니다. PopState 가 호출되면 이전의 상태가 복구되고 PushState 가 호출된 지점에서부터 집행을 계속합니다. PushState 는 가능한 경우 마치 잠재적 함수처럼 (오직 상태 코드 내에서만) 작용하므로, PushState 를 함수 내에서 호출하면 그 집행 행태가 다릅니다. 함수에서 이것을 호출하는 것은 코드의 집행을 방해하지 않는 반면 (함수 내에서 GotoState 를 호출하는 것과 매우 비슷), 상태 코드 내에서 이것을 호출하면 자식 상태가 스택에서 인출될 때까지는 집행이 보류됩니다 (이 또한, 상태 코드에서 GotoState 를 호출하는 것과 비슷합니다).
상태는 오직 한 번만 스택에 올려질 수 있습니다. 같은 상태를 두 번째로 스택에 올리려고 하면 실패합니다. PushState 는 GotoState 와 마찬가지로 작용, 상태의 이름과 상태의 진입점에 대한 선택적 라벨을 취합니다. 새 상태는 PushedState 이벤트를 받게 되며, 현재의 상태는 PausedState 이벤트를 받습니다. PopState 를 호출한 후 현 상태는 PoppedState 이벤트를, 새 상태는 (스택에서 그 다음에 있던 것) ContinuedState 를 받습니다.
state FirstState { function Myfunction() { doSomething(); PushState('SecondState'); // 이는 함수 내에 있으므로 즉시 집행될 것임 (잠재적 기능 없음) JustPushedSecondState(); } Begin: doSomething(); PushState('SecondState'); // 이것은 상태 코드 블록 내에 있으므로 SecondState가 인출된 후 집행될 것 (잠재적 기능) JustPoppedSecondState(); } state SecondState { event PushState() { // 푸시 되었음. 되밀침. PopState(); } }
IsInState 함수를 사용하면 특정 상태가 스택에 있는지 확인할 수 있습니다. 이 함수는 상태의 이름만을 확인하기 때문에 부모 상태를 확인하는데는 사용될 수 없습니다. 예:
state BaseState { ... } state ExtendedState extends BaseState { ... }
활성화 되어 있는 상태가 ExtendedState
이면 IsInState('BaseState') 는 false 를 반환합니다. 물론 IsInState('BaseState', true) 의 호출은 BaseState 가 스택에 있을 경우 true 를 반환합니다.
복제
UnrealScript 에서의 복제에 대한 자세한 정보는 네트워킹 개요 페이지를 참고하십시오.
반복 (ForEach)
UnrealScript 의 foreach
명령은 커다란 액터의 그룹, 예를 들어 레벨 내의 모든 액터, 또는 다른 액터와 일정 거리에 있는 모든 액터 등을 다루기 쉽게 해줍니다 . "foreach" 는 액터의 목록을 반복하는 것이 목적인 특별한 종류의 함수 "iterator" 와 함께 작용합니다.
다음은 foreach
의 간단한 예입니다:
// 레벨 내의 광원 목록을 모두 표시함. function Something() { local actor A; // 레벨 내의 모든 액터들을 거쳐감. log( "광원:"); foreach AllActors( class 'Actor', A ) { if( A.LightType != LT_None ) log( A ); } }
모든 foreach
명령의 첫 번째 매개변수는 어떤 종류의 액터를 검색할 것인지 지정하는 constant 클래스입니다. 이것을 검색을, 예를 들자면, 모든 Pawn 에 국한하는 것으로 제한하는데 사용할 수 있습니다.
foreach
명령에서 두 번째 매개변수는 foreach
루프를 통한 각 반복의 기간에 대해 액터에게 배정된 변수입니다.
다음은 "foreach"와 함께 작용하는 반복자 함수들의 총목록입니다.
AllActors ( class<actor> BaseClass, out actor Actor, optional name MatchTag )
레벨 내의 모든 액터들을 반복합니다. 선택적으로 MatchTag 를 지정할 경우에는, 지정한 태그와 일치하는 "Tag" 변수를 가진 액터들만이 포함됩니다.
DynamicActors( class<actor> BaseClass, out actor Actor )
레벨이 시작된 이래 스폰된 모든 액터들을 반복합니다. 레벨에 배치된 액터들은 무시합니다.
ChildActors( class<actor> BaseClass, out actor Actor )
이 액터가 소유한 모든 액터들을 반복합니다.
BasedActors( class<actor> BaseClass, out actor Actor )
이 액터를 베이스로 사용하는 액터들을 모두 반복합니다.
TouchingActors( class<actor> BaseClass, out actor Actor )
이 액터를 건드리는 (서로 궤뚫는) 모든 액터들을 반복합니다.
TraceActors( class<actor> BaseClass, out actor Actor, out vector HitLoc, out vector HitNorm, vector End, optional vector Start, optional vector Extent )
충돌 크기의 박스 Extent 를 사용하여, 시작 지점에서 끝 지점까지를 그리는 선을 건드리는 모든 액터들을 반복합니다. 각 반복 때마다 HitLoc 이 타격 위치에 설정되고, HitNorm 은 바깥쪽을 가리키는 hit 노멀로 설정됩니다.
OverlappingActors( class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc, optional bool bIgnoreHidden )
지정 위치의 지정 반경 (아무것도 지정된 것이 없을 경우에는 액터의 위치) 안의 모든 액터들을 반복합니다.
VisibleActors( class<actor> BaseClass, out actor Actor, optional float Radius, optional vector Loc )
지정된 위치 (아무것도 지정된 것이 없을 경우에는 액터의 위치) 에서 눈에 보이는모든 액터들을 반복합니다.
VisibleCollidingActors ( class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc, optional bool bIgnoreHidden );
(호출자의 Location으로 기본 설정된) Loc 에서부터 해당 액터의 위치까지의 자취가 세계에 부딪치지 않는 특정 반경 내의 충돌하는 액터들(bCollideActors==true) 을 모두 반환합니다. 충돌 해시를 사용하기 때문에 AllActors() 보다 훨씬 빠릅니다.
CollidingActors ( class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc );
특정 반경 내의 충돌하는 액터들(bCollideActors==true) 을 반환합니다. 충돌 해시를 사용하기 때문에 비교적 작은 반경에 대해서 AllActors() 보다 훨씬 빠릅니다.
주: 반복자 함수들은 모두 특정 클래스의 멤버들입니다. 따라서 액터가 아닌 함수에서 반복자를 사용하려면 반드시 액터 변수를 가지고 있어야 하며, 다음 구문을 사용해야 합니다:
foreach ActorVar.DynamicActors(class'Pawn', P)
그러면 Interaction 클래스에서 다음과 같이 할 수 있습니다:
foreach ViewportOwner.Actor.DynamicActors(class'Pawn', P)
주: 이제는 반복자가 동적 배열도 지원합니다. 고급 언어 기능 섹션을 참고하십시오.
함수 호출 지정자
복잡한 프로그래밍의 상황에서는 현재의 범위에 있는 함수보다는 특정 버전의 함수를 호출할 필요가 있을 때가 흔히 있습니다. 이러한 경우들을 처리하기 위해 UnrealScript 는 다음의 키워드들을 제공합니다:
- Global
- 해당 함수의 가장 많이 파생된 버전(비상태)을 호출합니다.
- Super
- 부모 클래스에서 해당하는 버전의 함수를 호출합니다. 호출되는 함수는 컨텍스트에 따라 상태일 수도 있고 비상태일 수도 있습니다.
- Super(classname)
- 지정된 (또는 보다 상위의) 클래스에 있는 해당 버전의 함수를 호출합니다. 호출되는 함수는 컨텍스트에 따라 상태일 수도 있고 비상태일 수도 있습니다.
복수의 지정자를 함께 사용하는 것은 (예: Super(Actor).Global.Touch) 허용되지 않습니다.
다음은 호출 지정자의 몇 가지 예입니다:
class MyClass extends Pawn; function MyExample( actor Other ) { Super(Pawn).Touch( Other ); Global.Touch( Other ); Super.Touch( Other ); }
추가의 예로, BeginPlay() 함수는 액터가 gameplay 에 들어갈 즈음에 호출됩니다. BeginPlay() 함수는 Actor 클래스에서 구현되었으며, 집행될 필요가 있는 몇 가지의 중요한 기능성을 가지고 있습니다. 이제 새 클래스인 MyClass 에 몇 가지 새 기능성을 추가하기 위해 BeginPlay() 를 오버라이드 하려 한다고 가정해 보십시오. 이것을 안전하게 하려면 부모 클래스에 있는 BeginPlay() 버전을 호출해야 합니다:
class MyClass extends Pawn; function BeginPlay() { // 부모 클래스에 있는 BeginPlay 버전을 호출(중요함). Super.BeginPlay(); // 사용자 지정 BeginPlay. //... }
변수 클래스에서의 static 함수 이용
변수 클래스에서의 Static 함수는 다음 구문을 사용해서 호출합니다.
var class C; var class<Pawn> PC; class'SkaarjTrooper'.static.SomeFunction(); // 특정 클래스에서 // Static 함수 호출. PC.static.SomeFunction(); // 변수 클래스에서 Static 함수 호출. class<Pawn>(C).static.SomeFunction(); // 캐스트된 클래스 표현에서 // Static 함수 호출.
변수의 기본값
변수의 기본값 이용
UnrealEd 는 레벨 디자이너들이 객체 클래스의 “기본” 변수를 편집하는 것을 허용합니다. 클래스의 새 액터가 스폰되면, 그 변수들은 모두 이들 기본값으로 초기화됩니다. 가끔은 수동으로 변수들을 그 기본값에 재설정하는 것이 도움이 됩니다. 예를 들면, 플레이어가 inventory 아이템을 떨어뜨리면, inventory 코드는 액터의 값 가운데 일부를 기본값으로 재설정할 필요가 있습니다. UnrealScript 에서는, "Default." 키워드로 클래스의 기본 변수에 접근할 수 있습니다. 예:
var() float Health, Stamina; //... // 몇몇 변수를 기본값으로 재설정. function ResetToDefaults() { // health 와 stamina를 재설정함. Health = Default.Health; Stamina = Default.Stamina; }
클래스 참조를 통한 변수의 기본값 이용
클래스 참조 (class
또는 class<classlimitor>
타입의 변수) 를 가지고 있는 경우에는, 해당 클래스의 객체를 가지지 않고도 그것이 참조하는 클래스의 기본 속성에 접근할 수 있습니다. 이 구문은 클래스 타입으로 평가되는 어떠한 표현과도 작용합니다.
var class C; var class<Pawn> PC; Health = class'Spotlight'.default.LightBrightness; // Spotlight 클래스에 있는 // LightBrightness의기본값을 이용. Health = PC.default.Health; // PC에 의해 식별된 변수 클래스에 있는 // Health의 기본값을 이용 . Health = class<Pawn>(C).default.Health; // 캐스트된 클래스 표현에 있는 // Health의 기본값 이용.
defaultproperties 블록을 사용하여 기본값 지정하기
UnrealEd 에서 속성창을 사용하여 Actor 의 속성에 대한 기본값을 설정하는 것 외에, 클래스의 defaultproperties 블록 내부에 특별한 배정식을 배치함으로써 멤버 변수에 기본값을 배정할 수도 있습니다.
- 동적 배열의 연산을 제외하고, defaultproperties 블록에서는 선언문이 허용되지 않습니다.
- 각 줄의 끝에 세미콜론을 붙일 수 있지만, 요구 사항은 아닙니다.
- 기본값은 자식 클래스에 상속됩니다. 자식 클래스의 defaultproperties 에서 지정된 값들은 부모 클래스에서 지정된 값들을 오버라이드 합니다.
구문
defaultproperties 블록의 구문은 표준 unrealscript 의 구문과 약간 다릅니다:
- 단순 타입(Ints, Floats, Bools, Bytes):
- VarName=Value
- 정적 배열:
ArrayProp(0)=Value1
ArrayProp(1)=Value2
또는ArrayProp[0]=Value1
ArrayProp[1]=Value2
- 동적 배열:
ArrayProp=(Value1,Value2,Value3)
또는ArrayProp(0)=Value1
ArrayProp(1)=Value2
ArrayProp(2)=Value3
또는ArrayProp.Add(Value1)
ArrayProp.Add(Value2)
ArrayProp.Add(Value3)
- 이름
NameProp='Value'
또는NameProp=Value
- 객체
ObjectProp=ObjectClass'ObjectName'
- 하위 객체
Begin Object Class=ObjectClass Name=ObjectName
VarName=Value
...
End Object
ObjectProperty=ObjectName
- 구조체 (Vector 포함):
StructProperty=(InnerStructPropertyA=Value1,InnerStructPropertyB=Value2)
또는StructProperty={(
InnerStructPropertyA=Value1,
InnerStructPropertyB=Value2
)}
- 인라인 정적 배열은 아래와 같이 선언돼야 합니다(배열의 구분자에 대해 괄호 “()” 대신 대괄호 “[]”가 사용된 것을 주목하십시오):
StructProperty=(StaticArray[0]=Value,StaticArrayProp[1]=Value)
- 인라인 동적 배열은 반드시 한 줄의 구문을 사용해서 선언되어야 합니다:
StructProperty=(DynamicArray=(Value,Value))
- 인라인 name 변수는 반드시 큰따옴표 안에 싸여 있어야 합니다:
StructProperty=(NameProperty="Value")
- 동적 배열 연산.다음은 부모에게서 물려받을수 있는 동적 배열의 내용을 변경하기 위해 사용됩니다.
Array.Empty
- 배열을 전부 지웁니다.Array.Add(element)
- 배열의 끝에 요소를 추가합니다.Array.Remove(element)
- 배열에서 요소를 제거합니다. 이는 배열 내에 나타나는 이 요소를 모두 제거합니다.Array.RemoveIndex(index)
- 주어진 인덱스에 있는 요소를 제거합니다.Array.Replace(elm1, elm2)
- elm1 을 elm2 로 대체합니다. 배열 내의 elm1이 모두 대체됩니다. elm1 이 발견되지 않으면 경고가 나옵니다.
다음의 예를 살펴보십시오 (Actor.uc 에서 따옴):
defaultproperties { // objects MessageClass=class'LocalMessage' // SpriteComponent 클래스의 인라인 하위 객체 "Sprite" 선언 Begin Object Class=SpriteComponent Name=Sprite // 이곳에서 지정된 값들이 SpriteComponent 의 defaultproperties 를 오버라이드 함 Sprite=Texture2D'EngineResources.S_Actor' HiddenGame=true End Object //할 일 Components.Add(Sprite) // CylinderComponent 클래스의 인라인 하위 객체 "CollisionCylinder" 선언 Begin Object Class=CylinderComponent Name=CollisionCylinder // 이곳에서 지정된 값들이 CylinderComponent 의 defaultproperties 를 오버라이드 함 CollisionRadius=10 CollisionHeight=10 AlwaysLoadOnClient=True AlwaysLoadOnServer=True End Object //할 일 Components.Add(CollisionCylinder) CollisionComponent=CollisionCylinder // floats (맨 앞의 '+'와 맨 끝의 'f' 캐릭터들은 무시됨) DrawScale=00001.000000 Mass=+00100.000000 NetPriority=00001.f // ints NetUpdateFrequency=100 // enumerations Role=ROLE_Authority RemoteRole=ROLE_None // structs DrawScale3D=(X=1,Y=1,Z=1) // bools bJustTeleported=true bMovable=true bHiddenEdGroup=false bReplicateMovement=true // names InitialState=None // dynamic array (이 경우에는,동적 클래스 배열) SupportedEvents(0)=class'SeqEvent_Touch' SupportedEvents(1)=class'SeqEvent_UnTouch' SupportedEvents(2)=class'SeqEvent_Destroyed' SupportedEvents(3)=class'SeqEvent_TakeDamage' }
구조체의 기본값
UnrealScript 에서 구조체를 선언할 때, 선택적으로 그 구조체의 속성에 대한 기본값을 지정할 수 있습니다. UnrealScript 에서 그 구조체가 사용될 때는 언제든지 그 멤버들이 이 기본값으로 초기화 됩니다. 구문은 클래스에 대한 defaultproperties 블록과 똑같습니다- 유일한 예외는 그 블록에 반드시 structdefaultproperties 라는 이름을 붙여야 한다는 점입니다. 예:
struct LinearColor { var() config float R, G, B, A; structdefaultproperties { A=1.f } };
UnrealScript 에서 변수 LinearColor 를 선언할 때는 언제든지, 그 A 속성의 값이 1.f로 설정되어야 합니다. 그밖에 이해해야 할 중요한 것은 structdefaultproperties 를 사용할 경우에는 클래스의 기본값이 구조체의 기본값을 오버라이드 한다는점입니다. LinearColor 타입의 클래스 멤버 변수를 가지고 있는 경우 클래스에서 그 멤버 변수에 값을 배정하면, defaultproperties 가 구조체의 기본에 있는 값을 오버라이드 합니다.
var LinearColor NormalColor, DarkColor; defaultproperties { NormalColor=(R=1.f,B=1.f,G=1.f) // 이 속성에 대한 A의 값은 1.0f가 될 것임 DarkColor=(R=1.f,B=1.f,G=1.f,A=0.2f) // 이 속성에 대한 A의 값은 0.2f임 }
동적 배열
앞에서 정적인 배열에 대해 살펴 보았습니다. 배열이 정적이라는 것은 컴파일 타임에 그 사이즈 (배열 내 요소의 수) 가 정해지고 변경될 수 없다는 뜻입니다.동적 배열과 정적 배열에는 다음과 같은 공통적 특징이 있습니다:
- 일정한 탐색 시간- 배열 내의 요소 수에 상관없이, 코드가 배열의 주어진 요소에 접근하기 위해 소비하는 시간은 같습니다.
- 무제한의 요소 타입- ints, vectors, Actors 등 어떤 타입의 배열이라도 가질 수 있습니다 (boolean은 예외로, 이는 동적 배열에서만 유효합니다).
- 접근 행태- 인덱스를 사용해서 배열 내의 어느 요소에라도 접근할 수 있습니다. 거꾸로, 배열의 범위 밖인 인덱스로 요소에 접근하려고 하면accessed none이 던져집니다.
동적 배열은 변화하는 요구를 수용하기 위해 런타임에 요소의 수를 변경하는 기능을 갖춘 정적 배열을 갖는 수단을 제공합니다. 동적 배열을 사용하기 위해서는 다음 몇 가지 사실을 알아두어야 합니다.
첫 번째는 변수의 선언입니다.동적 배열의 선언은 다른 unrealscript의 변수를 선언하는 것과 대부분 같습니다 (즉, var/local type varname). 동적 배열에서는, array 키워드에 이어 꺽음괄호 안에 배열의 타입을 넣음으로써 타입이 지정됩니다. 배열의 타입에도 꺽음 괄호가 들어 있으면 (예: class<Actor>), 타입의 닫힘 괄호와 배열 래퍼 사이에 여백을 두어야 합니다. 그렇지 않으면 컴파일러가 이중 닫힘괄호를 >>
연산자로 해석합니다. 예:
Int 타입의 동적 배열 IntList의 선언: var array<int> IntList;
class<PlayerController> 타입의 동적 배열 Players의 선언: var array<class<PlayerController> > Players;
스크립트가 시작되면, IntList 는 0개의 요소로 시작하게 됩니다. 배열에 요소 추가하기, 요소 빼내기, 배열의 길이를 임의로 늘리거나 줄이기 등을 위한 메소드들이 동적 배열에 의해 지원됩니다. 이 메소드들을 호출하는 구문은 (IntList 를 예로 사용): IntList.MethodName()
입니다. 이용할 수 있는 동적 배열 메소드들은 다음과 같습니다:
- Add(int Count): 배열의 길이를 Count 만큼 늘립니다. FArray::AddZeroed()와 똑같습니다.
- Insert(int Index, int Count): Index 는 요소가 삽입되는 배열 인덱스,그리고 Count 는 삽입할 요소의 수입니다. 그 위치에 있는 기존의 요소는 뒤로 밀려나고, 새 요소가 만들어져 지정된 자리에 삽입됩니다. 인덱스 3에서 5개의 요소를 삽입하는 것은 배열 내에서 인덱스 3의 요소부터 시작해서 5 개의 (인덱스 값을) 모두 밀어 올리게 됩니다. 이전에 인덱스 3의 위치에 있던 요소가 이제는 인덱스 8의 위치에 있게 되고, 요소 4는 이제 요소 9가 되는 식입니다. 새로 추가된 요소들은 모두 기본값으로 (structdefaultproperties를 가지고 있는 구조체를 제외하고 모든 타입에 대해 zero/null) 초기화 됩니다.
- Remove(int Index, int Count): Index 는 요소를 제거하기 시작할 배열 인덱스 이고, Count 는 제거할 요소의 수입니다. 이는 배열 내의 어떤 유효한 인덱스에서부터 시작하여 한 그룹의 요소를 배열에서 제거할 수 있도록 해줍니다. 제거할 요소들의 인덱스 범위보다 높은 인덱스는 모두 값이 변경됩니다. 동적 배열에 인덱스 값을 저장할 경우에는 이 점을 염두에 두십시오 .
- AddItem(Item): Item 을 배열의 끝에 추가, 배열의 길이를 1만큼 늘립니다.
- RemoveItem(Item): 선형 검색을 사용해서 Item 의 인스턴스를 모두 제거합니다.
- InsertItem(int Index, Item): 배열에 Item 을 Index 위치에 삽입, 배열의 길이를 1만큼 늘립니다.
- Find(...) - 배열에서 요소의 인덱스를 찾습니다. 이 Find 에는 두 가지 버전이 있습니다: 전체 요소의 값과 매치하는 것을 찾는 표준 검색, 그리고 구조체의 단일 속성 값을 기반으로 구조체를 매치하는 특수 버전입니다.
- Find(Value): Value 는 검색할 값입니다. 배열에서 검색된, 지정된 값과 일치하는 첫 번째 요소의 인덱스를 반환합니다. 배열에서 그 값이 발견되지 않으면 -1을 반환합니다. Value 는 어떠한 유효한 표현을 사용해서도 나타낼 수 있습니다.
- Find(PropertyName, Value): PropertyName 은 구조체 내에서 검색대상이 되는 속성의 이름입니다 (반드시 'Name' 타입이어야 함). Value 는 검색할 값 입니다. 배열에서 PropertyName 속성에 대해 지정된 값과 일치하는값을 가진 첫 번째 구조체의 인덱스를 반환합니다. 그 값이 발견되지 않으면 -1을 반환합니다. 유효한 표현은 모두 Value 에 사용될 수 있습니다.
- Sort(SortDelegate) - _SortDelegate_를 사용하여 배열의 내용들을 제자리에 정렬합니다. _SortDelegate_는 다음의 예와 일치하는 시그내처를 가져야 합니다:
- delegate int ExampleSort(ArrayType A, ArrayType B) { return A < B ? -1 : 0; } // 반환값이 마이너스인 것은 그 아이템이 교환되어야 한다는 것을 가리킵니다.
동적 배열에는 또한 Length
라는 변수가 있습니다. 이것은 동적 배열의 현재 길이 (요소의 수) 입니다. 앞서 예로 든 배열을 사용해서 Length에 접근하려면 IntList.Length
를 사용합니다. Length 변수는 단지 읽기만 할 수 있는 것이 아니라, 직접 설정하여 배열 내의 요소 수를 변경할 수도 있습니다. Length 변수를 직접 변경할 경우, 배열의 길이에 대한 변화는 모두 배열의 맨 ‘끝’에서 일어납니다. 예를 들어, IntList.Length = 5로 설정한 다음에 IntList.Length = 10으로 다시 설정하면, 방금 추가한 5개의 요소들이 배열의 맨 뒤에 추가되어, 원래의 5 요소는 그 값을 유지할 수 있습니다. Length 를 줄이면, 마찬가지로 요소가 뒤에서 잘립니다. Insert() 를 사용하거나 Length 를 늘임으로써 배열에 요소가 추가되면, 추가된 요소들은 변수 타입의 기본값 (int는 0, 클래스 참조는 None 등) 으로 초기화됩니다. 동적 배열의 길이를 또한 배열의 현재 Length 의 값보다 큰 요소 인덱스를 설정함으로써 늘릴 수 있다는 점도 주목할만 합니다. 이것은 Length를 더 큰 값으로 설정한 것처럼 배열을 연장합니다.
OldLength = Array.length
Array.Length = OldLength + 1
Array[OldLength] = NewValue
Array[Array.Length] = NewValue
Array.AddItem(NewValue)
위의 코드는 모두 같은 연산을 행하는 동등한 방식입니다.
그러나 배열의 길이(length) 증가하기와 배열의 멤버에 접근하기를 동시에 할 수는 없습니다.
Array[Array.length].myStructVariable = newVal
이 코드는 작용하지 않습니다.
경고의 말- 동적 배열의 Length 수치는 절대로 '++', '--', '+=', 또는 '-=' 에 의해 증가/감소 할 수 없습니다. 함수에 Length 를 (함수가 그 값을 변경할 수 있는) out 매개변수로서 전달해도 안됩니다. 이러한 일들을 하면 Length 가 더 이상 정확하지 않게 되어, 메모리 누출 및 크래시의 결과를 낳습니다; Length 를 '=' 연산자를 통해 설정 (그리고 Length 보다 큰 인덱스에 요소를 설정) 하는 것만이 동적 배열 의 실제 길이를 알맞게 변경하는 방법입니다.
주: array<bool>
은 지원되는 타입이 아닙니다!
맺는 말 - 동적 배열은 복제되지 않습니다. 두 개의 인수와 동적 배열에의 인덱스, 그리고 그곳에 저장할 값을 가진, 복제하는 함수를 가짐으로써 이를 극복할 수는 있습니다. 그러나 클라이언트와 서버에서의 틱 공간 내에서 요소가 똑같지 않게 되는 결과도 고려해야만 합니다.
동적 배열의 반복
현재 동적 배열은 단순한 반복을 허용하기 위해 'foreach' 연산자를 지원합니다. 기본 구문은 'foreach ArrayVariable(out ArrayItem,optional out ItemIndex) {}'로, 각 반복은 인덱스를 증분하고 속성이 제공된 경우 인덱스와 함께 아이템을 출력해 냅니다.
function IterateThroughArray(array<string> SomeArray) { local string ArrayItem; local int Index; foreach SomeArray(ArrayItem) { `log("배열 반복자 테스트 #1:"@ArrayItem); } foreach SomeArray(ArrayItem,Index) { `log("배열 반복자 테스트 #2:"@ArrayItem@Index); } }
인터페이스 클래스
인터페이스에 대한 상세한 정보는 UnrealScript의 인터페이스 페이지를 참고하십시오.
함수 델리게이트
델리게이트의 사용법에 대한 상세한 정보는 UnrealScript 의델리게이트 페이지를 참고하십시오.
Native 클래스
native 클래스에 대한 상세한 정보는 원시 클래스의 컴파일과 원시 클래스 작성 페이지를 참고하십시오.
메타데이터 지원
게임 내, 그리고 에디터 내의 기능성을 속성의 메타데이터를 통해 확장할 수 있습니다.
메타데이터 개요
UnrealScript 에서 임의의 메타데이터는 다음과 같이 속성에 연결됩니다:
변수:
var float MyVar<TAG=VALUE>
열거형:
enum EMyEnum { EME_ValA<TAG=VALUE>, EME_ValB<TAG=VALUE>, };
클래스:
다음의 UnProg3 메일링 리스트 스레드를 참고하십시오: https://udn.epicgames.com/lists/showpost.php?id=24834&list=unprog3
다수의 메타데이터 명세 사용
| 캐릭터로 구분함으로써 같은 속성에 대해 다수의 메타데이터 명세를 사용할 수 있습니다.
예:
var() LinearColor DrawColor<DisplayName=Draw Color|EditCondition=bOverrideDrawColor>;
이용 가능한 메타데이터의 명세
다음은 현재 지원되는 메타데이터의 태그들 및 그들이 하는 일입니다:
<ToolTip=TEXT_STRING>
에디터의 속성창에서 해당 속성에 마우스가 올려진 경우 TEXT_STRING
이 툴팁으로서 나타나도록 합니다.
주: 컴파일러에 의해 /** VALUE */
코멘트가 자동으로 ToolTip 메타데이터로 전환되는 지원이 최근 추가되었습니다.
<DisplayName=TEXT_STRING>
에디터의 속성창에서 속성의 이름이 실제 이름 대신 TEXT_STRING
으로 나타나도록 합니다.
예:
Var() bool bEnableSpawning<DisplayName=Spawning Enabled>;
경고: 에디터의 콤보박스에서 열거형을 정렬하기 위해 UPropertyInputCombo 를 변경할 경우 DisplayName 을 열거형으로 사용하는 것은 문제를 일으킬 수 있습니다.
다음의 UnProg3 메일링 리스트 스레드를 참고하십시오: https://udn.epicgames.com/lists/showpost.php?list=unprog3&id=24302
<EditCondition=ConditionalPropertyName>
이것은 에디터 속성의 편집 가능성 상태가 다른 속성의 값 (Boolean) 에 따라 유효화 되거나 무효화 되게 할 수 있도록 합니다.
예를 들면, UnrealScript 의 클래스 MyPackage.MyClass 에서 다음과 같은 설정을 할 수 있습니다:
/** 스폰하기를 유효화 또는 무효화*/ Var() bool bEnableSpawning; /** AI들이 스폰되는 속도 설정. bEnableSpawning = TRUE 가 아닌 한 효과 없음 */ Var() float RespawnsPerSecond<EditCondition=bEnableSpawning>;
그러면 에디터에서 bEnableSpawning 이 false 일 때마다 RespawnsPerSecond 가 회색으로 변합니다. 이것은 디자이너들을 조금 덜 혼동스럽게 해줍니다.
중요한 점: 이 메타데이터 설정은 제어된 변수 (RespawnsPerSecond) 가 커스텀 속성 아이템 바인딩 (WxCustomPropertyItem_ConditionalItem) 을 이용할 것을 요구합니다.
이것을 유효화 하려면,
[UnrealEd.CustomPropertyItemBindings] CustomPropertyClasses=(PropertyPathName="MyPackage.MyClass: RespawnsPerSecond ",PropertyItemClassName="WxCustomPropertyItem_ConditionalItem")
이에 관한 상세 정보는 https://udn.epicgames.com/lists/showpost.php?list=unprog3&id=30824를 참고하십시오.
<FriendlyName=TEXT_STRING>
이것이 내부에서만 사용되며 Unrealscript 에서 사용할 의도가 아니라는 것이 확실하지 않음. 아마 EPIC 이 명백히 해주지 않을까?
<AllowAbstract>
Class 속성에 있는 경우, 그 속성의 편집을 위한 에디터의 드롭다운 박스가 abstract 클래스를 포함합니다. 존재하지 않으면, 드롭다운 박스는 견고한 클래스만을 포함하게 됩니다. 이 메타데이터 명세에서는 True 나 False 같은 값을 지정할 필요가 없습니다.
<AutoComment=BOOLEAN_VALUE>
Kismet Sequence Action 의 속성에 추가되었을 때, 자동으로 속성 및 그 현재 값이 코멘트로서 해당 액션의 위에 나타납니다. 이 액션을 보기 위해서는 새 "Gate" 시퀀스 액션을스크립트에 넣습니다. 이 경우에는 bOpen 과 AutoCloseCount 가 이 메터데이터 옵션을 사용합니다.
고급 기술적 문제
UnrealScript의 구현
컴파일 과정 에서부터 집행, 그리고 바이트 코드 표현에 이르기가지, UnrealScript 가 배후에서 어떻게 작용하는지에 대한 상세한 정보는 UnrealScript 의 구현 페이지를 참고하십시오.
UnrealScript 의 바이너리 호환성 문제
UnrealScript 는 패키지 파일 내의 클래스들이 바이너리 호환성을 저해하는 일 없이 서서히 발전하도록 디자인되었습니다. 여기서 바이너리 호환성이란 “개별적인 바이너리 파일들이 오류 없이 로드되고 또 링크되는 것” 을 의미합니다; 변경한 코드가 의도했던 대로 기능을 발휘하는지의 여부는 별도의 문제입니다. 구체적으로, 안전하게 행해질 수 있는 변경의 종류는 다음과 같습니다:
- 패키지 내의 .uc 스크립트 파일들은 바이너리 호환성을 저해하는 일 없이 재컴파일 할 수 있음.
- 패키지에 새 클래스를 추가하는 일.
- 클래스에 새 함수를 추가하는 일.
- 클래스에 새 상태를 추가하는 일.
- 클래스에 새 변수를 추가하는 일.
- 클래스에서 private 변수를 제거하는 일.
다음의 일들을 포함한 그밖의 변환들은 대체적으로 안전하지 않습니다 (그러나 이것들에 만 한정되는 것은 아닙니다):
- 구조체에 새 멤버를 추가하는 일.
- 패키지에서 클래스를 제거하는 일.
- 변수, 함수의 매개변수 또는 반환값의 타입을 바꾸는 일.
- 함수에서 매개변수의 수를 바꾸는 일.
기술 노트
가비지 수집. Unreal 의 모든 객체와 액터들은 Java VM과 비슷한 tree-following 가비지 수집자를 사용해서 가비지 수집됩니다. Unreal의 가비지 수집자는 UObject 클래스의 직렬화 기능을 사용, 그밖에 어느 객체들이 각 활성적 객체에 의해 참조되는지 되풀이해서 결정합니다. 그 결과, 객체들을 명시적으로 삭제할 필요가 없습니다. 객체들이 참조되지 않는 상태로 되면 가비지 수집자가 그것들을 찾아내어 없애주기 때문입니다. 여기에는 참조되지 않는 객체의 삭제가 지연되는 부작용이 있습니다; 그러나 이것은 빈번하지 않은 삭제의 경우 참조를 계수하는 것보다는 한층 더 효과적입니다. 상세한 정보는 가비지 수집 페이지를 참고하십시오.
UnrealScript 는 바이트 코드에 기반을 두고 있습니다. UnrealScript 의 코드는 p-code 또는 Java 바이트코드와 비슷한 일련의 바이트 코드로 컴파일됩니다. 이것은 UnrealScript 가 플랫폼 중립적이 되게 합니다; Unreal 의 클라이언트 및 서버 컴포넌트를 다른 플랫폼, 즉 Macintosh 나 Unix 로 이식하는 것은 간단하며, 모든 버전이 같은 스크립트를 집행함으로써 쉽게 상호연동할 수 있습니다.
가상 머신으로서의 Unreal. Unreal 엔진은 Java 언어 및 내장된 Java 의 클래스 계층구조가 웹페이지 스크립팅을 위한 가상 머신을 정의하는 것과 같은 면에서 3D 게임을 위한 가상 머신으로 간주될 수 있습니다. Unreal 가상 머신은 (플랫폼 종속적인 코드를 모두 별도의 모듈에 분리했기 때문에) 천부적으로 이식이 가능하고 (확장이 가능한 클래스 계층구조 덕분에) 확장성이 있습니다. 그렇지만, 현재로서는 Unreal VM 을 다른 이들이 독립적이고 호환성 있는 구현을 만들어내는데 필요한 정도까지 문서화할 계획이 없습니다.
UnrealScript 컴파일러는 3 패스입니다. C++ 와 달리, UnrealScript 는 3개의 뚜렷이 다른 패스에서 컴파일됩니다. 첫 패스에서는 변수, 구조체, 열거형, 불변수, 상태 및 함수의 선언을 해석하고 기억합니다; 각 클래스의 뼈대가 구축됩니다. 두 번째 패스에서는 스크립트 코드가 바이트 코드로 컴파일됩니다. 이것은 원형 종속성을 가진 복잡한 스크립트 계층구조가 완전하게 컴파일되어 별도의 연결 단계 없이 두 패스에 연결되도록 해줍니다. 세 번째 패스는 .uc 파일의 defaultproperties
블록에서 지정된 값들을 사용해서 클래스에 대한 기본 속성들을 해석하고 임포트합니다.
지속적인 액터의 상태. Unreal 에서는 사용자가 언제라도 게임을 저장할 수 있기 때문에, 모든 액터의 상태가 (그들의 스크립트 집행 상태를 포함하여) 그들이 UnrealScript 스택의 가능한 최저 레벨에 있을 때에만 저장된다는 것을 이해하는것이 중요합니다. 이 지속성에 대한 요구는 잠재적 함수가 상태 코드에서만 호출되어야 한다는 한계의 숨은 이유입니다: 상태 코드는 가능한 최저 스택 레벨에서 집행되며, 따라서 쉽게 직렬화될 수 있습니다. 함수 코드는 어떤 스택 레벨에도 존재할 수 있으며, 스택에서 그 아래에 (예를 들자면) C++ 의 원시 함수를 가질 수 있습니다. 이것은 분명히 디스크에 저장하고 나중에 복구할 상황이 아닙니다.
Unrealfile 은 Unreal 의 원시 바이너리 파일 포맷입니다. 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 를 참고하십시오.
UnrealScript 프로그래밍 전략
이제 UnrealScript 코드를 효율적으로 작성하고, 난관을 피해가면서 UnrealScript의 장점을 활용하는 방법에 대한 몇 가지 주제를 다루려 합니다.
UnrealScript 는 C/C++보다 느린 언어입니다. 전형적인 C++ 는 UnrealScript 보다 20배 빠르게 실행됩니다. 모든 스크립트 작성에 대한 저희의 프로그래밍 철학은 거의 항상 대기하는 상태의 스크립트를 쓰는 것입니다. 다시 말해서, UnrealScript 를 오직 여러분이 커스터마이즈하기 원하는 “흥미로운” 이벤트를 처리하기 위해서만 사용하는 것입니다. 기본 동작 같은 기계적인 과제는 Unreal 의 물리 코드가 처리해 줍니다. 예를 들어 발사하는 스크립트를 쓸때는 대개 주요 이벤트가 일어났을 때 무엇을 할지 설명하는 HitWall(), Bounce(), 그리고 Touch() 함수를 작성하게 됩니다. 따라서 발사 스크립트는 95% 의 시간동안은 아무런 코드도 집행하지 않고 물리 코드가 이벤트를 통지해 주기를 기다리고 있습니다. 이것은 사실 매우 효율적입니다. 대부분의 레벨에서 UnrealScript 는 C++ 와 비교할 때 훨씬 느리지만, UnrealScript 의 집행시간은 평균적으로 CPU 시간의 5-10% 입니다.
(FinishAnim 및 Sleep 같은) 잠재적 함수를 되도록 많이 이용하십시오. 스크립트 흐름의 기반을 이들 잠재적 함수에 둠으로써 UnrealScript 에서 아주 효율적인 , 애니메이션 또는 시간 주도적 코드를 작성할 수 있습니다.
스크립트를 테스트할 때는 Unreal의 로그를 주시하십시오. UnrealScript 의 런타임은 치명적이지 않은 문제점의 발생을 알리는 유용한 경고들을 자주 로그에 내보냅니다.
무한 재귀를 야기할 수 있는 코드를 조심하십시오. 예를 들어, "Move" 명령은 무언가를 타격했을 때 액터를 이동하고 Bump() 함수를 호출합니다. 따라서, Bump 함수 내에서 Move 명령을 사용할 때는, 끝없는 재귀에 대한 위험이 있습니다. 무한 재귀와 무한 루프는 UnrealScript 가 잘 처리하지 않는 두 가지 오류 컨디션입니다. 주의하십시오.
액터의 스폰과 파괴는 서버측에서 매우 부담이 많이 가는 작업이며, 스폰과 파괴는 네트워크 대역폭을 차지하기 때문에 네트워크 게임에서는 더 부담이 큽니다. 이것들을 분별있게 사용하고, 액터들을 "중량의" 객체로서 취급하십시오. 예를 들어,물리 코드에서 100 개의 독특한 액터를 스폰하여 이들을 각각 다른 궤도로 내보내는 입자 시스템을 작성하지 마십시오. 이것은 아주아주 느릴 것입니다.
UnrealScript 의 객체 지향적 특성을 되도록 많이 활용하십시오. 기존의 함수 및 상태를 오버라이드함으로써 새 기능을 만드는 것은 변경하기 쉽고 다른 사람들이 한 작업과 통합하기 쉬운 깔끔한 코드를 가지게 되는 것입니다. 액터나 상태의 클래스를 근거로 switch() 문을 사용하는 것 같은 종래의 C 테크닉 사용을 삼가십시오. 이같은 코드는 새 클래스를 추가하거나 변경을 가하면 깨지는 경향이 있습니다.
UnrealScript 의 .u 패키지들은 .ini 파일의 EditPackages 목록에서 지정된 순서 그대로 컴파일됩니다 따라서 각 패키지는 오직 그 자체 및 이전에 컴파일된 패키지 내의 다른 객체만을 참조할 수 있으며, 이어서 컴파일되는 패키지 내의 것은 절대 참조할 수 없습니다. 패키지간의 원형 참조의 필요성이 생기는 경우에는 다음 두 가지 해결책이 있습니다:
-
- 클래스를 첫 번째 .u 패키지에서 컴파일된 베이스 클래스의 세트와 두 번째 .u 패키지에서 컴파일된 자식 클래스의 세트로 분해합니다. 이때 베이스 클래스가 절대 자식 클래스를 참조하지 않도록 합니다. 이것은 좋은 프로그래밍 습관이며 대개 잘 작용합니다.
주어진 클래스 C가 나중에 컴파일된 패키지의클래스 또는 객체 O를 참조할 필요가 있을 경우,흔히 해당 클래스를 두 부분으로 분해할 수 있습니다: 첫 번째 패키지에서 변수 MyO 를 정의하는 abstract 베이스 클래스 정의 C (그러나 기본 속성에 MyO 에 대한 기본값을 가지고 있지 않은), 그리고 두 번째 패키지에서, MyO 의 알맞은 기본값을 지정하는 (이 두 번째 패키지에서만 할 수 있는) 하위클래스 D입니다. - 만일 두 개의 .u 패키지가 풀 수 없을 정도로 참조에 의해 얽혀있다면, 이 둘을 하나의 패키지로 병합합니다. 이것이 합리적입니다. 왜냐하면 패키지들은 코드 모듈화의 한 단위로서 의도된 것이므로, 분리할 수 없는 클래스의 세트를 여러 개의 패키지로 나누는 것은 사실상의 이득(메모리 절약 등)이 없기 때문입니다.
- 클래스를 첫 번째 .u 패키지에서 컴파일된 베이스 클래스의 세트와 두 번째 .u 패키지에서 컴파일된 자식 클래스의 세트로 분해합니다. 이때 베이스 클래스가 절대 자식 클래스를 참조하지 않도록 합니다. 이것은 좋은 프로그래밍 습관이며 대개 잘 작용합니다.
'engine' 카테고리의 다른 글
스크립트 튜토리얼 (0) | 2010.08.26 |
---|---|
UDKEngine.ini 파일의 EditPackages와 ModEditPackages의 차이점 (0) | 2010.08.26 |
nFringe(언리얼 스크립트 비주얼 IDE) (0) | 2010.08.26 |
kismet에서 set vectorparam 없어짐 문제 (0) | 2010.08.26 |
Unreal Engine 구성 하기 (0) | 2010.08.26 |