UDN
Search public documentation:

GameCenterKR
English Translation
日本語訳
中国翻译

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

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

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > 모바일 홈 > 언리얼 엔진 3: 모바일 개요 > 언리얼 엔진 3: 애플 iOS 개요 > 게임 센터 (Game Center)
UE3 홈 > 네트워킹과 리플리케이션 > 게임 센터 (Game Center)

게임 센터 (Game Center)


문서 변경내역: Josh Adams 작성. 홍성진 번역.

개요


Game Center 는 애플의 온라인 게이밍 네트워크입니다. 게이머가 iOS 디바이스를 사용하여 연결, 다른 사람과 비교/협동/경쟁할 수 있도록 해 주는 것입니다. 언리얼 엔진 3를 사용하여 iOS 디바이스용으로 개발된 게임은 Game Center 기능을 지원하여, 게임에 소셜 및 커뮤니티 주도적인 느낌을 부여해 줄 수 있습니다.

ALERT! 주: 이하 GC 는 Garbage Collection 이 아닌, Game Center 를 일컫습니다.
ALERT! 주: UnrealScript 에서 OnlineSubsystemGameCenter 를 직접 리퍼런스하지 않도록 하는 것이 중요한데, PC 에서 UDK 를 실행시키려 할 때 문제가 생기기 때문입니다. 항상 OnlineSubsystem 리퍼런스를 사용하면 플랫폼에 따라 언리얼 엔진 3 가 자동으로 올바른 종류의 OnlineSubsystem 을 사용할 것입니다.

환경설정 셋업


언리얼 엔진 3 가 iOS 에서 GC 를 지원하는 방식은 OnlineSubsystemGameCenter 인터페이스를 통해서입니다. 그러나 시동 도중 자동으로 Game Center 로그인/환영 창을 띄우기 때문에, GC 는 환경설정 파일로 켜고 끌 수 있습니다. 개발자가 GC 를 테스트할 필요가 없는 경우 참으로 설정하고, 아니면 거짓으로 설정하면 됩니다.

.\UDKGame\Config\IPhone\IPhoneEngine.ini
[OnlineSubsystemGameCenter.OnlineSubsystemGameCenter]
bDisableGameCenter=false

업적(achievement)이나 순위표(leaderboard)를 사용하려는 경우, 모든 업적 및 순위표 식별자에 사용할 접두사를 구성해 둬야 합니다. 애플은 업적 및 순위표의 식별을 위해 전역적으로 고유한 문자열 이름을 사용하므로, 이미 고유한 앱/번들 식별자를 그 기반으로 사용하는 것이 좋습니다:

UDKGame\Config\IPhone\IPhoneEngine.ini
[OnlineSubsystemGameCenter.OnlineSubsystemGameCenter]
bDisableGameCenter=false
UniqueAchievementPrefix=com.epicgames.exploreue3.achievement_
UniqueCategoryPrefix=com.epicgames.exploreue3.leaderboard_
EpicUniqueAchievementPrefix=com.epicgames.exploreue3.achievement_
EpicUniqueCategoryPrefix=com.epicgames.exploreue3.leaderboard_

iTunes Connect를 사용하여 업적 및 순위표 범주를 만들 때, 그 이름은 지정한 접두사로 시작해서 하나마다 01, 02, 03 식으로 덧붙습니다. (업적 1을 풀면, 코드가 UniqueAchievementPrefix (고유 업적 접두사)에 01을 덧붙입니다.) UDK 사용자의 경우, 고유와 에픽 업적 접두사와 범주 접두사 둘 다 설정해야 합니다.

OnlineSubsystemGameCenter 컴파일하기


iOS 게임에 게임 센터를 사용하기 전에 OnlineSubsystemGameCenter 를 컴파일해야 할 수 있습니다. OnlineSubsystemGameCenter 를 컴파일하려면 DefaultEngine.ini 내 EditPackages 배열에 추가해 줘야 합니다.

UDKGame\Config\DefaultEngine.ini
[UnrealEd.EditorEngine]
+EditPackages=UTGame
+EditPackages=UTGameContent
+EditPackages=OnlineSubsystemGameCenter

EditPackages 목록에 추가하고 나면 언리얼스크립트 소스 코드를 다시 컴파일해 줘야 합니다. 그러면 OnlineSubsystemGameCenter 패키지가 UDKGame\Script 폴더에 나타날 것입니다. 언리얼스크립트 컴파일 관련 정보는 Basic Game Quick Start KR 문서를 참고하시기 바랍니다.

업적 (Achievement)


Achievement.png

업적 작동방식은 다른 플랫폼과 꽤 비슷합니다만, GC에서는 업적을 처음 풀 때 화면위 메시지가 뜨지 않는다는 점이 다릅니다. 게임 코드가 업적 풀기를 시도할 때, 사용자에 대해 이미 풀려있지 않은가 먼저 검사를 해야 합니다. 그 일반적인 작업방식은 이와 같습니다:

  • 게임 시동 (한 번만 해 주면 되지만, 더 해도 괜찮습니다.)
    • 업적 데이터 읽기 완료시 통지받기 위해 OnlineSub.PlayerInterface.AddReadAchievementsCompleteDelegate() 호출
    • 업적 읽기 시작을 위해 OnlineSub.PlayerInterface.ReadAchievements() 호출
    • 델리게이트에서 업적이 읽히는 것을 눈여겨 보다가, 그 스테이트 질의
  • 플레이어가 게임을 플레이하다가, 업적 조건을 충족
    • 모든 업적 스테이트를 구하기 위해 OnlineSub.PlayerInterface.GetAchievements() 호출
    • ID가 일치하는 업적에 대해 반환 배열 살펴보기
      • 업적 ID를 업적 배열 속으로의 인덱스로 사용하지 마십시오! 한 가지 이유로, 업적 ID는 1부터 시작됩니다. (언리얼스크립트를 비롯한 여러 언어의 배열 시작 인덱스는 0 입니다.)
    • bWasAchievedOnline 이 거짓이면
      • UI 표시, 사운드 재생 등
      • 플레이어가 업적을 풀었음을 GC에게 알리기 위해 OnlineSub.PlayerInterface.UnlockAchievement() 호출

플레이어가 푼 업적이 무엇인지 확인할 수 있게 하려면, (OnlineSub.PlayerInterface.GetAchievements() 결과를 사용하는) 자체 UI를 사용하든지, GC 내장 화면을 표시하든지 해야 합니다. OnlineSub.PlayerInterfaceEx.ShowAchievementsUI() 를 호출하면, GC UI가 화면에 밀려나옵니다.

업적 기술적인 세부사항

GC 코드가 시동될 때 즉시 업적 내려받기를 시작하므로, 게임 코드가 실행될 때 쯤에는 이미 전부 받아져 있을 것입니다. 그래도 확실히 하려면, 업적 내려받기가 끝났는지를 확인하는 델리게이트와 함께 OnlineSub.PlayerInterface.ReadAchievements() 를 호출하십시오. 그러면 OnlineSub.PlayerInterface.GetAchievements() 가 정말로 믿을 수 있는 결과를 반환할 것입니다.

내부적으로 업적에 대해 벌어지는 작업은 훨씬 복잡한데, 그 이유는 사용자가 오프라인일 때 업적을 풀면 GC는 서버에게 알리지 않기 때문입니다. 그래서 업적 스테이트를 로컬 iOS 플래시 디스크에 저장하여 유지합니다. 사용자가 나중에 GC에 연결할 때마다, 엔진은 원격지(GC 서버)와 로컬의 업적 스테이트 차이를 검사한 다음 둘을 병합시키고, UI 표시 없이 원격지 측의 업적을 풀어 업데이트합니다.

ALERT! 주: 업적 중 ID 가 -1 로 설정된 것이 있고, 그 업적을 제대로 다운받지 못했다면, UniqueAchievementPrefix 및/또는 EpicUniqueAchievementPrefix 가 제대로 설정되지 않은 것입니다.

업적 예제

이 예제에서는 업적 처리기 액터 클래스를 만들어 업적 관련 호출을 전송할 수 있도록 합니다. 물론 커스텀 GameInfo 클래스 등 다른 클래스로 바꿔도 됩니다. 업적 처리기는 (여러 업적을 연속해서 달성한 경우) 대기중인 업적 목록을 보관한 다음, 대기중인 업적 전부 풀릴 때까지 반복하는 비동기 함수를 호출합니다.

업적을 풀기 위해서는, 업적 ID 를 파라미터로 붙여 YourAchievementHandler::UnlockAchievement() 를 호출합니다. 이 업적 ID 는 업적 ID 끝의 번호와 일치해야 합니다. 예를 들어 com.epicgames.exploreue3.achievement_01 라는 ID 를 가진 업적은 업적 ID 가 1 이어야 합니다. 기억하실 것은, 업적 ID 는 보통 0 이 아닌 1 부터 시작된다는 점입니다.

첫 검사는 업적 ID 가 대기중인 업적 배열에 있는지 확인하는 것입니다. 이를 통해 업적이 여러번 풀리지 않도록 합니다. 업척 처리기가 현재 아무것도 처리하지 않고 있다면, 대기중인 업척 처리를 시작시키고 ProcessingAchievements 플랙을 참으로 바꿉니다. 비동기 업적 처리 루프를 시작시키기 위해서입니다.

여기서 서버의 업적 읽기가 완료되었을 때 호출되는 델리게이트가 할당되며, 업적을 읽기 위한 비동기 호출을 합니다.

YourAchievementHandler.uc
class YourAchievementHandler extends Actor;

// 대기중인 업적
var array<int> PendingAchievements;
// 현재 업적을 처리중이면 참
var bool ProcessingAchievements;

/**
 * 플레이어의 업적을 풉니다.
 *
 * @param    AchievementId      풀 업적
 * @param    LocalUserNum      로컬 유저 인덱스
 */
function UnlockAchievement(int AchievementId)
{
  local OnlineSubsystem OnlineSubsystem;
  local int PlayerControllerId;

  // 이 업적은 이미 대기중으로 처리되고 있으니 그냥 기다립니다.
  if (PendingAchievements.Find(AchievementId) != INDEX_NONE)
  {
    return;
  }

  // 대기 목록에 업적 ID 를 추가합니다.
  PendingAchievements.AddItem(AchievementId);

  // 지금 업적을 처리중이지 않으면 처리합니다.
  if (!ProcessingAchievements)
  {
    // GameCenter 에 접속하여 업적 델리게이트를 연결합니다.
    OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
    if (OnlineSubsystem != None && OnlineSubsystem.PlayerInterface != None)
    {
      // 로컬 플레이어 콘트롤러 ID 를 구합니다.
      PlayerControllerId = GetALocalPlayerControllerId();

      // 업적 읽기 델리게이트를 할당합니다.
      OnlineSubsystem.PlayerInterface.AddReadAchievementsCompleteDelegate(PlayerControllerId, InternalOnReadAchievementsComplete);

      // 모든 업적을 읽습니다.
      OnlineSubsystem.PlayerInterface.ReadAchievements(PlayerControllerId);

      // 참으로 설정하여 다시 발동되지 않도록 합니다.
      ProcessingAchievements = true;
    }
  }
}

이 함수는 로컬 플레이어 콘트롤러 ID 를 반환하는 간단한 헬퍼 함수입니다.

YourAchievementHandler.uc
/**
 * 로컬 플레이어 콘트롤러 ID 를 반환합니다. 같은 규칙이 Actor::GetALocalPlayerController() 에 적용됩니다.
 *
 * @return    로컬 플레이어 콘트롤러 ID 를 반환
 */
function int GetALocalPlayerControllerId()
{
  local PlayerController PlayerController;
  local LocalPlayer LocalPlayer;

  // 로컬 플레이어 콘트롤러를 구합니다.
  PlayerController = GetALocalPlayerController();
  if (PlayerController == None)
  {
    return INDEX_NONE;
  }

  // 로컬 플레이어 정보를 구합니다.
  LocalPlayer = LocalPlayer(PlayerController.Player);
  if (LocalPlayer == None)
  {
    return INDEX_NONE;
  }

  return class'UIInteraction'.static.GetPlayerIndex(LocalPlayer.ControllerId);
}

업적 데이터를 게임 센터에서 읽어오면, YourAchievementHandler::InternalOnReadAchievementsComplete() 가 호출됩니다. DownloadedAchievements 배열을 먼저 비워 데이터가 들어있지 않도록 하고, PlayerInterface::GetAchievements() 를 호출하여 DownloadedAchievements 배열을 최근 내려받은 업적 데이터로 채웁니다.

거기서 첫 대기중인 업적 ID 를 키로 사용하여 간단한 배열 인덱스 검색을 할 수 있습니다. 업적을 온라인 상태에서 달성한 것이 아니라면, 업적이 풀렸을 때 호출되는 델리게이트를 설정한 후 PlayerInterface::UnlockAchievement() 를 호출합니다. 그러면 게임 센터를 통해 업적을 푸는 비동기 요청이 시작됩니다. 그런 다음 가비지 콜렉션이 정상 작동하도록 업적 읽기 델리게이트를 제거합니다.

YourAchievementHandler.uc
class YourAchievementHandler extends Actor;

// 모든 내려받은 업적 배열
var array<AchievementDetails> DownloadedAchievements;

/**
 * 비동기 업적 읽기 완료시 호출됩니다.
 *
 * @param    TitleId      읽고 있던 것에 해당하는 타이틀 ID (0 은 현재 타이틀)
 */
function InternalOnReadAchievementsComplete(int TitleId)
{
  local OnlineSubsystem OnlineSubsystem;
  local int AchievementIndex, PlayerControllerId;

  // 온라인 서브시스템이 있는지, 연결된 플레이어 인터페이스가 있는지 확인합니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem == None || OnlineSubsystem.PlayerInterface == None)
  {
    return;
  }

  // 로컬 플레이어 콘트롤러 ID 를 구합니다.
  PlayerControllerId = GetALocalPlayerControllerId();

  // 새로운 데이터를 복사할 것이기에 현재 다운받은 업적 배열을 비웁니다.
  DownloadedAchievements.Remove(0, DownloadedAchievements.Length);

  // 다운받은 업적 배열에 업적을 읽어들입니다.
  OnlineSubsystem.PlayerInterface.GetAchievements(PlayerControllerId, DownloadedAchievements, TitleId);

  // 모든 업적을 구합니다.
  if (DownloadedAchievements.Length > 0 && PendingAchievements.Length > 0)
  {
    // 업적 인덱스를 구합니다.
    AchievementIndex = DownloadedAchievements.Find('Id', PendingAchievements[0]);

    // 업적을 풉니다.
    if (AchievementIndex != INDEX_NONE && !DownloadedAchievements[AchievementIndex].bWasAchievedOnline)
    {
      // 업적 풀기 완료 델리게이트를 할당합니다.
      OnlineSubsystem.PlayerInterface.AddUnlockAchievementCompleteDelegate(PlayerControllerId, InternalOnUnlockAchievementComplete);

      // 풀기 프로세스를 시작합니다.
      OnlineSubsystem.PlayerInterface.UnlockAchievement(PlayerControllerId, PendingAchievements[0]);
    }
  }

  // 가비지 콜렉션이 일어나도록 델리게이트 리퍼런스를 제거합니다.
  OnlineSubsystem.PlayerInterface.ClearReadAchievementsCompleteDelegate(PlayerControllerId, InternalOnReadAchievementsComplete);
}

게임 센터가 업적이 풀렸다고 응답하면, 앞서 델리게이트에 할당된 YourAchievementHandler::InternalOnUnlockAchievementComplete() 가 호출됩니다. 이 곳이 GUI 에 뭔가 표시하거나 사운드를 재생하여 업적이 풀렸음을 알리는 곳입니다. 그 후 업적 ID 를 PendingAchievements 배열에서 뽑아냅니다. 이 배열에 풀 업적 ID 가 남아있으면, 프로세스를 다시 반복합니다. 그렇지 않으면 사용된 델리게이트를 지우고 ProcessingAchievements 플랙을 다시 거짓으로 돌려놓습니다.

YourAchievementHandler.uc
class YourAchievementHandler extends Actor;

/**
 * 업적 풀기가 완료되면 호출됩니다.
 *
 * @param bWasSuccessful 비동기 동작이 에러없이 끝나면 참, 에러가 있었으면 거짓
 */
function InternalOnUnlockAchievementComplete(bool bWasSuccessful)
{
  local OnlineSubsystem OnlineSubsystem;
  local PlayerController PlayerController;
  local int AchievementIndex, PlayerControllerId;

  // 로컬 플레이어 콘트롤러 ID 를 구합니다.
  PlayerControllerId = GetALocalPlayerControllerId();

  if (bWasSuccessful && PendingAchievements.Length > 0)
  {
    // 로컬 플레이어 콘트롤러를 구합니다.
    PlayerController = GetALocalPlayerController();
    if (PlayerController != None)
    {
      // 업적 인덱스를 구합니다.
      AchievementIndex = DownloadedAchievements.Find('Id', PendingAchievements[0]);

      // 플레이어의 유저 인터페이스에 업적을 표시합니다.
    }
  }

  // 성공 여부와 관계없이 처리한 업적을 뽑아냅니다.
  PendingAchievements.Remove(0, 1);

  // 온라인 서브시스템이 있는지, 연결된 플레이어 인터페이스가 있는지 확인합니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem == None || OnlineSubsystem.PlayerInterface == None)
  {
    return;
  }

  // 대기중인 업적이 남아있으면 다음 것을 처리합니다.
  if (PendingAchievements.Length > 0)
  {
    // 게임센터에 접속하여 업적 델리게이트를 연결합니다.
    // 업적 읽기 델리게이트를 할당합니다.
    OnlineSubsystem.PlayerInterface.AddReadAchievementsCompleteDelegate(PlayerControllerId, InternalOnReadAchievementsComplete);

    // 모든 업적을 읽습니다.
    OnlineSubsystem.PlayerInterface.ReadAchievements(PlayerControllerId);
  }
  else // 아니면 끝난 것이니 정리합니다.
  {
    // 델리게이트 바인드를 지웁니다.
    OnlineSubsystem.PlayerInterface.ClearUnlockAchievementCompleteDelegate(PlayerControllerId, InternalOnUnlockAchievementComplete);

    // 더이상 업적을 처리하지 않는 상태로 플랙 설정합니다.
    ProcessingAchievements = false;
  }
}

마지막으로 중요한 것은, 모든 델리게이트 리퍼런스를 지워주는 것입니다. 그렇지 않으면 가비지 콜렉션이 제대로 되지 않습니다.

YourAchievementHandler.uc
class YourAchievementHandler extends Actor;

/**
 * 액터 소멸시 호출됩니다.
 */
event Destroyed()
{
  local OnlineSubsystem OnlineSubsystem;
  local int PlayerControllerId;

  Super.Destroyed();

  // 온라인 서브시스템이 있는지, 연결된 플레이어 인터페이스가 있는지 확인합니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem == None || OnlineSubsystem.PlayerInterface == None)
  {
    return;
  }

  // 아직 업적을 처리중이라면, 가비지 콜렉션이 일어날 수 있도록 할당된 델리게이트를 지워야 합니다.
  if (ProcessingAchievements)
  {
    // 로컬 플레이어 콘트롤러 ID 를 구합니다.
    PlayerControllerId = GetALocalPlayerControllerId();

    // 가비지 콜렉션이 일어날 수 있도록 델리게이트 리퍼런스를 지웁니다.
    OnlineSubsystem.PlayerInterface.ClearReadAchievementsCompleteDelegate(PlayerControllerId, InternalOnReadAchievementsComplete);

    // 델리게이트 바인드를 지웁니다.
    OnlineSubsystem.PlayerInterface.ClearUnlockAchievementCompleteDelegate(PlayerControllerId, InternalOnUnlockAchievementComplete);
    }
  }

순위표 (Leaderboard)


Leaderboard.png

GC의 또다른 주요 비-멀티플레이어 기능은 순위표(leaderboard) 입니다. 마찬가지로 다른 플랫폼과 비슷한 방식으로 작동합니다만, GC 순위표에는 이해해 둬야 할 제약이 몇 가지 있습니다. GC는 실제적으로 여러 "범주"에 하나의 "순위표"만 지원합니다. 하나의 순위표에 대해 표의 한 열로 범주를 취급합니다. 그러나 모든 범주가 (그 UI로) 동일한 포맷과 라벨로 표시되므로, 모든 범주는 시간이 될 수도, 정수형 점수가 될 수도, 정수형 킬 수가 될 수도 있습니다. 게다가 그들 전부 오름차순 아니면 내림차순 입니다. 이는 점수를 구성하는 방법에 영향을 끼치게 됩니다.

점수 보고는, OnlineStatsWrite 의 서브클래스를 만들어, defaultproperties 블록 내에 (1 시작 ID를 가지고) 그 속성을 구성한 뒤, 거기서 오브젝트의 인스턴스를 만들고, 값을 설정한 다음, OnlineSub.StatsInterface.WriteOnlineStats() 로 보고합니다.

YourOnlineStatsWrite.uc
class YourOnlineStatsWrite extends OnlineStatsWrite;

const PROPERTY_KILLS = 1;
const PROPERTY_LEVEL = 2;
const PROPERTY_GOLD = 3;

defaultproperties
{
   Properties=((PropertyId=PROPERTY_KILLS,Data=(Type=SDT_Int32,Value1=0)),(PropertyId=PROPERTY_LEVEL,Data=(Type=SDT_Int32,Value1=0)),(PropertyId=PROPERTY_GOLD,Data=(Type=SDT_Int32,Value1=0)))
}

GC는 순위표 증가를 지원하지 않습니다. (예를 들어 킬 수 같은) 점수를 증가시키려면:

  • 플레이어 킬 수 저장된 값을 읽습니다. (환경설정 값이나 BasicSaveObject/BasicLoadObject 를 사용할 수도 있습니다.)
  • 값을 알맞게 증가시킵니다.
  • 점수를 게임 센터에 씁니다.

점수 표시를 위해서는 게임 센터의 내장 UI 를 사용하면 되는데, 다음과 같이 호출하면 됩니다:

local OnlineSubsystem OnlineSubsystem;
local OnlineSuppliedUIInterface OnlineSuppliedUIInterface;
local array<UniqueNetId> PlayerIds;
local UniqueNetId PlayerId;
local YourOnlineStatsRead YourOnlineStatsRead;
local PlayerController PlayerController;
local LocalPlayer LocalPlayer;
local byte LocalUserNum;

// 온라인 서브 시스템을 구합니다.
OnlineSubSystem = class'GameEngine'.static.GetOnlineSubsystem();
// 온라인 서브 시스템에 접근할 수 있는지 확인합니다.
if (OnlineSubSystem != None)
{
  // 로컬 플레이어 콘트롤러에서 PlayerIds 배열을 만듭니다.
  PlayerController = GetALocalPlayerController();
  if (PlayerController != None)
  {
    // 로컬 플레이어를 구합니다.
    LocalPlayer = LocalPlayer(PlayerController.Player);
    if (LocalPlayer != None)
    {
      // 로컬 사용자 번호를 구합니다.
      LocalUserNum = class'UIInteraction'.static.GetPlayerIndex(LocalPlayer.ControllerId);
      // 로컬 사용자 번호에서 고유 플레이어 id 를 구합니다.
      OnlineSubSystem.PlayerInterface.GetUniquePlayerId(LocalUserNum, PlayerId);
      // 고유 플레이어 id 를 PlayerIds 배열에 추가합니다.
      PlayerIds.AddItem(PlayerId);

      // 온라인 제공 UI 인터페이스를 구합니다.
      OnlineSuppliedUIInterface = OnlineSuppliedUIInterface(OnlineSubSystem.GetNamedInterface('SuppliedUI'));
      if (OnlineSuppliedUIInterface != None)
      {
        // 온라인 통계 읽기 클래스 인스턴싱 입니다.
        YourOnlineStatsRead = new () class'YourOnlineStatsRead';
        if (YourOnlineStatsRead!= None)
        {
          // 온라인 통계 UI 를 표시합니다.
          OnlineSuppliedUIInterface.ShowOnlineStatsUI(PlayerIds, YourOnlineStatsRead);
        }
      }
    }
  }
}

YourOnlineStatsRead.uc
class YourOnlineStatsRead extends OnlineStatsRead;

const PROPERTY_KILLS = 1;
const PROPERTY_LEVEL = 2;
const PROPERTY_GOLD = 3;

defaultproperties
{
   ColumnIds=(PROPERTY_KILLS,PROPERTY_LEVEL,PROPERTY_GOLD)
}

여러 열의 여러 줄을 읽는 경우, 각 줄은 첫 열에 따라 정렬됩니다. (이 부분은 나중에 순위 열을 사용하도록 바뀔 수 있습니다)

순위표 기술적인 세부사항

업적과 비슷하게 사용자가 오프라인일 때 점수가 보고되는 경우, GC가 자동으로 재전송하지 않습니다. 그래서 디스크에 보고된 최고 및 최저 점수를 저장하기 위해 내부적으로 많은 작업을 하고서, 다음 번 사용자가 접속할 때 서버에 보고해 줍니다. 순위표가 오름차순인지 내림차순인지를 알지 못하기 때문에 최고와 최저를 같이 제출합니다.

통계를 읽을 때 모든 점수는 QWORD / Int64 (64 비트 정수형, 범위는 - 9,223,372,036,854,775,808 에서 9,223,372,036,854,775,807) 로 보고되는데, GC의 순위표 값 저장 방식이 그렇기 때문입니다.

멀티플레이어


멀티플레이어는 현재 로비-기반-상대검색(matchmaking) 방식으로만, 최대 4 명까지 지원됩니다. GC가 GKMatch 오브젝트를 통해 네트워크 트래픽을 처리하기 때문에, GC 상대검색을 통해 발견된 플레이어만 게임 네트워킹의 통신 대상이 될 수 있습니다. 네 개의 iOS 디바이스 중 하나가 서버 역할을 하게 됩니다.

상대검색

Matchmaking(상대검색)은 GC의 내장 인터페이스를 사용해서 이루어집니다:

   OnlineSuppliedUIInterface(OnlineSub.GetNamedInterface('SuppliedUI')).ShowMatchmakingUI();

ALERT! 주: ShowMatchmakingUI() 로 전달되는 GameSettings 오브젝트는 현재 사용되지 않으며, None 이어도 됩니다.

현재 서버를 누가 할 것인지 고를 수 있는 방법은 없으며, 각 플레이어는 상대검색 모드로 들어가고, 한 명이 서버로 선택되면 나머지는 클라이언트가 됩니다. (같은 플레이어 구성에 대해서는 언제나 같은 플레이어가 서버 역할을 합니다. 서버를 결정하는 데 각 플레이어의 고유 식별자를 사용하기 때문인데, 이런 식으로 각 디바이스는 부가적인 통신 없이도 자신이 클라이언트인지 서버인지를 알 수 있습니다.)

플레이어가 서버인지 클라이언트인지에 따라, 상대검색이 끝난 후 델리게이트 둘 중 하나가 호출됩니다. 서버라면 OnCreateOnlineGameComplete() 델리게이트가, 클라이언트라면 OnJoinOnlineGameComplete() 델리게이트가 호출됩니다. 두 경우 다 처리하려면 게임에 두 델리게이트 모두 등록해야 합니다.

Join (클라이언트측) 델리게이트에서는, (ClientTravel(URL, TRAVEL_Absolute) 를 통해) 서버로 이동하는 데 사용할 수 있는 URL을 구하기 위해 OnlineSub.GameInterface.GetResolvedConnectString(SessionName,URL) 을 사용합니다.

ALERT! 중요: 초대를 수락할 때 (아래 참고), PlayerController 에는 Join 델리게이트를 추가한 다음 URL로 이동하는 코드가 있습니다. PlayerController.ucOnInviteJoinComplete() 를 확인해 보십시오. 이런 이유로 사용자가 상대검색 모드로 들어설 때는 Join 델리게이트만 등록해 줘도 되는 것입니다.

(서버측) Create 델리게이트에서는, 일반적으로 맵을 불러오기 위해 ClientTravel(SomeURL, TRAVEL_Absolute) 같은 것을 할 것입니다. 클라이언트는 서버 연결 정보를 가질 것이며, 연결 URL을 열어 서버에 참가할 것입니다. 사용자가 상대검색에 들어가거나, 초대를 수락한 다음 서버가 된 경우에는 이 델리케이트가 사용될 것입니다. 그래서 항상 Create 델리게이트를 등록시켜야 하는 것입니다.

초대

GC는 상대검색 UI에서 플레이어를 초대하는 기능을 지원합니다. 초대된 플레이어가 다른 게임을 플레이하던 중인 경우 초대 메시지를 보게 될 것이며, 초대를 수락하면 게임을 실행시키고, 게임이 부팅될 때 플레이어에게 게임에 참가중이라 알리는 GC 상대검색 UI가 밀려올라오게 됩니다.

그 플레이어가 이미 같은 게임에 있는 경우, PlayerController 내의 코드 (OnGameInviteAccepted() 참고)가 현재 온라인 게임을 전부 없애고, 해당 플레이어가 게임에 참가중임을 알리는 GC 상대검색 UI를 불러옵니다. 초대된 플레이어가 실제로 서버가 될 수도 있음에 유념하십시오.

음성 채팅

GC에는 매우 기본적이고 사용하기 쉬운 음성 채팅 시스템이 있습니다. 멀티플레이어 게임에 들어간 후, 플레이어끼리 대화를 가능하게 하려면 OnlineSub.VoiceInterface.StartNetworkedVoice() 를 호출하면 됩니다. 음소거나 대화 상대자를 알아보는 기능 역시도 (MuteRemoteTalker(), IsRemotePlayerTalking() 등의) 표준 함수를 사용해 작동됩니다.

가비지 콜렉션


온라인 서브시스템 델리게이트에 바인딩할 때마다, 레벨의 가비지 콜렉션 예약시 항상 지우도록 하는 것이 중요합니다. 그렇게 하지 않으면 가비지 콜렉션이 실패하여 메모리 일부가 절대 해제되지 않기 때문입니다! 이에 대해 자세한 내용은 키즈멧 온라인 서브시스템 UDK 젬을 참고하시기 바랍니다.

관련 토픽


내려받기