UDN
Search public documentation:

MasteringUnrealScriptInterfacesKR
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

제 13 장 - 인터페이스

지금까지 Unreal Script에서의 거의 모든 작업의 실시를 충분히 가고 있습니다. 문법, 변수 및 함수의 인스턴스화의 방법을 이해했습니다. 이테레이타, 상태 및 Delegate의 이용을 허락하기 위한 몇 안 되는 선진적인 토픽도 설명했습니다. 본장에서는, 앞으로 1 보진응으로 가, Interface(인터페이스)의 도입에 의해, 프로젝트의 성장에 따라 의존 가능한 프로그래밍에의 문을 열고 있습니다. 그러한 인터페이스의 목적, 인스턴스화 및 프로젝트를 통해서 코딩 표준의 유지를 지원하기 위해서 사용할 수 있는 2 개의 예를 보고 갑니다.

인터페이스란, 무엇입니까 ?

프로그래밍은, 아마추어의 관찰자에게는 전혀 모르는, 많은 도전을 가져옵니다. 특히, 게임의 개발은, 그러한 도전이 많이 일어나기 (위해)때문에, 소프트웨어 공학의 거의 모든 측면을 이용하는 분야입니다. 큰 객체 지향 시스템으로 작업하는 경우는, 가끔, 같은 함수의 서명을 제공하는 함수의 그룹을 정의해, 모두 단일의 방법으로 사용되는 많은 클래스를 작성합니다.

  class MyPistol extends MyWeapon;
  
  function bool PrimaryFire(float rate)
  {
     /* 피스톨의 처리는 여기 */
  }
  
  /* 그 외의 처리 */
  
  class MyRifle extends MyWeapon;
  
  function bool PrimaryFire(float rate)
  {
     /* 라이플의 처리는 여기 */
  }
  
  /* 그 외의 처리 */
  
  class MyShotgun extends MyWeapon;
  
  function bool PrimaryFire(float rate)
  {
     /* 산탄총의 처리는 여기 */
  }
  
  /* 그 외의 처리 */
  

이것은, 영국 해협을 헤엄쳐 건너거나 모두 이쑤시개로 집을 짓거나 하는 것이 가능하다라고 같이에입니다만 - 과감히, 여기서 코드를 기술하는 것은, 완전하게 실현 가능합니다만, 필요한 함수를 적절히 실장한 것을 확인하고 있습니다. 또, 함수가 향후 변경되지 않는다고 하는 보증도 없기 때문에, 후일 코드의 리파크타링이 문제가 될 가능성이 있습니다 ; 물론, 동료나 공동 개발자가 정말로 지시에 따르고 있는지를 확인하는 수단을 제공하고 있지 않는 것에는 언급하고 있습니다.

많은 객체 지향 언어는, 이 문제를 경감하기 위한 툴을 제공하고 있어, Unreal Script 는, Java 및 CPP 의 루트에 대해서 툴을 가지고 있습니다. 정의된 표준에 따라 클래스를 계획하는 명시적인 의미를 제공하는 것으로 코드의 품질 향상을 지원하는 동안에, 부분적으로는, 컴파일러에 대한 모니터링 코드 개발의 합병증을 조작해, 인터페이스는, 이러한 타입의 상황내에서 이용됩니다.

이 설명으로 편리한 용어는, Implement(실장)입니다. 이후 이 장에서는, 유저에게 있는 기능의 그룹을 제공하는 클래스 또는 디바이스를 참조하기 위해서 이 용어를 사용합니다. 스푼은 도구의 인터페이스를 실장합니다, 이것은, 스푼은, 도구로서의 모든 기능을 제공한다고 하는 것과 같습니다. 스푼은 도구를 실장할지도 모르지 않습니다만, 더욱 기능을 제공해, 자기 자신의 방식에 특화하지 않는다고 하는 것은 아닙니다.

인터페이스의 예

근처의 Ikea 나 식기가게에 갔다고 하면(자), 포크, 스푼 또는 나이프는 어떻게 있어야할 것인가라고 하는 장식의 모습이나, 찬스가 있으면, 그러한 것과 다른 외관의 것을 볼 수 있겠지요. 이러한 가능성을 고려하지 않아도, 전체로 보았을 때에는, 모든 다른 식기와 같은 표준을 실장하고 있습니다. 몇개의 것은, 야채를 취급하는데 편리한 구멍 빈 곳 스푼이나 「양자의 장점을 살린 것」으로서도 알려진, 2 개의 요소의 편리함을 조합한 환진한 붉은 털의 의붓자식인 스포크와 같이, 보다 특수화 되는 것 조차도 가능합니다.

포크 스푼 나이프
  • 음식을 취급하려면 , 간단합니다
  • 입수한 것을 용이하게 떼어내기 위해서(때문에) 제공
  • 액체 및 젤리를 있는 장소로부터 다른 장소로 이동하기 (위해)때문에
  • 구강에 넣어지도록(듯이), 충분히 작은가 모임에 음식을 나누기 (위해)때문에.
표 13.1 도구를 사용하는 인터페이스의 간단한 예

도구로서는, 이하를 확실히 하는, 포크, 스푼 및 나이프에 있어서의 다른 표준이나 요건의 제시가 가능합니다 :

  • 충분히 사용할 수 있을 만큼 크지만, 소문이나 손에 충분히 적합하도록(듯이) 작아지고 있습니다
  • 망가지기 쉬운 또는 표준외의 물질에서는 작성되고 있지 않습니다 (발포스티롤의 도구는 (들)물었던 적이 없습니다)
  • 자신의 손가락을 씹거나 식사중에 걸프렌드의 눈을 찌르거나 하지 않는 것을 확실히 하는 적절한 길이되고 있습니다.
  • 식사를 가능하게 하기 위한(해) 억지로 몸을 비트는 것 같은, 허용 할 수 없는 것 같은 복잡한 방식으로 구부러질 것은 없습니다

USB

인터페이스의 이제(벌써) 1 개의 예는, 일상적으로 이용하고 있어서 , 아마, 각각의 컴퓨터상의 것입니다. USB 디바이스를 플러그 인 했을 때에, Wacom 의 타블렛으로부터, 마우스, 하드 드라이브까지의 모든 것을 플러그 인 하기 위한(해) 1 개의 포토의 이용을 허락한다, 자주(잘) 정의된 인터페이스를 이용합니다. USB 동글을 확실히 컴퓨터에 밀어넣는 것이 가능하고, 몇 초의 사이에 windows 는 디바이스를 인식해, (때에는 그렇지 않은 경우도 있습니다만) 곧바로 사용을 허가합니다. USB 는, 영구히 이용할 수 있는 것이 아닙니다. 나의 아는 사람의 상당수는 Commodore 64 상에서 동작하는 죠이스틱을 입수했을 때에, 얼마나 큰 일이었던인지를 기억하고 있습니다만, 그것은, 똑같이 표준화 된, 시리얼 포트로 불리는 인텔의 인터페이스를 가지고 있었습니다.


그림 13.1 - 일반적인 USB 포토

컴퓨터 마우스

마우스를 채택해 왼쪽 및 오른쪽의 버튼의 동작에 의존하는 것을 허가한다, 또, 예기 했던 대로 움직이는 방법 등, 인터페이스는, 주위안에 있습니다. 마우스는 인터페이스의 2 개째의 성질 - 실장은 변경할 가능성이 있는 것 - 을 나타내기 때문에, 인터페이스의 매우 좋은 예입니다. 마우스는, 볼, 트랙볼, 빛, 레이저에서도 좋습니다만, 그 실장과 관계되지 않고 몇년이나 걸려 습득한 방법으로, 이 마우스를 안심해 사용할 수 있습니다. 마우스 패드상을 움직여도, 트랙볼을 돌려도, 화면상에서 커서를 움직이기 (위해)때문에, 컴퓨터에는 적절한 커멘드가 보내지는 ; 버튼을 클릭했을 경우도, 컴퓨터로부터 기대한 응답을 얻을 수 있습니다.

전원 콘센트

양극 및 삼극의 전원 콘센트가, 냉장고 또는 천정 환기팬 및 시로부터 제공된 전력망과 같은, 전자 디바이스간의 인터페이스로서 제공되고 있습니다. 이것을 취급하기 위해서(때문에) 복수의 인터페이스가 있습니다. 삼극의 인터페이스에서는 지구를 이용할 수 있습니다만, 양극에서는 이용할 수 없습니다.

전력의 요구 사항과 서지의 방지를 위해서(때문에), 랩탑 또는 데스크탑·컴퓨터와 같은 디바이스는 삼극 콘센트에게만 꽂을 수가 있습니다. 탁상 선풍기 또는 휴대전화 어댑터는, 양극만을 사용합니다만, 삼극 콘센트에서는, 양극의 콘센트와 모두 같은 기능을 제공하고 있어, 거기로부터 먼저 진행되어 지구도 제공했기 때문에, 양극의 아이템은, 어느 쪽의 콘센트에도 꽂아 가능합니다.

삼극의 콘센트는, 양극의 인터페이스를 Implement(실장) 하고 있다고 말할 수 있습니다. 만약, 몇개의 일반적인 콘센트의 타입에 대해서 계승나무를 작성하면(자), 이러한 것이 됩니다만, 여기에서는, 냉장고나 대형 냉동고를 쑤실 가능성이 있는 고전압의 선은 제외해 확실히 간략화되고 있습니다.


그림 13.2 - 전원 콘센트의 트리도

메모를 올바른 위치에 두어, 클래스와 그 상호 관계를 기술한 5 만 피트의 그림을 유지하는 것을 지원하기 위해서, 이러한 타입의 그림을 사용할 수가 있습니다. 어느 레벨로 무엇을 사용할 수 있을까를 더듬는 일도 흥미로운 것입니다. 이 그림을 봐, 탁상 선풍기는, 집안의 어디에라도 쑤실 수 있습니다만, 강력한 헤어 드라이어를 사용한다면, 욕실가운데를 봐, 삼극의 콘센트를 실장하고 있는 것을 찾을 수가 있는 것을 압니다.

프로그래밍의 사양

프로그래밍에 관해서 말하면, 다른 클래스에 의존하는 것이 가능한 특정의 함수를 정의하기 위해서 인터페이스를 사용합니다. 프로그래밍을 실시할 때는, 같은 입력, 출력 및 이름을 가지는 많은 함수를 정의하는 것이 자주 있습니다. 이 편성을 함수의 시그니체라고 부릅니다. 이러한 제한 중(안)에서 작업해, 어떤 종류의 요구 사항을 작성해, 어떠한 기능을 제공할까를 고해, 프로그래머에게 건네줄 수가 있습니다만, Interface 에서는, 제공한 것에 임해서, 컴파일러에 확인을 강제할 수 있습니다.

이것을 보는 하나의 방법은, 이하와 같은 오브젝트의 그룹의 예를 생각하는 것입니다 :


그림 13.3 인터페이스의 개요

이 계층 구조에서는, 무기에 대할 계획을 볼 수가 있습니다만, IWeapon 및 IZoomedWeapon 의 2 개의 인터페이스를 작성합니다만, 그러한 내용의 상세한 것에 대하여는 이 시점에서는 고려하지 않습니다. Pistol, MachineGun 및 RocketLauncher 는, IWeapon 인터페이스를 실장하고 있어, SniperRifle 는, IZoomedWeapon 인터페이스를 실장하고 있는 것을 알 수 있습니다. 인터페이스로 작업하는 경우에는, 구별하기 위해(때문에), 인터페이스에는 "I" 의 접두사를 사용하는 것은 표준적인 규범입니다.

IWeapon 및 IZoomedWeapon 와 같은 인터페이스를, 집행자 또는 명시적인 법칙으로서 보는 것은 편리할지도 모릅니다. 컴파일러는 인터페이스를 코드에 대한 요구 사양 문서로서 사용합니다. 각각의 인터페이스는 필요한 함수를 정의해, 그것을 정의할 때에는, 컴파일러는 그것들을 확실히 실장한 것의 확인을 실시합니다. IWeapon 를 보면(자) 이하의 의사 코드와 같은 것이 될 것입니다 :

모든 무기는, 발포하는 비율을 받는 Primary Fire 와 발포해야 할 일제 사격의 수를 받는 Secondary Fire 의 메소드를 실장합니다. 실행이 성공하면(자), 이 양쪽 모두 false 를 리턴 합니다.

데이터의 완전성을 보증하기 위해서, 실장을 통해 자유롭게 사용할 수 있는 2 개의 정수를 정의합니다. 그것은, Maximum Firing Rate 및 Minimum Firing Counter 입니다.

이상의 클래스를 컴파일 하려고 했을 때에, 컴파일러는, 피스톨, 기관총 및 다른 클래스가, 필요한 함수를 실장하고 있는 것을 확인하기 위해서, IWeapon 인터페이스를 실장하고 있는 것을 확인합니다. 언어에 의존해, 인터페이스로 정의한 함수를 실장하지 않으면, 아마, 그 결과가 에러가 되는지, 컴파일시에, 어느 종류, 무서운 출력 내용이 되겠지요.

인터페이스의 정의

Unreal 는, 이전 설명한 것처럼, 함수 시그니체에 의해, 클래스와는 다른 인터페이스를 나타내기 위해서(때문에) Interface 키워드를 사용합니다. Unreal Script 에서는, 임의의 수의 적당이라고 생각되는 함수나 데이터 타입을 자유롭게 정의할 수 있습니다. 이것에는, 함수, 구조체, 열거형 또는 메모리를 실제로 인스턴스화하지 않는 것 같은, 무엇인가 다른 일을 포함합니다. 이러한 선언을 집중화해, 발을 디딜 가능성이 있는 코드의 중복의 문제를 최소화하기 위해서 이 사실을 사용할 수 있습니다.

Unreal Script 에서는, Interfaces 는, 클래스에 의해 설정된 표준에 따라, 단순한 방식으로 정의됩니다. 이하에, 의사 코드 대신에, Unreal Script 내에서 정의되고 있다, 지금까지 봐 온 IWeapon 인터페이스를 나타냅니다.

  Interface IWeapon;
  
  /* 정수의 정의 */
  const MaximumFiringRate    = 0.05; // 60/1200
  const MinimumFiringCounter = 3;
  
  /* 이하에 모든 함수 선언 */
  function bool PrimaryFire(float Rate);
  function bool SecondaryFire(int Counter);
  

선언과 정의

Unreal 3 으로 컴파일 되려면 , 리턴 타입으로부터 입력치까지의 모든 것이, 이러한 함수 시그니체와 일치하지 않으면 안됩니다. 이것은, 이 환경내의 프로그래밍의 다른 중대한 국면을 강조합니다. 그것은, Declaration(선언)와 Definition(정의)의 사이의 구별입니다.

  • 리턴 타입이나 입력 인수와 같은 필수 요소를 제공해, 인터페이스는 함수를 선언합니다.
  • 클래스내에 인터페이스를 실장할 때에는, 어떻게 동작하는지, 무엇을 실시할 것 같은 실제의 함수의 상세를 정의할 수 있습니다.

레스토랑에서의 외식의 관점으로부터 이것을 생각해 보면 도움이 될지도 모릅니다. 인터페이스로서 최초로 메뉴가 주어집니다만, 메뉴를 선택하면(자) 고기와 고구마의 식사가 나옵니다. 인터페이스는 메뉴로, 음식 자체는 실장입니다.

인터페이스의 계승

전원의 어댑터의 논의와 닮아 있습니다만, 외로부터 인터페이스를 작성할 수가 있습니다. 이것은, 클래스의 확장을 실시할 때와 같게, Extends 키워드를 사용해 실현됩니다.

  Interface IZoomedWeapon extends IWeapon;
  
  function bool ZoomedFire(float Rate, int FOV);
  

이것은, 확실히, 인터페이스에 대해 복잡한 계층 구조의 계승나무를 구축 가능한 케이스입니다만, 그것을 피하는 것이 가능하면 그렇게 해야 합니다. 클래스에 도착해 말하면 인터페이스는 필요한 때 마셔 작성해야 하고, 명시적으로 정의해야 합니다. 인터페이스를 불명확에, 또는, 근거가 없는 함수를 포함해 정의하면, 하늘의 함수를 가지는 복잡한 클래스가 되기 쉽습니다. 인터페이스의 사용은, 미리 계획을 했을 때에 예외적으로 유익하게 됩니다. 이런 종류의 논의에 흥미를 가졌을 경우는, 객체 지향 분석과 설계를 실시한 클래스 또는, UML 에 관한 책을 강하게 추천 합니다.

인터페이스의 실장

클래스내에의 Interface(인터페이스)의 실장은, 클래스의 파생으로 사용된 방식과 같게, Unreal Script 에서는, 실은, 매우 단순합니다. 그렇다고 해도, 인터페이스는, 아마 스탠드얼론이 되겠지요. 이 홈을 파생시키기 위한 2 개의 예를 봅시다. 피스톨 클래스는 IWeapon 인터페이스를 실장합니다만, 그 클래스는 UScript 에서는 이하와 같은 것이 됩니다 :

  Class Pistol extends UTWeapon implements(IWeapon);
  
  function bool PrimaryFire(float Rate)
  {
     if (Rate > class'IWeapon'. const.MaximumFiringRate)
             return true;
  
         /* 여기서, 여러 가지 실행 */
         return false;
  }
  
  function bool SecondaryFire(int Counter)
  {
     if (Counter < class'IWeapon'. const.MinimumFiringCounter)
        return true;
  
     /* 여기서, 여러 가지 실행 */
     return false;
  }
  

분 덧없는 세상 게, 이 실장은, 인터페이스내에서 선언된 필요한 함수이며, 컴파일시에는, 에러가 되지 않습니다. 이러한 독특한 행이 됩니다…

  if (Rate > class'IWeapon'. const.MaximumFiringRate)
  

이 행은, 인터페이스내의 정수에의 액세스를 얻기 위한 예입니다. 룰로서 그것들이 클래스인것 같이 인터페이스를 취급해 주세요, 그러면 똑같이 정의된 요소에의 액세스를 얻을 수 있습니다. (이것은, 제 3 장으로 논의되었습니다)

단순한 예라고 해도, 올바르게 실장되어 있지 않은 인터페이스를 컴파일 하려고 하면(자), 이하와 같은 에러가 알림됩니다 :

  Error, Implementation of function 'SecondaryFire' conflicts with interface 'IWeapon' - parameter 0 'Counter'
  (에러, 함수 'Secondary Fire'는, 인터페이스 'IWeapon'와 모순되고 있습니다 - 파라미터 0 'Counter')
  Compile aborted due to errors.
  (컴파일은 에러를 위해서(때문에) 중지되었습니다. )
  

스나이파라이훌을 봅시다.

  Class SniperRifle extends UTWeapon implements(IZoomedWeapon);
  
  function bool PrimaryFire(float Rate)
  {
     if (Rate > class'IWeapon'. const.MaximumFiringRate)
        Rate = class'IWeapon'. const.MaximumFiringRate;
  
     /* 여기서 여러 가지 실행 */
     return false;
  }
  
  function bool SecondaryFire(int Counter)
  {
     if (Counter < class'IWeapon'. const.MinimumFiringCounter)
        Counter = class'IWeapon'. const.MinimumFiringCounter;
  
     /* 여기서 여러 가지 실행 */
     return false;
  }
  
  function bool ZoomedFire(float Rate, int FOV)
  {
     /* 밴과 일발 !  */
     return false;
  }
  

여기서 볼 수가 있도록(듯이), 이 클래스는 IZoomedWeapon 를 실장해, 그것은 차례로 IWeapon 인터페이스를 확장합니다. 컴파일러는 쌍방의 인터페이스가 실장되고 있는 것을 기대해, 룰로서 포함되는 함수가와 같이 정의되고 있는 것을 확실히 하기 위해서, 현재의 인터페이스상의 모든 인터페이스를 확인합니다. 이것은, 인터페이스의 나무를 작게 유지하기 위한 하나 더의 이유입니다 - 나 낮은 나무를 생각해 보세요.

어느 쪽의 클래스에서도 정수를 정의할 필요가 없는 일도 유의해 주세요.

최초로 인터페이스를 사용해 작업을 개시했을 때에는, 틀림없이 괴로운 경험이 되어, 압도 되어 초조해할지도 모릅니다. 인터페이스를 더듬기 위해서(때문에) Interface 인덱스 카드를 작성하면(자) 편리할지도 모릅니다.

IWeapon
함수
  • PrimaryFire(rate)
    • Success 를 리턴 한다
  • SecondaryFire(spread)
    • Success 를 리턴 한다
정수
  • MaximumFiringRate
  • MinimumFiringSpread
표 13.2 - IWeapon 인터페이스

IZoomedWeapon extends IWeapon
함수
  • ZoomedFire(Rate, FOV)
  • Success 를 리턴 한다
정수
  • 없음
표 13.3 - IZoomedWeapon 인터페이스

어떠한 인터페이스가 있을까를 리뷰 해, UT3 패러다임(paradigm)내의 2 개의 특이한 점을 강조해 봅시다. 그리고, 더욱 많은 코드에 관련되어, 정말로 재미있는 것을 만들어내는 것을 봅시다.

  • 인터페이스는, 클래스내에 실장해야 하는 함수의 선언을 허가합니다
  • 그것들은, 함수의 선언에 요구 사항을 주는 수단을 제공하고 있습니다
  • 그것들은, 선언으로서도 알려져 있는, 함수의 시그니체 이외에는 실장에는 무슨 요구 사항도 제시하고 있습니다
    • 그 대신해, 어떻게 함수가 사용될까에 초점을 맞추고 있습니다
    • 실장은, 클래스간에서 바뀔 가능성이 있는 ; 정의가 다를지도 모릅니다
    • 친클래스내에서 함수가 실장되고 있었을 경우는, 인터페이스의 요구 사항을 만족할 것입니다
  • UT3 는, Enumerations(열거형), Structures(구조체) 및 Constants(정수)를 정의하는 수단을 제공하고 있습니다만, 어느 것도 실제의 메모리를 요구하지 않습니다
  • 복수의 인터페이스의 실장이 가능합니다만, 선택한 환경에서만 사용되어야 합니다
    • 동일한 계층에 2 개의 인터페이스를 실장하면(자) 코드내에서 문제가 되는 경우가 있기 (위해)때문에, 철저하게 피해야 합니다

왜 인터페이스가 사용되는 것일까요 ?

이미 설명한 것처럼, 클래스가 특정의 사양에 적합하고 있을까를 확실히 하기 위한(해), 인터페이스는 컴파일러에 대해서 기계수리공을 제공합니다. EpicGames 사는, 온라인 게임 플레이에 대한 요소가 많고 및 얼마인가의 기묘한 보존 데이터를 포함합니다만 UI 요소로 한정하는 것은 아닌, 매우 소수의 인터페이스 밖에 배포하지 않습니다. 만약, 자세하게 보면, 변수 타입으로서 클래스내에서 경험한 것과 같게, 예, 및 2 개째의 메소드로 이미 봐 온 것처럼 개개의 인터페이스가 실장되고 있는 것에 눈치채겠지요. 이 주요한 예는 OnlineSubsystem 입니다 :

  /** 어카운트 정보를 생성 및 / 또는 열거를 위해서(때문에) 사용되는 인터페이스 */
  var OnlineAccountInterface AccountInterface;
  
  /** 온라인 플레이어 메소드에 액세스하기 위한 인터페이스 */
  var OnlinePlayerInterface PlayerInterface;
  
  /** 온라인 플레이어 확장 메소드에 액세스하기 위한 인터페이스 */
  var OnlinePlayerInterfaceEx PlayerInterfaceEx;
  
  /** 시스템 와이드인 네트워크 함수에 액세스하기 위한 인터페이스 */
  var OnlineSystemInterface SystemInterface;
  
  /** 온라인 게임을 작성, 검색 또는 파괴하기 위해서 사용되는 인터페이스 */
  var OnlineGameInterface GameInterface;
  
  /** 온라인 컨텐츠를 사용하기 위한 인터페이스 */
  var OnlineContentInterface ContentInterface;
  
  /** 음성 커뮤니케이션을 이용하기 위한 인터페이스 */
  var OnlineVoiceInterface VoiceInterface;
  
  /** 상태의 읽고 쓰기 조작에 대해서 사용하는 인터페이스 */
  var OnlineStatsInterface StatsInterface;
  
  /** 게임에 관계된 뉴스의 통지를 읽기 위해서(때문에) 사용하는 인터페이스 */
  var OnlineNewsInterface NewsInterface;
  

이것은, 이미 인터페이스로 배운 것을 보충합니다만, 완전하게 명백하지 않을지도 모릅니다. 한번 더, 이전 정의한 무기를 다시 봅시다. 인터페이스를 정의했다고 하는 것은, 그것이 존재하면, 다른 클래스에 의존해 사용 가능해집니다. 유저는, 이하에 드는 것 같은 함수를 사용할 수 있습니다 :

  Function bool foo (IWeapon w)
  {
     w.PrimaryFire(60/1200);
     /* 실행하고 싶은 다른 어떠한 처리 */
  }
  

이전 경험한 것처럼, 클래스, 함수 파라미터 및 구조 체내에서 변수 타입을 선언하고 있습니다. 이점은, 견해에 의하면 결점일지도 모릅니다만, 사용하려고 하고 있을 때에, 이미 있는 타입에 캐스트 하는 변수를 가지고 있는 것입니다. 이것은, 디폴트 코드 베이스 전체를 통해 이용되어 그 결과, 참조 매테리얼에 대해서 매우 일반적으로 되어 있습니다.

베이스 타입 클래스 정수
  • 이 타입의 아이템만
  • 례. Bool
  • 이 클래스 또는 어떠한 그 계승
  • 례. UTPawn
  • 이 인터페이스를 실장하는 임의의 클래스
  • 례. OnlineAccountInterface
표 13.4 - 인터페이스는, 타입으로서 사용될 때에 받아들여진다

마지막에

언제 서브 클래스화하는지, 또, 언제 인터페이스내에서 생성할까는, 많은 사람들이 묻는 질문이므로, 나쁜 질문이 아닙니다. 스스로 실행해 보면, 인터페이스는, 1 개(살)이 다른 점을 제외해, 서브 클래스화와 같은 결과가 되는 것을 알겠지요 - 서브 클래스화에서는, 인터페이스를 사용할 때에는, 주요한 예로서 설명되는, 함수의 서명의 변경의 정지는 할 수 없습니다. 이전 설명한 것처럼, 어느 프로젝트에서는, 클래스의 정의전에, 기정도리의 인터페이스가 필요합니다.

대담한 일을 실시하는 것은, 운명을 제어할 찬스에 맡기는 1 개의 방법입니다. 단순한 타입 미스는 많은 디버그 시간이 필요하게 될지도 모릅니다만, 인터페이스에서는, 함수가 올바르게 선언되지 않았던 경우에, 그 행을 가르쳐 줍니다.

튜토리얼 13.1 - 컴퍼스, 파트 I: ICOMPASS 인터페이스

단지 이 시점까지의 논의를 이해하기 위해서, 약간 시간을 사용해, 어떤 종류의 인터페이스를 실장해 봅시다. 처음이므로 상세하게 초점을 맞혀, 낮은을 노립시다. 후에는, 보다 흥미로운 일이 생기게 됩니다.

정의하는 새로운 요소, 컴퍼스를 찾아, 맙파 및 HUD 의 코다-가 옵니다. 이 요소의 목적은, 자신의 레벨안에 놓여져 Interface 및 후에는 실제의 Compass 클래스를 정의할 생각의, HUD 의 코다-가 가지는 어떤 종류의 필요성으로부터, 맙파에 북쪽향을 나타내는 고정밀도의 컨트롤을 제공하는 것입니다.

Compass 의 요구 :

Mapper(맙파) Coder(코다-)
  • 맵내에 배치될 예정
  • Yaw 로서 참조되는 회전 가능한 방위를 가진다
  • 맵내에 표시 가능한 아이콘을 가진다
    • 맙파에 의해 제공되어 컴퍼스 라고 명명된 CompassContent 패키지내에 있다
  • Yaw 에 대한 Getter 함수를 제공한다
  • UI 변경시에는, 라디안 및 각도를 계산할 필요가 있다
  • 선택되었을 경우에는, 개발자에게 Rotator 의 획득을 허가한다
  • 맵의 재로드시에는 삭제되거나 변경 되거나 하지 않는 것을 확실히 한다
표 13.5 - 컴퍼스 오브젝트의 사양의 개요

이 정보의 대부분은 실장에 관련한 것입니다만, 코다-는 명확하게 요구를 이해합니다. 이것을 1 개씩 실시해 나가야 하는 것으로, 그러면, 코드는 명확하게 알 수 있게 되겠지요.

*1. * ICompass Interface 를 선언해 주세요.

  interface ICompass;
  

*2. * 오브젝트의 회전하고 있지 않는 방위를 리턴 하는 GetRadianHeading() 함수를 선언해 주세요.

  function float GetRadianHeading();
  

*3. * 라디안치를 도수로 변환해 리턴 하는 GetDegreeHeading() 함수를 선언해 주세요.

  function float GetDegreeHeading();
  

*4. * 오브젝트 Rotation 의 Yaw 를 리턴 하는 GetYaw() 함수를 선언해 주세요.

  function int GetYaw();
  

*5. * 완전한 로테이타오브제크트를 리턴 하는 GetRotator 함수를 선언해 주세요.

  function Rotator GetRotator();
  

*6. * 로테이타를 벡터로 변환해 리턴 하는 GetVectorizedRotator() 함수를 선언해 주세요.

  function vector GetVectorizedRotator();
  

튜토리얼 13.2 - 컴퍼스, 파트 II: 컴퍼스 클래스의 실장

이 인터페이스를 사용해, 컴퍼스를 간단한 방법으로 작성할 수가 있습니다만, 차바퀴의 재발명과 같이 쓸데없는 노력에 끝나지 않는 것을 확실히 하기 위해서 몇개의 리서치를 실시해야 합니다. 모든 요소가 그처럼 보일지도 모릅니다만, 자신으로 기술하는 것을 피하는 편이 좋은 유일한 요소는, 아마 로테이타 뿐입니다. 그것은, 또, 맙파가 익숙해진 인터페이스를 로테이션 툴과 함께 제공합니다.

Object 클래스는, 가장 도움이 되는, 많은 훌륭한 함수 및 요소를 가지고 있습니다만, 먼지 클래스가 찾고 있는 로테이타를 실제로 실장하고 있어, 위치 벡터도 이와 같이 갖추고 있기 때문에, 비평 없습니다 !

  var(Movement) const vector   Location; // Actor 의 위치 ; 설정에는 Move 를 사용해 주세요.
  var(Movement) const rotator Rotation; // 로테이션.
  

이 시점에서, 클래스의 코딩을 개시할 수 있어 인터페이스를 실장해, 클래스로부터 파생시키는 방법을 알 수 있습니다.

*1. * ICompass 인터페이스를 실장해, 먼지 클래스 및 placeable 를 확장해, 클래스를 정의해 주세요.

  class Compass extends actor placeable implements(ICompass);
  

*2. * Rotator 를 취급하는 함수를 정의해 주세요. 파생을 선택하는 장소에 대해서, 로테이타는 이미 이용 가능합니다. 간단하게 3 개의 함수 정의를 봅시다.

  // 먼지의 yaw 를 리턴
  function int GetYaw()
  {
     return Rotation.Yaw;
  }
  
  function Rotator GetRotator()
  {
     return Rotation;
  }
  
  function vector GetVectorizedRotator()
  {
     return vector(Rotation);
  }
  

Yaw 만을 고려하고 나서, 개발자에게 기본적인 액세스를 제공하고 있습니다.

*3. * Yaw 는, 실제로는, HUD 를 이용하기 쉽게 하기 위해서 필요한 형식이 아닙니다만, 맙파에 대해서는, 충분히 동작합니다. UI 동작에 대해서, 방위가 정확한 것을 확실히 하기 위해서, 로테이타에 있는 종의 조작을 실시할 필요가 있습니다. 2 ~ 3 분 사용해, 이것을 분할해 설명합시다. 우선, GetRadianHeading() 함수를 선언합니다.

  function float GetRadianHeading()
  {
  }
  

*a. * 3 개의 로컬 변수가 필요합니다.

  local Vector v;
  local Rotator r;
  local float f;
  

*b. * Yaw 컴포넌트를 취득해 주세요. 그것을 신규 로테이타오브제크트의 yaw 치에 카피하도록(듯이) 유의해 주세요. 이것이, 로테이타를 단순화 하는 최초의 스텝입니다.

  r.Yaw = GetYaw();
  

*c. * 각도 조작을 간단하게 취급하기 (위해)때문에, 로테이타를 Vector 로 변환해 주세요.

  v = vector(r);
  

*d. * EpicGames 사가 제공하는 함수를 이용해 Heading 를 되감아 주세요. Unreal 내의 프로그래밍의 수학적인 목적으로 발을 디뎠을 경우에는, 같은 많은 함수가 매우 유용하다고 하는 것을 알 수 있겠지요.

  f = GetHeadingAngle(v);
  

*e. * 다른 편성 함수를 사용해, 방위를 되감아 주세요. 이것은, 실제로는 라디안치를 리턴 합니다만, 부의 값이 될지도 모릅니다.

  f = UnwindHeading(f);
  

*f. * 2Pi 를 가산해 값을 정의 값으로 변환합시다.

  while (f < 0)
     f += PI * 2.0f;
  

*g. * 그리고 마지막으로, 값을 리턴 해 주세요.

  return f;
  

이것은, 비클과 함께 사용되어 라디안 변환을 보존하는 알고리즘으로, UTVehicle.uc 의 1199 은행내에서 볼 수가 있습니다.

*4. * Radian 계측치를 각도로 변환해 주세요. 라디안 계측치를 얻는 것을 가능하게 하기 위한(해), 계산한지 얼마 안된 라디안 계측치를 사용합니다. 정수를 격납한 변수 RadToDeg 는 매우 편리해 그 값은, 180/PI 로, 미리 계산되고 있습니다.

  function float GetDegreeHeading()
  {
     local float f;
  
     f = GetRadianHeading();
  
     f *= RadToDeg;
  
     return f;
  }
  

*5. * 육체 노동은 끝났습니다. 남은 유일한 일은, UI 에 관계하는 것입니다. 그렇지만, 착수하기 전이나, 후에 맵으로 작업하고 있을 때에, 어떤 종류의 디버그 정보의 출력은 도움이 되겠지요. 레벨을 로드하고 있을 때에, 디버그에 매우 편리한 함수는 PostBeginPlay 입니다. 게임 개시시에, 로그 파일에 방위를 출력 가능합니다.

  event PostBeginPlay()
  {
     `log("===================================", , 'UTBook');
      `log("Compass Heading"@GetRadianHeading() @GetDegreeHeading(), , 'UTBook');
     `log("===================================", , 'UTBook');
  }
  

*6. * 모든 함수를 설정했습니다만, 맙파에 2 ~ 3 의 비주얼 항목을 달 필요가 있습니다. 특히, 오브젝트상에는, 아이콘 및 화살표를 그릴 필요가 있습니다. 1 개의 요소가 적절의 값을 보관 유지하고 있어, 맵이 리셋트 되었을 때에 삭제되거나 변경 되거나 하지 않은 것을 확인하기 위해서 2 ~ 3 의 엔트리를 변경하기 위해서 시간을 사용할 수도 있습니다.

  DefaultProperties
  {
  }
  

*a. * 신규의 ArrowComponent 요소, 이름 첨부 화살표를 정의해 주세요. 이것은, 오브젝트가 지시하는 실제의 방향을 반영하기 위해(때문에), 맙파로 표시되는 실제의 화살표가 될 것입니다.

  Begin Object Class=ArrowComponent Name=Arrow
  

*b. * Arrow Color 는, 0-255 의 범위입니다만, 이것은, Photoshop 나 Web 개발의 경험으로, 친숙한 값일지도 모릅니다. 희망하도록(듯이) 이것을 조정할 수 있습니다. UnrealEditor 중에 색선택 기능이 있어, 매우 적절히 동작합니다만, 정수치로 놀 수도 있습니다.

  ArrowColor = (B=80, G=80, R=200, A=255)
  

*c. * 이것은, Arrow 의 길이여, 굵기가 아닙니다. 이 값을 늘리는 것에 따라, 화살표의 길이 또는 사이즈가 증가하는 것에 깨닫겠지요.

  ArrowSize = 1.000000
  

*d. * 혼란을 최소화하기 위해서 적절하고 쉽게 친숙 해진 이름을 붙여 주세요.

  Name = "North Heading"
  

*e. * 여기서 매듭지어에, 컴포넌트 배열에 그것을 추가해 주세요. Unreal Script 내의 맵 컴포넌트 전체로 이 패턴을 볼 수 있습니다.

End Object

  Components(0) = Arrow
  

*f. * 예를 들면, Sprite 컴포넌트입니다. 이것은, 실장이 약간 간단이 됩니다만, 표시하고 싶은 패키지, 그룹 및 / 또는, texture의 이름을 알 필요가 있습니다. 이전에 실행한 것처럼 Begin 는, Sprite 에 대해 새로운 컴포넌트 오브젝트를 정의하고 있습니다.

  Begin Object Class=SpriteComponent Name=Sprite
  

*g. * 요소에 달고 싶은 새로운 texture를 플러그 인 할 생각입니다. 에디터내의 모든 2d 스프라이트는, 항상 개발자와 얼굴을 대면시치고 있어, 침입은 최소한이어야 합니다. 이전 설명한 대로, Sprite 는, 이미 'UTBookTextures.compass'에 정의되고 있습니다.

  Sprite=Texture2D' CompassContent.compass'
  

*h. * 게임중에는 숨겨, 플레이중에는 실제의 게임중에 로딩 하는 것을 피하기 (위해)때문에, 여기서, 2 ~ 3 의 불리언 값을 설정하기로 하겠습니다.

  HiddenGame = True
  AlwaysLoadOnClient = False
  AlwaysLoadOnServer = False
  

*i. * 그리고, 마지막에 거기에 결말을 붙여, Components 배열에 추가해 주세요. 건네받는 이 요소는, Name=Sprite 엔트리내에 있습니다.

  End Object
  Components(1) = Sprite
  

*7. * 적절한 유지 관리를 위해서(때문에) 타글 되어야 할, 2 ~ 3 의 요소가, 아직, 있기 때문에, 이 작업을 완성하기 전에 그것들을 실시합시다.

*a. * bStatic 는, 게임의 플레이중에, 이 먼지에 대해 Mappers 가 임의의 무엇인가를 변경하는 능력을 가질지 어떨지를 제어하는 Boolean 입니다. 컴퍼스는 북쪽을 계속 가리킬 것 이기 때문에, static(정적)로 해야 합니다.

  bStatic   = True
  

*b. * bHidden 는, 이 먼지의 기본 컴포넌트의 비지비리티를 제어합니다. 이것은 Sprite 컴포넌트로 변경한 불리언 값에 대한 fail-safe라고 생각할 수가 있습니다.

  bHidden   = True
  

*c. * bNoDelete 는, 게임 플레이중에 이 먼지를 삭제하는 능력을 제어합니다. 컴퍼스의 존재를 지우면(자), 매우 혼란할 것 같아서, 이 프롭퍼티는, 명확하게 True 로 설정합니다.

  bNoDelete = True
  

*d. * bMovable 는, 먼지의 동작에 관련하고 있습니다. 먼지 클래스내의 이제(벌써) 1 개의 fail-safe입니다.

  bMovable  = False
  

Component 하가 많은 상속 클래스를 보는 것으로, 컴포넌트에 관한 정보를 더욱 얻을 수 있습니다. 이러한 2 개(살)은, 그 기원에 근거해, 오브젝트상의 스프라이트 및 화살표의 컴포넌트에 단다고 하는 것만으로 충분합니다.

튜토리얼 13.3 - 컴퍼스, 파트 III: 컴퍼스의 테스트, 파트 I

적어도 컴퍼스의 요소에 대해서는, 코딩은 모두 완료했습니다. 코드를 빌드 해, 모든 것을 적절히 행해졌는지를 확인하기 위해서 에디터를 오픈해 요소가 동작하고 있는 것을 테스트하기 위해(때문에), 맵내에 배치하고 나서, UT3 에 로드해 주세요.

*1. * 먼저 진행되어, 에디터를 로드해 주세요. 새로운 맵을 작성 (또 희망하는 경우에는 기존의 맵을 오픈)해, 필요한 요소를 설정해야 합니다.

*2. * Actor Classes 브라우저를 오픈해 주세요. 이것은, Actor Classes 하의 Generic Browser(범용 브라우저)에 있어, Unreal Script 내의 클래스 계층을 포함합니다..

*3. * 스크립트 패키지는 UTEditor.ini 파일내의 ModPackages 리스트내에 있으므로, 이미 로드 되고 있을 것입니다만, 어떠한 이유로써 로드되어 있지 않은 경우는, File > Open 메뉴에 가, 컴파일 후의 . u 파일이 격납되는 Unpublished\CookedPC\Scripts 에 가 주세요.


그림 13.4 - MasteringUnrealScript.u 를 검색하는 File 다이얼로그

*4. * 그것이 로드 되면, 그림 13.5 와 같이, Actor 아래의 클래스 트리내에 컴퍼스 요소가 표시될 것입니다. 그것을 선택해, 맵을 바꾸어 주세요. 여기서 오른쪽 클릭이 가능해져, “Add Compass Here”메뉴 옵션이 제공될 것입니다.


그림 13.5 - 컴퍼스내의 Actor Class Browser

*5. * 이 요소를 선택해, 그림 13.6 내와 같은 요소를 작성합니다. 에디터내에서 선택해, 로테이션 툴로 전환해 화살표의 회전을 보는 것으로, 적절히 동작하고 있는 것을 확인할 수 있습니다.


그림 13.6 - 맵내의 Compass 오브젝트

*6. * 자신의 레벨을 로드하면(자), 로그 파일이 이하와 같이 되어 있어 확실히 동작하고 있는 것을 압니다.


그림 13.7 - games(게임) 로그 파일의 인용.

여기서 볼 수가 있도록(듯이), 작성한 컴퍼스는, 라디안 및 번의 형식의 쌍방에서 적절한 값을 보고하고 있기 때문에, 개발자는, 실행할 필요가 있는 것을 실행할 수 있습니다. 이것은, 계획과 인터페이스를 모두 실시하는 기본적인 예입니다. 요건만 반복하면(자), 이하와 같은 처리였습니다 :

  • 클래스에서 실시하는 필요한 일을 확인
  • 사양 마다 인터페이스를 정의
  • fail-safe로서 인터페이스를 실장해, 작성하는 클래스용으로 함수를 정의
  • 필요한 컴포넌트를 매달려라
  • 테스트

이 컴퍼스 오브젝트의 실장 검토를 실시하면(자), 플레이어의 맵내의 위치를 더듬어, 맵내의 다른 플레이어의 위치도 동시에 표시하는 동적 미니 맵 시스템의 완전 동작판을 작성하기 위해(때문에), 튜토리얼의 다음의 시리즈에 대해서는, 이 생각을 기본으로 한 구축을 실시합니다.

튜토리얼 13.4 - 미니 맵, 파트 I: MU_MINIMAP 클래스

미니 맵은, 최근 릴리스 된 샌드 박스를 오픈하는 스타일의 몇개의 게임을 플레이 한 (분)편에 있어서는, 꽤 친숙한 것이지요. 기본적으로, 이것은, 스크린상에 상시 표시되는 맵으로, 플레이어의 주위의 월드의 일부를 표시하고 있습니다. 플레이어가 이동하거나 방향을 바꾸거나 하는 것에 따라, 맵은 움직이거나 회전하거나 합니다. 작성하는 미니 맵은 2 개의 부분에서 완성되어 있습니다. 맵과 컴퍼스 오버레이입니다.

동작 가능한 미니 맵 시스템을 작성하기 위해서는, 3 개의 것이 필요합니다. 문제가 되고 있는 맵에 특유가 있는 종의 데이터를 보관 유지하는, 맵내에 놓여지는 Compass 클래스의 서브 클래스, 스크린에 맵을 그리기 위해서(때문에) 조작하는 신규의 헤드 업 디스플레이 (HUD) 클래스, 및, 신규 HUD 클래스의 사용을 강제하기 위해서 새로운 게임 타입입니다. HUD 클래스는, 기저 UTHUD 클래스를 확장해, 단지 미니 맵을 그리기 위해서(때문에) 필요한 기능을 추가하고 있습니다. 게임 타입 클래스는, 사용된 HUD 의 타입을 오버라이드(override) 하는 UTDeathMatch 게임 타입의 매우 간단한 확장이며, 미니 맵 시스템에 관련한 약간의 설정을 실행합니다.

초에, 이 튜토리얼에서는, Compass 클래스의 서브 클래스인 MU_Minimap 클래스를 선언합니다.

*1. * ConTEXT 를 오픈해, UnrealScript 하이 라이터를 사용해 MU_Minimap.uc 라는 이름의 신규 파일을 작성해 주세요.

*2. * Compass 클래스로부터 확장해, 신규 MU_Minimap 클래스를 선언해 주세요.

  class MU_Minimap extends Compass;
  

*3. * 이 클래스에서는, 몇개의 편집 가능한 변수의 선언이 필요합니다. 우선, 매테리얼에 대한 참조를 보관 유지하는 MaterialInstanceConstant 가 맵 자신에 대해서 사용됩니다. 모든 코드를 다 작성한 후에 한번 더 매테리얼의 설정을 확인하면(자), 맵내에서 MU_Minimap 먼지를 설정할 준비를 할 수 있습니다.

  var() MaterialInstanceConstant Minimap;
  


그림 13.8 - 미니 맵 texture의 예.

*4. * 컴퍼스 오버레이를 위해서(때문에) 매테리얼을 참조하는 다른 MIC 변수도 필요합니다.

  var() MaterialInstanceConstant CompassOverlay;
  


그림 13.9 - 컴퍼스 오버레이 texture의 예.

*5. * 클래스에 추가하는 sphere(구체) 컴포넌트는, 레벨을 설정해 올바른 맙스 크린 쇼트를 취득하기 위해서 편집 가능하게 됩니다. 이 먼지의 위치는 맵의 중심을 나타내, 구체의 반경은 맵으로 나타내지는 각각의 방향에의 확장을 나타낸다고 하는 생각입니다.

  var() Const EditConst DrawSphereComponent MapExtentsComponent;
  

*6. * bForwardAlwaysUp 라는 이름 전의 Bool 변수는, 플레이어의 전방에의 동작은 항상 스크린 최상 방향에의 동작, 또는, MU_Minimap 먼지의 회전에 의해 결정되는 North 방향각에 들르는 올바른 오프셋(offset)로서 표시될지 어떨지의 지정을 디자이너에 허락합니다. 이것은, 리에 들어맞고 있도록(듯이) 모아 두어 언제라도 True 로 설정해 두는 편이 좋은 기색이 농후합니다만, 선택사항으로서 남겨 둡니다.

  var() Bool bForwardAlwaysUp;
  


그림 13.10 - 화살표는, 전방에의 동작 방향을 나타냅니다. 좌측에서는 bForwardAlwaysUp 는 False 로 설정되어 우측에서는 True 로 설정되어 있습니다.

*7. * 맵내에서 플레이어의 위치를 추적해, 맵 texture의 위치를 변환하기 위해서, 맵 texture에 의해 커버되는 X-축 및 Y-축내의 형태로 월드 스페이스 좌표의 범위를 알 필요가 있습니다. 2 개의 Vector2D 변수는 이하의 값을 보관 유지합니다.

  var Vector2D MapRangeMin;
  var Vector2D MapRangeMax;
  

*8. * 다른 Vector2D 변수는, 맵 texture의 중심으로 대하는 X 및 Y 의 월드 스페이스 좌표를 보관 유지합니다.

  var Vector2D MapCenter;
  

*9. * MapCenter 변수의 값은, PostBeginPlay() 함수내에서 대입됩니다. 친클래스의 PostBeginPlay() 함수의 소환도 확인하기 위해서, 본함수를 오버라이드(override) 해, 이 변수에 값을 대입해 주세요.

  function PostBeginPlay()
  {
     Super.PostBeginPlay();
  
     MapCenter.X = MapRangeMin.X + ((MapRangeMax.X - MapRangeMin.X) / 2);
     MapCenter.Y = MapRangeMin.Y + ((MapRangeMax.Y - MapRangeMin.Y) / 2);
  }
  

*10. * 다음에, 여전히 PostBeginPlay() 함수내입니다만, MapCenter 로 시작해 MapExtentsComponent 의 SphereRadius 를 감산해, 맵의 각각의 축의 연장을 계산해 주세요.

  MapRangeMin.X = MapCenter.X - MapExtentsComponent.SphereRadius;
  MapRangeMax.X = MapCenter.X + MapExtentsComponent.SphereRadius;
  MapRangeMin.Y = MapCenter.Y - MapExtentsComponent.SphereRadius;
  MapRangeMax.Y = MapCenter.Y + MapExtentsComponent.SphereRadius;
  

*11. * 마지막으로, 디폴트의 1024.0 의 반경에서, 녹색을 사용해, DrawSphereComponent 를 작성해 주세요. 또, 이것이 가장 바람직한 기능과 같므로, 디폴트 프롭퍼티내의 bForwardAlwaysUp 의 값도 True 로 설정해 주세요.

  defaultproperties
  {
     Begin Object Class=DrawSphereComponent Name=DrawSphere0
          SphereColor=(R=0, G=255, B=0, A=255)
          SphereRadius=1024. 000000
     End Object
     MapExtentsComponent=DrawSphere0
     Components.Add(DrawSphere0)
  
     bForwardAlwaysUp=True
  }
  

*12. * 작업 결과를 잃지 않기 위해(때문에) 스크립트를 보존해 주세요.

튜토리얼 13.5 - 미니 맵, 파트 II: MINIMAPGAME 클래스

본튜토리얼은, 신규 Minimap 게임 타입 클래스의 생성에 초점을 맞히고 있습니다. 이 목적은, 단지, 맵내에 배치된 MU_Minimap 먼지에의 참조를 보관 유지해, 이하의 튜토리얼로 작성하는 신규 HUD 클래스의 이용을 게임에 고할 뿐입니다.

*1. * ConTEXT 를 오픈해, UnrealScript 하이 라이터를 사용하는 MinimapGame.uc 라는 이름의 신규 파일 작성해 주세요.

*2. * UTDeathMatch 클래스로부터 확장한 신규의 MInimapGame 클래스를 선언해 주세요.

  class MinimapGame extends UTDeathMatch;
  

*3. * 본클래스는 선언하는 변수를 1 개 가져, GameMInimap 와 이름 붙여진 MU_Minimap 오브젝트 참조를 가집니다.

  var MU_Minimap GameMinimap;
  

*4. * 게임의 초기화시에, 맵내에 배치된 MU_Minimap 먼지에 대한 참조를 이 변수로 설정하지 않으면 안됩니다. 함수의 친클래스의 버젼을 확실히 호출하기 위해서(때문에) InitGame() 함수를 오버라이드(override) 해 주세요.

  function InitGame( string Options, out string ErrorMessage )
  {
     Super.InitGame(Options, ErrorMessage);
  }
  

*5. * InitGame() 함수의 내부에서, 로컬 MU_Minimap 변수가 필요합니다.

  local MU_Minimap ThisMinimap;
  

*6. * AllActors 이테레이타는, 어느 레벨내에서 MU_Minimap 먼지를 검색해, GameMinimap 변수에 대입하기 위해서 사용됩니다.

  foreach AllActors(class'MasteringUnrealScript.MU_Minimap', ThisMinimap)
  {
     GameMinimap = ThisMinimap;
     break;
  }
  

*7. * 디폴트 프롭퍼티에서는, 게임 타입의 HUDType 변수는, 사용하기 위해서 작성한 HUD 클래스를 강제하기 위해서 오버라이드(override) 됩니다.

  HUDType=Class'MasteringUnrealScript.MinimapHUD'
  

*8. * 또, MapPrefixes(0) 변수는, 이 게임 타입과 관련하는 맵이 어떤 것인지를 결정하기 위해서(때문에) 오버라이드(override) 됩니다.

  MapPrefixes(0)="COM"
  

*9. * 작업 내용을 잃지 않게 스크립트를 보존해 주세요.

튜토리얼 13.6 - 미니 맵, 파트 III: MINIMAPHUD 초기설정

미니 맵 먼지 및 게임 타입 클래스를 다 작성했으면, HUD 클래스에 주의를 돌립시다. 본튜토리얼에서는, 클래스의 선언과 그 변수 및 그러한 변수를 위해서(때문에) 몇개의 디폴트 프롭퍼티의 설정에 초점을 맞춥니다.

*1. * ConTEXT 를 오픈해, UnrealScript 하이 라이터를 사용하는 MinimapHUD.uc 라는 이름의 신규 파일을 생성해 주세요.

*2. * UTHUD 클래스로부터 확장한 MinimapHUD 클래스를 선언해 주세요.

  class MinimapHUD extends UTHUD;
  

*3. * 본클래스는, 레벨내의 미니 맵 먼지에 대한 자기 참조 포인터도 보관 유지합니다.

  var MU_Minimap GameMinimap;
  

*4. * TileSize 라는 이름의 Float 변수는, 상시 표시되는, 풀 맵의 크기를 지정하는 값을 보관 유지합니다. 만약 풀 맵 texture가 2048x2048 로, 이 값은 0.25 라면, 맵 texture의 일부는, 512x512 에 표시됩니다.

  var Float TileSize;
  


그림 13.11 - TileSize 치의 0.25 및 0.5 를 사용해 그려진 맵의 일부.

*5. * MapDim 라고 명명된 Int 변수는, 1024x768 의 디폴트 해상도의 화면상에 그려진 맵의 크기를 나타냅니다.

  var Int MapDim;
  


그림 13.12 - MapDim 는, 화면상 그려지는 맵의 크기를 지정합니다.

*6. * 다른 Int 변수는, 1024x768 의 디폴트 해상도의 맵상의 플레이어를 나타내는 박스의 사이즈를 지정합니다.

  var Int BoxSize;
  


그림 13.13 - BoxSize 는, 스크린상에 그려지는 플레이어 박스의 크기를 지정합니다.

*7. * 마지막 변수는, 맵상의 플레이어를 그리기 위해서(때문에) 사용되는 2 개의 색의 배열입니다. 1 색은, HUD 의 오너이기 때문에, 이제(벌써) 1 색은 맵내의 다른 모든 플레이어를 위해입니다.

  var Color PlayerColors[2];
  

*8. * 디폴트 프롭퍼티 블록은, 꽤 직접적으로 될 것입니다.

  defaultproperties
  {
     MapDim=256
     BoxSize=12
     PlayerColors(0)=(R=255, G=255, B=255, A=255)
     PlayerColors(1)=(R=96, G=255, B=96, A=255)
     TileSize=0. 4
     MapPosition=(X=0. 000000, Y=0. 000000)
  }
  

*9. * 작업 결과를 잃지 않게 스크립트를 보존해 주세요.

튜토리얼 13.7 - 미니 맵, 파트 IV: MINIMAPHUD 함수

맵을 그리기 위한 기능의 실장으로 옮기기 전에, PostBeginPlay() 및 DrawHUD() 함수는 MinimapHUD 클래스내에서 오버라이드(override) 될 필요가 있어, GetPlayerHeading()로 불리는 신규 함수가 추가됩니다.

*1. * ConTEXT 및 MinimapHUD.uc 파일을 오픈해 주세요.

*2. * 최초로, PostBeginPlay() 함수는 오버라이드(override) 되어, 맵내의 미니 맵 먼지에 대한 게임 타입의 참조를 클래스내의 GameMIniMap 에 대입하기 위해서 사용됩니다.

  simulated function PostBeginPlay()
  {
     Super.PostBeginPlay();
  
     GameMinimap = MinimapGame(WorldInfo.Game). GameMinimap;
  }
  

*3. * 다음에, DrawHUD() 함수는 오버라이드(override) 되어, 맵을 그릴 책임을 가지는 함수, DrawMap() 함수에의 호출이 추가됩니다. 이것은, 기본적으로는, 플레이어의 생사에 관련되지 않고, 또, 게임이 아직도 계속되고 있는지 종료했는지를 관련되지 않고, 언제라도 맵의 묘화를 강제합니다.

  function DrawHUD()
  {
     Super.DrawHUD();
  
     DrawMap();
  }
  

*4. * GetPlayerHeading() 함수는, 이전 작성된 Compass 클래스내로 보여지는 GetRadianHeading() 함수로 자주(잘) 닮았습니다. 여기에서는, 이 함수를 Compass 클래스로부터 카피해, MinimapHUD 클래스로 붙여 주세요. 이하의 코드가 MinimapHUD 클래스내에 있을 것입니다.

  function float GetRadianHeading()
  {
     local Vector v;
     local Rotator r;
     local float f;
  
     r.Yaw = GetYaw();
     v = vector(r);
     f = GetHeadingAngle(v);
     f = UnwindHeading(f);
  
     while (f < 0)
        f += PI * 2.0f;
  
     return f;
  }
  

*5. * GetPlayerHeading()의 함수의 이름을 변경해 주세요.

  function float GetPlayerHeading()
  {
     local Vector v;
     local Rotator r;
     local float f;
  
     r.Yaw = GetYaw();
     v = vector(r);
     f = GetHeadingAngle(v);
     f = UnwindHeading(f);
  
     while (f < 0)
        f += PI * 2.0f;
  
     return f;
  }
  

*6. * 다음에, 이하의 행을 :

  r.Yaw = GetYaw();
  

다음과 같이 변경해 주세요 :

  r.Yaw = PlayerOwner.Pawn.Rotation.Yaw;
  

*7. * 작업 결과를 잃지 않게 스크립트를 보존해 주세요.

튜토리얼 13.8 - 미니 맵, 파트 PART V: DRAWMAP() 초기설정

DrawMap() 함수는, 필요한 나머지의 계산의 모든 것을 실시해, 화면상에 맵을 그리기 위한 책임이 있습니다. 튜토리얼내에서는, 함수 및 모든 로컬 변수가 선언되고 있습니다.

*1. * ConTEXT 및 MinimapHUD.uc 스크립트를 오픈해 주세요.

*2. * DrawMap 함수를 선언해 주세요.

  function DrawMap()
  {
  }
  

*3. * 2 개의 로컬 Float 변수는, 맵내의 미니 맵 먼지에 의해 지정된 North 의 방향에 대하는 (분)편 위, 및 현재 플레이어가 적합할 방향에 대하는 (분)편 위를 보관 유지합니다.

  local Float TrueNorth;
  local Float PlayerHeading;
  

*4. * 맵의 회전 및 컴퍼스 오버레이의 회전에 관한 로컬 Float 변수를 선언해 주세요

  local Float MapRotation;
  local Float CompassRotation;
  

*5. * 몇개의 로컬 Vector 변수가 선언되고 있습니다. 그러한 사용법은, 후에 자세하게 설명됩니다.

  local Vector PlayerPos;
  local Vector ClampedPlayerPos;
  local Vector RotPlayerPos;
  local Vector DisplayPlayerPos;
  local vector StartPos;
  

*6. * 미니 맵 매테리얼은, 맵의 원형 표시를 강제하기 위해서 투과 마스크를 사용합니다. 적절한 위치에 이 마스크를 움직이기 (위해)때문에, Vector Parameter 의 R 및 G 컴포넌트가, 마스크 texture의 위치의 오프셋(offset)를 취하기 위해서(때문에) texture 좌표에 추가됩니다. LinearColor 로컬 변수에서는, 매테리얼내의 Vector Parameter 에 적절한 값을 건네주는 것이 필요합니다.

  local LinearColor MapOffset;
  

*7. * 로컬 Float 변수는, 맵에 의해 나타내지는 월드 스페이스 좌표내의 거리를 보관 유지합니다. 간략화를 위해서(때문에), 네모진 맵 texture를 사용하는 것을 요구하기 때문에, 1 개의 범위만이 필요합니다.

  local Float ActualMapRange;
  

*8. * 마지막으로, 로컬의 Controller 변수가, 맵내의 모든 플레이어의 위치를 그리기 위해서(때문에) 이테레이타와 함께 사용됩니다.

  local Controller C;
  

*9. * 다음에 진행되기 전에, 화면상에 맵을 쓰는 위치와 맵의 조정 후의 사이즈와 플레이어 박스의 사이즈가 설정됩니다. 클래스의 MapPosition 변수는, 상대 위치의 값을 보관 유지하고 있습니다. 이것들과 뷰포트의 폭과 높이를 적산하는 것으로, 맵을 묘화 하는 절대 위치를 얻을 수 있습니다. 뷰포트의 현재의 폭과 높이는 FullWidth 및 FullHeight 변수의 형식에서 제공됩니다.

  MapPosition.X = default.MapPosition.X * FullWidth;
  MapPosition.Y = default.MapPosition.Y * FullHeight;
  

*10. * 맵 및 플레이어 박스의 사이즈는, 이러한 변수의 디폴트치와 현재의 해상도의 뷰포트에 대한 스케일 팩터를 적산해 개개의 프레임으로 계산됩니다. 스케일 팩터는, ResolutionScale 변수내에서 보관 유지됩니다.

  MapDim = default.MapDim * ResolutionScale;
  BoxSize = default.BoxSize * ResolutionScale;
  

*11. * 작업 결과를 잃지 않게 스크립트를 보존해 주세요.

튜토리얼 13.9 - 미니 맵, 파트 VI: PLAYERPOS 및 CLAMPEDPLAYERPOS

PlayerPos 및 ClampedPlayerPos 변수에서는, 맵의 중심으로부터의 정규화된 오프셋(offset)치로서 플레이어의 현재의 위치를 보관 유지합니다. 풀 맵의 길이를 각방향 1.0 으로 생각하면(자), 이러한 변수의 각각의 컴포넌트는, 중심으로부터의 오프셋(offset)치이므로, -0. 5 에서 0.5 까지의 값을 가질 수가 있습니다. 어째서 맵의 중심으로부터의 오프셋(offset)치를 사용하는지 생각할지도 모릅니다. 그 이유는, 맵이 매테리얼의 내부에서, 그 중심의 주위를 회전해, 이 이후 보도록(듯이) 올바르고 모든 것을 계산하기 위해서 상대 위치를 알 필요가 있기 때문입니다.

물론, 정규화한 값을 계산하기 전에는, 월드 스페이스의 좌표치로 맵이 커버하는 길이를 모르면 안됩니다. 이것이, 본튜토리얼의 출발점입니다.

*1. * ConTEXT 및 the MinimapHUD.uc 스크립트를 오픈해 주세요.

*2. * ActualMapRange 는, X-축 및 Y-축의 사이의 2 개의 범위에서 큰 것을 취해, 그것들은 동일할 것입니다만, 계산됩니다. 이것은, 단지 fail-safe입니다. 각각의 축의 범위는 GameMinimap 의 MapRandMin 및 MapRangeMax 요소로 설정된 값의 차분을 취해 계산됩니다.

  ActualMapRange = FMax(GameMinimap.MapRangeMax.X - GameMinimap.MapRangeMin.X,
           GameMinimap.MapRangeMax.Y - GameMinimap.MapRangeMin.Y);
  

*3. * 맵으로서 사용하기 위해서 레벨의 screen shot를 취득하기 위해(때문에), 다음의 부분은, 복잡합니다. 투시도의 일그러짐이 없어지므로 UnrealEd 의 내부의 Top 뷰포트를 사용하지 않으면 안됩니다. 그렇지만, 그 뷰포트에 표시된 축은, 수직 방향이 X 수평 방향이 Y 가 됩니다. HUD 및 Canvas 가 관계하는 한은, 뷰포트의 수평 방향은 X 수직 방향은 Y 입니다. 더욱, 귀찮은 일에는, Top 뷰포트로부터 본 UnrealEd 내의 X-축의 값은, 그것이 아래에서 위로 움직이는 것에 따라 증가합니다만, 그에 대해, 게임의 뷰포트는, 위에서 아래로 움직이는 것에 따라 증가합니다.

그것은, 차는 곳, 그러한 축을 변환해야 하는 것을 나타내, X-축의 월드 좌표를 취급할 때에, 쌍방의 값은 반대의 부호가 되지 않으면 안됩니다. 이와 같이 해 HUD 를 취급하는 방식으로 UnrealEd 의 Top 뷰포트내에 두도록(듯이) 해, 월드 스페이스 좌표를 조정합니다.

PlayerPos 의 X 컴포넌트로부터 개시합시다. 중앙으로부터의 정규화된 오프셋(offset)치를 얻기 위해서(때문에), 맵의 중심은 플레이어의 위치로부터 감산되지 않으면 안됩니다. 그리고, 그 값은, 계산한지 얼마 안된 범위에서 제산되지 않으면 안됩니다. HUD 내의 위치의 X 컴포넌트가, 월드 스페이스의 위치의 Y 컴포넌트에 대응하는 것을 기억해 둬 주세요.

  PlayerPos.X = (PlayerOwner.Pawn.Location.Y - GameMinimap.MapCenter.Y) / ActualMapRange;
  

*4. * PlayerPos 의 Y 컴포넌트는, 월드 스페이스의 위치의 X 컴포넌트에 대응합니다만, 정부역의 값을 얻기 위해서(때문에) -1 로 곱셈하지 않으면 안됩니다. 이것을 실시하는 가장 간단한 방법은, 감산의 차례를 넣고 바꾸는 것입니다.

  PlayerPos.Y = (GameMinimap.MapCenter.X - PlayerOwner.Pawn.Location.X) / ActualMapRange;
  

*5. * 이것으로, 맵상의 플레이어의 위치를 얻을 수 있습니다만, 플레이어가 인연에 매우 근처 되었을 때는 무엇이 일어날까요 - 미니 맵은 플레이어의 위치를 중앙에 두어 그 주위의 맵을 표시하도록(듯이) 설계되고 있기 (위해)때문에, 만약, 쭉 플레이어를 미니 맵의 중심으로 표시하면서, 플레이어에게 인연 가까운 시일내에에 들르는 것을 허가했다면, 맵 texture 타일링의 리스크를 무릅쓰게 됩니다. 이것에 대응하기 위해서, 전혀 타일링을 허락하지 않을 정도 인연으로부터 멀게 멀어진 장소에 항상 있도록(듯이) 제한하는 2 번째의 위치를 가지는 ClampedPlayerPos 를 사용합니다.


그림 13.14 - 왼쪽이 클램프를 실시하지 않는 맵으로, 오른쪽이 클램프 한 맵.

이것을 실시하기 위해서(때문에), FClamp() 함수가 사용됩니다. 안으로 클램프 하는 2 개의 한계치와 함께, 클램프 되어야 할 값을 건네주는 것으로, 위치가 안전한 범위내에 있는 것을 보증할 수 있습니다. 이 2 개의 한계는 이하와 같은 것입니다 :

  -0. 5 + (TileSize / 2.0)
  

  0.5 - (TileSize / 2.0)
  

벌써, 정규화된 오프셋(offset)치는 -0. 5 로 0.5 의 사이이다고 말했습니다. 그것들로부터, 표시되고 있는 맵의 반의 부분을 가산 또는 감산하면, 맵 texture의 타일링의 오버랩을 일으키지 않는 부분인 것을 확인할 수가 있습니다.

플레이어의 위치의 X 컴포넌트를 클램프 해 주세요.

  ClampedPlayerPos.X = FClamp(   PlayerPos.X,
              -0. 5 + (TileSize / 2.0),
              0.5 - (TileSize / 2.0));
  

*6. * 같은 것을 Y 컴포넌트에도 실시합니다.

  ClampedPlayerPos.Y = FClamp(   PlayerPos.Y,
              -0. 5 + (TileSize / 2.0),
              0.5 - (TileSize / 2.0));
  

*7. * 작업 결과를 잃지 않게 스크립트를 보존해 주세요.

튜토리얼 13.10 - 미니 맵, 파트 VII: 맵의 회전

여기에서는, 플레이어가 적합한 (분)편 모퉁이를 나타내기 위해서(때문에) 맵이 회전하므로, 즐거운 일이 시작됩니다. 맵의 회전 자신은 실은 매우 간단합니다 ; Rotator 식을 실행하는 매테리얼내의 Scalar Parameter 에 라디안치를 건네줄 뿐입니다. 이것을 보다 간단하게 해, 맵은, 플레이어가 방향을 바꾸고 있는 것과 반대 방향으로 회전해야 하므로, 매테리얼내의 Rotator 는, GetPlayerHeading() 또는 GetRadianHeading()의 이상적인 함수에 의해 계산된 회전의 방향과 반대로 회전합니다.

맵내의 플레이어의 회전한 위치를 계산하는 부분은, 정말 즐거운 곳입니다. texture의 중심으로부터의 플레이어의 상대 위치는 압니다만, texture가 회전을 시작한 순간에, 계산한지 얼마 안된 위치는 플레이어가 표시되어야 할 맵상의 점에는 이미 관계하지 않습니다. 그렇지만, 조금(뿐)만 삼각법을 사용해, 회전한 위치를 계산할 수 있습니다. 우선, 전부 얼마나 회전할까를 알 필요가 있습니다.

*1. * ConTEXT 및 MinimapHUD.uc 스크립트를 오픈해 주세요.

*2. * TrueNorth 및 PlayerHeading 변수에는 적절한 라디안치를 설정할 필요가 있습니다.

  TrueNorth = GameMinimap.GetRadianHeading();
  Playerheading = GetPlayerHeading();
  

*3. * 여기서, 이러한 값을 MapRotation, CompassRotation 및 InverseRotation 의 설정에 사용 가능합니다만, 어떻게 실시할까는 GameMInimap 미니 맵 먼지의 bForwardAlwaysUp 의 값에 의존합니다. 이 변수의 값을 조건으로 하는 If-문장을 작성해 주세요.

  if(GameMinimap.bForwardAlwaysUp)
  {
  }
  else
  {
  }
  

*4. * 만약, bForwardAlwaysUp 가 True 이면, 맵은 주로 PlayerHeading 상에서 회전되어 CompassRotation 는, Playerheading 와 TrueNorth 의 사이의 차분이 됩니다.

  MapRotation = PlayerHeading;
  CompassRotation = PlayerHeading - TrueNorth;
  

*5. * 만약, bForwardAlwaysUp 가 False 이면, 맵은, Playerheading 와 TrueNorth 의 사이의 차분에 근거해 회전되어 CompassRotation 는, MapRotation 와 같게됩니다.

  MapRotation = PlayerHeading - TrueNorth;
  CompassRotation = MapRotation;
  

*6. * 다른 지점의 주위의 점을 회전할 때의 기본적인 생각은, 엔의 파라미터식을 사용하는 것입니다 :

이 경우, 반경은, 맵의 중심으로부터 플레이어의 위치까지의 거리, 또는, PlayerPos 벡터의 길이가 됩니다.

  VSize(PlayerPos)
  

회전각의 해석은, 약간 복잡함이 요구됩니다. 회전각은, 정의 X-축, 또는 0 라디안, 및 맵의 중심으로부터, 회전한 후의 플레이어의 위치에의 벡터의 사이의 각도입니다.


그림 13.15 - 회전한 플레이어의 위치를 계산하기 위해서 필요한 각도.

“결국은, 회전한 후의 플레이어의 위치를 계산하면 좋은 것이다. 위치를 몰랐으면, 어떻게 각도를 찾아내면 좋겠지 - " (이)라고 생각할지도 모릅니다. 플레이어의 진정한 위치를 알아, 정의 X-축과 맵의 중심으로부터 그 위치에의 벡터의 사이의 각도를 찾아낼 수가 있습니다. 이 처리는, 플레이어의 위치의 Y 및 X 컴포넌트를, 삼각형의 대변과 린변의 길이를 주어, arctangent를 계산하는 Atan() 함수에 건네주는 것으로 실행됩니다. 예를 들면 :

  Atan(PlayerPos.Y, PlayerPos.X)
  


그림 13.16 - 플레이어의 실제의 위치에의 각도.

여기에서는, 회전하는 위치의 양을 계산합니다. 정의 X-축 및 플레이어의 위치동안의 각도로부터 MapRotation 를 감산해, 정의 X-축과 회전한 위치동안의 각도를 계산할 수가 있습니다. 거기서, 위의 식의 진정한 값은 다음과 같이 됩니다 :

  Atan(PlayerPos.Y, PlayerPos.X) - MapRotation
  


그림 13.17 - 회전각을 감산하면(자) 요구하는 각도가 됩니다.

모든 것을 맞추면(자), 회전한 플레이어의 위치는 이하와 같이 계산됩니다 :

  DisplayPlayerPos.X = VSize(PlayerPos) * Cos( ATan(PlayerPos.Y, PlayerPos.X) - MapRotation);
  DisplayPlayerPos.Y = VSize(PlayerPos) * Sin( ATan(PlayerPos.Y, PlayerPos.X) - MapRotation);
  

*7. * PlayerPos 를 회전해 DisplayPlayerPos 를 설정했던 것에 유의해 주세요. (와)과 같이 ClampedPlayerPos 를 회전해 RotPlayerPos 를 설정할 필요도 있습니다.

  RotPlayerPos.X = VSize(ClampedPlayerPos) * Cos( ATan(ClampedPlayerPos.Y, ClampedPlayerPos.X) - MapRotation);
  RotPlayerPos.Y = VSize(ClampedPlayerPos) * Sin( ATan(ClampedPlayerPos.Y, ClampedPlayerPos.X) - MapRotation);
  

*8. * DisplayPlayerPos 는, 회전한 맵상의 플레이어의 실제의 위치이며, 플레이어 박스를 그리기 위해서(때문에) 사용됩니다. RotPlayerPos 는, 맵의 표시된 부분의 중심을 나타내는 맵상의 위치입니다. 이것은, StartPos 또는, 표시된 맵의 부분의 좌상의 구석, 을 발견하기 위해서 사용되는 위치입니다. 이것은, 중앙으로부터의 오프셋(offset)치이며, 여기서 절대치가 필요하므로, X 및 Y 컴포넌트의 양쪽 모두에 0.5 를 추가하는 것으로 계산됩니다. 그리고, TileSize 의 반이 각각으로부터 감산됩니다. 결과는, 타일링이 일어나지 않는 것을 확실히 하는 마지막 예방 조치와 같게, 0.0 으로 1.0 - TileSize 의 사이에 클램프 됩니다만, 이 값은 이미 이러한 한계치내에 낙담하고 있을지도 모릅니다.

  StartPos.X = FClamp(RotPlayerPos.X + (0.5 - (TileSize / 2.0)), 0.0, 1.0 - TileSize);
  StartPos.Y = FClamp(RotPlayerPos.Y + (0.5 - (TileSize / 2.0)), 0.0, 1.0 - TileSize);
  


그림 13.18 - 묘화 되는 부분의 좌상구석은 StartPos 입니다.

*9. * 맵의 회전의 마지막 국면은, 투과 마스크를 올바르고 빵 하기 위한(해), 매테리얼에게 건네지는 MapOffset 치를 설정하는 것입니다. MapOffset 의 R 및 G 의 컴포넌트는 반대로 RotPlayerPos 의 X 및 Y 컴포넌트에 관련합니다. 바꾸어 말하면, RotPlayerPos 의 값은 -1 으로 적산되고 MapOffset 의 R 및 G 컴포넌트에 대입됩니다. 그러나 제일에, 그것들은, 마지막 예방 조치와 같이, 재차, 이전 ClampedPlayerRot 치가 클램프 한 것과 같은 범위에 클램프 됩니다.

  MapOffset.R =  FClamp(-1. 0 * RotPlayerPos.X,
            -0. 5 + (TileSize / 2.0),
            0.5 - (TileSize / 2.0));
  MapOffset.G =  FClamp(-1. 0 * RotPlayerPos.Y,
            -0. 5 + (TileSize / 2.0),
            0.5 - (TileSize / 2.0));
  

*10. * 작업 결과를 잃지 않게, 스크립트를 보존해 주세요.

튜토리얼 13.11 - 미니 맵, 파트 VII: 매테리얼 파라미터와 드로맙의 설정

매테리얼 파라미터의 갱신과 맵의 묘화를 개시하는데 필요한 모든 것은 계산되어 실행의 준비를 할 수 있었습니다. 이 튜토리얼에서는, 맵, 컴퍼스 오버레이 및 플레이어 박스의 묘화 같이 맵 및 컴퍼스 오버레이 매테리얼의 파라미터의 설정을 설명합니다.

*1. * ConTEXT 및 MinimapHUD.uc 스크립트를 오픈해 주세요.

*2. * 맵 매테리얼은, MapRotation, TileSize 및 MapOffset 의 파라미터를 가집니다. MapRotation 는, 맵 texture의 회전을 제어하는 스칼라 파라미터입니다. TileSize 도 스칼라 파라미터로, 투과 마스크의 타일링, 따라서, 사이즈를 제어합니다. MapOffset 는, 투과 마스크의 위치를 제어하는 벡터 파라미터입니다. 컴퍼스 오버레이 매테리얼은, 오버레이의 회전을 제어하는 1 개의 스칼라 파라미터 CompassRotation 를 가집니다. 이것들은, MaterialInstanceConstant 클래스의 적절한 Set*Paramater() 함수를 사용해, 파라미터의 이름을 건네주어 값을 대입하는 것으로, 모든 것을 설정하는 것이 가능합니다. 파라미터명으로 무엇을 실시하려 하고 있을까를 간단하게 알 수 있는 것과 같게, 개개의 파라미터에 대한 값을 보관 유지하는 변수에는 이름을 붙일 수 있습니다.

  GameMinimap.Minimap.SetScalarParameterValue('MapRotation', MapRotation);
  GameMinimap.Minimap.SetScalarParameterValue('TileSize', TileSize);
  GameMinimap.Minimap.SetVectorParameterValue('MapOffset', MapOffset);
  GameMinimap.CompassOverlay.SetScalarParameterValue('CompassRotation', CompassRotation);
  

*3. * 묘화에 들어가기 전에, 어떻게 HUD 가 스크린상에 아이템을 그릴까를 간단하게 설명해야 합니다. 실제로는, HUD 는, 자기 자신에서는 전혀 묘화를 실시하지 않습니다. 다른 클래스 Canvas 가 모든 묘화 기능을 가지고 있습니다. HUD 클래스는, 현재의 Canvas 에의 참조를 포함해, 임의의 아이템을 스크린상에 묘화 할 필요가 있을 때에 사용됩니다. 어떻게 동작할까를 1 번 이해해 버리면, 맵의 묘화는 꽤 단순합니다. 타아이템의 뒤, 같은 장소에 그려진 아이템은, 최초의 물건 위에 그려지기 (위해)때문에, 명심해 두는 1 개(살) 소중한 (일)것은, 아이템을 묘화 하는 차례입니다.

최초로, Canvas 의 묘화의 위치를 맵이 그려져야 할 장소로 설정할 필요가 있습니다. 이것은 MapPosition 변수에 의해 지정되고 있습니다.

  Canvas.SetPos(MapPosition.X, MapPosition.Y);
  

*4. * 다음에, 맵이 Canvas 의 DrawMaterialTile() 함수를 사용해 그려집니다. 이 함수는, 묘화 해야 할 매테리얼, 묘화 하는 타일의 폭과 높이, 묘화를 개시하는 매테리얼내의 위치, 묘화 하는 매테리얼의 1 부의 폭과 높이를 받습니다.

  Canvas.DrawMaterialTile(GameMinimap.Minimap,
              MapDim,
              MapDim,
              StartPos.X,
              StartPos.Y,
              TileSize,
           TileSize );
  


그림 13.19 - 맵은, 스크린에 묘화 됩니다.

*5. * 다음에, Canvas 의 위치는 플레이어가 그리고 있는 장소로 설정해 둡니다. 이것은, DisplayPlayerPos 에서는, 0.5 를 가산하는 오프셋(offset)치로부터 절대 위치에의 변환이 행해질 필요가 있는 것을 나타냅니다. 그리고, StartPos 를 감산하는 것에 의해 풀 맵의 일부만이 묘화 되기 (위해)때문에, 그 값은 StartPos 로부터 오프셋(offset)에 변환되고 있을 것입니다. 이 값은, 0.0-1. 0 의 범위의 값에 정규화하기 위해서, 현재의 TileSize 로 제산됩니다. UV 좌표의 정규화된 위치는, 스크린의 좌표로 변환하기 위해서(때문에), 맵 타일의 면적 또는 MapDim 에 의해 곱셈됩니다. 그리고, 플레이어 박스 사이즈를 반으로 줄이기 (위해)때문에, 플레이어 박스는, 그 자리소상에서 centering 됩니다. 마지막으로, 모든 물건이 MapPosition 에 추가됩니다.

  Canvas.SetPos(   MapPosition.X + MapDim * (((DisplayPlayerPos.X + 0.5) - StartPos.X) / TileSize) - (BoxSize / 2), MapPosition.Y + MapDim * (((DisplayPlayerPos.Y + 0.5) - StartPos.Y) / TileSize) - (BoxSize / 2));
  

*6. * Canvas 의 DrawColor 는, 플레이어를 위해서(때문에) 선택한 PlayerColors 배열의 선두의 요소로 설정됩니다.

  Canvas.SetDrawColor(   PlayerColors[0]. R,
           PlayerColors[0]. G,
           PlayerColors[0]. B,
           PlayerColors[0]. A);
  

*7. * 여기서, 플레이어 박스는, 적절한 사이즈로 묘화 됩니다.

  Canvas.DrawBox(BoxSize, BoxSize);
  


그림 13.20 - 플레이어의 박스는, 맵의 전면의 스크린에 그려집니다.

*8. * 컴퍼스 오버레이를 그리기 위해서(때문에), Canvas 의 위치는 MapPosition 에 되돌려집니다.

  Canvas.SetPos(MapPosition.X, MapPosition.Y);
  

*9. * 그리고, GameMinimap 의 CompassOverlay 매테리얼은, 재차, DrawMaterialTile() 함수를 사용해 그려집니다.

  Canvas.DrawMaterialTile(GameMinimap.CompassOverlay, MapDim, MapDim, 0.0, 0.0, 1.0, 1.0);
  


그림 13.21 - 컴퍼스 오버레이는, 맵의 전면에 묘화 됩니다.

*10. * 작업 결과를 잃지 않게 스크립트를 보존해 주세요.

튜토리얼 13.12 - 미니 맵, 파트 VIII: 다른 플레이어를 묘화 한다

본튜토리얼에서는, 레벨내의 다른 플레이어는, 미니 맵내에서 표시 가능한 범위내의 장소에 있으면(자) 가정해, 개별적으로 맵상에 묘화 됩니다.

*1. * ConTEXT 및 MinimapHUD.uc 스크립트를 오픈해 주세요.

*2. * 플레이어를 그리는 코드의 다음에, 컴퍼스 오버레이가 묘화 되기 전에, WorldInfo 참조를 사용해, 이전 선언한 기저 Controller 클래스와 C 로컬 변수를 건네주어, AllControllers 이테레이타를 셋업 해 주세요. 플레이어 박스를 그린 후, 컴퍼스 오버레이가 그려지기 전에 이것을 실시하는 이유는, 2 개 있습니다. 제 1 에, 플레이어의 위치를 계산하기 위해서 사용한 변수의 몇개인가를, 그러한 값을 덧쓰기할 걱정없이 재이용할 수 있습니다. 제 2 에, 모든 맨 앞면에 컴퍼스 오버레이를 그리는 것으로, 다른 플레이어가 맵의 표시 가능한 영역으로부터 빠질 때에, 다른 플레이어의 박스의 존재가 사라져 가는 것을 숨겨, 예쁜 천이가 됩니다.

  foreach WorldInfo.AllControllers(class'Controller', C)
  {
  }
  

*3. * 여기서, 이테레이타내의 현재의 Controller 가 PlayerOwner 는 아니기 때문에 그 위에 묘화 할 수 없는 것을 확인하기 위해서 If-문장을 사용해 주세요.

  if(PlayerController(C) ! = PlayerOwner)
  {
  }
  

*4. * 이 If-문장 속에서는, 현재의 Controller 의 Pawn 의 정규화된 오프셋(offset) 위치를 계산할 필요가 있습니다. 이것은, 이전, 현재의 Controller (을) 위해서만, 플레이어용으로 계산된 DisplayePlayerPos 와 같습니다. 이것은, 이미 존재하는 PlayerPos 및 DisplayPlayerPos 를 계산하고 있는 코드를 카피해, If-문내에 붙이는 것이, 아마 가장 간단하겠지요.

  PlayerPos.X = (PlayerOwner.Pawn.Location.Y - GameMinimap.MapCenter.Y) / ActualMapRange;
  PlayerPos.Y = (GameMinimap.MapCenter.X - PlayerOwner.Pawn.Location.X) / ActualMapRange;
  
  DisplayPlayerPos.X = VSize(PlayerPos) * Cos( ATan(PlayerPos.Y, PlayerPos.X) - MapRotation);
  DisplayPlayerPos.Y = VSize(PlayerPos) * Sin( ATan(PlayerPos.Y, PlayerPos.X) - MapRotation);
  

여기서, PlayerOwner 의 말이 존재하면(자), 단순하게 변수 C 로 옮겨놓아 주세요.

  PlayerPos.X = (C.Pawn.Location.Y - GameMinimap.MapCenter.Y) / ActualMapRange;
  PlayerPos.Y = (GameMinimap.MapCenter.X - C.Pawn.Location.X) / ActualMapRange;
  
  DisplayPlayerPos.X = VSize(PlayerPos) * Cos( ATan(PlayerPos.Y, PlayerPos.X) - MapRotation);
  DisplayPlayerPos.Y = VSize(PlayerPos) * Sin( ATan(PlayerPos.Y, PlayerPos.X) - MapRotation);
  

*5. * 이것에 의해, 맵의 중심으로부터의 현재의 Controller 의 Pawn 의 상대적인 회전 후의 위치를 알 수 있습니다. 여기서, 이 Controller 를 그려야할 것인가 어떤가를 결정하기 위해서(때문에), 이 정도치가 회전 후의 위치로부터 특정의 거리안에 있을지 어떨지를 확인하지 않으면 안됩니다.

VSize() 함수는, 플레이어의 위치로부터 Controller 의 위치에의 거리를 취득하기 위해서 사용됩니다.

  VSize(DisplayPlayerPos - RotPlayerPos)
  

이 거리의 상한은, 기본적으로 TileSize 의 반으로, 플레이어 박스의 대각선의 길이의 반이하가 됩니다. 유일한 문제는, TileSize 는, 0.0-1. 0 의 범위에 정규화되고 있어 BoxSize 는, 스크린의 좌표치이므로 정규화가 필요한 일입니다.

플레이어 박스의 대각선의 길이의 반은, 이하와 같이 계산됩니다 :

  Sqrt(2 * Square(BoxSize / 2))
  

이 길이를 정규화하기 위해서, 맵의 면적으로 제산하고 나서, TileSize 로 곱셈합니다.

  (TileSize * Sqrt(2 * Square(BoxSize / 2)) / MapDim)
  

그 때문에, 최종적인 거리는, TileSize 의 반으로부터, 계산 결과를 감산한 것이 됩니다.

  ((TileSize / 2.0) - (TileSize * Sqrt(2 * Square(BoxSize / 2)) / MapDim))
  

여기서, 2 명의 플레이어의 사이의 거리와 이 거리를 비교하는 If-문장을 작성해 주세요.

  if(VSize(DisplayPlayerPos - RotPlayerPos) <= ((TileSize / 2.0) - (TileSize * Sqrt(2 * Square(BoxSize / 2)) / MapDim)))
  {
  }
  

*6. * 스크린상에 플레이어의 박스를 묘화 하는 3 행의 코드를 카피해, If-문내에 붙여 주세요.

  Canvas.SetPos(   MapPosition.X + MapDim * (((DisplayPlayerPos.X + 0.5) - StartPos.X) / TileSize) - (BoxSize / 2), MapPosition.Y + MapDim * (((DisplayPlayerPos.Y + 0.5) - StartPos.Y) / TileSize) - (BoxSize / 2));
  
  Canvas.SetDrawColor(   PlayerColors[0]. R,
           PlayerColors[0]. G,
           PlayerColors[0]. B,
           PlayerColors[0]. A);
  
  Canvas.DrawBox(BoxSize, BoxSize);
  

*7. * SetDrawColor() 함수 소환내에서 액세스 되고 있는 PlayerColors 배열의 인덱스를 2 번째의 요소로 변경해 주세요.

  Canvas.SetDrawColor(   PlayerColors[1]. R,
           PlayerColors[1]. G,
           PlayerColors[1]. B,
           PlayerColors[1]. A);
  


그림 13.22 - 레벨내의 다른 플레이어는 맵상에 나타나게 됩니다.

*8. * 스크립트를 보존해 컴파일 해 주세요. 이 장용의 파일과 함께 DVD 상에서 제공된 CompassContent.upk 패키지가 Unpublished\CookedPC 디렉토리내에 있는 것을 확인해 주세요. 문법 에러가 있으면 수정해 주세요.

튜토리얼 13.13 - 미니 맵, 파트 IX: 맵의 설정 및 screen shot

미니 맵 시스템을 시험하기 위한 모든 환경의 정비를 개시할 때입니다. 우선, MU_Minimap 먼지의 맵을 설정해, 맵으로서 그것을 사용하기 위해서 screen shot를 설정할 필요가 있습니다.

*1. * UnrealEd 를 오픈해, 본장의 파일과 함께 DVD 로 제공된 COM-CH_13_Minimap.ut3 맵을 오픈해 주세요.


그림 13.23 - COM-CH_13_Minimap.ut3 맵.

*2. * Actor Classes Browser 를 오픈해, Actor->Compass->MU_Minimap 아래에 리스트 표시된 MU_Minimap 클래스를 선택해 주세요.


그림 13.24 - MU_Minimap 클래스가 선택되었습니다.

*3. * 뷰포트내에, 신규 MU_Minimap 먼지를 맵에 추가해 주세요. 할 수 있는 한 맵의 중심으로 가까운 시일내에 그것을 배치해라고 주세요. 정확한 필요는 없습니다, 가까우면 좋습니다. 이 맵내에서 North 로서 사용되고 있을 방향의 조정을 바란다면, 먼지를 Y-축의 주위에서 회전해도 좋습니다.


그림 13.25 - MU_Minimap 먼지의 배치.

*4. * Top 뷰포트내에서는, 적절히 축소하고 나서, MU_Minimap 먼지를 선택해 Properties Window 를 오픈해 주세요.

MapExtentsComponent 섹션을 확장해 MU_Minimap 카테고리내의 SphereRadius 프롭퍼티를 검색해 주세요. 뷰포트내의 구체가 맵의 완전하게 플레이 가능한 영역을 포함할 때까지, 이 프롭퍼티의 값을 늘려 주세요. 맵의 외측에도, 어느 정도의 아무것도 없는 공간을 남기도록 해 주세요. 이 프롭퍼티에 대한 적절한 값은 1600 정도가 됩니다.


그림 13.26 - 구체의 반경은 조정됩니다.

*5. * 이 튜토리얼의 나머지로, 따로 취급할 수가 있도록(듯이), 이 맵을 보존해 주세요.

*6. * 맵 매테리얼 및 컴퍼스 오버레이 매테리얼을 할당하기 전에, 미니 맵으로 사용되는 레벨의 screen shot를 취득할 필요가 있습니다. 이것이 인도어 레벨이기 (위해)때문에, Top 뷰포트로부터의 screen shot를 취득하려면 , 아웃도어 맵에 대해서는, 보다 많은 작업이 필요합니다 ; 주로 천정을 제거할 필요가 있어, 그러면, Top 뷰포트내에서 방의 내부를 보는 것이 가능합니다.

*a. * 이것은, 이 경우는 그만큼 어려운 일이 아닙니다. 천정을 작성한 정적 메쉬의 1 개를 선택해, 게다가로 오른쪽 클릭해, Matching Static Meshes (This Class)를 선택해 주세요. 이것으로, 천정과 마루를 선택합니다.


그림 13.27 - 모든 마루 및 천정의 메쉬가 선택되었습니다.

*b. * Front 또는 Side 뷰포트내에서, Hold Ctrl + Alt + Shift + 오른쪽 mouse button를 계속 눌러 , 선택되는 마루의 면의 메쉬의 주변에 marquee 선택을 드러그 해 주세요. 이것은, 천정의 선택만을 남겨, 선택을 해제합니다. 천정을 없애기 위해서(때문에) Delete 키를 눌러 주세요.


그림 13.28 - marquee 선택은, 선택 상태의 아이템을 삭제합니다.

*c. * 여기서, 개개의 방의 중앙의 2 살의 라이트 메쉬를 선택해 주세요. 라이트 먼지 자신은 선택하지 않게 해 주세요. 그리고, 그것들을 삭제하기 위해서 Delete 키를 눌러 주세요.


그림 13.29 - 라이트 메쉬는 삭제되었습니다.

*d. * 마지막으로, 맵 전체를 둘러싸는 푸른 Additive 브러쉬를 선택해, 그것을 삭제하기 위해서 Delete 키를 눌러 주세요.


그림 13.30 - 브러쉬는 제거되었습니다.

*e. * 마지막으로, BSP 및 라이팅을 갱신하기 위해서 메인 툴바내의 Build All 버튼을 눌러 주세요.

*7. * MU_Minimap 먼지를 선택하고 나서, Sheet Brush 옵션을 오픈하기 위해서 Toolbox 내의 Sheet Brush 빌딩 다보 탄상에서 오른쪽 클릭해 주세요. X 및 Y 의 값을 3200 ( SphereRadius 의 2 배)으로 설정해 Build 를 클릭해 주세요. 빨강의 비르다브라시가 뷰포트내에서 갱신될 것입니다.


그림 13.31 - 비르다브라시는, MU_Minimap 먼지상에서 centering 되고 있습니다.

*8. * 빨강의 비르다브라시를 선택해, 레벨내에 존재하는 지오메트리아래에 움직여 주세요. Generic Browser 내의 CompassContent 패키지내에 놓여진 M_Black 매테리얼을 검색해 선택하고 나서, CSG 를 클릭해 주세요 : M_Black 매테리얼을 적용한 빨강의 비르다브라시를 사용하는 추가의 시트를 작성하기 위해서 Toolbox 내에 버튼을 추가해 주세요.


그림 13.32 - 시트 브러쉬가 추가되었습니다.

*9. * Top 뷰포트를 최대화해, 맵의 릿트뷰를 표시하기 위해서 툴바내의 Lit 버튼을 눌러 주세요. 다음에, 게임 모드를 타글 하기 위해서 G 키를 눌러 주세요. 미니 맵 사용시에 맵 texture가 어떻게 될까를 기본적으로 볼 수 있을 것입니다.


그림 13.33 - 릿트뷰를 표시하고 있는 Top 뷰포트.

*10. * 여기로부터 맵 texture를 마무리하는 것은, 꽤 간단합니다.

*a. * 뷰포트내에 꼭 적합하는 블랙 시트 직전까지 축소해, Print Screen 키를 눌러 주세요.


그림 13.34 - 시트 브러쉬는 뷰포트를 묻습니다.

*b. * 여기서, 이미지를 편집하는 프로그램을 오픈해, 새로운 이미지를 작성해 주세요. 이 예에 대해서는, Photoshop 를 사용합니다.


그림 13.35 - 신규 이미지가 작성되었습니다.

*c. * 카피된 screen shot를 이미지에 붙이기 위해서(때문에) Ctrl + V 를 눌러 주세요.


그림 13.36 - capther 된 screen shot는 이미지에 붙일 수 있습니다.

*d. * 맵 texture를 나타내는 검은 부분을 선택해, 이미지를 이 영역에 트리밍 해 주세요.


그림 13.37 - 이미지는, 검은 에리어에 트리밍 됩니다.

*e. * 이미지를 확대 축소하는지, 그 사이즈를 조정해, 2048x2048 로 해 주세요.


그림 13.38 - 이미지는 사이즈 조정됩니다.

*f. * Unreal 가 임포트 가능한 형식에서 파일을 보존해 주세요. 24-bit Targa (. tga) 파일이 통상 가장 적절히 동작합니다.

*11. * 다른 screen shot를 얻기 위해서(때문에) 후에 필요할지도 모르기 때문에, 보존을 하고 싶은 경우는, UnrealEd 의 맵을 보존해도 좋습니다. 확실히 다른 이름으로 보존해 주세요, 그러면 실제의 맵을 덧쓰기하지 않습니다.

튜토리얼 13.14 - 미니 맵, 파트 X: 미니 맵 매테리얼 및 마지막 마무리

미니 맵에 대한 이미지를 작성하면(자), UnrealEd 에 그것을 임포트 해, 미니 맵과 CompassOverlay 에 대해서 MaterialInstanceConstants 를 작성할 때입니다. 이것들은, 레벨내의 MU_Minimap 먼지의 대응하는 프롭퍼티에도 대입되지 않으면 안됩니다.

*1. * UnrealEd 와 screen shot에 대해서 사용되는 맵은 아닌 MU_Minimap 먼지를 추가한 이전의 튜토리얼로부터의 맵을 오픈해 주세요.

*2. * Generic Browser 를 오픈해 File 메뉴로부터 Import 를 선택해 주세요. 이전의 튜토리얼로 보존한 맵 이미지를 선택해, Open 를 클릭해 주세요.

*a. * 표시된 다이얼로그내에서, 패키지의 드롭 다운 리스트내의 COM-CH_13_Minimap 패키지를 선택해, 희망하는 경우는 새로운 이름을 입력해 이름을 붙여 그렇지 않으면, 디폴트의 파일명대로 해 두어 주세요.


그림 13.39 - 레벨의 패키지가 선택되었습니다.

*b. * Options 리스트에서는, LODGroup 를 TEXTUREGROUP_UI 로 설정해 주세요. 이것은, Unreal Tournament 3 이 texture의 사이즈 제한 용도에, 이러한 그룹을 사용하므로 중요합니다. UI group 는, texture에 풀의 2048x2048 의 사이즈에서의 표시를 허가하기 때문에, 통상의 압축 아치 팩트를 넘은 품질 열화는 없습니다.


그림 13.40 - TEXTUREGROUP_UI LODGRoup 가 선택되었습니다.

*c. * 임포트 처리의 고속화를 실시하고 싶으면, DeferCompression 옵션도 확인 가능합니다. 패키지나, 이 경우는 맵을 보존하기까지 압축을 하지 않게 합니다. 물론, 이것에 의해, 보존 프로세스가 늦어지기 때문에, 마지막에는, 같게 됩니다.


그림 13.41 - DeferCompression 옵션이 선택되었습니다.

*d. * texture를 임포트 하기 위해서 OK 를 클릭해 주세요.

주기 : 선택한 패키지는 사용중의 레벨의 이름이 될 것입니다. 만약, 어떤인가 다른 이름을 붙였다면, 패키지 리스트로부터 대신의 것을 선택해 주세요.

*3. * Generic Browser 내에서, 신규에 임포트 된 Texture 상에서, 프롭퍼티를 오픈하기 위해서, 오른쪽 클릭하는지, 더블 클릭 해 주세요. SRGB 프롭퍼티까지 하향 스크롤 해, 그 체크를 제외해 주세요. 이것으로, 감마 보정을 사용하지 않게 됩니다. 만약, 이 옵션을 오프로 하지 않았으면, texture는 스크린상에 표시되었을 때에, 극단적으로 어두워집니다.


그림 13.42 - SRGB 플래그는 타글로 오프로 합니다.

*4. * 여기서, CompassContent 패키지내의 M_minimap 매테리얼을 검색해 주세요. 게다가로 오른쪽 클릭해, New Material Instance Constant 를 선택해 주세요.


그림 13.43 - 신규의 MaterialInstanceConstant 는, M_minimap 매테리얼로부터 작성됩니다.

*a. * 표시된 다이얼로그 중(안)에서, 패키지의 드롭 다운 리스트내의 COM-CH_13_Minimap 패키지를 선택해, 희망한다면 새로운 이름을 입력하는지, 디폴트의 M_minimap_INST 대로 해 둡시다. OK를 클릭해 주세요.

주기 : 선택한 패키지는 사용중의 레벨의 이름에 될 것입니다. 만약, 다른 어떠한 이름을 붙였다면, 패키지 리스트로부터 대신의 것을 선택해 주세요.

*5. * Material Instance Editor 가, 신규의 MaterialInstanceConstant 를 위해서(때문에) 표시되었을 때는, ScalarParameterValues 섹션을 전개해, 리스트 된 쌍방의 파라미터의 근처의 체크 박스를 클릭해 주세요. 그리고, TextureParameterValues 섹션을 전개해, 똑같이 그러한 파라미터의 각각의 근처의 체크 박스를 클릭해 주세요. 마지막으로, VectorParameterValues 섹션을 전개해, 그리고, 여기서 발견한 파라미터의 근처의 체크 박스를 클릭해 주세요.


그림 13.44 - MaterialInstance Editor.

*6. * 임포트 한 맵 texture를 선택하고 나서, 맵 texture를 매테리얼에 할당하기 위해서(때문에) TextureParameterValues 섹션내의 MinimapTex 파라미터의 Use Current Selection In Browser 버튼을 눌러 주세요.


그림 13.45 - 맵 texture는 디폴트를 옮겨놓습니다.

*7. * CompassContent 패키지내로 돌아가, M_compass 매테리얼상에서 오른쪽 클릭해, New Material Instance Constant 를 선택해 주세요.


그림 13.46 - M_compass 매테리얼로부터, 신규 MaterialInstanceConstant 가 생성되었습니다.

*a. * 표시된 다이얼로그내에서, 패키지의 드롭 다운 리스트로부터 COM-CH_13_Minimap 패키지를 선택해, 희망이 있으면 새로운 이름을 입력해 주세요. 또는, 디폴트의 M_compass_INST 의 디폴트치대로 해 두어 주세요. OK 를 클릭해 주세요.

주기 : 선택한 패키지는 사용중의 레벨의 이름에 될 것입니다. 만약, 다른 어떠한 이름을 붙였다면, 패키지 리스트로부터 대신의 것을 선택해 주세요.

*8. * 신규 MaterialInstanceConstant 를 위해서(때문에) Material Instance Editor 가 표시되었을 때에는, ScalarParameterValues 섹션을 전개해, 리스트 표시된 쌍방의 파라미터의 근처의 체크 박스를 클릭해 주세요. 그리고, TextureParameterValues 섹션을 전개해, 같이 그러한 파라미터 각각의 근처의 체크 박스를 클릭해 주세요.

*9. * 레벨내에서 MU_Minimap 먼지를 선택해, F4 를 눌러, 그 프롭퍼티를 오픈해 주세요. 생성한지 얼마 안된 미니 맵 MaterialInstanceConstant 를 선택해, Minimap 프롭퍼티에 대해, Use Current Selection In Browser 버튼을 클릭해 주세요. 그리고, 컴퍼스 오버레이 MaterialInstanceConstant 를 선택해, CompassOverlay 프롭퍼티에 대해 Use Current Selection In Browser 버튼을 클릭해 주세요.


그림 13.47 - MaterialInstanceConstants 는, MU_Minimap 먼지에 대해서 대입됩니다.

*10. * “COM-“으로 시작되어 있는 한은, 희망하는 임의의 이름으로 이 맵을 보존하고 나서, 메인 툴바내의 Publish Map 버튼을 클릭해, 그것을 발행하는지, 이 경우는 단지 간단한 테스트이므로, Published\CookedPC\CustomMaps 폴더에 카피를 보존해 주세요. Published\CookedPC 디렉토리에 CompassContent.upk 도 같이 카피하는 것을 잊지 않게 해 주세요.

튜토리얼 13.15 - 미니 맵, 파트 XI: 미니 맵의 테스트

모든 코드가 완성되었으므로, 신규의 미니 맵 시스템을 이용하기 위해(때문에), 여기에서는 맵을 설정합니다.

*1. * UT3 를 로드해, 오프 라인으로 플레이 하기 위해서 로그인 또는 선택을 해 주세요.

*2. * Instant Action 게임을 선택해 주세요.


그림 13.48 - Instant Action 가 선택되었습니다.

*3. * 다음의 메뉴로부터 MinimapGame 게임 타입을 선택해 주세요.


그림 13.49 - MinimapGame 가 선택되었습니다.

*4. * 리스트내의 유일한 맵으로서 이전의 튜토리얼내에서 보존한 맵을 보고 있을 것입니다. 이 맵상에서 더블 클릭 해 주세요.


그림 13.50 - 맵이 선택되었습니다.

*5. * 이 작은 레벨의 PlayerStarts 에서는, 플레이어 이외를 spawn(스폰) 하기 위해서 2 개의 보트가 있으면 충분하므로, 다음의 메뉴로 보트의 수를 2 이하로 설정해 주세요.


그림 13.51 - 보트가 설정되었습니다.

*6. * 게임을 개시해 주세요. 레벨이 로드 되자 마자, 스크린의 좌상구석에 표시된 맵을 봐야 합니다가, 대전이 시작되지 않는 한은, 돌거나 이동하거나 해도 무슨 효과도 없습니다.


그림 13.52 - 맵이 스크린상에 표시됩니다.

*7. * 대전을 개시해 주세요, 그러면, 맵내에서의 플레이어의 실제의 위치를 반영한 맵을 볼 수 있을 것입니다. 이동하거나 돌거나 하면(자) 맵이 갱신될 것입니다. 표시해야 할 정도 충분히 가까울 때로는, 초록의 박스로서 보트도 표시되는 일도 알겠지요.


그림 13.53 - 맵은, 플레이어의 회전 및 위치를 반영합니다.

미니 맵 시스템은, 꼭 기대한 것처럼 동작하고 있을 것입니다. 분명하게, 이 효과는, 큰 야외의 환경에서는 좀 더 훨씬 유용하고 흥미로운 것이 되겠지요. 이 작은 인도어의 맵은, 단지 테스트를 행하기 위한 간단한 수단입니다.

튜토리얼 13.16 - 포획 볼륨, 파트 I: 초기설정

맙파가 돌아와, 새로운 요구를 실시했습니다. 맵으로, 몇개의 훌륭한 효과에 접속 가능한 신규 볼륨이 필요했습니다. 이 시점까지, 작업은 완전하게 Kismet 내에서 행해지고 있어 2~3의 매우 간단한 작업을 실시하기 (위해)때문에, 매우 복잡한 순서를 작성했습니다. 이 새로운 볼륨을 작성해, 완전한 Kismet 순서를 옮겨놓기 위해서(때문에), 필요한 Kismet 정의를 실장하는 것이, 우리가 양쪽 어깨에 짊어지고 있습니다.

그와의 미팅 후에, 이하의 사양의 리스트를 작성했습니다 :

  • 배치 가능한 브러쉬의 볼륨이 아니면 안된다
  • 디폴트의 다른 브러쉬와 구별을 하기 위한(해), 색은 Light Green 이어야 한다
  • kismet 내에 3 개의 출력 이벤트를 가진다
    • Red Captured (레드 포획제) - 레드 팀이 포획을 실행했을 때
    • Blue Captured (블루 포획제) - 블루 팀이 포획을 실행했을 때
    • Unstable (불안정) - 싸우고 있다, 또는, 포획 상태가 변화했을 때
  • 설정 가능한 요소가 몇개인가 있습니다
    • Time to Capture(포획까지의 시간) - 개개의 볼륨에 대해서 에디터내로 설정된 정수 (디폴트 3)
    • Capture(포획)가 가능한 최소의 플레이어의 수 (디폴트 1)
    • Rewarded Points(획득 포인트) - 포획자에 대한 보수 (디폴트 1)
  • 포획이 개시되었는지를 확인하기 위해(때문에), 반초 마다 설정되는 타이머를 가져야 하는 것이다
  • 타글 가능해야 한다

50000피트의 크기의 표시안에 있었다고 해도, 이것은 해결하기에는 복잡한 문제입니다. 가장 직접적으로 보이기 때문에, 이 문제에는, 주요한 조목별로 나누어 쓴 글로 나눈 부분 마다 어프로치 합니다.

볼륨에 의해 사용되는 인터페이스를 정의하는 것으로부터 시작합니다.

*1. * ConTEXT 를 오픈해, UnrealScript 하이 라이터를 사용해 ICaptureVolume.uc 라고 명명된 신규 파일을 작성해 주세요.

*2. * 새로운 CaptureVolume 에 대한 Interface 를 정의해 주세요.

  interface ICaptureVolume;
  

*a. * 볼륨의 유효화 된 상태와 연결시키는 것 같은 OnToggle() 함수를 정의해 주세요.

  function OnToggle(SeqAct_Toggle action);
  

*b. * 볼륨의 점유자를 테스트하기 위해서 사용되어 볼륨의 포획이 개시되고 있는지 어떤지를 리턴 하는 CheckBeginCapture() 함수를 선언해 주세요.

  function bool CheckBeginCapture();
  

*c. * GetTouchingUTPawns() 함수는 그 유틸리티를 위해서(때문에) 사용됩니다. 이것은, 모든 터치한 pawn(폰)를 모아 그리고 리턴 된, 빨강 또는 파랑의 볼에 처넣습니다. 단일의 값만을 리턴 가능하다 것으로, out 변수가 이용됩니다. 함수는, 볼륨내의 캐릭터의 총계치를 리턴 합니다

  function int GetTouchingUTPawns(out array<UTPawn> redTouching, out array<UTPawn> blueTouching);
  

*d. * tCheckCapture()는, 타이머로 훅 된 함수로, 뒤에서 많은 계산을 실행합니다.

  function tCheckCapture();
  

*e. * UpdateEvents() 함수는, 출력 운명 인터페이스를 조작해, 방아쇠를 걸 수 있었던 이벤트에 대한 플래그를 받습니다.

  function UpdateEvents(int flag);
  

*3. * 작업 결과를 잃지 않게 스크립트를 보존해 주세요.

*4. * 인터페이스를 명확하게 정의하면(자), CaptureVolume 자신에 대해서, 배관을 작성할 때입니다. 모든 볼륨은 Vollume 클래스로부터 파생하기 때문에, 다른 상속 클래스내로부터 파생할 필요가 있는 것은 아무것도 없기 때문에, 선례에 따라야 합니다. 이것에 대해서는 그만큼 많지 않기 때문에, 단지, 이 볼륨의 코드를 기술하는 스텝을 더듬어 보아야 하는이지요.

*a. * UnrealScript 하이 라이터를 사용하는 CaptureVolume.uc 라는 이름의 신규 파일을 작성해 주세요.

*b. * 클래스를 정의해, ICaptureVolume Interface 를 실장합니다

  class CaptureVolume extends Volume placeable implements(ICaptureVolume) Config(UTBook);
  

*c. * 여기서, 디폴트 프롭퍼티안에 피트 인 합시다. 여기에, 주목해야 할 1 개의 요소가 있습니다만, 그것은, BrushColor 입니다. 이것은, 컴퍼스의 튜토리얼로 경험한 것과 같은 정수치를 받습니다.

  defaultproperties
  {
     // UnrealEd 용으로, 삭제되지 않게
     BrushColor = (B=128, G=255, R=128, A=255)
     bColored = True
     bStatic = false
  }
  

*d. * 만약, 전회 설명한 스텝을 더듬었을 경우는, 이 시점에서, 컴파일이 가능해, 실제로 에디터내에 신규 볼륨을 보았습니다. 이 볼륨은, 다른 볼륨과 같게 동작해, 볼륨을 간략하게 정리한 리스트로 배포됩니다만, 이것은, 절구초록이 됩니다.


그림 13.54 - UnrealEditor 내의 신규 CaptureVolume

*5. * 여기로부터가, 점점 어려워집니다. 이 볼륨의 여러가지 요소의 구성을 가능하게 하기 위해서 맙파에 대한 많은 변수를 작성합니다. 여기는, 또 코드의 가독성을 돕기 위해서 2 개의 열거형의 작성도 가능해지는 곳(중)입니다. 이하에, 변수의 목적에 관한 인 라인 코멘트를 포함한 코드 블록을 볼 수 있습니다. 디폴트 프롭퍼티는 갱신되어, 말미에 추가됩니다.

*a. * ECaptureEvent 는, 3 개의 방아쇠를 걸칠 수 있었던 상태의 열거형입니다. 이것은, 코드중의 부동 소수점의 마법과 같은 정수를 옮겨놓아 사용해, 주로 읽기 쉽게 하기 위해서 행해지고 있습니다.

  enum ECaptureEvent
  {
     CAP_REDCONT,
     CAP_BLUECONT,
     CAP_UNSTABLE
  };
  

*b. * ETeams 는, 직접적으로 직전의 열거형과 같은 목적을 가집니다.

  enum ETeams
  {
     RED_TEAM,
     BLUE_TEAM,
     NO_TEAM
  };
  

*c. * 여기서, 클래스의 본체에 들어갑니다. iTimeToCapture 는, 볼륨을 포획 하기 위해서 필요한 초수를 제어하는 정수형의 변수입니다. (Capture) 문장에 의해 제어된 Capture 부범위하에서, 다음의 3 개의 변수의 각각이, 맙파에 대해서 이용 가능합니다.

  var (Capture) int iTimeToCapture;
  

*d. * iPointReward 는, 개인 베이스로, 포획 하는 그룹에 대해서 용서된 보수입니다. 맙파가 여기에서는 값을 바라지 않는 것을 선택했을 경우는, 그것을 0 으로 변경할 수 있습니다.

  var (Capture) int iPointReward;
  

*e. * 이 변수는 실은 중요합니다. 볼륨내에, 이 수의 플레이어가 존재하고 있는 경우에게만 볼륨에 방아쇠를 걸칠 수 있습니다.

  var (Capture) int iMinimumPlayersForCapture;
  

*f. * 이러한 2 개의 변수는, 볼륨 상태를 계속 감시하기 (위해)때문에 , 즉, 누가 그것을 제어하고 있어, 누가 제어를 빼앗으려 하고 있는지, 의 각각 대해 사용됩니다.

  var int CapturingTeamID;
  var int CapturedTeamID;
  

*g. * TimeCapturing 는, 이 볼륨의 포획에 괘시간의 Interval(간격)를 계속 감시합니다.

  var float TimeCapturing;
  

*h. * 이 볼륨으로 누가 포획을 실시하는 역할인가를 계속 감시하기 위해서(때문에), CapturingMembers 는, 사용됩니다.

  var array<UTPawn> CapturingMembers;
  

*i. * 이것은, 맙파가 적절이라고 생각하도록(듯이), 볼륨을 이용 불가로 하거나 이용 가능하게 하거나 하는 것을 확실히 하기 위한(해), 타글 루틴에 의해 사용됩니다.

  var bool bEnabled;
  

*6. * 신규 변수의 디폴트 변수를 반영해 디폴트 프롭퍼티를 갱신해야 합니다. 디폴트 프롭퍼티 블록은, 자립합니다만, CapturedTeamID 에 대해서는, 작성한 열거형으로부터의 정수를 사용해, 디폴트치를 명확하게 정의한 값을 설정해, 대입하고 있는 것을 지적해 둡니다. 그 외의 값은, 맙파에 의해 건네받습니다.

  defaultproperties
  {
     // 주로 UEd 에 대한 설정.
     BrushColor = (B=128, G=255, R=128, A=255)
     bColored = True
     bStatic = false
  
     // 볼륨에 대한 디폴트치
     iMinimumPlayersForCapture = 1
     CapturedTeamID = NO_TEAM
     iTimeToCapture = 3
     iPointReward = 5
  }
  

*7. * 작업 결과를 잃지 않기 위해(때문에), 스크립트를 보존해 주세요.

튜토리얼 13.17 - 포획 볼륨, 파트 II: TOUCH 및 시간

에디터내에 볼륨을 가집니다, 여기서, 볼륨의 몇개의보다 흥미로운 일면에 발을 디뎌 가야 합니다. 특히, 볼륨에 터치된 시간 ; 곧바로 그것을 선언하면, 보다 용이하게 되는 것이 증명됩니다. 이것은, 매우 붐비어 들어간 문제가 될 가능성이 있습니다만, 다행히, 이벤트 함수, Touch 의 이용이 인정되고 있습니다.

이 Touch Event 는, 획득된 상태의 변경을 확인하는 타이머에 방아쇠를 겁니다. 타이머는, 매틱은 체크할 필요가 없지만, 이라고 해도, 어느 시간 간격으로 체크할 필요가 있는 것을 계속 감시하려면 매우 편리합니다. 그것들에는, 단지, 그것이 재귀적인가 어떤가의 float 치, 및, 이 시간 간격으로 실행하려고 하는 콜백 함수를 건네줍니다. 사양에서는, 이 타이머에 대해서는 이미 기술되고 있기 때문에, 처리에도 착수합시다.

*1. * ConTEXT 및 CaptureVolume.uc 스크립트를 오픈해 주세요.

*2. * 우선, Touch 이벤트를 정의할 필요가 있습니다.

  event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal )
  {
  }
  

*3. * 많은 파생 함수에 대해, 의존관계(dependencies)나 예측된 값의 대입의 문제를 막기 위해서(때문에), 그 슈퍼 버젼을 호출하는 것은 유익합니다.

  Super.Touch(Other, OtherComp, HitLocation, Hitnormal);
  

*4. * 볼륨이 이용 가능하게 되었을 때에는, 함수로 결합된 타이머를 실행하고 싶다고 생각하고 있습니다. 여기에서는, 시간 간격을 0.5 초로 설정해, 정지할 때까지 0.5 초 마다 타이머의 실행을 허가하기 위한 제 2 의 파라미터로서 true 도 건네줍니다.

  if (bEnabled) // 유효화 되었을 경우는,... 열중해 처리한다.
     SetTimer(0.5f, true, 'tCheckCapture');
  

*5. * Touch 이벤트로부터 이동해, 터치 실행의 문제를 취급하지 않으면 안됩니다. 도움이 되는 함수는, 그것 자신이 둘도 없는 것으로 있는 것으로 증명되기 때문에, 이것에 착수합시다. 함수 및 그 인수를 정의하고 있습니다.

  function int GetTouchingUTPawns(out array<UTPawn> redTouching, out array<UTPawn> blueTouching)
  {
  }
  

*6. * Count 변수는, 포괄적이 되어, 그것은 볼륨내의 팀, UTPawn 의 수의 양쪽 모두입니다. 그것은, 최종적으로 리턴 됩니다. 즉시, 반복에 대해서 P 가 이용됩니다.

  local int Count;
  local UTPawn P;
  
  Count = 0;
  

*7. * 필요한 폰에 대해 반복 처리를 실시하는 것은, 심한 것에 들릴지도 모릅니다만 그만큼이 아닙니다. UnrealScript 는, 많은 매우 편리한 이테레이타를 가지고 있습니다만, 목적이나 생각 없음으로 이것들을 사용하지 않게 확인해 주세요. 특히 tick(틱) 함수내에서는 매우 코스트가 걸릴 가능성이 있습니다.

  foreach self.TouchingActors(class'UTPawn', P)
  {
  }
  

*8. * 다음의 것으로 이동하지 않는 경우는, Pawn 가 살아 있는 것을 확인하고 싶습니다.

  if (P == None || P.health <= 0 || P.Controller.IsDead() || P.GetTeam() == None)
  {
     continue;
  }
  

*9. * 살아 있는 것이 확인되면(자), 적절한 팀에 그것들을 추가할 필요가 있습니다.

  if (P.GetTeam(). TeamIndex == RED_TEAM)
  {
     redTouching.AddItem(P);
     Count++;
  }
  else
  {
     blueTouching.AddItem(P);
     Count++;
  }
  

*10. * 마지막에 Count 를 리턴 합니다.

  return Count;
  

*11. * 작업 결과를 잃지 않게 스크립트를 보존해 주세요.

튜토리얼 13.18 - 포획 볼륨, 파트 III: 포획 된 상태

볼륨의 대부분을 면밀하게 계획했으므로, 그 비전을 1 개로 정리합니다. 작성한 볼륨은, 유용한 목적이나 그 외의 것을 제공하는 흥미로운 함수를 몇개인가 가집니다만, 더욱 넘지 않으면 안 되는 허들이 아직 몇개인가 있습니다. 다음에, 이 상황으로 Boolean 의 true 를 리턴 할 예정의 CheckBeginCapture 루틴에 발을 디딥니다.

*1. * ConTEXT 및 CaptureVolume.uc 스크립트를 오픈해 주세요.

*2. * 통상 대로, 인터페이스마다, 함수를 정의할 필요가 있습니다.

  simulated function bool CheckBeginCapture()
  {
  }
  

*3. * 빨강과 파랑의 폰을 보관 유지하기 위해서, 2 개의 배열이 필요해, 계산은 이것들을 사용해 행해집니다.

  local array<UTPawn> redTouching;
  local array<UTPawn> blueTouching;
  

*4. * 최종 테스트를 간략화하기 위해(때문에), 포획 하고 있는 팀의 사이즈의 확인을 계속하기 위해서(때문에) 사용되는 카운터를 생성해 주세요.

  local int Count;
  

*5. * 즉시 배열을 묻기 (위해)때문에, GetTouchingUTPawns 유틸리티 함수를 사용할 수 있습니다.

  GetTouchingUTPawns(redTouching, blueTouching);
  

*6. * 만약, 이 볼륨안에 플레이어가 없으면, 사이즈를 확인해, 타이머를 클리어 하고 나서 리턴 해 주세요.

  if (blueTouching.length == 0 && redTouching.length == 0)
  {
     ClearTimer('tCheckCapture', self);
            return false;
  }
  

*7. * 팀이 1 개 이상 존재하는 경우는, CAP_UNSTABLE 방아쇠를 보내, 리턴 할 필요가 있습니다.

  else if (! (blueTouching.length == 0 ^^ redTouching.length == 0))
  {
     UpdateEvents(CAP_UNSTABLE);
     return false;
  }
  

*8. * 그러한 2 개의 테스트가 종료하면(자), 양쪽 모두로 없고, 빨강 또는 파랑의 팀만이 존재하고 있는 것 를 확인해 휴지할 수 있습니다. 우선 빨강으로부터 집중적으로 실시합니다 …

  if (redTouching.length > 0)
  {
  }
  

*a. * 후에 포인트의 집계를 실시할 때에 사용하는 배열에, 볼륨에 터치하고 있는 플레이어를 카피해 주세요.

  CapturingMembers = redTouching;
  

*b. * 여기서 player의 수를 취득해 주세요.

  Count = redTouching.length;
  

*c. * 빨강 팀에 CapturingTeamID 를 설정해 주세요.

  CapturingTeamID = RED_TEAM;
  

*9. * 또, 여기서, 파랑 팀에 대해서, 같은 처리를 반복해 주세요.

  else
  {
     CapturingMembers = blueTouching;
     Count = blueTouching.length;
     CapturingTeamID = BLUE_TEAM;
  }
  

*10. * 이 볼륨의 포획이 가능한 것을 확인해 포획 하는 팀에서 있어, 포획 되는 팀은 아닌 일을 확인하기 위해서 카운트를 테스트해 주세요. 이 2 개째의 테스트는, 스코아가 증가될 때에, 같은 팀에 의해 포획되어 있지 않은 볼륨을 확인하기 위한의 것입니다.

  if ((iMinimumPlayersForCapture <= Count) && (CapturingTeamID ! = CapturedTeamID))
      return true;
  else
     return false;
  

*11. * 작업 결과를 잃지 않게 스크립트를 보존해 주세요.

이것으로, 클래스의 대부분은 완성되었습니다. 아주 조금 함수를 설명이 남아 있습니다만, 완성해, 게임의 정말로 훌륭한 이벤트를 보기까지는 그만큼 길게 걸리지 않습니다.

튜토리얼 13.19 - 포획 볼륨, 파트 IV: 타이머 함수

다음의 스텝은 타이머 함수가 됩니다. 이 함수는 0.5 초 마다 실행되고 있기 (위해)때문에, 리얼타임의 게임 개발에서는 이 함수는 효율적이 아니게 되어, 꽤 쓰기가 나쁜 것이 될 가능성이 있는 것은, 명심해 두면 좋을 것입니다.

*1. * tCheckCapture 함수를 정의해 주세요.

  simulated function tCheckCapture()
  {
  }
  

*2. * 후에 실시할 예정의 반복을 돕는 2 개의 변수를 작성할 필요가 있습니다.

  local UTPawn P;
  local UTPlayerReplicationInfo ScorerPRI;
  

*3. * TimeCapturing 의 값이 부이면, 값을 클리어 해 주세요.

  if (TimeCapturing < 0)
     TimeCapturing = 0;
  

*4. * 여기서, 무언가에 방아쇠를 걸칠 필요가 있는지를 확인하기 위해서, 작성한 CheckBeginCapture 함수를 호출합니다. 그렇지 않으면, 상당한 수의 것을 클리어 할 필요가 있습니다. 종료했을 때에는 타이머를 클리어 할 필요가 있는 것에 유의해 주세요.

  if (! CheckBeginCapture())
  {
     CapturingTeamID = NO_TEAM;
        TimeCapturing = 0;
     ClearTimer('tCheckCapture', self);
     return;
  }
  

*5. * 만약, 포획을 개시한다고 하면(자), 여기서, 포획에 소비한 시간을 갱신해야 합니다. 이 시간은, 완전하게 직감적은 아닙니다만, EpicGames 사의 덕분에, 이 처리를 도와 주는 함수가 있습니다.

  TimeCapturing += GetTimerRate('tCheckCapture', self);
  

*6. * 이 신규의 시간치를 이 볼륨에 대한 설정치라고 확인할 수가 있습니다. 만약, 이것이 큰가 동일하면, 포획에 진행될 필요가 있습니다.

  if (TimeCapturing >= iTimeToCapture)
  {
  }
  

*a. * 포획중의 팀이 파랑인 경우는, 파랑을 포획 하는 이벤트를 보냅니다, 또, 빨강의 경우도 같습니다.

  UpdateEvents(CapturingTeamID == BLUE_TEAM - CAP_BLUECONT : CAP_REDCONT);
  

*b. * 포획중의 플레이어에 대한 스코아를 늘려 주세요. 여기가, 이전 정의한 2 개의 변수를 사용하는 장소입니다.

  foreach CapturingMembers(P)
  {
     ScorerPRI = UTPlayerReplicationInfo(P.Controller.PlayerReplicationInfo);
     ScorerPRI.Score += (iPointReward);
     ScorerPRI.bForceNetUpdate = TRUE;
  }
  

*c. * Captured Team ID 를 갱신해 주세요.

  CapturedTeamID = CapturingTeamID;
  

*d. * 그리고 마지막으로, 포획 시간 카운터와 같이 포획중의 팀 ID 를 클리어 해, 타이머의 클리어 실행이 계속됩니다. 이 함수를 종료시에 재소환 하지 않기 때문에, 이것을 실시하는 것은 중요합니다.

  CapturingTeamID = NO_TEAM;
  TimeCapturing = 0;
  ClearTimer('tCheckCapture', self);
  

*7. * 스크립트를 보존해 주세요.

튜토리얼 13.20 - 포획 볼륨, 파트 V: 이벤트의 갱신

순서 이벤트에 방아쇠를 걸고 싶을 때로는, 작성한 볼륨의 모든 순서 이벤트 전체를 대상으로 반복해, 적절한 플래그를 배웅할 필요가 있습니다. 이 함수는, 루프를 포함해, 이 처리를 취급합니다.

*1. * ConTEXT 및 CaptureVolume.uc 스크립트를 오픈해 주세요.

*2. * 함수를 정의해, 간단하게 사용하기 위해(때문에), 또 반복을 위해서(때문에) 2 개의 변수를 선언해 주세요.

  function UpdateEvents(int flag)
  {
  }
  

*3. * For 루프 및 이테레이타와 함께 사용하기 위한 SeqEvent_VolumeCaptured 오브젝트 참조로 사용되는 로컬 Int 변수를 선언해 주세요.

  local int i;
  local SeqEvent_VolumeCaptured CaptureEvent;
  

*4. * 모든 GeneratedEvents 를 대상으로 한 루프를 개시해 주세요. 이것은, kismet(운명) 및 다른 이벤트 순서내에서 작업할 때에, 플레이에 사용되는 배열입니다.

  for (i = 0; i < GeneratedEvents.Length; i++)
  {
  }
  

*5. * 루프의 내부에서, 생성된 이벤트를 VolumeCaptured 순서 이벤트에 캐스트 해 주세요, 또, 캐스트가 동작하면(자), 적절한 플래그를 보내 주세요. 이 함수 Notify_VolumeCaptured 에서는, 인터페이스를 사용합니다.

  CaptureEvent = SeqEvent_VolumeCaptured(GeneratedEvents[i]);
  if (CaptureEvent ! = None)
  {
     CaptureEvent.Notify_VolumeCaptured(flag);
  }
  

*6. * 스크립트를 보존해 주세요.

튜토리얼 13.21 - 포획 볼륨, 파트 VI: 볼륨의 타글을 오프 해, DEFAULTPROPERTIES 블록을 갱신

이 클래스에 도착해, 1 개의 함수가 남아 있는 것만으로, 그것은, 타글 함수입니다.

*1. * ConTEXT 및 CaptureVolume.uc 스크립트를 오픈해 주세요.

*2. * 타글 이벤트에 아탓치 했을 때에는 OnToggle 가 불려 갑니다. Lights 및 다른 먼지가 실행하도록(듯이), 이것은 동작합니다.

  simulated function OnToggle(SeqAct_Toggle action)
  {
  }
  

*3. * 그것은, Sequence Action 를 받아들여 그것들에 대한 임펄스(inpulse)를 체크합니다. 인덱스는 0, 1 및 2 이며, 각각 On , Off 및 Toggle 에 관련하고 있습니다.

  if (action.InputLinks[0]. bHasImpulse)
     bEnabled = TRUE;
  else if (action.InputLinks[1]. bHasImpulse)
     bEnabled = FALSE;
  else if (action.InputLinks[2]. bHasImpulse)
     bEnabled = ! bEnabled;
  

*4. * 그리고, 네트워크의 갱신을 강제합니다.

  ForceNetRelevant();
  

*5. * 여기서, 금방 실장을 실시할 생각의 Capture 순서 Event 를 포함하기 위해서(때문에), 작성한 디폴트 프롭퍼티 블록은 갱신할 필요가 있습니다.

  defaultproperties
  {
     // 주로 UEd 를 설정.
     BrushColor = (B=128, G=255, R=128, A=255)
     bColored = True
     bStatic = false
  
     // 볼륨에 대한 디폴트치
     iMinimumPlayersForCapture = 1
     CapturedTeamID = NO_TEAM
     iTimeToCapture = 3
     iPointReward = 5
  
     // 출력 이벤트에 아탓치
     SupportedEvents(0) =Class'UTBook.SeqEvent_VolumeCaptured'
  }
  

*6. * 스크립트를 보존해 주세요.

튜토리얼 13.22 - 포획 볼륨, 파트 VII: 순서 이벤트의 인터페이스 및 실장

Volume 를 완성시켰으므로, Captured Volume Sequence Event 에 관한 코드를 기술할 필요가 있습니다. 이것에는 조금 주물러들 당할지도 모릅니다만, 그만큼 심해 지지 않게 노력합시다. 이것은, 충분히 단순하므로, 시작해 봅시다.

*1. * ConTEXT 를 오픈해 UnrealScript 하이 라이터를 사용하는 ICaptureSequenceEvent.uc 라는 이름 전의 신규 파일을 작성해 주세요.

*2. * 순서 이벤트에 대한 인터페이스에서는 1 개의 함수를 선언하는 것만이 필요합니다. 그 선언을 실시해 주세요.

  interface ICaptureSequenceEvent;
  
  function Notify_VolumeCaptured(int outputIndex);
  

*3. * 스크립트를 보존해 주세요.

*4. * 함수의 실장은, 이전 실시한 것과 같은 작업에 따라 행해집니다. UnrealScript 하이 라이터를 사용하는 SeqEvent_VolumeCaptured.uc 라고 명명한 신규 파일을 작성합니다.

*5. * SequenceEvent 를 확장해, 선언한 인터페이스를 실장해, 클래스를 정의해 주세요.

  class SeqEvent_VolumeCaptured extends SequenceEvent DependsOn(CaptureVolume) implements(ICaptureSequenceEvent);
  

*6. * Notify_VolumeCaptured()의 함수를 정의해 주세요.

  function Notify_VolumeCaptured(int outputIndex)
  {
  }
  

*7. * Int 의 로컬 동적 배열을 선언해, 함수에 보낸 파라미터의 값을 출력하는 log 문을 작성해 주세요.

  local array<int> ActivateIndices;
  `log("Notify_VolumeCaptured has been executed" @ outputIndex, , 'UTBook');
  

*8. * 걱정하고 있는 것 (일)것은 다만 1 개(살)이므로, 1 회에 1 개의 방아쇠를 보낼 뿐입니다.

  ActivateIndices[0] = outputIndex;
  if (CheckActivate(Originator, None, false, ActivateIndices))
  {
     `log("Notify_VolumeCaptured has been activated", , 'UTBook');
  }
  

*9. * 디폴트 프롭퍼티 블록에 발을 디디면(자), 고려할 만한다, 필요에 따라서 접속해요 두일까 링크를 갖고 있습니다.

  defaultproperties
  {
  }
  

*10. * 이러한 Links 는, 중대해, 그러한 인덱스는 보내지는 실제의 값입니다.

  OutputLinks(0) = (LinkDesc="Red Capture")
  OutputLinks(1) = (LinkDesc="Blue Capture")
  OutputLinks(2) = (LinkDesc="Unstable")
  

*11. * kismet(운명) 요소에 대해서, 이름, 카테고리의 대입 및 최대 방아쇠 카운트의 디폴트의 2 개의 조정을 실시해 주세요.

  ObjName = "Volume Captured"
  ObjCategory = "Objective"
  MaxTriggerCount = 0 // 디폴트는 무한하게 방아쇠를 계속 겁니다.
  

*12. * 마지막으로, 플레이어는 예외적으로, 이 이벤트에는 방아쇠를 걸지 않는 것을 확인하고 싶습니다.

  bPlayerOnly = False
  

*13. * 스크립트를 보존해 주세요.

튜토리얼 13.23 - CAPTUREVOLUME 의 배치와 동작의 확인

여기서, 볼륨 및 그 순서 이벤트를 완성했으므로, 자기 자신의 mod 에 대해서도 작성하는 방법의 명확한 예를 보았습니다. 이 튜토리얼은, 다른 언어에 대해 개개의 루프로 실행하도록(듯이) 동작하는 것을 나타내, 이테레이타를 매우 자주(잘) 가리키고 있습니다.

*1. * 한번 더, Editor 및 컴파일 된 코드 패키지를 시작해 주세요. 에디터를 시작해, 새로운 맵을 작성해 주세요.

*2. * Actor Classes 브라우저를 오픈해 주세요.

*3. * File > Open 로 이동하고 나서, 컴파일 끝난 . u 파일이 격납된 Scripts 디렉토리를 지정해 주세요.

*4. * 한 번, 패키지가 로드 되면, 그림 13.55 로 볼 수 있도록(듯이) Actor > Brush > Volume 아래에서, 신규 볼륨을 볼 수가 있을 것입니다. 그것을 선택해, 맵을 수정해 주세요. 오른쪽 클릭을 실시할 수가 있을 것으로, “Add Compass Here”메뉴 옵션이 제공됩니다.


그림 13.55 - CaptureVolume 를 가지는 Actor Class Browser

*5. * 맵중에 볼륨의 1 개를 배치해 주세요. 곧바로 kismet(운명) 순서를 설정하려고 생각합니다. 그림 13.56 에서는, 신규 볼륨에 대해서 지정된 옵션을, Capture 부범위로 표시해 볼 수가 있습니다.


그림 13.56 - 맙파에 대한 새로운 설정

*6. * 다음의 스텝은 kismet(운명)를 오픈해, 새로운 순서 이벤트를 보는 것입니다. 볼륨의 1 개를 선택해, kismet(운명) 에디터를 오픈해 오른쪽 클릭해 주세요. context menu안에는, 선택한 포획 볼륨에 대한 신규 이벤트를 작성하는 옵션이 있을 것으로, 그 아래에 작성한 순서 이벤트 Volume Captured 가 있을 것입니다. 제시되는 요소는 이하와 같은 것입니다 :


그림 13.57 - 볼륨의 Kismet(운명)

*7. * 먼저 진행되어, 여기서 순서의 생성이 가능하므로, 동작중의 이 요소를 볼 수가 있습니다. 이번 작성의 것은 이하와 같이 됩니다.


그림 13.58 - Custom Kismet Event 를 사용한 Kismet Demo

*8. * 이것이 완료하면(자), 맵을 보존하고 나서, 개시할 수가 있습니다. 로그 파일은, 이하와 같은 내용이 될 것입니다 :

  Log: Family Asset Package Loaded: CH_Corrupt_Arms_SF
  Log: CONSTRUCTIONING: LoadFamilyAsset (LIAM) Took: -0. 01 secs
  ScriptLog: Finished creating custom characters in 1.8737 seconds
  Error: Can't start an online game that hasn't been created
  ScriptLog: START MATCH
  ScriptLog:   Num Matches Played: 0
  UTBook: Notify_VolumeCaptured has been activated
  Log: Kismet: Red Capture
  UTBook: Notify_VolumeCaptured has been activated
  UTBook: Notify_VolumeCaptured has been activated
  Log: Kismet: Blue Capture
  UTBook: Notify_VolumeCaptured has been activated
  Log: Kismet: Red Capture
  Error: Can't end an online game that hasn't been created
  Log: Closing by request
  Log: appRequestExit(0)
  

*9. * 게임중에 포획 된 볼륨으로, 기록 게시판이 갱신된 것을 볼 수가 있습니다.


그림 13.59 - 40 초를 나타내는 기록 게시판은 좌측, 95 초를 나타내는 것은 우측에 있습니다.

이것으로, 본튜토리얼은 완료했습니다. 간단하게 되돌아 봐, 몇개의 중요한 점을 강조합니다.

  • 개발하려고 하는 임의의 클래스에 대해서 인터페이스를 작성할 수 있습니다.
  • 볼륨은, 명령을 실행하기 위해서 생성하거나 구성하거나 하는 것은 어렵지는 않습니다.
  • Kismet(운명)는, 실제는 Sequence Event 를 통해 작성되어 매우 간단한 인터페이스를 가지고 있습니다
  • 이테레이타는, 많은 시간과 노력을 절약할 수 있습니다만, 어디에서 그것들을 호출할까에 의해 매우 비용이 걸리는 경우가 있습니다.
  • 만약, 올바르게 어프로치 했다면, 무엇인가를 계획하면, 개발의 프로세스를 간단하게 해, 스피드업 가능하게 하는 것을 지원합니다.

UT3 내의 인터페이스

이하의 일람표는, 모든 미테스트의 Unreal Tournament 3 내의 관련하는 인터페이스를 포함하기 때문에, 편리할지도 모릅니다. 다른 것도 있습니다만, 게임내에서, 모두 native(네이티브) 또는 native(네이티브) 프로세스에 관련하고 있어, 본서의 범위를 넘고 있습니다.

IQueryHandler

struct KeyValuePair

struct WebAdminQuery

function init(WebAdmin)

function cleanup()

function bool handleQuery(WebAdminQuery)

function bool unhandledQuery(WebAdminQuery)

function registerMenuItems(WebAdminMenu)

ISession

function string getId()

function reset()

function Object getObject(string)

function putObject(string, Object)

function removeObject(string)

function string getString(string, optional string)

function putString(string, string)

function removeString(string)

ISessionHandler

function ISession create()

function ISession get(string)

function bool destroy(ISession)

function destroyAll()

IWebAdminAuth

function init(WorldInfo)

function cleanup()

function IWebAdminUser authenticate(string, string, out string)

function bool logout(IWebAdminUser)

function bool validate(string, string, out string)

function bool validateUser(IWebAdminUser, out string)

IWebAdminUser

struct MessageEntry

function string getUsername()

function bool canPerform(string)

function PlayerController getPC()

function messageHistory(out array, optional int)

OnlineAccountInterface

function bool CreateOnlineAccount(string, string, string, optional string)

delegate OnCreateOnlineAccountCompleted(EOnlineAccountCreateStatus)

function AddCreateOnlineAccountCompletedDelegate(delegate)

function ClearCreateOnlineAccountCompletedDelegate(delegate)

function bool CreateLocalAccount(string, optional string)

function bool RenameLocalAccount(string, string, optional string)

function bool DeleteLocalAccount(string, optional string)

function bool GetLocalAccountNames(out array)

function bool IsKeyValid()

function bool SaveKey(string)

OnlineContentInterface

delegate OnContentChange()

function AddContentChangeDelegate(delegate, optional byte)

function ClearContentChangeDelegate(delegate, optional byte)

delegate OnReadContentComplete(bool)

function AddReadContentComplete(byte, delegate)

function ClearReadContentComplete(byte, delegate)

function bool ReadContentList(byte)

function EOnlineEnumerationReadState GetContentList(byte, out array)

function bool QueryAvailableDownloads(byte)

delegate OnQueryAvailableDownloadsComplete(bool)

function AddQueryAvailableDownloadsComplete(byte, delegate)

function ClearQueryAvailableDownloadsComplete(byte, delegate)

function GetAvailableDownloadCounts(byte, out int, out int)

OnlineGameInterface

function bool CreateOnlineGame(byte, OnlineGameSettings)

delegate OnCreateOnlineGameComplete(bool)

function AddCreateOnlineGameCompleteDelegate(delegate)

function ClearCreateOnlineGameCompleteDelegate(delegate)

function bool UpdateOnlineGame(OnlineGameSettings)

function OnlineGameSettings GetGameSettings()

function bool DestroyOnlineGame()

delegate OnDestroyOnlineGameComplete(bool)

function AddDestroyOnlineGameCompleteDelegate(delegate)

function ClearDestroyOnlineGameCompleteDelegate(delegate)

function bool FindOnlineGames(byte, OnlineGameSearch)

delegate OnFindOnlineGamesComplete(bool)

function AddFindOnlineGamesCompleteDelegate(delegate)

function ClearFindOnlineGamesCompleteDelegate(delegate)

function bool CancelFindOnlineGames()

delegate OnCancelFindOnlineGamesComplete(bool)

function AddCancelFindOnlineGamesCompleteDelegate(delegate)

function ClearCancelFindOnlineGamesCompleteDelegate(delegate)

function OnlineGameSearch GetGameSearch()

function bool FreeSearchResults(optional OnlineGameSearch)

function bool JoinOnlineGame(byte, const out OnlineGameSearchResult)

delegate OnJoinOnlineGameComplete(bool)

function AddJoinOnlineGameCompleteDelegate(delegate)

function ClearJoinOnlineGameCompleteDelegate(delegate)

function bool GetResolvedConnectString(out string)

function bool RegisterPlayer(UniqueNetId, bool)

delegate OnRegisterPlayerComplete(bool)

function AddRegisterPlayerCompleteDelegate(delegate)

function ClearRegisterPlayerCompleteDelegate(delegate)

function bool UnregisterPlayer(UniqueNetId)

delegate OnUnregisterPlayerComplete(bool)

function AddUnregisterPlayerCompleteDelegate(delegate)

function ClearUnregisterPlayerCompleteDelegate(delegate)

function bool StartOnlineGame()

delegate OnStartOnlineGameComplete(bool)

function AddStartOnlineGameCompleteDelegate(delegate)

function ClearStartOnlineGameCompleteDelegate(delegate)

function bool EndOnlineGame()

delegate OnEndOnlineGameComplete(bool)

function AddEndOnlineGameCompleteDelegate(delegate)

function ClearEndOnlineGameCompleteDelegate(delegate)

function EOnlineGameState GetOnlineGameState()

function bool RegisterForArbitration()

delegate OnArbitrationRegistrationComplete(bool)

function AddArbitrationRegistrationCompleteDelegate(delegate)

function ClearArbitrationRegistrationCompleteDelegate(delegate)

function array GetArbitratedPlayers()

function AddGameInviteAcceptedDelegate(byte, delegate)

function ClearGameInviteAcceptedDelegate(byte, delegate)

delegate OnGameInviteAccepted(OnlineGameSettings)

function bool AcceptGameInvite(byte)

function bool RecalculateSkillRating(const out array)

OnlineNewsInterface

function bool ReadGameNews(byte)

delegate OnReadGameNewsCompleted(bool)

function AddReadGameNewsCompletedDelegate(delegate)

function ClearReadGameNewsCompletedDelegate(delegate)

function string GetGameNews(byte)

function bool ReadContentAnnouncements(byte)

delegate OnReadContentAnnouncementsCompleted(bool)

function AddReadContentAnnouncementsCompletedDelegate(delegate)

function ClearReadContentAnnouncementsCompletedDelegate(delegate)

function string GetContentAnnouncements(byte)

OnlinePlayerInterface

delegate OnLoginChange()

delegate OnLoginCancelled()

delegate OnMutingChange()

delegate OnFriendsChange()

function bool ShowLoginUI(optional bool)

function bool Login(byte, string, string, optional bool)

function bool AutoLogin()

delegate OnLoginFailed(byte, EOnlineServerConnectionStatus)

function AddLoginFailedDelegate(byte, delegate)

function ClearLoginFailedDelegate(byte, delegate)

function bool Logout(byte)

delegate OnLogoutCompleted(bool)

function AddLogoutCompletedDelegate(byte, delegate)

function ClearLogoutCompletedDelegate(byte, delegate)

function ELoginStatus GetLoginStatus(byte)

function bool GetUniquePlayerId(byte, out UniqueNetId)

function string GetPlayerNickname(byte)

function EFeaturePrivilegeLevel CanPlayOnline(byte)

function EFeaturePrivilegeLevel CanCommunicate(byte)

function EFeaturePrivilegeLevel CanDownloadUserContent(byte)

function EFeaturePrivilegeLevel CanPurchaseContent(byte)

function EFeaturePrivilegeLevel CanViewPlayerProfiles(byte)

function EFeaturePrivilegeLevel CanShowPresenceInformation(byte)

function bool IsFriend(byte, UniqueNetId)

function bool AreAnyFriends(byte, out array)

function bool IsMuted(byte, UniqueNetId)

function bool ShowFriendsUI(byte)

function AddLoginChangeDelegate(delegate, optional byte)

function ClearLoginChangeDelegate(delegate, optional byte)

function AddLoginCancelledDelegate(delegate)

function ClearLoginCancelledDelegate(delegate)

function AddMutingChangeDelegate(delegate)

function ClearMutingChangeDelegate(delegate)

function AddFriendsChangeDelegate(byte, delegate)

function ClearFriendsChangeDelegate(byte, delegate)

function bool ReadProfileSettings(byte, OnlineProfileSettings)

delegate OnReadProfileSettingsComplete(bool)

function AddReadProfileSettingsCompleteDelegate(byte, delegate)

function ClearReadProfileSettingsCompleteDelegate(byte, delegate)

function OnlineProfileSettings GetProfileSettings(byte)

function bool WriteProfileSettings(byte, OnlineProfileSettings)

delegate OnWriteProfileSettingsComplete(bool)

function AddWriteProfileSettingsCompleteDelegate(byte, delegate)

function ClearWriteProfileSettingsCompleteDelegate(byte, delegate)

function bool ReadFriendsList(byte, optional int, optional int)

delegate OnReadFriendsComplete(bool)

function AddReadFriendsCompleteDelegate(byte, delegate)

function ClearReadFriendsCompleteDelegate(byte, delegate)

function EOnlineEnumerationReadState GetFriendsList(byte, out array, optional int, optional int)

function SetOnlineStatus(byte, int, const out array, const out array)

function bool ShowKeyboardUI(byte, string, string, optional bool, optional bool, optional string, optional int)

function AddKeyboardInputDoneDelegate(delegate)

function ClearKeyboardInputDoneDelegate(delegate)

function string GetKeyboardInputResults(out byte)

delegate OnKeyboardInputComplete(bool)

function bool AddFriend(byte, UniqueNetId, optional string)

function bool AddFriendByName(byte, string, optional string)

delegate OnAddFriendByNameComplete(bool)

function AddAddFriendByNameCompleteDelegate(byte, delegate)

function ClearAddFriendByNameCompleteDelegate(byte, delegate)

function bool AcceptFriendInvite(byte, UniqueNetId)

function bool DenyFriendInvite(byte, UniqueNetId)

function bool RemoveFriend(byte, UniqueNetId)

delegate OnFriendInviteReceived(byte, UniqueNetId, string, string)

function AddFriendInviteReceivedDelegate(byte, delegate)

function ClearFriendInviteReceivedDelegate(byte, delegate)

function bool SendMessageToFriend(byte, UniqueNetId, string)

function bool SendGameInviteToFriend(byte, UniqueNetId, optional string)

function bool SendGameInviteToFriends(byte, array, optional string)

delegate OnReceivedGameInvite(byte, string)

function AddReceivedGameInviteDelegate(byte, delegate)

function ClearReceivedGameInviteDelegate(byte, delegate)

function bool JoinFriendGame(byte, UniqueNetId)

delegate OnJoinFriendGameComplete(bool)

function AddJoinFriendGameCompleteDelegate(delegate)

function ClearJoinFriendGameCompleteDelegate(delegate)

function GetFriendMessages(byte, out array)

delegate OnFriendMessageReceived(byte, UniqueNetId, string, string)

function AddFriendMessageReceivedDelegate(byte, delegate)

function ClearFriendMessageReceivedDelegate(byte, delegate)

function bool DeleteMessage(byte, int)

OnlinePlayerInterfaceEx

function bool ShowFeedbackUI(byte, UniqueNetId)

function bool ShowGamerCardUI(byte, UniqueNetId)

function bool ShowMessagesUI(byte)

function bool ShowAchievementsUI(byte)

function bool ShowInviteUI(byte, optional string)

function bool ShowContentMarketplaceUI(byte)

function bool ShowMembershipMarketplaceUI(byte)

function bool ShowDeviceSelectionUI(byte, int, bool)

function AddDeviceSelectionDoneDelegate(byte, delegate)

function ClearDeviceSelectionDoneDelegate(byte, delegate)

function int GetDeviceSelectionResults(byte, out string)

delegate OnDeviceSelectionComplete(bool)

function bool IsDeviceValid(int)

function bool UnlockAchievement(byte, int)

function AddUnlockAchievementCompleteDelegate(byte, delegate)

function ClearUnlockAchievementCompleteDelegate(byte, delegate)

delegate OnUnlockAchievementComplete(bool)

function bool UnlockGamerPicture(byte, int)

delegate OnProfileDataChanged()

function AddProfileDataChangedDelegate(byte, delegate)

function ClearProfileDataChangedDelegate(byte, delegate)

function bool ShowFriendsInviteUI(byte, UniqueNetId)

function bool ShowPlayersUI(byte)

OnlineStatsInterface

function bool ReadOnlineStats(const out array, OnlineStatsRead)

function bool ReadOnlineStatsForFriends(byte, OnlineStatsRead)

function bool ReadOnlineStatsByRank(OnlineStatsRead, optional int, optional int)

function bool ReadOnlineStatsByRankAroundPlayer(byte, OnlineStatsRead, optional int)

function AddReadOnlineStatsCompleteDelegate(delegate)

function ClearReadOnlineStatsCompleteDelegate(delegate)

delegate OnReadOnlineStatsComplete(bool)

function FreeStats(OnlineStatsRead)

function bool WriteOnlineStats(UniqueNetId, OnlineStatsWrite)

function bool FlushOnlineStats()

delegate OnFlushOnlineStatsComplete(bool)

function AddFlushOnlineStatsCompleteDelegate(delegate)

function ClearFlushOnlineStatsCompleteDelegate(delegate)

function bool WriteOnlinePlayerScores(const out array)

function string GetHostStatGuid()

function bool RegisterHostStatGuid(const out string)

delegate OnRegisterHostStatGuidComplete(bool)

function AddRegisterHostStatGuidCompleteDelegate(delegate)

function ClearRegisterHostStatGuidCompleteDelegateDelegate(delegate)

function string GetClientStatGuid()

function bool RegisterStatGuid(UniqueNetId, const out string)

OnlineSystemInterface

function bool HasLinkConnection();

delegate OnLinkStatusChange(bool)

function AddLinkStatusChangeDelegate(delegate)

function ClearLinkStatusChangeDelegate(delegate)

delegate OnExternalUIChange(bool)

function AddExternalUIChangeDelegate(delegate)

function ClearExternalUIChangeDelegate(delegate)

function ENetworkNotificationPosition GetNetworkNotificationPosition()

function SetNetworkNotificationPosition(ENetworkNotificationPosition)

delegate OnControllerChange(int, bool)

function AddControllerChangeDelegate(delegate)

function ClearControllerChangeDelegate(delegate)

function bool IsControllerConnected(int)

delegate OnConnectionStatusChange(EOnlineServerConnectionStatus)

function AddConnectionStatusChangeDelegate(delegate)

function ClearConnectionStatusChangeDelegate(delegate)

function ENATType GetNATType()

delegate OnStorageDeviceChange()

function AddStorageDeviceChangeDelegate(delegate)

function ClearStorageDeviceChangeDelegate(delegate)

OnlineVoiceInterface

function bool RegisterLocalTalker(byte)

function bool UnregisterLocalTalker(byte)

function bool RegisterRemoteTalker(UniqueNetId)

function bool UnregisterRemoteTalker(UniqueNetId)

function bool IsLocalPlayerTalking(byte)

function bool IsRemotePlayerTalking(UniqueNetId)

function bool IsHeadsetPresent(byte)

function bool SetRemoteTalkerPriority(byte, UniqueNetId, int)

function bool MuteRemoteTalker(byte, UniqueNetId)

function bool UnmuteRemoteTalker(byte, UniqueNetId)

delegate OnPlayerTalking(UniqueNetId)

function AddPlayerTalkingDelegate(delegate)

function ClearPlayerTalkingDelegate(delegate)

function StartNetworkedVoice(byte)

function StopNetworkedVoice(byte)

function bool StartSpeechRecognition(byte)

function bool StopSpeechRecognition(byte)

function bool GetRecognitionResults(byte, out array)

delegate OnRecognitionComplete()

function AddRecognitionCompleteDelegate(byte, delegate)

function ClearRecognitionCompleteDelegate(byte, delegate)

function bool SelectVocabulary(byte, int)

function bool SetSpeechRecognitionObject(byte, SpeechRecognition)

function bool MuteAll(byte, bool)

function bool UnmuteAll(byte)

UIDataStoreSubscriber

native function SetDataStoreBinding(string, optional int)

native function string GetDataStoreBinding(optional int) const

native function bool RefreshSubscriberValue(optional int)

native function NotifyDataStoreValueUpdated(UIDataStore, bool, name, UIDataProvider, int)

native function GetBoundDataStores(out array)

native function ClearBoundDataStores()

UIDataStorePublisher extends UIDataStoreSubscriber

native function bool SaveSubscriberValue(out array, optional int)

UIEventContainer

native final function GetUIEvents(out array, optional class)

native final function bool AddSequenceObject(SequenceObject, optional bool)

native final function RemoveSequenceObject(SequenceObject)

native final function RemoveSequenceObjects(array)

UIListElementCellProvider

const UnknownCellDataFieldName = 'NAME_None';

UIStringRenderer

native final virtual function SetTextAlignment(EUIAlignment, EUIAlignment)

UIStyleResolver

native function name GetStyleResolverTag()

native function bool SetStyleResolverTag(name)

native function bool NotifyResolveStyle(UISkin, bool, optional UIState, const optional name)

요약

여기에서는, UnrealScript 마시자 1 개의 그림자의 존재였던 인터페이스에 대해 배웠습니다. 그러한 정의 방법, 그러한 목적과 이 환경에서의 동작, 및 2 개의 튜토리얼을 봐 왔습니다. 2 개의 튜토리얼에서는 그것들을 실장하기 위한 작업을 실시했습니다. 그것들은, 매우 간단한 개념에도 관계되지 않고, 개발 프로젝트에서는 중요한 역할을 완수할 수가 있어 또 그렇게 되어야 합니다.

인터페이스는, 클래스간의 실장의 제어를 컴파일러에 의존하는 것을 가능하게 합니다. 이것들은, 실장이 변경될 가능성이 있을 때에, 제시되고 있는 특정의 그룹의 함수가 의존할 수가 있는 템플릿입니다. 인터페이스는 함수, Delegate, 정수 또는 구조체를 정의 형식에 포함해도 좋습니다만, 그 이상은 포함하지 않습니다.

추가 파일