UDN
Search public documentation:

DevelopmentKitFirstScriptProjectKR
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

첫 UnrealScript 프로젝트

문서 요약: 언리얼 개발 키트로 처음 UnrealScript 프로젝트를 제작해 보는 분들을 위한 안내서입니다.

문서 변경내역: Michiel Hendricks 작성. 홍성진 번역.

개요

이 튜토리얼은 UDK에서의 첫 UnrealScript 프로젝트 구성 과정에 대한 안내입니다. 이 튜토리얼은 UDK를 사용해서 게임 제작하기 완전판같은 것이 아니며, 현재 UDK의 게임 프레임워크에 대한 세부적인 설명도 아닙니다.

환경 구성하기

프로그래밍을 시작하기 전에 개발 환경을 구성해야 합니다.

UDK 구성하기

먼저 UDK 최신 버전 부터 받아야 하겠죠. 인스톨러를 실행시키고 지시에 따릅니다. 개발 도중 UDK 디렉토리를 자주 들락거려야 하니, "Program Files" 아래 어딘가 숨겨두기 보다는 쉽게 찾을 수 있는 디렉토리에 두는 것이 좋습니다.

UDK는 정기적으로 새로운 버전이 발표됩니다. 그래도 최신 버전이 나오자 마자 바로 업데이트하는 것은 좋지 않을 수 있습니다. 어떤 변경사항은 현재 프로젝트와 충돌이 있을 수 있거든요. 새로운 UDK 버전이 발표된 경우, 프로젝트가 안정된 상태에 있는가를 먼저 확인한 다음, 새로운 버전으로 이전하면서 모두 제대로 작동하는가 확인하시기 바랍니다.

UDK 디렉토리 구조

UDK가 설치된 디렉토리의 구조는 다음과 같습니다. (일부는 UDK 첫 실행 이전에는 없을 수 있습니다).

  • Binaries ; 다양한 유틸리티가 들어 있습니다.
    • ActorX ; 다양한 3D 모델링 프로그램용 ActorX 플러그인.
    • FaceFXPlugins ; 다양한 3D 모델링 프로그램을 위한 FaceFX 플러그인.
    • InstallData ; UDK 설치 도중 사용된 파일.
    • SpeedTreeModeler ; SpeedTree 툴.
    • Win32 ; UDK 실행파일 및 다양한 필수 라이브러리가 들어 있습니다.
    • Win64 ; MS 윈도우 64비트 버전에서 사용할 수 있는 64비트 버전 UDK 실행파일을 찾아볼 수 있습니다.
    • Windows ; UDK에서 요구되는 다양한 런타임 라이브러리를 설치하기 위한 설치 파일이 들어 있습니다. 이 라이브러리는 구성 과정 도중 이미 시스템에 설치됩니다.
  • Development ; 개발 디렉토리.
    • Src ; UnrealScript 소스를 포함한 모든 소스 파일에 사용되는 디렉토리로, 이미 UDK 에 포함된 UnrealScript 소스 코드가 들어 있습니다. 여러분의 소스도 이 디렉토리에 추가됩니다.
  • Engine ; UnrealEngine의 기본 파일에 사용되는 디렉토리입니다. 이 안의 콘텐츠를 변경해서는 안되며, 그게 바로 UDKGame 디렉토리의 존재 이유입니다. 상용 UDK 어플리케이션을 제작하는 경우일지라도, 이 디렉토리에 있는 것은 무엇이든 여러분의 어플리케이션 제작에 사용할 수 있습니다.
    • Config ; 기본 환경설정 파일이 들어 있으며, 대개 UDKGame 디렉토리에 있는 환경설정 파일에서 상속된 것들입니다.
    • Content ; 콘텐츠 패키지입니다.
    • EditorResources ; 에디터에 의해 사용되는 리소스 파일입니다.
    • Localization ; 현지화 파일입니다. 각 언어에 대한 하위 디렉토리에 해당 언어로 된 현지화 파일이 포함됩니다.
    • Shaders ; 셰이더 소스 파일입니다.
    • Stats ; 다양한 개발 통계에 대한 보고서 작성에 사용되는 템플릿 파일입니다.
  • UDKGame ; 새로운 콘텐츠가 추가될 디렉토리입니다.
    • Build
    • Config ; 이 디렉토리에는 2 가지 유형의 환경설정 파일-기본 환경설정 파일과 시간 경과에 따라 업데이트되는 소재화된 (materialized) 환경설정 파일-이 들어 있습니다. 기본 환경설정 파일은 시스템의 기본 구성을 제공하며 엔진에 의해 절대 업데이트 되지 않습니다.
    • Content ; 어플에 대한 (레벨 포함) 콘텐츠 패키지 전부가 담겨 있습니다. 상용 UDK 어플의 디렉토리에 있는 애셋 중 콘텐츠 브라우저에서 ([[ContentBrowserReferenceKR][콘텐츠 브라우저 참고서]) 스켈레탈 메시로 마크되어있는 애셋만 제외하고는 어느 콘텐츠든지 사용하셔도 됩니다. 하위디렉토리에 대한 고정된 설정은 없으며, 전부 환경설정에 따릅니다.
    • Localization ; 현지화 파일.
    • Logs ; 로그 파일 및 크래시 리포트에 사용됩니다.
    • Movies ; 게임내 나타나는 빙크 무비용으로 예약된 디렉토리입니다. 주: 귀하의 어플에 UE3_logo.bik 무비를 포함시켜 표시해야 합니다.
    • Script ; 컴파일된 스크립트 패키지가 들어 있는 디렉토리입니다.
    • Splash ; 에디터와 게임이 시작될 때 나타나는 스프래시 화면이 들어 있습니다.

IDE/에디터

UDK에는 UnrealScript 작성을 위한 에디터가 들어 있지 않습니다. UnrealScript는 Java와 매우 유사합니다. 소스 코드는 단순 텍스트 파일로 저장됩니다. 따라서 선호하는 텍스트 에디터를 사용해서 코드를 작성할 수 있습니다.

그러나 기능이 뛰어난 특수 에디터들이 몇 있습니다.

nFringe

현재 가장 뛰어난 에디터는 Pixel Mine Games 사의 nFringe 입니다. nFringe는 UE3 용으로 완전히 통합된 개발 환경을 제공하며, 따라서 UDK 용으로도 사용할 수 있습니다. nFringe는 Microsoft에 의해 제공되는 Visual Studio IDE에 기초합니다. nFringe 는 Visual Studio의 전문가 버전뿐만 아니라 무료 Visual Studio Express 버전 모두에서 작용합니다.

nFringe는 비상업용 프로젝트의 경우 무료입니다. nFringe에 대한 상세 정보는 여기서 찾을 수 있습니다.

nFridge를 설치하려면 여기 지시를 따르십시오.

nFringe를 "General/Mod Project" 모드에서가 아닌 라이선시 프로젝트 모드에서 사용해야 합니다. UDK는 UnrealScript 액세스만 가능하긴 해도, 라이선싱된 UE3와 거의 비슷한 방식으로 동작하기 때문입니다.

구성한 프로젝트는 딱 한 개의 프로젝트만을 위한 것이 아닌, 하나의 프로젝트로써의 전체 UDK를 위한 것입니다. 따라서 지시에 언급된 바와 같이 프로젝트와 솔루션 파일은 UDK\Development\src 에 저장해야 합니다.

nFringe는 자동으로 UnrealScript 패키지를 관리하지 않습니다. 각 패키지에 대한 디렉토리 구조를 적절하게 수동으로 생성하고, 환경설정도 업데이트하여 해당 패키지가 컴파일 과정에 포함되도록 해야 합니다. 자세한 내용은 프로젝트 구성 을 참고 하십시오.

WOTgreal

UnrealEngine 용으로 특수 제작된 다른 IDE 로는 WOTgreal이 있습니다. WOTgreal은 UnrealEngine3 를 부분적으로 지원합니다. UE3 구성에서 잘 돌아가지 않는 WOTgreal 기능이 여럿 있습니다만, 그래도 UnrealScript 작성은 물론 코드 분석(insight) 및 완성 기능도 사용할 수 있습니다.

WOTgreal을 사용하려면 먼저 최신의 안정된 릴리스를 다운로드해야 합니다. 다운받은 최신 릴리스 내용을 설치 디렉토리에 풀어 이전 파일을 덮어씁니다. 윈도우 비스타에서 WOTgreal을 사용하려면 관리자 권한으로 시작해야 합니다.

WOTGrealConfig1.png
UDK용 WOTgreal 구성에는 작업이 좀 필요합니다. 시작할 때 먼저 "game type"을 구성해야 합니다. "tools" 메뉴에서 "Game Types Configuration"을 선택합니다. "Configure Game Types" 대화창에 다음과 같은 정보로 새로운 게임 종류를 추가합니다:

WOTGrealConfig2.png
Display name
UDK (또는 아무렇게나)
Game EXE name
udk
Menu name
UDK $ "expands " 대신 "extends"를 사용하십시오.
Game Architecture
"UE3"

다른 세팅은 전부 지금 시점에는 별로 소용이 없습니다. "OK" 버튼을 눌러 설정을 저장하고 메인 WOTgreal 화면으로 돌아가십시오.

"Options" 메뉴를 통해 preferences를 엽니다. Preferences 창에서 "Game Information"을 선택하십시오. 목록에서 게임 유형 "UDK"를 선택하십시오.

WOTGrealConfig3.png
그리고 다음 설정을 업데이트 하십시오. {UDK directory} 는 여러분이 UDK 를 설치한 디렉토리입니다.

UCC.exe File
{UDK directory}\binaries\win32\udk.com
Game Root Dir
{UDK directory}
Source Root Dir
{UDK directory}\Development\Src

주: udk.exe 가 아니라 udk.com 을 사용하는 것이 중요합니다.

WOTGrealConfig4.png
WOTGrealConfig5.png
그 다음, "Compiling & Debugging" 부분으로 이동하십시오. "Pre-Compile" 탭에서 "Do nothing for pre-compilation" 옵션을 선택하십시오. 컴파일 탭에서 "Run this program/... to compile" 옵션을 선택하십시오. 그 다음, 다음을 입력하십시오. c:\windows\system32\cmd.exe /C {UDK directory}\binaries\win32\udk.com make.

"Post-Compile" 탭에서 로그 이름으로 다음 위치를 입력하십시오: {UDK directory}\UDKGame\Logs\Launch.log.

이 단계들을 거치고 나면 WOTgreal을 사용할 수 있습니다. 패키지와 클래스 트리를 채우려면 F5 를 누르십시오. 시간이 좀 걸립니다.

WOTgreal에는 UnrealScript 인터페이스가 표시되지는 않지만, 에디터를 통해 작성할 수는 있습니다. 전처리기(pre-processor) 디렉티브도 인식되지 않지만, 선언부에 사용하지만 않는다면 문제가 되지는 않습니다.

nFringe와 달리 WOTgreal은 UnrealScript 패키지에 대한 디렉토리 구조를 제대로 만들긴 합니다. 그러나 패키지가 컴파일에 포함되도록 환경설정을 업데이트하지는 않습니다. 자세한 내용은 프로젝트 구성 부분을 참고 하십시오.

기타 에디터

물론 손에 익은 텍스트 에디터를 사용해도 됩니다. 일부 에디터의 경우 UnrealScript 용 구문 강조 기능도 있고, 또다른 고급 기능이 있는 것도 있습니다. 기타 에디터에 대한 상세 정보는, 커뮤니티 주도 UnrealWiki 페이지를 참고해 주시기 바랍니다.

일반 텍스트 에디터를 사용하는 경우, UnCodeX 라는 툴을 사용하시는 편이 좋습니다. nFringe 또는 WOTgreal에서 볼 수 있는 클래스와 패키지 트리는 물론, 단순 텍스트 에디터에 추가적으로 사용하기 좋은 기능이 다양하게 제공됩니다. 최신 안정 버전을 다운받아 설치하신 다음, UE3가 지원되는 최신 버전을 받으시기 바랍니다.

기타 중요 사항

머지 않아 어플리케이션 개발을 시작하려는 경우, 버전 관리 시스템 을 사용하는 것이 좋습니다. 버전 관리 시스템은 두 가지 중요한 역할을 합니다. 무엇보다 가장 중요한 역할은 백업과 개발 기록 추적입니다. 개발 중 실수를 했을 경우 이전 버전으로 쉽게 복구시킬 수 있는 것입니다. 두 번째 중요한 역할은 동일한 프로젝트를 여러 사람이 같이 작업할 수 있게 되는 것입니다.

어플리케이션 예제

여기서는 UDK로 작은 어플을 개발하는 것에 대해 얘기해 보겠습니다. 이 어플을 CluOne 이라 해 보죠.

별다른 작업을 하는 어플은 아닙니다. 현재 들여다보고 있는 게임내 요소에 대한 정보를 약간 요청하는 정도의 단순한 시스템입니다.

프로젝트 구성

SrcDir.png

앞서 언급했듯이 UDK\Development\src 디렉토리는 소스 코드를 위한 곳입니다. 이 디렉토리에는 이미 UDK에서의 모든 UnrealScript 클래스에 대한 소스 코드가 들어 있습니다. 각 하위 디렉토리는 단일 패키지용입니다.

CluOne 의 소스 코드는 이 디렉토리에 추가될 것입니다. 예제 어플리케이션은 소규모 어플리케이션이므로 하나의 패키지만으로 충분합니다. UDK\Development\src 디렉토리에 CluOne 이라는 이름의 디렉토리를 만듭니다. 그 CluOne 디렉토리에 Classes 라는 디렉토리를 또하나 만듭니다. UnrealScript 컴파일러는 UDK\Development\src\*\Classes 디렉토리에서 UnrealScript 코드를 검색합니다. 그 외에 패키지 디렉토리에 있는 다른 내용은 전부 무시됩니다.

src 디렉토리에 CluOne 디렉토리가 있지만 컴파일러가 자동으로 집어내지는 못합니다. 환경설정 파일을 업데이트하여 CluOne 패키지를 유효한 UnrealScript 패키지로 등록해야 합니다.

UDK\UDKGame\Config\UDKEngine.ini 파일을 열고 [UnrealEd.EditorEngine] 부분을 찾습니다. 여기에 EditPackages 라는 항목이 있을 것입니다. 이게 바로 컴파일러가 컴파일할 디폴트 패키지입니다. EditPackages 는 기본 UDK 패키지용으로 예약된 것이며, 커스텀 패키지는 ModEditPackages 로 추가해야 합니다.

따라서 CluOne 패키지에 대해서는 ModEditPackages=CluOne 항목을 추가해야 합니다. EditPackagesModEditPackages 에 수록된 패키지의 순서는 중요합니다. 패키지가 컴파일 되는 순서를 정의하며, 클래스의 계층구조에 영향을 줍니다. 부모 클래스가 컴파일되지 않은 클래스는 컴파일되지 않습니다. UnrealScript 컴파일러는 Java 컴파일러처럼 세련되지는 않아서, 컴파일 순서를 스스로 알아내지는 못합니다.

UnrealScript 클래스의 계층구조

UDK에는 2000 개가 넘는 UnrealScript 클래스가 있습니다. 이 모든 클래스가 어떤 일을 하는지 아는 것은 중요하지 않습니다. 클래스의 계층 구조 전체를 검토하려면 nFringe, WOTgreal, 또는 UnCodeX 를 이용하는 것이 좋습니다.

많은 클래스 중에 매우 중요하고 아주 독특한 기본 클래스가 둘 있습니다. 첫째는 기본 클래스 Object 입니다. Object 는 다양한 연산자를 포함한 많은 핵심 기능들을 정의합니다. 두 번째로 중요한 클래스는 Actor 입니다. Actor 클래스는 게임에서 “움직이고” 또는 “살아있는” 모든 요소들의 기본 클래스입니다. 레벨에 보이는 모든 요소가 Actor 이지만 모든 Actor 가 보이는 것은 아닙니다. 예를 들어 Info 의 모든 서브클래스는 기본 설정에 의해 보이지 않습니다. Object 클래스는 유틸리티 또는 간단한 기능성에 사용되지만 사용자 인터페이스에도 사용됩니다. 네트워크 기능(, 예로 클라이언트와 서버간의 통신)이 요구되는 UnrealScript 클래스는 Actor 의 서브클래스여야 합니다.

이 튜토리얼에서 확장할 클래스는 아래와 같습니다:

GameInfo
게임 논리에 대한 메인 클래스입니다. UDK를 사용하여 어플리케이션을 제작할 경우는 거의 이 게임의 서브클래스를 작성할 가능성이 높습니다.
PlayerController
이 클래스는 플레이어를 나타내며, 플레이어의 보이는 부분이 아닌 입력을 처리하고 액션을 수행하는 부분입니다. 플레이어의 실제 시각적인 부분은 서브클래스인 Pawn 에 의해 수행됩니다.

GameInfo 서브클래스

대부부의 어플리케이션 진입점은 GameInfo 서브클래스입니다. 이 클래스는 어떤 PlayerController 클래스가 생성되는지, 기타 여러가지 중요한 게임 논리 클래스를 정의합니다. 전부 설명하기에는 정보가 너무 많으니, 여기서는 간단히 추려 보도록 하겠습니다.

클래스 선언

새 클래스를 만들려면 그 클래스를 포함할 패키지의 Classes 디렉토리에 원하는 클래스의 이름과 동일한 이름의 파일을 만들면 됩니다. CluOne 어플리케이션의 경우 GameInfo 서브클래스가 CluOneGame 이라는 이름으로 만들어져 UDK\Development\Src\CluOne\Classes\CluOneGame.uc= 파일에 저장됩니다.

UnrealScript 클래스의 클래스 선언은 다음과 같습니다:

  class CluOneGame extends GameInfo;
  

세미콜론(;)으로 마치고 중괄호가 사용되지 않는 것에 유의하십시오. 클래스 선언의 모든 것은 해당 클래스에 속하게 됩니다. 파일당 하나의 클래스만 선언될 수 있습니다.

새 플레이어 컨트롤러

CluOne 어플리케이션은 새 PlayerController 클래스를 사용합니다. 사용자가 게임에 접속하면 GameInfo 클래스가 해당 사용자에 대한 컨트롤러를 만듭니다. GameInfo 클래스가 정확한 클래스를 만들도록 하기 위해, 다음 코드를 소스 끝에 추가했습니다.

  defaultproperties
  {
      PlayerControllerClass=class'CluOnePlayerController'
  }
  

defaultproperties 블록은 특정 변수의 기본값을 정의합니다. 이것은 다른 프로그래밍 언어의 변수 이니셜라이저와 (완전히 똑같지는 않지만) 비슷합니다. 기본 GameInfo 구현은 PlayerControllerClass 변수의 값에 기초하여 PlayerController 인스턴스를 만듭니다. 물론 올바른 클래스 인스턴스를 만드는 방법은 여럿 있지만, 이 방법이 가장 쉬운 최선의 방법입니다.

사용자 맞이하기

사용자가 게임 서버에 접속하면 GameInfo 클래스에서 많은 이벤트들이 연속적으로 트리거됩니다. (싱글 플레이어나 "StandAlone" 모드에서도, 언리얼 엔진을 "게임 서버"로 생각해 볼 수 있습니다.)

이 이벤트들 중 3가지는 PreLogin, Login, PostLogin 입니다. 이벤트 이름이 그 순서를 나타냅니다. 사용자는 PostLogin 이벤트에서 완전히 연결되고, 인터페이스가 가능해집니다.

이 시점에서 다음과 같이 사용자를 맞이합니다:

  event PostLogin( PlayerController NewPlayer )
  {
      super.PostLogin(NewPlayer);
      NewPlayer.ClientMessage("그리드에 잘 오셨습니다"$NewPlayer.PlayerReplicationInfo.PlayerName);
      NewPlayer.ClientMessage("오브젝트에 마우스 왼클릭하면 대상 정보를 구합니다.");
  }
  

디폴트 플레이어 이름은 DefaultPlayerName 변수로 정의되며, 이는 현지화된 문자열로 정의됩니다. 디폴트 이름을 바꾸기에 적절한 방법은, 현지화 파일을 업데이트하는 것입니다. 그런데 지금은 다음 함수를 사용하여 플레이어 이름을 하드코딩시키겠습니다.

  event PlayerController Login(string Portal, string Options, const UniqueNetID UniqueID, out string ErrorMessage)
  {
  	local PlayerController PC;
  	PC = super.Login(Portal, Options, UniqueID, ErrorMessage);
  	ChangeName(PC, "Clu", true);
      return PC;
  }
  

PlayerController 서브클래스

가장 압권인 부분은 커스텀 PlayerController 클래스입니다. 새로 만든 클래스가 호출되게 하려면, 해당 클래스를 포함하는 패키지의 Classes 디렉토리 안에다 클래스명과 똑같은 이름의 파일로 각각 저장해야 합니다. CluOne 을 예로 들자면, PlayerController 서브클래스는 CluOnePlayerController 라는 이름으로 생성되며, UDK\Development\Src\CluOne\Classes\CluOnePlayerController.uc 파일에 저장되게 됩니다.

이 클래스는 간단한 클래스 선언으로 시작됩니다:

  class CluOnePlayerController extends PlayerController;
  

십자선

PlayerController 클래스는 기본적으로 십자선(Crosshair)을 표시하지 않으나, 기존 서브클래스 여럿은 십자선을 표시합니다. 십자선을 그려내는 방법에는 여러가지가 있습니다. 플레이어의 현재 인벤토리 아이템에 위임(deligate)할 수도 있고, HUD에서 그릴 수도 있습니다. 아니면 PlayerController에서 직접 그릴 수도 있습니다.

PlayerController 상의 현재 HUD에서 DrawHud(...) 함수가 호출되는데, 화면상에 부가적인 것들을 그릴 수 있게 하기 위함입니다. 다음의 코드는 화면 가운데에 보기좋은 작은 초록색 십자선을 그려냅니다.

  /**
   * 십자선을 그림. 이 함수는 Engine.HUD 클래스에 의해 호출됨.
   */
  function DrawHUD( HUD H )
  {
  	local float CrosshairSize;
  	super.DrawHUD(H);
  
  	H.Canvas.SetDrawColor(0,255,0,255);
  
  	CrosshairSize = 4;
  
  	H.Canvas.SetPos(H.CenterX - CrosshairSize, H.CenterY);
  	H.Canvas.DrawRect(2*CrosshairSize + 1, 1);
  
  	H.Canvas.SetPos(H.CenterX, H.CenterY - CrosshairSize);
  	H.Canvas.DrawRect(1, 2*CrosshairSize + 1);
  }
  

fire 버튼

이제 보고 있는 것이 무엇인지 알 수 있게 되었으니, "뭔가를 볼" 방법을 찾아야 하겠습니다. 항상 보고 있을 수도 있지만, 사용자가 마우스 왼쪽 버튼을 클릭했을 때 특정 정보만을 보도록 하겠습니다.

입력 처리 시스템을 통해 마우스 왼쪽 버튼이 fire 액션에 매핑됩니다. fire 액션은 사실상 "콘솔 명령" 연속 실행입니다. (Engine\Config\BaseInput.ini 파일 참고).

(플레이어 콘트롤러 클래스같은) 특정 클래스에 정의된 exec 모디파이어 포함 함수는 소위 "콘솔 명령"으로써 받아들여집니다. fire 액션에 의해 실행되는 콘솔 명령 중 하나는 StartFire 입니다. StartFire 명령은 PlayerController 클래스에 정의되어 있습니다.

여기 커스텀 플레이어 콘트롤러 클래스에서는 이 함수를 덮어쓰고 자체적인 논리를 추가해 보겠습니다.

  /*
   * 플레이어 컨트롤러에 대한 디폴트 상태
   */
  auto state PlayerWaiting
  {
  	/*
  	 * 사용자가 fire 키 (디폴트는 왼클릭)를 누르면 함수가 호출됩니다.
  	 */
  	exec function StartFire( optional byte FireModeNum )
  	{
  		showTargetInfo();
  	}
  }
  

위의 코드에서 보듯이 StartFire 함수는 사실상 PlayerWaiting 이라는 상태에서 선언됩니다. 언리얼 엔진의 Actor 클래스는 내재적 상태 머신입니다. 즉 액터의 현재 상태에 따라 함수의 이런 구현 저런 구현을 호출한다는 뜻입니다. 고로 액터의 상태에 따라 fire 버튼을 눌렀을 때의 작동방식을 다르게 할 수 있습니다. 플레이어 콘트롤러용 디폴트 (자동) 상태는 PlayerWaiting 상태입니다. 그래서 그 상태의 StartFire 함수를 덮어쓴 것입니다.

바라보는 것에 대한 정보를 표시하는 실제 코드는 showTargetInfo 함수에 구현되어 있습니다.

바라보는 것

현재 플레이어가 바라보는 것이 무엇인지를 알아내기 위해서는, 시선을 따라 처음 걸리는 것이 무엇인지를 추적해야 합니다. 그 작업에는 Actor 클래스에 정의된 =trace(...) 함수를 사용합니다. Trace 함수는 뭐가 보이는지를 알아내거나 무기 발사용 타겟을 설정하는 데 많이 쓰입니다.

시선에 처음 걸리는 게임 엘리먼트를 찾기 위해 trace 함수를 사용합니다. 뭔가 걸리면 소리를 재생하고 그 엘리먼트에 대한 정보를 몇 줄 출력하겠습니다.

  /*
   * 바라보는 것에 대한 정보를 출력합니다.
   */
  function showTargetInfo()
  {
  	local vector loc, norm, end;
  	local TraceHitInfo hitInfo;
  	local Actor traceHit;
  
  	end = Location + normal(vector(Rotation))*32768; // "무한" 추적
  	traceHit = trace(loc, norm, end, Location, true,, hitInfo);
  
  	ClientMessage("");
  
  	if (traceHit == none)
  	{
  		ClientMessage("Nothing found, try again.");
  		return;
  	}
  
  	// 정보 확인을 위해 소리 재생
  	ClientPlaySound(SoundCue'A_Vehicle_Cicada.SoundCues.A_Vehicle_Cicada_TargetLock');
  
  	// 디폴트로 4 콘솔 메시지만 표시합니다.
   	ClientMessage("Hit: "$traceHit$"  class: "$traceHit.class.outer.name$"."$traceHit.class);
   	ClientMessage("Location: "$loc.X$","$loc.Y$","$loc.Z);
   	ClientMessage("Material: "$hitInfo.Material$"  PhysMaterial: "$hitInfo.PhysMaterial);
  	ClientMessage("Component: "$hitInfo.HitComponent);
  }
  

시작 지점에서 주어진 종료 지점까지 추적합니다. 시작 지점으로는 플레이어 콘트롤러의 현재 위치를 사용합니다. 종료 지점으로는 "무한" 지점을 가리키려 하고 있습니다. 이는 플레이어 콘트롤러의 현재 위치를 현재 회전의 정규화된 벡터를 가지고 확장하는 식으로 이루어집니다. 회전은 카메라의 앵글을 정의합니다. 정규화된 벡터는 월드의 최대 거리(크기로 -32767에서 32768 사이)로 곱해줍니다. 이는 기본적인 벡터 연산으로, 3D 작업을 하려면 제대로 이해하셔야 하는 부분입니다.

ClientMessage(...) 함수는 현재 플레이어의 콘솔에 정보를 약간 출력합니다. 플레이어와 특정 게임 이벤트간의 채팅에도 사용되는 함수입니다. 화면에 디버그 정보를 출력하는 것도 좋은 방법입니다(만 어플리케이션 출하 전에는 디버그 코드를 지워야 한다는 것 잊지 마시고요).

예제의 끝

자그마한 UnrealScript 예제의 끝입니다. UDK로 진짜 어플리케이션을 만들기 위해 알아야 할 것은 훨씬 더 많이 있습니다. 언리얼 스크립트 및 언리얼 엔진의 게임 프레임워크에 대한 부가 정보는 이 문서의 끝부분을 참고하시기 바랍니다.

이 예제의 소스 코드 도 다운받으실 수 있습니다.

컴파일하기

소스 코드를 컴파일하는 방법도 여럿 있습니다. UDK가 지원되는 IDE를 사용하는 경우, 그냥 컴파일 버튼을 누르면 프로젝트가 컴파일됩니다.

코드를 컴파일하기 위한 다른 방법은, UnrealFrontend 를 사용하는 것입니다. 프론트엔드를 시작하고 툴바에서 "Make" 버튼을 누르면 컴파일러가 시작됩니다. 버튼은 얼마든지 눌러도 됩니다. 소스 파일이 변경된 코드만 컴파일됩니다. 전체 리컴파일을 할 수도 있습니다. Make 버튼 옆의 아래 화살표 버튼을 누른 다음 "Full Recompile"을 선택하면 됩니다.

FrontendMake.png

프론트엔드의 문제점 하나는, 항상 전체 컴파일 출력이 표시되지 않는다는 점입니다. 그래서 수동으로 로그 파일을 열어야 합니다.

세 번째 방법은, 위의 두 방법이 실제로 컴파일러를 시작하는 방법인데, 명령줄을 실행시키는 것입니다. 일반 컴파일 명령은:

  C:\UDK\UDK-2009-11\Binaries\Win32\udk.com make
  

전체 리컴파일은 그냥 명령줄에다 -full 인수를 붙이기만 하면 됩니다.

콘솔 명령을 실행할 때는 udk.com 실행파일 보다는 udk.com 실행파일을 사용해야 합니다. 둘 다 효과는 같습니다만, udk.com 명령은 표준 출력으로 결과가 나타나며, udk.exe 는 전용 창에 나타납니다. (Makefile 또는 Ant 같은 ) 빌드 스크립트로 컴파일을 사용하려는 경우, 표준 출력으로 해야 합니다. 이 경우 명령줄에 -unattented 옵션을 추가하여, 컴파일 도중에 뜨는 입력창을 억제시킬 수 있습니다.

패키지가 컴파일 과정에 포함되지 않은 경우, UDKGame\Config\UDKEngine.ini 파일의 [UnrealEd.EditorEngine] 부분에 ModEditPackages 로 추가했는지 확인하시기 바랍니다.

테스팅

코드가 성공적으로 컴파일되면 새로운 함수성이 제대로 작동하나 어플리케이션을 테스트해 볼 수 있습니다.

대부분의 경우 테스팅은 UDK에 론치 URL을 줘서 클라이언트 모드로 시작합니다. CluOne 어플리케이션을 시작하려면, 다음과 같이 UDK를 실행시켜야 합니다:

  C:\UDK\UDK-2009-11\Binaries\Win32\udk ExampleMap?game=CluOne.CluOneGame
  

이 경우 론치 URL은 ExampleMap?game=CluOne.CluOneGame 입니다. URL은 맵 이름 (이 경우 ExampleMap) 또는 접속하려는 서버의 호스트명/IP 중 하나가 됩니다. 그 다음에 ?key=value 요소 세트가 옵니다. 이 부분으로 다양한 세팅을 제어합니다. 가장 중요한 keygame 으로, 게임을 시작할 때 어느 GameInfo 서브클래스를 생성할 지를 정의합니다. 그 값은 완전한 클래스 이름으로, PackageName.ClassName 같은 식입니다. 다른 가용 ?key=value 요소는 보통 생성된 GameInfo 서브클래스에 의해 결정됩니다.

ExampleMap 맵을 사용한 이유는, 작은 맵이라 로딩이 빠르기 때문입니다.

디버깅

이 글을 작성하는 시점에서는 UDK 용으로 지원되는 디버거가 마땅치 않습니다. 언젠가 nFringe 를 통해 (UT3가 가능하듯,) UDK에 대한 언리얼 스크립트 디버깅이 가능해 질런지는 모르겠습니다. 그렇게 되면야 중단점을 설정하고 코드로 들어가 볼 수 있겠지만, 그 전까지는 고전적인 디버깅 방법밖에는 없습니다.

뭔가가 예상한 대로 돌아가지 않을 때의 흔한 이유 두 가지는 이렇습니다:

  1. 코드가 호출되지 않았을 때
  2. 설정되지 않은 데이터에 접근(, 즉 널 포인터 참조)하려 했을 때

후자는 로그를 조사해 보면 다 나옵니다. 로그에는 목숨같은 정보가 담겨 있으니, 개발 도중에는 항상 로그를 눈여겨 보는 것이 중요합니다. 로그를 눈여겨 보기에 가장 좋은 방법은 콘솔 로킹을 켠 채로 클라이언트를 시작하는 것입니다. 다음 명령으로 가능합니다:

  C:\UDK\UDK-2010-05\Binaries\Win32\udk.exe -log
  

로그 출력이 생방송되는 콘솔 창이 열립니다. 이 방법이 아니고서는 클라이언트를 먼저 닫아야 UDKGame/Logs 디렉토리의 로그 파일을 읽어볼 수 있습니다.

로그 출력을 조사할 때는 다음과 같은 줄을 주의깊게 보셔야 합니다:

  Log: Accessed None 'AccessControl'
          UTDeathmatch DM-Deck.TheWorld:PersistentLevel.UTDeathmatch_0
          Function Engine.GameInfo:Login:0358
  

"Accessed None" 이라 함은 언리얼 스크립트에서의 널 포인터 예외에 해당하는 것입니다. 읽으려 했으나 none/null 인 변수의 이름을 로그가 알려주고 있습니다. 이 경우에는 AccessControl 이죠. 어디서 발생했는지도 알려줍니다: Engine.GameInfo 클래스 안의 Login 이죠. 숫자 0358 은 줄번호가 아니라, 바이트-코드 오프셋입니다. 이걸 줄번호로 쉽게 변환할 수 있는 방법이 없기에, 그냥 무시하시기 바랍니다. 최소한 어디서 뭐가 잘못됐는지는 알 수 있습니다.

뭐가 벌어지고 있는지, 뭐는 안벌어지고 있는지에 대한 기록을 유지하기 좋은 방법은, 로그 출력에 부가적인 정보를 더하는 것입니다. 이런 코드를 통해 간단히 붙일 수 있습니다:

  `log("로그 출력에 이 줄이 추가됩니다.");
  

log 앞에 붙어 있는 백틱(back-tick, ` 작은 따옴표 ' 가 아님)에 주의하시기 바랍니다. 이 기호는 진짜 로깅 호출을 만들어 내는 전처리기 지시어(directive) 입니다. 이 매크로의 전체 용법은:

  `log(logMessage,expression,prefix);
  

logMessage
기록할 메시지입니다.
expression
표현식으로, 이 조건이 참이 되어야 위의 메시지를 기록합니다.
prefix
로그 메시지에 붙일 접두사입니다. 다른 것과 구분하는 데 매우 좋습니다.

이와 같은 간단한 문장을 통해 특정 상황에 대한 기록을 유지할 수 있습니다.

  function foo(Bar quux)
  {
      if (quux == none)
      {
          `log("foo가 호출되었으나 quxx가 없음");
      }
      else {
          `log("foo 수행: "$quux)
      }
  }
  

기타 유용한 디버그 매크로 지시어에 대해서는, Development\src\Core\Globals.uci 파일을 참고하시기 바랍니다.

또하나 매우 우용한 디버깅 기능이라면 Object 에 정의된 ScriptTrace() 입니다. 이 함수를 호출하면 스택 트레이스를 로그 출력에 덤프합니다. 이를 통해 현 시점에 이르기까지의 함수 호출 체인을 확인해 볼 수 있습니다.

한 가지 중요한 점은, 코드 컴파일시 경고가 없도록 하십시오. 경고가 컴파일 시간에는 문제가 안될지 모르겠으나, 코드를 실행할 때는 종종 에러가 나곤 합니다.

더욱 깊은 내용