UDN
Search public documentation:

UT3ModsKR
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 홈 > 모드 홈 > 언리얼 토너먼트 3 모드 홈 > 언리얼 토너먼트 3 모드 제작하기

언리얼 토너먼트 3 모드 제작하기


소개

Unreal Engine용 모드 개발은 매우 보람이 큰 작업이 될 수 있습니다. 현재 게임 업계의 상황에서 보면 능력 있는 프로그래머나, 아티스트를 꿈꾸는 제작자가 자신의 능력을 세계에 보여 줄 수 있는 최고의 기회가 됩니다. 이 문서는 모드 제작에 관심이 있는 제작자들에게 성공적인 Unreal Engine 게임 모드 제작에 필요한 정보를 제공하기 위해 작성되었습니다. 이 문서에서는 기술적인 정보 외에도 모드 개발을 위한 조언을 제공합니다. 열심히 살펴보면 모드 제작에 활용할 수 있는 여러 흥미로운 내용을 발견할 수 있습니다.

시작을 위한 몇 가지 조언

모드 개발을 시작할 때는 작은 것에서 출발해야 합니다. 맨 처음부터 완전히 새로운 Total Convesrion(TC) 모드를 작성하려고 계획하지는 마십시오. 너무 이루기 어려운 목표를 설정하면 진행 중에 지쳐 결코 실현하지 못하게 될 수도 있습니다. 여러 작은 목표를 설정한 다음 차례로 하나씩 이루어 가는 편이 훨씬 낫습니다. 더 큰 게임으로 확대될 수 있는 단순한 아이디어부터 시작하십시오. 항상 자체적으로 릴리스할 수 있고 관리 가능한 작은 부분을 작업합니다. 대규모 프로젝트를 맡은 경우 릴리스 일정에 맞게 기능을 나누십시오. 게임에 5가지 신무기가 있다면 다른 것을 작업하는 동안 두세 가지를 릴리스해 봅니다. 페이스를 조절하고 장기적인 것을 고려합니다.

모두에게는 아이디어가 있습니다. 모든 사람들은 자신이 누구도 생각 못한 혁신적인 새로운 게임 컨셉트를 갖고 있다고 생각합니다. 게임 업계에서 그럴 듯한 아이디어를 갖고 있다고 해서 모든 것이 다 되는 경우는 없습니다. 아이디어를 구현할 수 있거나 이를 위한 유용한 기술을 제공할 수 있어야 합니다. 모드 제작에서도 마찬가지입니다. 능력 있거나 유명한 모드 제작자가 된다면 자신의 아이디어를 구현해 달라고 제안하는 사람들이 있을 것입니다. 아이디어를 낸 사람이나 그 리더가 명확한 개발 기술을 갖추지 못한 프로젝트에는 참여하지 마십시오. 웹 디자이너만 있는 프로젝트에도 참여하지 마십시오. 자신에게는 고유한 아이디어가 있습니다. 이러한 아이디어를 작은 부분으로 나누어 주의 깊게 집중한다면 훌륭한 프로젝트를 개발할 수 있습니다.

릴리스할 모드를 개발해야 한다는 점을 명심하시기 바랍니다. 신속하게 무언가를 릴리스한 후 모드가 완성되는 동안 기능을 추가 및 개선할 수 있도록 업무 목록을 조절하십시오. 모든 것이 완벽해질 때까지 모드를 그냥 가지고 있으면 아무 것도 릴리스할 수 없습니다.

아이디어가 있다면 그에 적합한 모드 유형을 선택해야 합니다. 그런 다음 모든 것을 설정하고 비전을 현실로 만드는 작업을 시작할 수 있습니다.

모드 프로젝트 설정

이제 실질적인 제작을 시작할 차례입니다. 먼저 Unreal Engine 게임이 일반적으로 설치되는 방식을 이해해야 합니다. 그런 다음 모드가 부합하는 게임 내 위치와, Unreal Engine의 컨텐트(기존 및 신규 모두) 처리 방식을 파악합니다. 이후 접근 방식에 따라 모든 것을 설정하고 모드 제작을 시작할 수 있습니다.

디렉토리 구조

기본적으로 Unreal Tournament 3은 다음 디렉토리에 설치됩니다.

<InstallDrive>:\Program Files\Unreal Tournament 3

또한 각 사용자에 대한 사용자 설정은 다음과 같이 저장됩니다.

<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\

실행 및 테스트

바탕화면 아이콘, Start 메뉴 또는 다음 명령을 통해 게임을 실행할 수 있습니다.

UTGame

게임 에디터는 Start 메뉴나 다음 명령을 통해 실행할 수 있습니다.

UTGame editor

유용한 콘솔 명령들

help - 주어진 명령에 대한 정보를 제공함. 유용한 매개 변수에는 ==list==가 있습니다.

UTGame help [명령]

run - Commandlet을 실행함. TestCommandlet란 코맨들릿의 경우 ==Test==를 사용하면 ==Commandlet==이 명령에 의해 추가됩니다.

UTGame run [코맨들릿 이름]

패키지

이제 Unreal Tournament를 설정하여 프로젝트를 구성할 시점입니다. 무엇보다도 먼저 UnrealScript가 패키지를 어떻게 사용하는지에 대한 이해가 필요합니다.

패키지는 게임 리소스의 집합입니다. 리소스는 텍스처, 모델, 애니메이션 사운드, 음악, UI 장면 등의 7가지 자산 유형에 해당할 수 있습니다. 모든 리소스에서 패키지 형식이 동일하며 한 패키지에서 여러 리소스 유형을 혼합할 수 있습니다. 당연히 Unreal Tournament도 패키지를 리소스로 구분합니다. 텍스처 디렉토리는 텍스처가 있는 패키지를, 사운드 디렉토리는 사운드가 있는 패키지를 포함하는 식입니다. 이러한 패키지가 다양한 컨텐트 유형을 포함할 수는 있지만 파일 확장자(.upk)는 모두 같으며 파일 종류 역시 같습니다.

프로그래머는 UnrealScript로 작업하고 컴파일된 게임 코드 패키지(.u) 파일을 처리할 수 있습니다. 코드 패키지는 주로 컴파일된 UnrealScript를 포함하나 코드가 의존하는 텍스처와 사운드를 포함할 수도 있습니다.

자세한 내용은 Unreal Packages 페이지를 참조하십시오.

프로그래밍

시작을 위해서는 자신의 아이디어에 대한 많은 연구가 필요합니다. 자신이 사용 가능한 코드와 프레임워크에 익숙해지는 데 시간을 소요한다면 그저 뛰어난 UnrealScript 프로그래머가 될 뿐입니다. 창의적인 방식으로 모드 설계 목표와 관련한 문제를 해결하는 방법을 학습하고 자신이 사용 가능한 도구 능력을 극대화하기 위해 노력하십시오.

UnrealScript

UnrealScript는 객체 지향(OO) 언어입니다. OO에 대해 잘 알지 못한다면 객체 지향 프로그래밍에 대한 안내 자료( http://www.orangesmoothie.org/tuts/GM-OOtutorial.html)를 살펴볼 시점입니다. 이 자료는 좀 오래되었으나 여전히 훌륭합니다.

UnrealScript가 객체 지향 언어이므로 원래 소스를 편집하지 않습니다. 이것이 다른 게임 엔진과 다른 점이 될 것입니다. Unreal에서는 Unreal Tournament에 들어 있는 클래스를 서브클래스화하여 자신의 필요에 따라 덮어쓰고 확장합니다.

UnrealScript에 대한 전체 참고 자료는 UnrealScript Reference 페이지를 참조하십시오.

UnrealScript 소스 파일

최신 UnrealScript 소스 파일은 http://udn.epicgames.com/Files/UT3/Mods/UT3ScriptSource_1.2.rar에서 다운로드할 수 있습니다.

이전 버전 제공 위치

패치가 릴리스되면 패치 업데이트에 부합하는 스크립트 소스 업데이트가 수행됩니다. 이러한 패치는 패치 릴리스로부터 며칠 후에 제공되며 이전 버전은 아카이브로 보관됩니다.

모드 프로젝트의 UnrealScript 소스 코드는 다음과 같은 위치에 배치됩니다.

<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\Src\YourModName\Classes

참고: Src\YourModName\Classes 디렉토리 구조를 생성해야 할 수 있습니다.

UnrealScript 소스를 컴파일하기 전에 먼저 UTEditor.ini를 열고 [ModPackages] 섹션 밑에 다음 행을 추가하여 스크립트 모드 이름을 엔진에 등록해야 합니다.

ModPackages=YourModName

게임의 UnrealScript 컴파일러는 다음 명령으로 실행할 수 있습니다.

UTGame make

... 또는 전체를 다시 빌드할 경우 다음 명령을 사용합니다.

UTGame make -full

참고: 핵심 UT UnrealScript 클래스는 절대 수정하지 마십시오.

컴파일된 UnrealScript 패키지는 다음과 같은 위치에 배치됩니다.

<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\Unpublished\CookedPC\Script\YourModName.u

참고: Script 디렉토리를 생성해야 할 수 있습니다.

모드 패키지에 대한 구성 파일이 자동으로 생성되어 다음과 같은 위치에 배치됩니다.

<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\Config\UTYourModName.ini

구성 파일 자동 생성에서는 UT* 클래스의 서브클래스만 탐지합니다. 이 클래스만이 제어를 위한 bExportMenuData 플래그를 갖기 때문입니다. 모드 이름이 UT 접두어로 시작되지 않는다면 이름이 변경됩니다. 모드가 UT* 클래스의 서브클래스를 갖지 않는 경우 직접 구성 파일을 생성해야 합니다.

에디터를 열면 에디터 시작 시 UTEditor.ini ModPackages 목록의 모든 모드 패키지 목록이 자동으로 로드되므로 모든 배치 가능한 Actor 클래스가 Actor Browser? 에 자동으로 표시됩니다. 이러한 작동을 중지하려면 =-nomodautoload 플래그를 사용하여 에디터를 실행합니다.

게임과 관련한 방대한 UnrealScript 소스 코드 작업에 많은 시간을 쏟고자 할 수도 있습니다. 실행 경로를 추적하고 다양한 클래스가 덮어쓰고 서로 상호 작용하는 방식을 살펴보십시오. 처음에는 매우 두려울 수도 있지만 시간이 지나면 그 위치와 작동에 익숙해질 것입니다. 온라인에 나서기를 두려워하지 말고 커뮤니티 메일링리스트, 포럼 등에서 질문하십시오. 시간을 투자하면 배울 수 있고 더 크고 까다로운 프로젝트를 처리할 수 있는 능력으로 보상받게 됩니다.

컨텐트 제작

레벨

게임 에디터를 사용할 때는 레벨이 다음과 같은 디렉토리에 저장됩니다.

<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\Unpublished\CookedPC\CustomMaps

게임 메뉴에 레벨을 보이게 하려면 CustomMaps의 하위 디렉토리에 저장해야 합니다. CustomMaps 디렉토리 자체에 직접 저장된 모든 항목은 무시되므로 모드 이름에 적합한 디렉토리를 생성합니다.

레벨을 저장할 때는 올바른 GameType 접두어(예: DM-, VCTF- 등)를 사용하십시오.

기본적으로 게임은 Published 디렉토리에 있는 사용자 지정 컨텐트를 찾습니다. 다음과 같은 두 가지 옵션이 있습니다.

  1. =UT3.exe –useunpublished를 실행하여 게임이 Unpublished 디렉토리 밖에서 실행하도록 합니다. 이 방법은 개발 중 회전 시간이 가장 우수합니다.
  2. 에디터의 Publish 버튼을 사용합니다. 그러면 Unpublished의 컨텐트가 Published에 복사되어 게임이 자동으로 탐지할 수 있습니다.

게임을 처음 실행할 때 CustomMaps 폴더의 하위 디렉토리에 새 레벨이 있는지 탐지하고 해당 레벨을 게임 내 UI에 등록하기 위해 레벨별 구성(.ini) 파일을 자동으로 생성합니다. 이러한 항목을 편집하여 레벨 설명 등을 제공할 수 있습니다.

Unreal Front End를 사용하여 모드를 쿠킹할 때는 'Cook Mod' 옵션을 선택하도록 합니다.

게시 및 쿠킹

CookedPC 이름 규칙은 다소 복잡해 보일 수 있으나 모든 것이 올바르게 설정되었다면 상대적으로 간단한 프로세스입니다. Cooking 은 맵 파일에서 불필요한 컨텐트를 빼내고 올바른 로드와 맵 실행에 필요한 쉐이더 캐시 정보를 생성하며 성능 및 로드 시간을 개선합니다. Publishing 은 생성한 것을 다른 사람들이 즐길 수 있도록 배포용 파일을 설정하고 모든 항목을 올바른 디렉토리에 배치합니다.

PublishedUnpublished 동작 관련: 게임 에디터에 저장된 컨텐트는 기본적으로 Unpublished 디렉토리에 기록됩니다. 이는 모드 저작자를 위한 작업 디렉토리로, 위치는 다음과 같습니다.

<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\
<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\Script\
<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\CustomMaps\
<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\CustomChars\
<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\Localization\<LANGUAGE>\

Published 디렉토리의 위치는 다음과 같습니다.

…\Documents and Settings\<UserID>\My Documents\My Games\Unreal Tournament 3\UTGame\Published\CookedPC\

에디터에서 게임을 저장하면 UnPublished 폴더가 생성됩니다. 파일을 게시하면 Published 폴더가 생성됩니다. 이 디렉토리 중 하나가 없으면 직접 만들어야 합니다. 맵을 다운로드하여 플레이하려면 Published 폴더에 넣어야 게임이 해당 맵을 인식하여 메뉴에 표시합니다. 올바른 디렉토리에 있지 않은 파일은 게임에서 인식할 수 없으며 올바르게 로드되거나 메뉴에 표시되지 않습니다.

게임 실행 시 -useunpublished 플래그를 지정하지 않은 한 게임은 기본적으로 Published 디렉토리의 컨텐트를 실행합니다. 일반적인 모드 작업 절차는 스크립트와 맵에 대한 개발을 반복하고 -useunpublished=로 게임을 실행하는 것입니다. 모드가 준비되면 마지막 Publish 에서 쿠킹한 컨텐트를 Published 디렉토리에 기록하고 이렇게 로드 최적화된 파일을 배포에 사용합니다. 스크립트와 현지화된 텍스트 파일은 직접 복사해야 합니다.

또는 모드 제작자가 컨텐트 변경 수행 시 Publish 를 실행하여 -useunpublished 플래그 없이 개발 중인 컨텐트를 실행할 수 있습니다. 이런 식으로, 게임이 자동으로 인식할 수 있는 Unpublished 디렉토리에 쿠킹되지 않은 모드 컨텐트를 복사합니다. 위의 경우 모드 배포 전에 마지막 Publish 를 수행하는 것이 좋습니다.

요약하면 -useunpublished 플래그는 작업 중에 계속 파일을 복사하지 않아도 되는 모드 개발자의 바로가기입니다.

현지화

모드 패키지의 현지화된 텍스트 파일을 생성하여 다음 디렉토리에 배치해야 합니다.

<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\Localization\<LANGUAGE>\YourModName.int

배포

과거에는 Unreal Engine의 commandlet을 통해 모드를 패키지화하여 모드 설치 프로그램 파일(UT의 경우 .umod, UT2003 및 UT2004의 경우 .ut2mod)을 생성했습니다. 이제 WinRAR 와 같은 유틸리티를 통해 압축 아카이브로 간단하게 모드를 패키지화할 수 있습니다.

이름 지정 규칙

일관성과 관리성을 위해 프로젝트 개발 초기 단계에서 코드 표준과 같은 여러 이름 규칙과 기타 모범 사례를 설정하는 것이 좋습니다. 이렇게 하면 여러 팀원 간에 일관된 작업이 가능합니다.

프로그래밍의 경우 다음과 같은 이름 규칙을 권장합니다.

  • UTMutator_ - UT3 Mutator 클래스용
  • UTGame_ - UT3 GameType 클래스용
  • UTGameRules_ - UT3 GameRules 클래스용(Mutator나 GameType에서 사용)
  • UTMod_ - 기타 UT3 모드용, 패키지 및 관련 디렉토리에도 유효함

Total Conversion의 경우 UT 모드로 간주된다 하더라도 프로젝트 이름이 UT와 무관할 수 있으므로 이와 다를 수 있습니다.

패키지와 관련 디렉토리의 이름을 정할 때는 클래스 파일에 이름에 국한되지 말고 생각하는 것과 일관된 규칙을 고려합니다. 예를 들어 이름이 Jailbreak 인 새 GameType을 만들 경우 패키지와 관련 디렉토리의 이름을 핵심 게임 유형 클래스 이름(UTGame_Jailbreak와 유사)이 아니라 UTMod_Jailbreak 또는 간단히 Jailbreak로 정할 수 있습니다. 뮤테이터 집합을 만들 경우 패키지와 관련 디렉토리 이름을 UTMod_MyMutators 또는 간단히 MyMutators로 정할 수 있습니다.

컨솔

PlayStation 3 모드 지원

PlayStation 3 컨솔용 사용자 제작 컨텐트를 만드는 자세한 방법은 PS3 모드 페이지를 참조하십시오.

4가지 모드 유형

Mutator

뮤테이터는 미니 모드로, Mutator 클래스에서 정의한 제한적인 기능을 갖습니다. 뮤테이터는 GameType에서 정한 특정 규칙을 따라야 합니다. 이러한 규칙이 따를 수 없는 것이거나 너무 제한적인 경우 GameType 모드에 대한 작업이 필요합니다.

첫 번째 규칙은, 뮤테이터는 다른 뮤테이터, 특히 게임과 함께 제공되는 뮤테이터와의 상호 작용이 가능해야 한다는 것입니다. 플레이어가 자신이 총을 쏜 적으로부터 생명을 빼낼 수 있도록 하는 "흡혈귀" 뮤테이터를 작성한다면 이 뮤테이터는 경기장 뮤테이터 중 하나 또는 작동 없음 뮤테이터와 결합했을 때 잘 작동해야 합니다. 이는 뮤테이터 시스템의 장점 중 하나라고 할 수 있습니다. 게임플레이를 약간 바꾸어(또는 변이를 일으켜 ) 흥미로운 조합을 가능하게 합니다.

두 번째 규칙은, 뮤테이터는 아주 가볍게만 게임플레이를 변경해야 한다는 것입니다. 다소 모호하게 느껴질 수 있으나 이를 지키고 뮤테이터의 작동을 제한하기 위해 노력해야 합니다. 뮤테이터 설계에 주의를 기울이면 자신의 뮤테이터가 다른 모드에서 활용될 기회가 높아지고 이를 지원하기 위한 업무도 줄어듭니다.

세 번째 규칙은, 뮤테이터는 타 뮤테이터와 리소스를 공유해야 한다는 것입니다. 뮤테이터가 ModifyPlayer 함수를 구현할 경우 함수 버전 안쪽 어딘가에서 NextMutator.ModifyPlayer 를 호출해야 합니다. 이렇게 하면 자신의 모드 다음에 있는 뮤테이터 목록의 모든 뮤테이터가 함수 호출을 처리할 수 있게 됩니다. 그렇지 못한 경우 우수하지 못한 프로그래밍 스타일이 되며 커뮤니티에도 친화적이지 않습니다.

GameType

GameType은 훨씬 더 큰 모드 클래스입니다. 뮤테이터가 할 수 없는 모든 것을 수행하며 사용자가 더 광범위한 기능에 액세스할 수 있도록 합니다. 뮤테이터에서 아이디어를 구현할 수 없다면 GameType 작업이 필요합니다.

GameType의 단점은 타 GameType과 혼합할 수 없다는 것입니다. 예를 들어 Unreal Tournament에서 Capture the Flag는 GameType입니다. 이것은 다른 GameType인 Deathmatch의 게임플레이와는 전적으로 스타일이 다릅니다.

GameType은 게임에 특정하게 GameInfo 클래스의 서브클래스로 구현됩니다. GameType에는 몇 가지 클라이언트-서버 문제에 유의해야 한다는 점을 제외하고는 특정한 규칙이 없습니다. 이에 대해서는 다음에 논의합니다.

Total Conversion

Total Conversion(TC)은 수립된 게임 클래스를 모두 건너뛰고 엔진 핵심에서 시작합니다. TC는 게임 특정 서브클래스를 사용하지 않고 GameInfo 클래스와 같은 기본 엔진 클래스의 서브클래스로 구현됩니다. 모드가 게임 세계의 범위에 없거나 전혀 다른 무언가를 시도할 경우 TC가 한 방법이 될 수 있습니다. TC의 경우 모든 것을 직접 새롭게 시작해야 한다는 점만 기억하십시오.

Custom Content

GameType에 따라 일부 모드에서는 AI나, 게임 규칙 자체와 별개인 특수 액터와 같은 여러 새로운 요소가 필요할 수 있습니다. GameInfo나 Mutator 클래스를 통해 게임플레이를 변경하지 않는 모드를 만들 수도 있습니다. 즉 플레이어 플러그인 모델(PPM), 신무기, 새로운 탈것 등이 이에 해당할 수 있습니다.

물론 레벨도 코드 작성 없이 새 컨텐트를 만드는 훌륭한 방법입니다. 간단히 새 레벨을 만든 후 게임에서 제공하는 기존 컨텐트 자산으로 장식하거나 자체 텍스처와 모델(정적 메쉬)을 제작하는 것도 가능합니다.

멀티플레이어 레벨 설계에 대한 자세한 지침은 Multiplayer Map Theory (UT3) 페이지를 참조하십시오.

뮤테이터 제작 방법

뮤테이터는 UnrealScript 경험을 쌓을 수 있는 좋은 기회를 제공합니다. 제한적이지만 강력한 엔진 부분 집합에 노출되기 때문입니다. 이전에 언급한 바와 같이 뮤테이터는 상대적으로 가벼운 방식으로만 게임 코드를 수정해야 합니다. 이렇게 해야 타 뮤테이터와 혼합 시 제대로 작동할 가능성이 높아집니다. 예를 들어 FatBoy, InstaGib, No Powerups deathmatch 등을 플레이할 수 있습니다. 즉 3가지 뮤테이터의 혼합입니다.

매우 간단한 다음 뮤테이터 모드를 살펴보겠습니다.

class ExampleMutator extends Mutator;

defaultproperties
{
}

구성

기본적으로 뮤테이터를 참조하여 패키지에 대한 구성 파일이 만들어집니다. 관련 정보는 다음과 같습니다.

FriendlyName=Example Mutator
Description=An Example Mutator

새 뮤테이터가 메뉴에 올바르게 표시되려면 몇 가지 구성 변경이 필요합니다.

Instant Action 및 Multiplayer 메뉴는 모든 구성 파일에서 UT 클래스의 서브클래스인 클래스를 찾습니다. 이러한 행을 패키지의 현지화된 텍스트 파일에 추가할 경우 뮤테이터 목록에 "Example Mutator"라는 항목이 생깁니다. 다음과 같이 모드 패키지 현지화 텍스트 파일에 텍스트를 입력해야 합니다.

;///////////////
; Mutator Names
;///////////////
[ExampleMutator UIDataProvider_Mutator]
FriendlyName=Example Mutator
Description=An Example Mutator.

일부 모드는 게임 메뉴에서도 구성할 수 있습니다. 이를 위해서는 UI 장면을 만들어 패키지에 저장한 다음 UIConfig 구성원 변수를 통해 패키지를 참조해야 합니다. 이 작업은 Mutator 클래스의 defaultproperties 에서 가능합니다.

예는 다음과 같습니다.

defaultproperties
{
   UIConfigScene=UIScenes_Example.Menus.ExampleMutatorConfig
}

간단한 뮤테이터를 통해 뮤테이터에서 사용 가능한 몇 가지 메소드를 살펴볼 수 있습니다.

뮤테이터 구조 - 개요

이제 간단한 UT 모드를 작성하기 위한 기초적인 정보를 살펴보았습니다. 분명 이것만으로는 세상을 놀라게 하거나 게임 업계에 진출하기에 부족합니다. 뮤테이터 기본 클래스 내부의 메소드를 상세히 살펴보는 것이 도움이 될 것입니다. 그러면 이러한 메소드를 사용해 할 수 있는 작업과 관련한 더 나은 아이디어를 얻을 수 있습니다. 표면적인 내용에 불과하지만 다양한 상속 함수를 사용하고 타 오브젝트와의 상호 작용이 가능하기 때문에 충분히 살펴볼 가치가 있습니다.

Mutate 는 플레이어가 키에 바인딩할 수 잇는 새 명령을 뮤테이터가 정의하도록 합니다. 플레이어가 mutate givehealth 를 키에 바인딩한 다음 이 키를 사용하면 모든 뮤테이터에 givehealth 매개 변수가 있는 mutate 호출이 있게 됩니다. 뮤테이터가 이 문자열을 찾고, 메시지를 보낸 플레이어에게 어떤 추가 상태를 제공할 수 있습니다.

ModifyLogin

ModifyPlayer 는 Pawn을 재생성할 때마다 게임에서 호출합니다. Pawn 변수를 수정하거나 Pawn에 대해 게임 로직을 수행할 수 있도록 합니다. 이 함수를 덮어쓰려면 Super.ModifyPlayer() 를 호출합니다. 이것은 함수의 상위 클래스 버전을 호출하게 됩니다.

AlwaysKeep 은 게임이 세계에 추가하려는 오브젝트를 사용자가 금지할 수 있도록 합니다. 해당 오브젝트가 표시되면 즉시 다른 오브젝트로 대체할 수 있습니다. 이에 해당하는 예로 Unreal Tournament Arena 뮤테이터를 들 수 있습니다. 게임에서 모든 무기를 취하여 다른 무기로 대체합니다. 새 무기를 게임에 추가할 경우 그에 대한 Arena 뮤테이터를 추가하고자 할 수 있습니다.

게임이 세계에 오브젝트를 추가하려는 경우 IsRelevant 가 호출됩니다. 특수 코드로 이를 덮어쓰고, True를 반환하여 오브젝트를 유지하거나 false를 반환하여 거부할 수 있습니다. False인 경우 오브젝트가 파기됩니다.

CheckRelevance

CheckReplacement

상세 정보: 게임 규칙

핵심적으로 Mutator 클래스는 조정을 위해 수정 가능한 특정 이벤트를 트랩합니다. 물론 사살 또는 치명적인 손상 카운터처럼 즉시 어떤 제약이 발생할 수도 있습니다. 여기서 게임 규칙이 개입됩니다. 각 GameInfo 게임 서브클래스에는 GameRulesModifiers라는 게임 규칙 목록이 있습니다. 특정 게임 이벤트 처리 방식을 조절하는 GameRules 서브클래스를 만들 수 있습니다.

FindPlayerStart

HandleRestartGame

CheckEndGame

OverridePickupQuery

PreventDeath

ScoreObjective

ScoreKill

NetDamage

좀 더 상세히 살펴보면 뮤테이터가 HUD에 정보를 모을 수도 있습니다. GameType에서 수행하는 것이 더 유연하지만 뮤테이터에서는 이 정도의 제한적인 기능도 충분합니다. 간단히, UTHud 클래스의 PostRenderedActors 배열에 자신을 추가하는 복제된 액터 서브클래스를 만들면 됩니다.

이 정보를 통해 극소수의 클래스만으로도 상당한 변경이 가능합니다. 뮤테이터의 능력을 극대화하는 방법에 대해 알아보려면 UT와 함께 제공되는 뮤테이터의 코드를 읽어 보는 것이 가장 좋습니다.

GameType 소개

뮤테이터는 몇 가지 훌륭한 점이 있습니다. 이해가 쉽고, 특정 게임 이벤트 및 규칙과 상호 작용하여 많은 일을 해낸다는 점입니다. 심지어 혼합을 통해 더 훌륭한 효과를 낼 수 있지만 그렇다고 항상 강력한 것은 아닙니다. 새로운 게임 유형(예: Jailbreak 또는 Rocket Arena 스타일 모드)을 만들려면 뮤테이터로는 불가능합니다. 게임 규칙에 대한 완벽한 제어가 필요하기 때문입니다. 바로 이런 경우를 위해 GameInfo 클래스 시리즈가 필요합니다.

GameInfo는 엔진에 있는 클래스로, 게임 엔진이 만들며 게임플레이 규칙의 핵심입니다. Unreal Tournament에서는 UTGame 패키지에 있는 여러 GameInfo 서브클래스를 사용합니다. UTGame 클래스는 모든 Unreal Tournament의 게임 유형에 공통인 코드를 포함합니다. UTDeathmatch는 일반적인 격투 실행을 위한 코드를 포함합니다. UTTeamGame은 팀 격투 및 일반 팀 관리 코드를 위한 코드를 포함합니다. UTTeamGame의 서브클래스인 UTCTFGame은 이러한 특정 GameType을 구현합니다.

새로운 게임 유형을 작성하기 위한 첫 단계는 서브클래스가 될 GameInfo 클래스를 결정하는 것입니다. 팀 게임을 작성한다면 UTTeamGame 서브클래스가 필요할 것입니다. 팀 없이 게임을 작성한다면 UTDeathmatch를 사용합니다. 이전의 게임 스타일과 상당히 동떨어진 게임(그러나 Unreal Tournament 게임 범주에 해당함)을 작성한다면 UTGame을 사용합니다. 서브클래스화는 다음과 같은 점에서 매우 유용합니다. 상위 클래스의 모든 코드를 즉시 상속한 다음 확장 또는 덮어쓰기를 통해 설계 목표를 달성할 수 있습니다.

물론 UT 게임과 전적으로 다른 Total Conversion(TC)을 작성하고자 한다면 GameInfo 서브클래스가 필요할 것입니다.

매우 간단한 다음 게임 유형 모드를 살펴보겠습니다.

class ExampleGameType extends UTDeathMatch;
defaultproperties
{
   Acronym="EX"
   MapPrefixes[0]="EX"
   Description="Example GameType"
}

ExampleGameType.uc라는 파일에 저장된 위의 코드는 새 GameType의 쉘입니다. 유일한 차이점은 설명을 "Example GameType"으로 바꾸었다는 점입니다. 이 새 이름은 Practice Session 선택 창, Scoreboard 헤더 등, 여러 위치에서 사용될 것입니다. 이 게임을 플레이하게 되면 새 동작이 구현되지 않았으므로 다른 전투 게임과 꼭 같습니다.

구성

뮤테이터와 유사하게 게임 유형을 참조하여 패키지에 대한 구성 파일이 만들어집니다. 관련 정보는 다음과 같습니다.

FriendlyName=Example GameType
Description=An Example GameType

뮤테이터처럼 새 게임 유형이 메뉴에 올바르게 표시되려면 몇 가지 구성 변경이 필요합니다. 다음과 같이 모드 패키지 현지화 텍스트 파일에 텍스트를 입력해야 합니다.

;///////////////
; Game Modes
;///////////////
[ExampleGameType]
Description=An Example GameType.
GameName=ExampleGameType
EndOfMatchRulesTemplateStr_Scoring=First one wins!
EndOfMatchRulesTemplateStr_Time=Most wins!


;///////////////
; GameType Names
;///////////////
[ExampleGameType UIDataProvider_GameModeInfo]
FriendlyName=Example GameType
Description=An Example GameType.

Instant Action 및 start Multiplayer 메뉴는 현지화된 문자열이 있는 클래스와 유관한 섹션 이름이 있는 패키지의 모든 현지화된 텍스트 파일에 표시됩니다. 이러한 행을 패키지의 현지화된 텍스트 파일에 추가할 경우 게임 목록에 "Example GameTyp"이라는 항목이 생깁니다. 이 이름은 GameInfo 클래스의 FriendlyName 변수에서 취한 것입니다. Description 행은 GameType에 메뉴에 대한 설명 텍스트의 행을 제공합니다. 이에 대해서는 아직 고려하지 않아도 됩니다.

간단한 GameType을 통해 GameInfo에서 사용 가능한 몇 가지 메소드를 살펴볼 수 있습니다.

GameInfo 개요

엔진의 GameInfo 클래스는 기본 게임 로직의 정의입니다. 파일 맨 위에는 긴 변수 선언 목록이 있습니다. 이 중 많은 변수에는 용도를 설명하는 주석이 있습니다. 변수 선언 밑에는 작업을 수행하는 함수(또는 메소드)가 옵니다.

먼저 살펴볼 것은 Timer 함수입니다. GameInfo에서는 매우 짧지만 UTDeathmatch에서는 매우 깁니다. Timer 는 특수한 UnrealScript 이벤트입니다. SetTimer(int Time, bool bLoop) 함수를 호출할 경우 액터에 반복 타이머를 설정할 수 있습니다. Time 매개 변수는 Timer 함수를 호출할 시점을 설명합니다. bLoop 매개 변수는 첫 번째 호출 후 루프에서 Timer를 호출할지 여부를 설명합니다. *모든 UTGame 클래스는 1초의 타이머 루프를 사용합니다. * 즉 Timer 함수가 매초 호출됩니다. 특정 시점에 발생해야 하는 이벤트에 타이머를 사용할 수 있습니다. 초를 세는 Watch 변수를 선언하여 최대 1초 범위에서 언제든 이벤트를 수행할 수 있습니다. UTDeathmatch는 이를 통해 게임에서 TimeLimit에 도달했는지를 점검 및 파악합니다.

알 필요가 있는 또 다른 중요한 시간 함수는 Tick 입니다. GameInfo에서는 사용되지 않으나 액터에서는 사용합니다. Tick 선언은 Tick(float DeltaTime)=입니다. =Tick 은 게임의 모든 액터가 각 프레임에서 호출합니다. DeltaTime은 마지막 Tick 이후 지나간 시간의 길이를 포함합니다. Tick 을 사용하면 1초 범위 안에 수행되어야 하는 동작을 수행할 수 있습니다. Tick 에서 너무 잦은 호출로 인해 CPU에 부하를 주는 동작을 수행하지 않도록 합니다.

GameInfo에는 Login 함수가 있습니다. 이 함수는 플레이어가 게임에 로그인할 때마다 엔진이 호출합니다. GameInfo의 로그인 버전은 플레이어에 이름, 스킨, 메쉬 등을 할당하는 것과 같은 중요한 설정 작업을 수행합니다. 또한 기초적인 텔레포트 효과를 내고 생성 포인트를 찾아 플레이어를 고정시킵니다. Login 다음은 Logout 입니다. 플레이어가 게임을 나갈 때마다 호출되며 플레이어 종료 후 로그아웃을 사용하여 정리할 수 있습니다.

GameInfo의 다른 흥미로운 함수는 AddDefaultInventory 입니다. 이 함수는 플레이어에게 초기 무기와 장비를 할당합니다. UTDeathmatch에서는 플레이어에게 충격 망치와 경호기가 주어집니다. AddDefaultInventory를 사용하여 모드를 결합하는 사용자 지정 자산을 플레이어에게 추가할 수 있습니다(예: 수류탄, 돈 등을 줄 수 있음).

FindPlayerStart 메소드는 생성에 적합한 NavigationPoints에 대한 레벨에서 액터를 검색합니다. 레벨 디자이너가 레벨에 추가한 PlayerStart 액터는 이러한 위치입니다. UTTeamGame에서는 FindPlayerStart 가 팀에 따라 플레이어와 봇을 생성합니다. 각 PlayerStart의 팀과 생성할 Pawn의 팀을 확인합니다. =FindPlayerStart를 사용하여 사용자 정의 생성 코드를 작성할 수 있습니다(예: 한 위치에 적군을, 다른 위치에 우방을 생성할 수 있음).

RestartPlayer 메소드는 플레이어가 다시 생성할 때마다 호출됩니다. 기본 GameInfo 버전은 FindPlayerStart 를 호출하여 시작 지점을 찾고 플레이어를 해당 지점으로 이동하여 텔레포트 효과를 생성합니다. 또한 플레이어의 상태를 복원하고 플레이어 충돌을 설명하며 플레이어에게 기본 자산을 부여합니다.

Killed 메소드는 매우 유용합니다. 플레이어가 다른 플레이어를 죽일 때마다 호출됩니다. 죽음 상황(플레이어가 죽었거나 사살된 경우)과 상해 유형에서 보이며 메시지를 출력합니다. 마지막으로 ScoreKill 을 호출합니다.

=ScoreKill=은 사살에 포인트를 부여합니다. UTDeathmatch는 성공적 사살에 플래그를 할당하고 자살에 대해 하나를 차감합니다. UTTeamGame은 킬러 팀의 TeamInfo에 포인트를 추가하거나, 자살의 경우 하나를 차감합니다.

DiscardInventory 는 플레이어가 죽었거나 게임에서 제거된 경우 호출됩니다. DiscardInventory 는 Pawn의 자산을 통과하며 적절하게 무기를 떨어뜨리고 나머지를 파기합니다. 이 함수를 덮어써서 배낭이나 소지품을 떨어뜨리게 할 수 있습니다.

마지막으로 EndGame 함수는 게임 종료 시마다 원인을 통해 호출됩니다. 여기서 특수한 기록이나 정리를 수행할 수도 있습니다.

따라서 더 중요한 GameInfo 함수에 대해 잠깐 살펴보겠습니다. UTDeathmatch와 같은 고급 GameInfo 클래스는 봇 및 단일 플레이어 게임 제어를 위해 중요한 새 동작을 추가하고 GameInfo 메소드를 특정 규칙으로 세분화합니다.

다시 말하지만 GameType을 최대한 활용하는 방법에 대해 알아보려면 UT와 함께 제공되는 GameType의 코드를 읽어 보는 것이 가장 좋습니다.

게임 규칙

경고 표시(HUD)

점수판

AI

사용자 지정 컨텐트 제작 방법

캐릭터

자세한 내용은 Creating Custom Characters for UT3 페이지를 참조하십시오.

레벨

다시 말하지만 새 레벨이 메뉴에 올바르게 표시되려면 몇 가지 구성 변경이 필요합니다. 다음과 같이 레벨의 현지화 텍스트 파일에 텍스트를 입력해야 합니다.

[DM-Example UIDataProvider_MapInfo]
FriendlyName=<Strings:UTGameUI.FriendlyMapNames.DM-Example>
NumPlayers=4 to 8 players

픽업 및 무기

곧 제공 예정

차량

곧 제공 예정

몇 가지 주의 사항

여기서는 Unreal Tournament용 모드를 작성할 때 모드 개발자들을 당혹스럽게 하는 몇 가지 사항에 대한 정보를 제공합니다. 이 정보 중 대부분은 엔진에 대해 더 경험을 쌓을 때까지는 자신과 무관할 수 있습니다. 새로운 게임 기술을 배우는 것은 정말 멋진 경험이지만 당황스러운 면도 있습니다. 여기서 제시하는 몇 가지 사항은 앞으로의 여정에 도움이 될 것입니다.

작성하는 코드의 성능 결과를 규칙처럼 항상 고려하십시오.

Accessed Nones(액세스 없음)

조만간 이런 것이 로그 파일에 보이게 될 것입니다. UnrealScript는 Accessed Nones(액세스 없음)를 경고로 처리하지만 개발자는 이를 오류로 간주해야 합니다. 이런 경고는 쉽게 해결 가능하며 항상 코드가 뭔가 잘못되었다는 신호입니다. C++이나 Java에 익숙하다면 액세스 없음이 무엇을 뜻하는지 쉽게 파악할 수 있습니다. 익숙하지 않은 분들을 위해 간략히 설명하겠습니다.

UnrealScript는 객체 지향 프로그래밍 언어입니다. UnrealScript로 프로그램을 작성할 때는 이러한 오브젝트가 따라야 할 여러 작동과 게임에서 상호 작용 하는 방식을 정의합니다. 오브젝트에는 구성원 변수 및 구성원 함수 등, 여러 등록 정보가 있습니다. 오브젝트 등록 정보에 액세스하려면 해당 오브젝트에 대한 참조가 필요합니다.

코드 예는 다음과 같습니다.

class ExampleInfo extends Info;

var PlayerReplicationInfo PlayerInfo;

function PlayerReplicationInfo GetPlayerInfo()
{
   return PlayerInfo;
}

Info의 서브클래스인 ExampleInfo라는 단순 오브젝트가 있다고 가정합니다. 이 오브젝트에는 PlayerInfo라는 변수와 GetPlayerInfo()라는 함수 등의 두 가지 등록 정보가 있습니다. 모드 내부로부터 이 오브젝트와 상호 작용하게 하고자 합니다. 모드 내부에 ExampleInfo 오브젝트에 대한 참조가 있고 PlayerInfo 등록 정보 내부로부터 어떤 정보를 얻고자 합니다. 이를 위해 다음과 유사한 코드를 작성할 수 있습니다. class MyMod expands TournamentGameInfo;

function string GetPlayerName()
{
   local ExampleInfo TheInfo;
   local string PlayerName;

   TheInfo = GetMyObject();
   PlayerName = TheInfo.PlayerInfo.PlayerName;
   Log("The player's name is" @PlayerName);
}

이 예에서는 GetMyObject()라는 함수를 호출하여 ExampleInfo에 대한 참조를 구했습니다. 그런 다음 이 참조에 액세스하여 플레이어 정보(TheInfo.PlayerInfo)를 얻고 다시 PlayerInfo 참조에 액세스하여 플레이어 이름(PlayerInfo.PlayerName)을 얻었습니다. 그러나 사용 가능한 TheInfo가 없거나, GetMyObject()의 버그로 인해 ExampleInfo를 반환하지 못한다면 어떻게 되겠습니까? 이 경우 함수가 None 을 반환할 것입니다. None은 빈 참조로, C++의 NULL 포인터와 상당 부분 유사합니다.

이 예에서 GetMyObject()가 =None=을 반환한다면 TheInfo 변수에 None이 할당됩니다. 다음 행에서는 TheInfo에 액세스하여 PlayerInfo 참조를 구하고자 합니다. 그러나 TheInfo가 None이라면 아무 것도 참조하지 않게 됩니다. 액세스할 수 없으므로 Unreal Engine이 코드가 깨졌다는 경고를 로그에 남깁니다.

Accessed None in ExampleMod.GetPlayerName!

이와 같이 버그가 있는 코드는 아주 간단하게 피할 수 있습니다. 코드에 몇 가지 확인을 추가하고 오류 시의 특수 작동을 정의하기만 하면 됩니다.

class ExampleGameType extends UTGame;

function string GetPlayerName()
{
   local ExampleInfo TheInfo;
   local string PlayerName;

   TheInfo = GetMyObject();
   if ((TheInfo != None) && (TheInfo.PlayerInfo != None))
   {
      PlayerName = TheInfo.PlayerInfo.PlayerName;
   }
   else
   {
      PlayerName = "Unknown";
   }

   Log("The player's name is" @PlayerName);
}

이제 TheInfo가 None 인지 확인한 다음 PlayerInfo 참조가 None 인지 확인합니다. UnrealScript의 if 문은 단순 순환 로직을 사용합니다. 즉 if 문은 왼쪽에서 오른쪽으로 평가됩니다. 코드에서 If 를 부정하는 문을 만나면 평가를 중지합니다. 즉 TheInfo가 =None=이면 코드는 다음 문을 절대로 평가하지 않습니다.

(TheInfo.PlayerInfo != None)

나머지 구문의 결과는 상관이 없으며 첫 부분이 거짓이기 때문에 전체 구문이 거짓이란 사실을 파악하게 됩니다.

Accessed Nones는 Timer()Tick() 과 같이 시간이 중요한 함수에서는 특히 위험합니다. 로그에 오류 메시지를 작성하는 데 상당한 시간이 소요되며 코드가 1초에 3,000건의 오류 메시지를 덤핑한다면 디스크 공간은 제쳐두고라도 시스템 성능을 저해할 수 있습니다.

반복자

UnrealScript는 반복자라고 하는 매우 유용한 언어 기능을 구현합니다. 반복자는 목록을 캡슐화하는 데이터 유형입니다. UnrealScript는 목록 반복자만 지원하며 앞으로의 언어 버전에서는 사용자 정의 반복자를 지원할 것입니다. 반복자를 구해 루프를 수행하여 반복자 내부의 모든 오브젝트에 대한 작업을 수행할 수 있습니다. 예는 다음과 같습니다.

local Ammo A;

foreach AllActors(class'Ammo', A)
{
   A.AmmoAmount = 999;
}

이 예에서는 AllActors() 함수를 사용하여 Actor List 반복자를 구했습니다. 그런 다음 foreach 반복자 루프를 사용하여 AllActors 함수가 반환하는 모든 오브젝트에 대해 몇 가지 동작을 수행합니다. AllActors() 는 원하는 액터 유형의 클래스와 그 안에 넣을 변수를 취합니다. AllActors() 는 현재 게임의 모든 액터에서 원하는 오브젝트를 검색합니다. 여기서 게임 내 모든 액터의 AmmoAmount를 999로 설정 한다고 가정합니다.

그럴 듯하게 들리나 부작용을 고려해야 합니다. 즉 몇 가지를 찾기 위해 수백 개의 액터 목록을 검색하게 되는데 이는 결코 신속한 작업이 아닙니다.

주의하여 사용한다면 반복자가 매우 유용할 수 있습니다. 반복자는 느려지는 경향이 있으므로 일초에 2회보다 빠르게는 반복자를 수행하지 말아야 합니다. Tick() 이나 다른 루프 내부에서 AllActors() 반복을 수행할 이유는 거의 없습니다. 신중한 판단이 필요합니다.

앞으로 작업하게 될 가장 일반적인 AllActors() 검색은 아마 PlayerReplicationInfo 액터 모두를 대상으로 하는 검색이 될 것입니다. PlayerReplicationInfo는 서버가 각 클라이언트에 전송하는 플레이어에 대한 중요 정보를 포함합니다. 각 클라이언트가 지나치게 많은 정보를 전송하지 않고도 타 플레이어의 상태에 대해 알 수 있도록 하며 점수판의 점수나 기타 일반적인 것을 표시하는 데 사용합니다.

일반적으로 전역 액터 목록에서 PlayerReplicationInfo 액터는 일부에 불과합니다. 단 몇 개의 결과를 얻기 위해 시간 소모적인 검색을 수행할 필요는 없습니다. 이러한 공통적인 반복을 간소화하기 위해 GameReplicationInfo 클래스에 PRI 배열이 정의되어 있습니다. 1/10초 마다 최신 PRIArray가 PlayerReplicationInfos 세트를 포함하도록 업데이트됩니다. 이렇게 하면 AllActors() 호출 없이도 PRIArray 연산을 수행할 수 있습니다.

다른 반복자도 사용 가능합니다. 이에 대한 정보는 Actor 클래스 정의를 살펴보시기 바랍니다. 이름 그대로 TouchingActors() 는 터칭 액터를, RadiusActors() 는 특정 반경 내 모든 액터를 반환합니다. 이러한 반복자를 지혜롭게 활용하면 코드 속도를 높게 유지하는 데 도움이 됩니다.

트레이스

Unreal 엔진에서는 보일 가능성이 있는 세트를 사용하지 않으므로 공간적인 개념의 세계에서 무언가를 찾고자 한다면 트레이스가 필요합니다. 대부분의 경우 트레이스 대상 위치가 어디인지에 대해서는 잘 알고 있고 라인의 반대쪽에 무엇이 있는지에 대해서만 알고자 합니다. 어떤 때에는 여러 트레이스를 활용하여 문제의 오브젝트를 둘러싼 것이 무엇인지에 대해 알고자 합니다.

첫 번째 조언은 가능하다면 트레이스 사용을 피하라는 것입니다. 트레이스를 사용하려는 대상에 대해 심사숙고하고 그 대안을 찾아보십시오. 트레이스는 리소스가 많이 소모되는 작업으로 모드가 다소 느려질 수 있습니다. 매 틱마다 플레이어에게 두 번의 트레이스를 허용하고자 한다고 합시다. 테스트 중에는 모든 것이 괜찮습니다. 여기서 직시하지 못한 것은 15명 이상이 온라인 상태에서 플레이를 시작하는 즉시 트레이스는 배가되기 시작한다는 사실입니다.

트레이스가 꼭 필요하다면 크기를 제한하십시오. 짧은 트레이스가 긴 트레이스보다 빠릅니다. UT용으로 새로운 권총 무기를 디자인하는 경우를 예로 들면 총알이 발사되어 총의 산탄을 확인하기 위해 12번의 트레이스를 수행하려 합니다. 12번의 트레이스는 아주 합당합니다. 플레이어가 1초에 30번씩 총을 발사하지는 않기 때문입니다. 그러나 모드에서 대규모 오픈 레벨을 사용한다면 12번의 트레이스에 리소스가 많이 소모될 수 있습니다. 장거리용 무기로서는 권총이 그다지 유용하지 않으므로 특정 지점에서 그 범위를 줄이는 편이 낫습니다. 그러면 엔진이 최악의 경우에 맵의 한 끝에서 다른 쪽 끝까지 트레이스를 수행하는 일이 발생하지 않게 됩니다.

트레이스는 매우 유의하여 사용해야 합니다. 단일 프레임에서 많은 트레이스를 수행하는 것은 큰 문제가 될 수 있습니다. 크게 신경을 써야 하는 부분입니다.

복제 - 암호 해독 및 다시 흐리기

복제를 이해하는 것은 성공적인 Unreal Engine 게임 모드 제작에서 가장 어려운 측면 중 하나입니다. 하지만 멀티플레이어 게임플레이를 계획 중이라면 반드시 이에 대한 이해가 필요합니다. 대다수의 문서는 본질적으로 매우 기술적이라 요약이 어려울 수 있습니다. 여기서는 몇 가지 기초적인 내용을 다루며 시간이 지나면 복제에 대한 이해와 인식을 더 높일 수 있을 것입니다.

simulated 함수는 클라이언트와 서버 모두에서 호출되나 simulated 함수를 통해서만 호출됩니다. 함수 호출이 시뮬레이션 체인을 분리한 즉시 시뮬레이션이 멈춥니다. 시뮬레이션하는 대상과 simulated 함수에서 수행하고자 하는 작업에 대해 잘 파악하고 있어야 합니다. Unreal 소스 코드 어디선가 보았다는 이유만으로 =simulated=와 같은 함수 수정자를 추가하지 마십시오. 추가하는 이유와 그 영향을 이해해야 합니다. 코드가 어떤 작업을 수행하는지 알지 못한다면 수준 높은 모드를 기대하기 어렵습니다.

simulated 함수는 클라이언트와 서버 모두에서 호출되기 때문에 어떤 데이터가 처리되는지에 대해 매우 잘 알고 있어야 합니다. 서버에서 사용 가능한 어떤 오브젝트 참조가 클라이언트에서는 그렇지 않을 수 있습니다. 예를 들어 모든 액터에는 현재 레벨에 대한 참조와 자신이 속한 현 세계에 대한 참조가 있습니다. 세계 참조 안쪽에는 현재 게임에 대한 참조가 있습니다. 이를 위해 다음과 유사한 코드를 작성할 수 있습니다.

simulated function bool CheckTeamGame()
{
   return World.Game.bTeamGame
}

위의 예는 현재 게임이 팀 게임인지의 여부에 따라 True 또는 False를 반환하는 간단한 simulated 함수입니다. 현재 레벨 GameInfo 참조의 bTeamGame 등록 정보를 확인하여 이를 수행하게 됩니다. 그러나 여기서 잘못된 것이 있습니다.

레벨 참조의 게임 등록 정보는 서버에서만 유효하다는 점입니다. 즉 클라이언트는 서버의 게임 오브젝트에 대해 알지 못하므로 클라이언트가 Accessed None을 로그에 남길 수 있습니다.

레벨 정보용 스크립트를 연다면 다음과 유사한 섹션이 있을 것입니다.

//-----------------------------------------------------------------------------
// Network replication.

replication
{
   reliable if( Role==ROLE_Authority )
      Pauser, TimeDilation, bNoCheating, bAllowFOV;
}

replication 블록은 Unreal 엔진에게 해당 등록 정보의 처리 방법을 알려 주는 특수 구문입니다. 자세히 살펴보겠습니다.

먼저 reliable if( Role = ROLE_Authority)= 라는 복제 조건이 있습니다. 이 조건의 첫 부분은 Reliable(확신)이거나 Unreliable(확신 없음)입니다. 신뢰라고 답하면 엔진이 각 클라이언트에 복제 정보가 안전하게 전달되는 것을 확인할 것입니다. UDP 프로토콜의 작동 방식 때문에 전송 중 패킷이 손실될 수 있습니다. 확신 없음의 경우 패킷이 안전하게 도착했는지 확인하지 않게 됩니다. 확신 복제에서는 확신 없음 복제의 경우보다 네트워크 부하가 약간 높습니다.

두 번째 패킷 조건인 (Role = ROLE_Authority)= 는 엔진에게 데이터 전송 시점을 알립니다. 이 상황에서는 현재 LevelInfo 오브젝트가 Authority일 경우일 때마다 데이터를 전송할 것입니다. 이 뜻을 정확히 해석하려면 해당 오브젝트의 특정 역할을 이해해야 합니다. LevelInfo를 통해 서버는 믿을 만한 오브젝트 버전을 유지하게 됩니다. 서버는 레벨의 작동 방식을 올바르게 클라이언트에 알립니다. 즉 위의 복제 블록 예에서는 데이터가 서버에서 각 클라이언트로 전달됩니다.

조건의 다른 공통 유형으로는 (Role < ROLE_Authority) 가 있습니다. 여기서는 엔진이 현재 오브젝트가 Authority가 아닌 경우에 데이터를 보내야 합니다. 또는 클라이언트가 서버에 정확한 정보를 알린다는 뜻이 될 수도 있습니다.

마지막으로 이 조건 아래 나열된 4가지 변수를 살펴보겠습니다. 이들은 해당 구문이 적용되는 변수입니다. 이 상황에서는 "우리가 서버고 클라이언트에 있는 해당 변수의 사본이 오래되었다면 해당 클라이언트에 Pauser, TimeDilation, bNoCheating 및 bAllowFOV에 대한 새로운 정보를 보내야 해. 데이터가 안전하게 도착했는지 항상 확인하자.""라고 알려 주는 구문이 됩니다.

replication 문은 LevelInfo의 나머지 변수를 포괄하지 않습니다. 여기에는 두 가지 상황이 있습니다. 클라이언트가 C++로 정보를 입력했거나(TimeSeconds의 경우) 또는 클라이언트에서 정보 업데이트가 전혀 이루어지지 않아 완전히 믿을 수 없는 상황(Game의 경우)입니다.

C++ 소스 코드에 대한 액세스 권한은 없지만 오브젝트의 등록 정보와 관련한 여러 인터페이스를 만들어 클래스가 기본적으로 채워지지 않는 비복제 등록 정보를 갖는지 여부를 판단할 수 있습니다. LevelInfo에 대한 다음 클래스 선언을 살펴보겠습니다.

class LevelInfo extends ZoneInfo
  native;

native이 오브젝트가 C++ 및 UnrealScript로 선언되었음을 나타냅니다 . 원시 클래스는 사용자가 볼 수 없는 C++ 동작을 가질 수 있습니다. 보통 성능상의 이유로 소수의 특수 클래스만이 원시 클래스입니다.

마지막으로 클래스 선언에서 nativereplication 라고 나타나는 클래스에 대해 살펴보겠습니다. 이 클래스는 UnrealScript 내부의 replication 블록이 아무 것도 하지 않으며 복제가 전적으로 C++로 정의됨을 나타냅니다. 네트워크를 많이 소모하는 특정 오브젝트에는 네트워크 성능 지원을 위한 원시 복제가 있습니다.

replicated 변수 및 simulated 함수에서 문제를 방지할 수 있는 방법에 대해 알아보았습니다. 이제 복제 함수를 살펴보겠습니다.

복제 함수는 클라이언트나 서버에서 호출되나 그 반대편에서 실행되는 함수입니다. 복제 함수의 예로 Say 함수를 들 수 있습니다. 게임에 있는 모든 사람에게 이야기하기 위해 T 키를 치면 사실은 말하는 모든 내용과 함께 Say 함수가 실행됩니다. 클라이언트가 이 함수와 그 매개 변수를 취하고 실행을 위해 서버로 보냅니다. 그러면 서버가 메시지를 모든 다른 클라이언트에 브로드캐스트하게 됩니다.

복제 함수는 값을 반환하지 못한다는 점만 기억하면 간단하게 사용할 수 있습니다. 복제 함수는 네트워크를 통해 상대방에게 전송되므로 시간이 소요됩니다(Ping 수행 시간과 유사함). 복제 함수가 차단되면(예: 값 반환 대기) 네트워크 통신이 중단될 수 있습니다.

이에 대해 생각하는 사람이라면 이를 당연히 받아들이지만 모드 작업 중에는 이를 간과할 수도 있습니다. 복제 함수는 즉시 반환됩니다. 이 함수를 사용하여 클라이언트의 동작을 트리거하거나(예: 특수 효과) 메시지를 보냅니다(예: 서버에 무기 발사 메시지 보내기).

마지막으로, 복제 함수는 소수의 클래스로 제한됩니다. 액터에서의 함수 호출은 해당 액터를 소유한 플레이어에게만 복제될 수 있습니다. 함수 호출은 한 액터에게만 복제되므로(해당 소유자인 플레이어) 멀티캐스트가 불가능합니다. 이 함수는 자신이 제작한 무기나 자산 아이템에 사용할 수 있습니다(함수가 아이템 소유 플레이어에게 복제됨).

이제 복제의 기본 개념을 파악했을 것입니다.

Mutator

게임 규칙

오버레이

GameType

게임 규칙

HUD

점수판

AI

전투 레벨

CTF 레벨

사용자 지정 캐릭터

다음 캐릭터 골격을 참고로 사용하면 Unreal Tournament 3의 사용자 지정 캐릭터 제작을 시작하는 데 도움이 될 것입니다.

또한 다음과 같은 최초의 인물 골격을 참고로 활용할 수 있습니다.

참고: A일부 골격에는 두발 장치가 포함됩니다. 두발 장치는 애니메이션 작업 편의를 위한 추가적인 골격에 불과합니다. 애니메이션이 완성되면 애니메이션을 원래 골격에 입히고 두 발 장치를 삭제한 다음 애니메이션을 익스포트합니다.

자세한 내용은 Custom Characters 페이지를 참조하십시오.