UDN
Search public documentation:

KismetOnlineSubsystemKR
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 홈 > UDK 젬 > 키즈멧 온라인 서브시스템

키즈멧 온라인 서브시스템


문서 변경내역: James Tan 작성. 홍성진 번역.
UDK 2012년 1월 버전으로 최종 테스팅
PC & iOS 호환

개요


키즈멧은 노드로 게임 플레이 로직을 만들 수 있도록 디자인된 비주얼 스크립팅 시스템입니다. 프로토타이핑 단계에서는 물론 프로덕션 단계에 이르기까지 매우 유용한 툴입니다. 약간의 게임 플레이 로직은 키즈멧 안에 존재하기 때문에, 키즈멧을 통해서도 Game Center 나 Steam Works 에 접근할 수 있어야 함이 마땅합니다. 이번 UDK 젬에서는 Game Center 나 Steam Works 와의 인터페이스가 되는 키즈멧 노드를 새로 추가해 보도록 하겠습니다.

ALERT! 주: UnrealScript 가 Game Center 와 Steam Works 둘 다에 똑같은 정의(define)를 제공하기는 하지만, 한 번에 하나만 사용할 수 있습니다. 그 이유는 전처리기가 UnrealScript 를 그에 맞는 버전으로 변환하기 때문인데요. Game Center 와 Steam Works 의 작동 방식이 서로 다르기 때문에 어쩔 수 없는 일입니다. 그러므로 멀티 플랫폼 게임을 제작하는 중이시라면, 각기 다른 버전의 키즈멧을 두 개 만들어 줘야 합니다. 가장 좋은 방법은 스트리밍 레벨을 Game Center 키즈멧 노드 버전 하나, Steam 키즈멧 노드 버전 하나 따로 만드는 것입니다.

UnrealScript 전처리에 대해 한 말씀


UnrealScript 전처리기는 C++ 같은 언어의 전처리기와 매우 비슷한 방식으로 작동합니다. 전처리기란 if - else 문에 따라 UnrealScript 의 일부를 포함/제거시키거나 상수를 정의할 수 있는 초간단 스크립트입니다.

이 코드 스니펫에는, 정의된 상수에 따라 전처리기가 포함시킬 코드 부분이 둘 있습니다. USE_STEAMWORKS 가 정의되면, if 문 안에 있는 모든 것이 컴파일에 포함됩니다. 그 외에 USE_STEAMWORKS 가 정의되지 않으면, else 문 안에 있는 모든 것이 컴파일에 포함됩니다. Game Center 와 Steam Works 의 작동 방식상의 차이때문에 코드를 다르게 써야 했습니다.

SeqAct_UnlockAchievement.uc
/**
 * 이 시퀸스 액션이 로컬 유저 번호가 붙어 뭔가 해야 하면 호출됩니다.
 */
protected function InternalOnActivated()
{
  local OnlineSubsystem OnlineSubsystem;

  // 온라인 서브시스템에 접속하여 업적 델리게이트를 링크합니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem != None && OnlineSubsystem.PlayerInterface != None)
  {
// ==========
// SteamWorks
// ==========
`if(`isdefined(USE_STEAMWORKS))
    // 업적 풀기를 시작합니다.
    BeginUnlockAchievement();

// ==========
// GameCenter
// ==========
`else
    // 게임 센터에서는 업적 리스트를 먼저 읽은 다음 풀어줘야 합니다.
    // 업적 읽기 델리게이트를 할당합니다.
    OnlineSubsystem.PlayerInterface.AddReadAchievementsCompleteDelegate(ProcessingPlayers[0].LocalUserNum, InternalOnReadAchievementsComplete);

    // 모든 업적을 읽습니다.
    OnlineSubsystem.PlayerInterface.ReadAchievements(ProcessingPlayers[0].LocalUserNum);
`endif
  }
}

USE_STEAMWORKSUSE_GAMECENTER 는 Globals.uci 라는 파일에 정의되어 있습니다. 사용하지 않으려는 부분을 코멘트로 빼냅니다. 그런데 Globals.uci 를 수정해도 UnrealScript 컴파일러가 자동으로 감지하지 못하니, 바꾸고 나서는 UnrealScript 를 강제로 다시 컴파일해 줘야 합니다.

가비지 콜렉션에 대해 한 말씀


온라인 서브시스템에서 이루어 지는 태스크 다수는 비동기 작업입니다. 그렇기에 다수의 태스크에서는 완료되면 호출되는 델리게이트를 사용합니다. 키즈멧 노드 자체는 델리게이트에 바인딩되는데, 이것이 제대로 청소(clean up)되지 않고서는 레벨이 가비지 콜렉션 대상에서 제외되어 메모리에서 제거되지 않습니다. GameKismetSceneClient 라는 인터랙션을 사용하여 게임 세션이 언제 끝나는지를 감지하다가, 게임 세션이 끝나면 모든 키즈멧 노드더러 스스로 청소하라 이르는 것입니다.

GameKismetSceneClient.uc
/**
 * GameKismetSceneClient 는 게임 세션이 언제 끝나는지 감시하는 인터랙션입니다.
 * OnlineSubsystems 에 바인딩된 키즈멧 노드를 전부 청소하는 데 필요합니다.
 *
 * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
 */
class GameKismetSceneClient extends Interaction;

/**
 * 현재 게임 세션이 끝나면 호출됩니다. 모든 OnlineSubsystemBase 키즈멧 노드를 청소합니다.
 */
function NotifyGameSessionEnded()
{
  local Sequence GameSequence;
  local array<SequenceObject> SequenceObjects;
  local SeqAct_OnlineSubsystemBase SeqAct_OnlineSubsystemBase;
  local int i;
  local WorldInfo WorldInfo;

  // 월드 인포를 구합니다.
  WorldInfo = class'WorldInfo'.static.GetWorldInfo();
  if (WorldInfo == None)
  {
    return;
  }

  // OnlineSubsystemBase 키즈멧 노드를 모두 청소합니다.
  GameSequence = WorldInfo.GetGameSequence();
  if (GameSequence != None)
  {
    GameSequence.FindSeqObjectsByClass(class'SeqAct_OnlineSubsystemBase', true, SequenceObjects);
    if (SequenceObjects.Length > 0)
    {
      for (i = 0; i < SequenceObjects.Length; ++i)
      {
        SeqAct_OnlineSubsystemBase = SeqAct_OnlineSubsystemBase(SequenceObjects[i]);
        if (SeqAct_OnlineSubsystemBase != None)
        {
          SeqAct_OnlineSubsystemBase.CleanUp(true);
        }
      }
    }
  }
}

defaultproperties
{
}

Steam Works 와 Game Center 공용 키즈멧 노드


SeqAct_OnlineSubsystemBase

SeqAct_OnlineSubsystemBase (시퀸스 액션_온라인 서브시스템 베이스)는 다른 모든 키즈멧 시퀸스 액션이 베이스로 삼는 추상 클래스입니다. 이 베이스 클래스는 온라인 서브시스템 명령에 대한 대기열(queue) 처리, 플레이어 정보의 체계적 정리(organize)와 청소(clean up) 작업을 담당합니다. 대부분의 출력 링크 호출을 담당하기도 합니다. 온라인 서브시스템을 활용하는 키즈멧 시퀸스 액션을 새로 만들 필요가 있다면, 이 클래스의 서브클래스를 만드는 것이 최고입니다. 이 키즈멧 시퀸스 액션이 발동되면, 붙은 Kismet Variable Players 노드에 따라 작업을 해야 하는 플레이어 배열을 컴파일합니다. 그런 다음 플레이어 배열을 순차적으로 처리합니다. 이 키즈멧 시퀸스 액션은 Out 을 즉시 출력합니다. 키즈멧 시퀸스 액션이 플레이어를 처리하느라 바쁘다면, Busy 을 즉시 출력합니다. 키즈멧 시퀸스 액션의 서브클래싱 결과에 따라 나중에 Succeeded 나 Failed 를 출력할 수도 있습니다.

함수

  • Activated 활성화됨 - 이 이벤트는 키즈멧 시퀸스 액션이 다른 키즈멧 시퀸스 노드로 인해 활성화되었을 때 호출됩니다.
  • FinishedProcessPlayerIndex 플레이어 인덱스 처리 완료됨 - 이 함수는 플레이어에 대한 처리를 끝마친 서브클래스에서 호출됩니다. bWasSuccessful 가 참(성공)이면 Succeeded 출력이 활성화되고, 거짓이면 Failed 출력이 활성화됩니다. 서브클래싱된 키즈멧 노드가 모든 플레이어를 한 번에 처리했다면 bProcessedAllPlayers 를 설정합니다.
  • InternalOnActivated 내부 활성화시 - 이 함수는 Activated 안에서 호출되는 스텁 함수입니다. 서브클래스에서는 Activated 이벤트를 확장해서는 안되기에, 그 서브클래스 확장용 함수입니다.
  • CleanUp 청소 - 이 함수는 GameKismetSceneClient 와 FinishedProcessPlayerIndex 안에서 호출됩니다. 이 함수는 델리게이트로 바인딩되거나 다른 종류의 청소 작업을 요하는 서브클래스로 확장해야 합니다. IsExiting 가 참(빠져나가는 중)이면 모든 것을 청소해야 합니다.
  • NotifyGameKismetSceneClientCreated GameKismetSceneClient 생성 알림 - 이 함수는 GameKismetSceneClient 가 이미 생성되었음을 나타내기 위해 다른 SeqAct_OnlineSubsystemBase 에 의해 호출되는 함수입니다. SeqAct_OnlineSubsystemBase 가 처음 활성화되면, 먼저 로컬 플레이어의 뷰포트에 삽입할 GameKismetSceneClient 생성을 시도합니다. 성공하면 다른 모든 SeqAct_OnlineSubsystemBase 더러 또 만들 생각 하지도 말라 이릅니다.

정의

  • SProcessingPlayer 처리중인 플레이어 구조체 - 처리중이거나 처리 대기중인 플레이어를 정의하는 구조체입니다.
  • IsProcessing 처리중 - 참이면, 이 키즈멧 노드는 다른 일을 하느라 바빠 더 이상의 요청을 즉시 거절합니다.
  • ProcessingPlayers 처리중인 플레이어 - 현재 처리되고 있는 플레이어 배열입니다. 플레이어는 순차적으로 처리됩니다. 각 플레이어가 처리되고 나면, 배열이 빌 때까지 배열 첫 항목을 제거하고 다음 항목을 처리합니다.
  • HasCreatedSceneClient 씬 클라이언트 생성여부 - 거짓이면, 이 키즈멧 시퀸스 액션이 활성화될 때 GameKismetSceneClient 을 생성하여 로컬 플레이어의 뷰포트에 삽입해 봅니다. 게임 세션이 끝나 청소해도 괜찮겠나 알아보기 위함입니다. 참이면, 이 키즈멧 시퀸스 액션은 이같은 작업을 하지 않습니다.

실행 흐름

다른 키즈멧 시퀸스 노드에 의해 이 키즈멧 시퀸스 액션이 활성화되면, Activated() (활성화됨 함수)가 먼저 호출됩니다. Activated() (활성화됨 함수)는 HasCreatedSceneClient (씬 클라이언트 생성 여부)값을 검사하여 GameKismetSceneClient (게임 키즈멧 씬 클라이언트)를 만들어야 하는지 알아봅니다. 만들었다면 다른 모든 SeqAct_OnlineSubsystemBase (온라인 서브시스템 베이스 시퀸스 액션)더러 그 작업이 완료되었으니 더이상 할 필요 없다고 알려줍니다. IsProcessing 이 참(처리중)이면 "Busy" 출력이 활성화되고 함수는 멈춥니다. 거짓이(처리중이 아니)면 붙은 Kismet Player Variable (키즈멧 플레이어 변수)에 따라 플레이어 목록을 컴파일합니다. Kismet Player Variable (키즈멧 플레이어 변수)에 나열된 각 플레이어에 대해, ProcessingPlayers (처리중인 플레이어) 배열을 플레이어 인덱스와 고유 플레이어 ID 로 채웁니다. ProcessingPlayers (처리중인 플레이어) 배열이 크기가 있는 배열인 경우, IsProcessing 는 참(처리중)으로 변경되고 InternalOnActivated() (내부 활성화시 함수)가 호출됩니다. 그런 다음 "Out" 출력이 활성화됩니다.

서브클래스가 플레이어 처리를 마치면, FinishedProcessPlayerIndex() (플레이어 인덱스 처리 마침 함수)가 호출됩니다. bProcessedAllPlayers 가 참이(모든 플레이어가 처리되었다)면, ProcessingPlayers (처리중인 플레이어) 배열을 비웁니다. 거짓이(모든 플레이어가 처리되지 않았다)면, ProcessingPlayers (처리중인 플레이어) 배열의 첫 항목만 제거됩니다. 그리고서 이 배열이 비어 있지 않으면, InternalOnActivated() (내부 활성화시 함수)를 호출하여 첫 항목을 다시 처리합니다. 그 외의 경우 bWasSuccessful 가 참(성공)이면, "Succeeded" 출력이 활성화됩니다. 거짓이(실패)면 "Failed" 출력이 활성화됩니다.

CleanUp() (청소 함수)가 호출되고 IsExiting 가 참(빠져나가는 중)이면, ProcessingPlayers (처리중인 플레이어) 배열을 비웁니다.

NotifyGameKismetSceneClientCreated() (GameKismetSceneClient 생성 알림 함수)가 호출되면, HasCreatedSceneClient (씬 클라이언트가 생성됨)이 참으로 설정됩니다.

UnrealScript

SeqAct_OnlineSubsystemBase.uc
/**
 * OnlineSubsystemBase 액션은 로컬 사용자 번호를 요하는 다른 온라인 서브시스템 함수를 호출하는 데 사용되는 추상 액션입니다.
 *
 * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
 */
class SeqAct_OnlineSubsystemBase extends SequenceAction
  abstract
  HideDropDown;

struct SProcessingPlayer
{
  var byte LocalUserNum;
  var UniqueNetId LocalNetId;
};

var private bool IsProcessing;
var protected array<SProcessingPlayer> ProcessingPlayers;
var protected bool HasCreatedSceneClient;

/**
 * 이 이벤트가 활성화되면 호출됩니다.
 */
event Activated()
{
  local SeqVar_Player SeqVar_Player;
  local bool AllPlayers;
  local array<int> PlayerIndices, ExistingPlayerIndices;
  local WorldInfo WorldInfo;
  local PlayerController PlayerController;
  local LocalPlayer LocalPlayer;
  local int i;
  local SProcessingPlayer ProcessingPlayer;
  local OnlineSubSystem OnlineSubSystem;
  local Sequence GameSequence;
  local array<SequenceObject> SequenceObjects;
  local SeqAct_OnlineSubsystemBase SeqAct_OnlineSubsystemBase;

  // 가비지 콜렉션 이벤트를 감시할 GameKismetSceneClient 를 삽입해야하는지 검사합니다.
  if (!HasCreatedSceneClient)
  {
    // 게임 키즈멧 씬 클라이언트를 삽입합니다.
    WorldInfo = GetWorldInfo();
    if (WorldInfo != None)
    {
      // 로컬 플레이어 콘트롤러를 구합니다.
      PlayerController = WorldInfo.GetALocalPlayerController();
      if (PlayerController != None)
      {
        LocalPlayer = LocalPlayer(PlayerController.Player);
        if (LocalPlayer != None && LocalPlayer.ViewportClient != None)
        {
          LocalPlayer.ViewportClient.InsertInteraction(new (LocalPlayer.ViewportClient) class'GameKismetSceneClient');
        }
      }
    }

    // 게임 키즈멧 씬 클라이언트가 생성된 SeqAct_OnlineSubsystemBase 를 전부 표시합니다.
    GameSequence = WorldInfo.GetGameSequence();
    if (GameSequence != None)
    {
      GameSequence.FindSeqObjectsByClass(class'SeqAct_OnlineSubsystemBase', true, SequenceObjects);
      if (SequenceObjects.Length > 0)
      {
        for (i = 0; i < SequenceObjects.Length; ++i)
        {
          SeqAct_OnlineSubsystemBase = SeqAct_OnlineSubsystemBase(SequenceObjects[i]);
          if (SeqAct_OnlineSubsystemBase != None)
          {
            SeqAct_OnlineSubsystemBase.NotifyGameKismetSceneClientCreated();
          }
        }
      }
    }
  }

  if (IsProcessing)
  {
    // Busy 출력을 활성화시킵니다.
    ActivateOutputLink(1);
    return;
  }

  // 온라인 서브시스템을 구합니다.
  OnlineSubSystem = class'GameEngine'.static.GetOnlineSubSystem();
  if (OnlineSubSystem == None || OnlineSubSystem.PlayerInterface == None)
  {
    return;
  }

  // 플레이어 인덱스를 구합니다.
  ForEach LinkedVariables(class'SeqVar_Player', SeqVar_Player)
  {
    if (SeqVar_Player.bAllPlayers)
    {
      AllPlayers = true;
      break;
    }
    else
    {
      PlayerIndices.AddItem(SeqVar_Player.PlayerIdx);
    }
  }

  // 처리가 필요한 플레이어를 찾습니다.
  WorldInfo = GetWorldInfo();
  if (WorldInfo != None)
  {
    // 모든 플레이어에게 참이면, 모든 플레이어를 대상으로 반복처리합니다.
    if (AllPlayers)
    {
      ForEach WorldInfo.AllControllers(class'PlayerController', PlayerController)
      {
        LocalPlayer = LocalPlayer(PlayerController.Player);
        if (LocalPlayer != None)
        {
          // 처리중인 플레이어 구조체를 만듭니다.
          ProcessingPlayer.LocalUserNum = class'UIInteraction'.static.GetPlayerIndex(LocalPlayer.ControllerId);
          OnlineSubSystem.PlayerInterface.GetUniquePlayerId(ProcessingPlayer.LocalUserNum, ProcessingPlayer.LocalNetId);

          // 처리중인 플레이어 구조체를 처리중인 플레이어 배열에 덧붙입니다.
          ProcessingPlayers.AddItem(ProcessingPlayer);
        }
      }
    }
    // 거짓이면 각 플레이어 인덱스에 대해 반복처리합니다.
    else if (PlayerIndices.Length > 0)
    {
      // 기존 플레이어 인덱스부터 전부 구합니다.
      ForEach WorldInfo.AllControllers(class'PlayerController', PlayerController)
      {
        LocalPlayer = LocalPlayer(PlayerController.Player);
        if (LocalPlayer != None)
        {
          ExistingPlayerIndices.AddItem(class'UIInteraction'.static.GetPlayerIndex(LocalPlayer.ControllerId));
        }
      }

      for (i = 0; i < PlayerIndices.Length; ++i)
      {
        if (ExistingPlayerIndices.Find(PlayerIndices[i]) != INDEX_NONE)
        {
          // 처리중인 플레이어 구조체를 만듭니다.
          ProcessingPlayer.LocalUserNum = PlayerIndices[i];
          OnlineSubSystem.PlayerInterface.GetUniquePlayerId(ProcessingPlayer.LocalUserNum, ProcessingPlayer.LocalNetId);

          // 처리중인 플레이어 구조체를 처리중인 플레이어 배열에 덧붙입니다.
          ProcessingPlayers.AddItem(ProcessingPlayer);
        }
      }
    }

    // 플레이어 1 을 처리합니다.
    if (ProcessingPlayers.Length > 0)
    {
      // 처리중 상태를 참으로 설정합니다.
      IsProcessing = true;

      // 활성화시킵니다.
      InternalOnActivated();
    }
  }

  // Out 출력을 활성화시킵니다.
  ActivateOutputLink(0);
}

/**
 * 키즈멧 노드가 이 플레이어에 대한 처리를 마치면 호출됩니다.
 *
 * @param      bWasSuccessful        참이면 키즈멧 노드가 작업에 성공한 것입니다.
 * @param      bProcessedAllPlayers    참이면 이 키즈멧 노드는 플레이어를 하나씩 처리한 것이 아니라 한 번에 모든 플레이어를 처리한 것입니다.
 */
protected function FinishedProcessPlayerIndex(bool bWasSuccessful, optional bool bProcessedAllPlayers)
{
  // 이 키즈멧 노드를 청소합니다.
  CleanUp();

  // 첫 번째 처리중인 플레이어 인덱스를 뽑아냅니다.
  if (ProcessingPlayers.Length > 0)
  {
    // 모든 플레이어를 처리했다면, 이제 그 모두를 제거합니다.
    if (bProcessedAllPlayers)
    {
      ProcessingPlayers.Remove(0, ProcessingPlayers.Length);
    }
    // 아니라면 하나만 처리한 것이니, 맨위에서 뽑아냅니다.
    else
    {
      ProcessingPlayers.Remove(0, 1);
    }
  }

  // 처리할 플레이어 인덱스가 남아있는 경우, 다음 것을 처리합니다.
  if (ProcessingPlayers.Length > 0)
  {
    InternalOnActivated();
  }
  // 남아있지 않으면, 이 키즈멧 노드는 처리가 끝났으며 출력중 하나를 활성화시켜야 합니다.
  else
  {
    IsProcessing = false;
    ForceActivateOutput((bWasSuccessful) ? 2 : 3);
  }
}

/**
 * 이 시퀸스 액션이 로컬 유저 숫자를 붙여 어떤 작업을 해야할 때 호출됩니다.
 */
protected function InternalOnActivated();

/**
 * 가비지 콜렉션이 일어날 수 있도록 이 키즈멧 노드가 모든 변수나 델리게이트 리퍼런스를 청소해야 할 때 호출됩니다.
 *
 * @param    IsExiting      빠져나가는 중이라면 모든 것을 청소합니다.
 */
function CleanUp(optional bool IsExiting)
{
  // 처리중인 플레이어 배열을 비웁니다.
  if (IsExiting)
  {
    ProcessingPlayers.Remove(0, ProcessingPlayers.Length);
  }
}

/**
 * 게임 세션이 언제 끝났는지, 게임을 언제 청소해야할 지 잡아내는 데 사용되는 GameKismetSceneClient 를, 키즈멧 노드가 만들었을 때 호출됩니다.
 */
function NotifyGameKismetSceneClientCreated()
{
  HasCreatedSceneClient = true;
}

defaultproperties
{
`if(`isdefined(USE_STEAMWORKS))
  ObjCategory="Steamworks"
`elseif(`isdefined(USE_GAMECENTER))
  ObjCategory="Game Center"
`endif
  ObjColor=(R=0,G=63,B=127,A=255)

  bAutoActivateOutputLinks=false

  VariableLinks.Empty
  VariableLinks(0)=(ExpectedType=class'SeqVar_Player',LinkDesc="Player")

  OutputLinks(1)=(LinkDesc="Busy")
  OutputLinks(2)=(LinkDesc="Succeeded")
  OutputLinks(3)=(LinkDesc="Failed")
}


SeqAct_UnlockAchievement

이 (압축 풀기) 키즈멧 액션은 프로퍼티 패널에 정의되거나 붙은 ID 에 따라 업적을 풉니다.

KismetOnlineSubsystem_00.png

KismetOnlineSubsystem_07.jpg

함수

  • InternalOnActivated 내부 활성화시 - 이 함수는 부모 클래스 SeqAct_OnlineSubsystemBase 에 의해 호출됩니다. Game Center 에서 업적을 풀기 위해서는 먼저 모두 읽어야 하므로, OnlineSubsystem.PlayerInterface.ReadAchievements() 가 호출됩니다. Steamworks 에서는 그냥 BeginUnlockAchievement() 가 호출됩니다.
  • InternalOnReadAchievementsComplete 내부 업적 읽기 완료시 - 델리게이트를 통해 OnlineSubsystem.PlayerInterface 에서 호출됩니다. 그러면 업적을 온라인에서 달성했는지 검사합니다. 온라인 달성했고 AlwaysUnlockAchievement 가 참이면, InternalOnUnlockAchievementComplete 가 호출됩니다. 거짓이면 BeginUnlockAchievement() 가 호출됩니다.
  • BeginUnlockAchievement 업적 풀기 시작 - OnlineSubsystem.PlayerInterface.UnlockAchievement() 에 호출을 보냅니다.
  • InternalOnUnlockAchievementComplete 내부 업적 풀기 완료시 - 델리게이트를 통해 OnlineSubsystem.PlayerInterface 에서 호출됩니다. 그러면 FinishedProcessPlayerIndex() 를 호출합니다.
  • CleanUp 청소 - 바인딩된 모든 델리게이트에 대한 청소 작업을 담당합니다.

정의

  • AchievementId 업적 ID - 풀고자 하는 업적 ID 입니다. 변수 링크에 키즈멧 Int 변수를 붙여 정의하는 것도 가능합니다.
  • AlwaysUnlockAchievement 항상 업적 풀기 - 참이면 업적이 이미 풀려 있어도 키즈멧 시퀸스 액션은 여전히 "Succeeded" 를 출력합니다. Game Center 전용.
  • DownloadedAchievements 다운로드 업적 - 온라인에서 내려받은 업적 배열입니다. Game Center 전용.

Game Center 실행 흐름

InternalOnActivated() (내부 활성화시 함수)가 호출되면, OnlineSubsystem.PlayerInterface.ReadAchievements (업적 읽기) 완료시 호출되는 OnlineSubsystem.PlayerInterface (온라인 서브시스템.플레이어 인터페이스)에 델리게이트가 추가됩니다.

온라인 서브시스템이 온라인 업적 읽기를 마치면 InternalOnReadAchievementsComplete() (내부 업적 읽기 완료시 함수)를 호출합니다. 이 함수는 업적이 이미 풀렸는지를 검사합니다. 풀리지 않은 상태면 BeginUnlockAchievement() (압축 풀기 시작 함수)를 호출합니다. 풀려 있고 AlwaysUnlockAchievement (항상 업적 풀기)가 참이면, InternalOnUnlockAchievementComplete() (내부 업적 풀기 완료시 함수)를 호출합니다. BeginUnlockAchievement() (업적 풀기 시작 함수)는 업적 풀기를 마쳤을 때 호출되는 또다른 온라인 서브시스템 델리게이트로 바인딩합니다. 그 후 OnlineSubsystem.PlayerInterface.UnlockAchievement() (업적 풀기 함수)를 호출합니다.

업적 풀기가 끝났으면, FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료 함수)를 호출하는 InternalOnUnlockAchievementComplete() (내부 업적 풀기 완료시 함수)를 호출합니다.

CleanUp (청소)가 호출되면, 바인딩된 모든 델리게이트가 제거됩니다.

Steamworks 실행 흐름

InternalOnActivated() (내부 활성화시 함수)가 호출되면 BeginUnlockAchievement() (업적 풀기 시작 함수)가 호출됩니다. 이 함수는 업적 풀기 완료시 호출되는 다른 온라인 서브시스템 델리게이트에 바인딩됩니다. 그 후 OnlineSubsystem.PlayerInterface.UnlockAchievement() (업적 풀기 함수)가 호출됩니다.

업적 풀기가 끝나면, FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료 함수)를 호출하는 InternalOnUnlockAchievementComplete() (내부 업적 풀기 완료시) 함수가 호출됩니다.

CleanUp (청소)가 호출되면, 바인딩된 모든 델리게이트가 제거됩니다.

UnrealScript

SeqAct_UnlockAchievement.uc
/**
 * 업적을 푸는 데는 UnlockAchievement 액션이 사용됩니다.
 *
 * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
 */
class SeqAct_UnlockAchievement extends SeqAct_OnlineSubsystemBase;

// 풀고자 하는 업적 ID 입니다.
var() int AchievementId;

// ==========
// GameCenter
// ==========
`if(`isdefined(USE_GAMECENTER))
// 참이면 업적이 이미 풀린 것으로, 여전히 성공 링크는 출력합니다.
var() bool AlwaysUnlockAchievement;
// 모든 다운로드 업적 배열
var protected array<AchievementDetails> DownloadedAchievements;
`endif

/**
 * 이 시퀸스 액션이 로컬 유저 번호를 붙여 어떤 작업을 해야 하면 호출됩니다.
 */
protected function InternalOnActivated()
{
  local OnlineSubsystem OnlineSubsystem;

  // 온라인 서브시스템에 접속하여 업적 델리게이트를 링크합니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem != None && OnlineSubsystem.PlayerInterface != None)
  {
// ==========
// SteamWorks
// ==========
`if(`isdefined(USE_STEAMWORKS))
    // 업적 풀기를 시작합니다.
    BeginUnlockAchievement();

// ==========
// GameCenter
// ==========
`else
    // Game Center 에서는 업적 리스트를 먼저 읽은 다음 업적을 풀어야 합니다.
    // 업적 읽기 델리게이트를 할당합니다.
    OnlineSubsystem.PlayerInterface.AddReadAchievementsCompleteDelegate(ProcessingPlayers[0].LocalUserNum, InternalOnReadAchievementsComplete);

    // 모든 업적을 읽습니다.
    OnlineSubsystem.PlayerInterface.ReadAchievements(ProcessingPlayers[0].LocalUserNum);
`endif
  }
}

// ==========
// GameCenter
// ==========
`if(`isdefined(USE_GAMECENTER))
/**
 * 비동기 업적 읽기가 끝나면 호출됩니다.
 *
 * @param    TitleId      읽기 대상이었던 타이틀 ID 입니다. (0 은 현재 타이틀)
 */
protected function InternalOnReadAchievementsComplete(int TitleId)
{
  local OnlineSubsystem OnlineSubsystem;
  local int AchievementIndex;

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

  // 업적을 다운로드 업적 배열에 읽어들입니다.
  OnlineSubsystem.PlayerInterface.GetAchievements(ProcessingPlayers[0].LocalUserNum, DownloadedAchievements, TitleId);

  // 업적 인덱스를 구해옵니다.
  AchievementIndex = DownloadedAchievements.Find('Id', AchievementId);

  // 업적을 풉니다.
  if (AchievementIndex != INDEX_NONE)
  {
    // 아직 풀지 않았으니, 풀기 프로세스를 시작합니다.
    if (!DownloadedAchievements[AchievementIndex].bWasAchievedOnline)
    {
      BeginUnlockAchievement();
    }
    // 이미 풀었으니 끝냅니다.
    else if (AlwaysUnlockAchievement)
    {
      InternalOnUnlockAchievementComplete(true);
    }
  }
}
`endif

/**
 * 업적 풀기를 시작해야 하면 호출됩니다.
 *
 * @param    AchievementId      풀 업적입니다.
 * @param    LocalUserNum      로컬 유저 인덱스입니다.
 */
protected function BeginUnlockAchievement()
{
  local OnlineSubsystem OnlineSubsystem;

  // 온라인 서브시스템을 구합니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem == None || OnlineSubsystem.PlayerInterface == None)
  {
    return;
  }

  // 업적 풀기 완료 델리게이트를 할당합니다.
  OnlineSubsystem.PlayerInterface.AddUnlockAchievementCompleteDelegate(ProcessingPlayers[0].LocalUserNum, InternalOnUnlockAchievementComplete);

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

/**
 * 업적 풀기가 끝나면 호출됩니다.
 *
 * @param     bWasSuccessful      참이면 비동기 작업이 에러 없이 끝난 것이며, 거짓이면 에러가 생긴 것입니다.
 */
protected function InternalOnUnlockAchievementComplete(bool bWasSuccessful)
{
  // 이 업적 풀기가 끝났습니다.
  FinishedProcessPlayerIndex(bWasSuccessful);
}

/**
 * 가비지 콜렉션이 일어날 수 있도록 이 키즈멧 노드가 모든 변수나 델리게이트 리퍼런스를 모두 청소해야 할 때 호출됩니다.
 *
 * @param    IsExiting      빠져나가는 중이라면 모든 것을 청소합니다.
 */
function CleanUp(optional bool IsExiting)
{
  local OnlineSubsystem OnlineSubsystem;
  local int i, InPlayerIndex;

  // 온라인 서브시스템을 구한 다음 모든 델리게이트 바인딩을 제거합니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem != None && OnlineSubsystem.PlayerInterface != None)
  {
    // 분할 화면 로컬 플레이어 최대 수는 4 입니다.
    for (i = 0; i < 4; ++i)
    {
      // 플레이어 인덱스를 구합니다.
      InPlayerIndex = class'UIInteraction'.static.GetPlayerIndex(i);
      if (InPlayerIndex >= 0)
      {
      // 델리게이트를 지웁니다.
// ==========
// GameCenter
// ==========
`if(`isdefined(USE_GAMECENTER))
        OnlineSubsystem.PlayerInterface.ClearReadAchievementsCompleteDelegate(InPlayerIndex, InternalOnReadAchievementsComplete);
`endif
        OnlineSubsystem.PlayerInterface.ClearUnlockAchievementCompleteDelegate(InPlayerIndex, InternalOnUnlockAchievementComplete);
      }
    }
  }

  // 그냥 빠져나가려는 경우엔 super 를 호출합니다.
  Super.CleanUp(IsExiting);
}

defaultproperties
{
// ==========
// GameCenter
// ==========
`if(`isdefined(USE_GAMECENTER))
  AlwaysUnlockAchievement=true
`endif
  ObjName="Unlock Achievement"
  VariableLinks(1)=(ExpectedType=class'SeqVar_Int',LinkDesc="Achievement Id",PropertyName=AchievementId)
}

키즈멧 예제

KismetOnlineSubsystem_10.png


SeqAct_ModifyOnlineStat

Modify Online Stat (온라인 통계 수정)은 온라인 서브시스템이 사용하는 온라인 통계를 수정할 수 있는 키즈멧 시퀸스 액션입니다. Game Center 의 경우 이 시스템을 통해 순위표에서 사용할 수 있는 통계를 새로 업로드할 수 있습니다. Steamworks 의 경우 이 시스템을 통해 업적에 사용할 통계를 추가, 제거, 설정할 수 있습니다.

KismetOnlineSubsystem_01.png

KismetOnlineSubsystem_08.jpg

함수

  • InternalOnActivated 내부 활성화시 - 부모 클래스 SeqAct_OnlineSubsystemBase 에서 호출됩니다. Game Center 에서는 단지 SetStatValue() 를 호출하며, Steamworks 에서는 ModifyMethod 에 따라 (기존 통계에 더하거나 빼려는 경우엔) ReadOnlineStats() 아니면 SetStatValue() 를 호출합니다.
  • ReadOnlineStats 온라인 통계 읽기 - Steamworks 전용으로, 기존 통계에 더하거나 빼고자 할 때 호출됩니다. 온라인 통계를 읽은 다음, 완료되면 InternalOnReadOnlineStatsComplete() 를 호출합니다.
  • InternalOnReadOnlineStatsComplete 내부 온라인 통계 읽기 완료시 - 통계 읽기가 끝나면, 통계에 더하거나 뺀 후, 그 결과를 Steamworks 에 도로 보냅니다.
  • SetStatValue 통계 값 설정 - 통계를 설정하고 Game Center 나 Steamworks 에 도로 보냅니다.
  • InternalOnFlushOnlineStatsComplete 내부 온라인 통계 Flush 완료시 - Steamworks 전용으로, 온라인 데이터베이스에 통계 쓰기 완료시 호출됩니다.
  • CleanUp 청소 - 키즈멧 노드가 스스로 청소해야할 때 호출됩니다.

정의

  • SStat 통계 구조체 - 베이스 구조체 정의입니다. StatId 는 Game Center 나 Steamworks 의 통계 ID 를 가리킵니다. LinkedVariableName 으로는 이 통계 ID 를 붙은 키즈멧 변수와 연결할 수 있습니다.
  • SIntStat 정수 통계 구조체 - SStat 확장입니다. 통계를 더하거나 빼거나 설정할 변수를 정의합니다.
  • SFloatStat 실수 통계 구조체 - SStat 확장입니다. 통계를 더하거나 빼거나 설정할 변수를 정의합니다.
  • EModifyMethod 수정 방식 열거형 - 온라인 통계 수정 방식을 설정합니다.
  • OnlineStatsWriteClass 온라인 통계 쓰기 클래스 - 온라인 서브시스템에 쓸 때 어느 OnlineStatsWrite 클래스를 인스턴싱할 지 정의합니다.
  • OnlineStatsReadClass 온라인 통계 읽기 클래스 - 온라인 서브시스템에서 읽을 때 어느 OnlineStatsRead 클래스를 인스턴싱할지 정의합니다.
  • IntStats 정수 통계 - 온라인 서브시스템에 업로드할 통계 배열 정의입니다.
  • FloatStats 실수 통계 - 온라인 서브시스템에 업로드할 통계 배열 정의입니다.
  • ModifyMethod 수정 방식 - 온라인 통계 수정 방식을 정의합니다. Game Center 에서는 설정만 가능하고, Steamworks 에서는 설정에 더하기, 빼기도 가능합니다.
  • SessionName 세션 이름 - 세션 이름을 정의합니다. 기본값은 "Game" 입니다.

Game Center 실행 흐름

GameCenter 에서 InternalOnActivated() (내부 활성화시 함수)가 호출되면, 바로 SetStatValue() (통계 값 설정 함수)가 호출됩니다. 이 함수는 OnlineStatsWriteClass (온라인 통계 쓰기 클래스)에 정의된 클래스를 사용하여 OnlineStatsWrite 클래스의 인스턴스를 만듭니다. 그런 다음 IntStatsFloatStats (정수/실수 통계)를 대상으로 반복처리한 다음 그 통계 값을 OnlineStatsWrite 클래스의 인스턴스에 씁니다. 그런 다음 이 온라인 통계를 쓰고 FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료 함수)를 호출합니다.

CleanUp() (청소 함수)가 호출되면, 바인딩된 모든 델리게이트가 해제됩니다. 오브젝트 리퍼런스도 모두 지워집니다.

Steamworks 실행 흐름

Steamworks 에서는 InternalOnActivated() (내부 활성화시 함수)가 호출되면, ModifyMethod (수정 방식)에 따라 두 가지 함수 중 하나가 호출됩니다. ModifyMethod (수정 방식)이 MM_Add 나 MM_Subtract (더하기나 빼기)인 경우 ReadOnlineStats() (온라인 통계 읽기 함수)가 호출되는데, 온라인 통계는 일단 먼저 읽어야 지지든 볶든 할 수 있기 때문입니다. 그 외의 경우는 SetStatValue() (통계 값 설정 함수)가 호출됩니다.

ReadOnlineStats() (온라인 통계 읽기 함수)가 호출되면, 델리게이트가 바인딩되고 OnlineStatsRead (온라인 통계 읽기 함수)의 인스턴스가 만들어집니다. 그리고 OnlineSubsystem.StatsInterface.ReadOnlineStats() (온라인 통계 읽기 함수)가 호출됩니다. 온라인 통계 읽기가 끝나면, InternalOnReadOnlineStatsComplete (내부 온라인 통계 읽기 완료시 함수)가 호출됩니다. 이 함수는 곧이어 IntStatsFloatStats (정수/실수 통계) 배열을 대상으로 반복처리한 다음 통계 값을 수정합니다. 그 후 OnlineStatsWrite (온라인 통계 쓰기) 인스턴스에 수정된 통계를 쓰고, OnlineSubsystem.StatsInterface.WriteOnlineStats() (온라인 통계 쓰기 함수)를 호출하여 Steamworks 로 보냅니다. OnlineSubsystem.StatsInterface.WriteOnlineStats() (온라인 통계 쓰기 함수)가 전달되면, 델리게이트가 바인딩되고 OnlineSubsystem.StatsInterface.FlushOnlineStats() (온라인 통계 Flush 함수)가 호출됩니다. 온라인 통계가 flush 되면 FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료됨 함수)를 호출하는 InternalOnFlushOnlineStatsComplete() (내부 온라인 통계 Flush 완료시 함수)를 호출합니다.

SetStatValue() (통계 값 설정 함수)가 호출되면, IntStatsFloatStats (정수/실수 통계)를 대상으로 반복처리한 다음 통계 값을 설정합니다. 그 후 OnlineStatsWrite (온라인 통계 쓰기) 인스턴스에 수정된 통계 값을 쓰고, OnlineSubsystem.StatsInterface.WriteOnlineStats() (온라인 통계 쓰기 함수)를 호출하여 Steamworks 로 보냅니다. OnlineSubsystem.StatsInterface.WriteOnlineStats() (온라인 통계 쓰기 함수)가 전달되면, 델리게이트가 바인딩되고 OnlineSubsystem.StatsInterface.FlushOnlineStats() (온라인 통계 Flush 함수)가 호출됩니다. 온라인 통게 flush 가 끝나면, FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료됨 함수)를 호출하는 InternalOnFlushOnlineStatsComplete() (내부 온라인 통계 Flush 완료시 함수)를 호출합니다.

CleanUp() (청소 함수)가 호출되면, 바인딩된 모든 델리게이트가 해제됩니다. 오브젝트 리퍼런스도 모두 지워집니다.

UnrealScript

SeqAct_ModifyOnlineStat.uc
/**
 * Modify Online Stat 액션은 통계를 수정하고 업로드하는 데 사용됩니다.
 *
 * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
 */
class SeqAct_ModifyOnlineStat extends SeqAct_OnlineSubsystemBase;

// 통계 기반 구조체입니다.
struct SStat
{
  var() Name LinkedVariableName;
  var() int StatId;
};

// 정수 기반 통계입니다.
struct SIntStat extends SStat
{
  var() int Value;
};

// 실수 기반 통계입니다.
struct SFloatStat extends SStat
{
  var() float Value;
};

// 사용자가 원하는 통계 수정 메서드입니다.
enum EModifyMethod
{
  MM_Set<DisplayName=Set>,
// ==========
// Steamworks
// ==========
`if(`isdefined(USE_STEAMWORKS))
  MM_Add<DisplayName=Add>,
  MM_Subtract<DisplayName=Subtract>,
`endif
};

// 통계와 연결된 온라인 통계 쓰기 클래스입니다.
var() class<OnlineStatsWrite> OnlineStatsWriteClass;
// ==========
// Steamworks
// ==========
`if(`isdefined(USE_STEAMWORKS))
// 통계와 연결된 온라인 통계 읽기 클래스입니다.
var() class<OnlineStatsRead> OnlineStatsReadClass;
`endif
// 정수 기반 통계 배열입니다.
var() array<SIntStat> IntStats;
// 실수 기반 통계 배열입니다.
var() array<SFloatStat> FloatStats;
// 통계 수정 메서드입니다.
var() EModifyMethod ModifyMethod;
// 세션 이름입니다.
var() Name SessionName;

// 온라인 통계 쓰기 오브젝트 인스턴스입니다.
var protected OnlineStatsWrite OnlineStatsWrite;
// 온라인 통계 읽기 오브젝트 인스턴스입니다.
var protected OnlineStatsRead OnlineStatsRead;

/**
 *  이 시퀸스 액션이 로컬 유저 번호를 붙여 어떤 작업을 해야 할 때 호출됩니다.
 */
protected function InternalOnActivated()
{
  // 온라인 통계 쓰기 클래스가 없으면 중단합니다.
  // 열 ID 가 없으면 중단합니다.
  if (OnlineStatsWriteClass == None || IntStats.Length <= 0)
  {
    return;
  }

// ==========
// Steamworks
// ==========
`if(`isdefined(USE_STEAMWORKS))
  // 사용자가 더하기나 빼기를 사용하여 통계를 수정하려 합니다.
  if (ModifyMethod == MM_Add || ModifyMethod == MM_Subtract)
  {
    ReadOnlineStats();
  }
  // 사용자가 값을 설정하여 통계를 수정하려 합니다.
  else
  {
`endif
    SetStatValue();
// ==========
// Steamworks
// ==========
`if(`isdefined(USE_STEAMWORKS))
  }
`endif
}

// ==========
// Steamworks
// ==========
`if(`isdefined(USE_STEAMWORKS))
/**
 * 사용자가 증가나 감소를 사용하여 통계를 수정하려 할 때 호출됩니다.
 */
protected function ReadOnlineStats()
{
  local OnlineSubsystem OnlineSubsystem;
  local array<UniqueNetId> PlayerIds;

  // 온라인 통계 읽기를 시도합니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None)
  {
    OnlineSubsystem.StatsInterface.AddReadOnlineStatsCompleteDelegate(InternalOnReadOnlineStatsComplete);

    PlayerIds.AddItem(ProcessingPlayers[0].LocalNetId);
    OnlineStatsRead = new () OnlineStatsReadClass;
    if (OnlineStatsRead != None)
    {
      OnlineSubsystem.StatsInterface.ReadOnlineStats(PlayerIds, OnlineStatsRead);
    }
  }
}

/**
 * 온라인 통계 읽기가 끝나면 호출됩니다.
 *
 * @param    bWasSuccessful      참이면 온라인 통계 읽기가 성공한 것입니다.
 */
function InternalOnReadOnlineStatsComplete(bool bWasSuccessful)
{
  local OnlineSubsystem OnlineSubsystem;
  local int i, ReadIntStatValue;
  local float ReadFloatStatValue;
  local SeqVar_Int SeqVar_Int;
  local SeqVar_Float SeqVar_Float;

  // 통계 읽기에 성공했다면
  if (bWasSuccessful && OnlineStatsRead != None)
  {
    // 통계를 쓰고
    OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
    if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None)
    {
      // 통계 쓰기 오브젝트 인스턴스를 만듭니다.
      OnlineStatsWrite = new () OnlineStatsWriteClass;
      if (OnlineStatsWrite != None)
      {
        // 정수 통계 쓰기 값을 수정합니다.
        for (i = 0; i < IntStats.Length; ++i)
        {
          OnlineStatsRead.GetIntStatValueForPlayer(ProcessingPlayers[0].LocalNetId, IntStats[i].StatId, ReadIntStatValue);

          // 이 스테이트에 변수명이 있으면, 찾아봅니다.
          if (IntStats[i].LinkedVariableName != '' && IntStats[i].LinkedVariableName != 'None')
          {
            ForEach LinkedVariables(class'SeqVar_Int', SeqVar_Int)
            {
              if (SeqVar_Int.VarName == IntStats[i].LinkedVariableName)
              {
                switch (ModifyMethod)
                {
                case MM_Add:
                  OnlineStatsWrite.SetIntStat(IntStats[i].StatId, ReadIntStatValue + SeqVar_Int.IntValue);
                  break;

                case MM_Subtract:
                  OnlineStatsWrite.SetIntStat(IntStats[i].StatId, ReadIntStatValue - SeqVar_Int.IntValue);
                  break;

                default:
                  break;
                }
              }
            }
          }
          // 없으면, 구조체에 정의된 값을 사용합니다.
          else
          {
            switch (ModifyMethod)
            {
            case MM_Add:
              OnlineStatsWrite.SetIntStat(IntStats[i].StatId, ReadIntStatValue + IntStats[i].Value);
              break;

            case MM_Subtract:
              OnlineStatsWrite.SetIntStat(IntStats[i].StatId, ReadIntStatValue - IntStats[i].Value);
              break;

            default:
              break;
            }
          }
        }

        // 실수 통계 쓰기 값을 수정합니다.
        for (i = 0; i < FloatStats.Length; ++i)
        {
          OnlineStatsRead.GetFloatStatValueForPlayer(ProcessingPlayers[0].LocalNetId, FloatStats[i].StatId, ReadFloatStatValue);

          // 이 스테이트에 변수명이 있다면, 찾아봅니다.
          if (FloatStats[i].LinkedVariableName != '' && FloatStats[i].LinkedVariableName != 'None')
          {
            ForEach LinkedVariables(class'SeqVar_Float', SeqVar_Float)
            {
              if (SeqVar_Float.VarName == FloatStats[i].LinkedVariableName)
              {
                switch (ModifyMethod)
                {
                case MM_Add:
                  OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, ReadFloatStatValue + SeqVar_Float.FloatValue);
                  break;

                case MM_Subtract:
                  OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, ReadFloatStatValue - SeqVar_Float.FloatValue);
                  break;

                default:
                  break;
                }
              }
            }
          }
          // 없다면, 구조체에 정의된 값을 사용합니다.
          else
          {
            switch (ModifyMethod)
            {
            case MM_Add:
              OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, ReadFloatStatValue + FloatStats[i].Value);
              break;

            case MM_Subtract:
              OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, ReadFloatStatValue - FloatStats[i].Value);
              break;

            default:
              break;
            }
          }
        }

        // 통계 핸들러에 쓰기 요청을 보냅니다.
        if (OnlineSubsystem.StatsInterface.WriteOnlineStats(SessionName, ProcessingPlayers[0].LocalNetId, OnlineStatsWrite))
        {
          // flush 델리게이트를 추가합니다.
          OnlineSubsystem.StatsInterface.AddFlushOnlineStatsCompleteDelegate(InternalOnFlushOnlineStatsComplete);
          // 온라인 통계를 flush 합니다.
          OnlineSubsystem.StatsInterface.FlushOnlineStats(SessionName);
        }
        // Failed 출력 링크를 활성화시킵니다.
        else
        {
          ForceActivateOutput(3);
        }
      }
    }
  }
  else
  {
    FinishedProcessPlayerIndex(bWasSuccessful);
  }
}
`endif

/**
 * 이 키즈멧 노드가 통계 값을 직접 설정할 때 호출됩니다.
 */
protected function SetStatValue()
{
  local OnlineSubsystem OnlineSubsystem;
  local int i;
  local SeqVar_Int SeqVar_Int;
  local SeqVar_Float SeqVar_Float;

  // 통계를 씁니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None)
  {
    // 통계 쓰기 오브젝트의 인스턴스를 만듭니다.
    OnlineStatsWrite = new () OnlineStatsWriteClass;
    if (OnlineStatsWrite != None)
    {
      // 정수 통계 쓰기 값을 설정합니다.
      for (i = 0; i < IntStats.Length; ++i)
      {
        // 이 스테이트에 변수명이 있다면, 찾아봅니다.
        if (IntStats[i].LinkedVariableName != '' && IntStats[i].LinkedVariableName != 'None')
        {
          ForEach LinkedVariables(class'SeqVar_Int', SeqVar_Int)
          {
            if (SeqVar_Int.VarName == IntStats[i].LinkedVariableName)
            {
              OnlineStatsWrite.SetIntStat(IntStats[i].StatId, SeqVar_Int.IntValue);
              break;
            }
          }
        }
        // 없다면, 구조체에 정의된 값을 사용합니다.
        else
        {
          OnlineStatsWrite.SetIntStat(IntStats[i].StatId, IntStats[i].Value);
          break;
        }
      }

      // 실수 통계 쓰기 값을 수정합니다.
      for (i = 0; i < FloatStats.Length; ++i)
      {
        // 이 스테이트에 변수명이 있다면, 찾아봅니다.
        if (FloatStats[i].LinkedVariableName != '' && FloatStats[i].LinkedVariableName != 'None')
        {
          ForEach LinkedVariables(class'SeqVar_Float', SeqVar_Float)
          {
            if (SeqVar_Float.VarName == FloatStats[i].LinkedVariableName)
            {
              OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, SeqVar_Float.FloatValue);
            }
          }
        }
        // 없다면, 구조체에 정의된 값을 사용합니다.
        else
        {
          OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, FloatStats[i].Value);
        }
      }

      // 통계 핸들러에 쓰기 요청을 보냅니다.
      if (OnlineSubsystem.StatsInterface.WriteOnlineStats(SessionName, ProcessingPlayers[0].LocalNetId, OnlineStatsWrite))
      {
// ==========
// SteamWorks
// ==========
`if(`isdefined(USE_STEAMWORKS))
        // flush 델리게이트를 추가합니다.
        OnlineSubsystem.StatsInterface.AddFlushOnlineStatsCompleteDelegate(InternalOnFlushOnlineStatsComplete);
`endif
        // 온라인 통계를 flush 합니다.
        OnlineSubsystem.StatsInterface.FlushOnlineStats(SessionName);
// ==========
// GameCenter
// ==========
`if(`isdefined(USE_GAMECENTER))
        // 모든 오브젝트 레퍼런스를 비웁니다.
        OnlineStatsWrite = None;
        OnlineStatsRead = None;

        // 플레이어 인덱스 처리를 핸들링합니다.
        FinishedProcessPlayerIndex(true);
`endif
      }
      // Failed 출력 링크를 활성화시킵니다.
      else
      {
        ForceActivateOutput(3);
      }
    }
  }
}

`if(`isdefined(USE_STEAMWORKS))
/**
 * 통계 플러시 작업이 완료되었을 때 호출됩니다.
 *
 * @param SessionName 플러시 대상 통계를 가진 세션 이름입니다.
 * @param bWasSuccessful 참이면 비동기 작업이 에러 없이 끝난 것이고, 거짓이면 에러가 생긴 것입니다.
 */
function InternalOnFlushOnlineStatsComplete(Name InSessionName, bool bWasSuccessful)
{
  // 모든 오브젝트 리퍼런스를 비웁니다.
  OnlineStatsWrite = None;
  OnlineStatsRead = None;

  // 플레이어 인덱스 처리를 핸들링합니다.
  FinishedProcessPlayerIndex(bWasSuccessful);
}
`endif

/**
 * 가비지 콜렉션이 일어날 수 있도록 이 키즈멧 노드가 모든 변수나 델리게이트 리퍼런스를 청소해야 할 때 호출됩니다.
 *
 * @param    IsExiting      빠져나가는 중이면 모든 것을 청소합니다.
 */
function CleanUp(optional bool IsExiting)
{
// ==========
// SteamWorks
// ==========
`if(`isdefined(USE_STEAMWORKS))
  local OnlineSubsystem OnlineSubsystem;
  local int i, InPlayerIndex;

  // 온라인 서브시스템을 구하고 모든 델리게이트 바인딩을 제거합니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None)
  {
    // 분할 화면 로컬 플레이어 최대 수는 4 입니다.
    for (i = 0; i < 4; ++i)
    {
      // 플레이어 인덱스를 구합니다.
      InPlayerIndex = class'UIInteraction'.static.GetPlayerIndex(i);
      if (InPlayerIndex >= 0)
      {
        // 델리게이트를 비웁니다.
        OnlineSubsystem.StatsInterface.ClearFlushOnlineStatsCompleteDelegate(InternalOnFlushOnlineStatsComplete);
        OnlineSubsystem.StatsInterface.ClearReadOnlineStatsCompleteDelegate(InternalOnReadOnlineStatsComplete);
      }
    }
  }
`endif

  // 온라인 통계 쓰기 오브젝트를 비웁니다.
  OnlineStatsWrite = None;
  // 온라인 통계 읽기 오브젝트를 비웁니다.
  OnlineStatsRead = None;

  // 딱 빠져나가는 경우라면 super 를 호출합니다.
  Super.CleanUp(IsExiting);
}

defaultproperties
{
  SessionName="Game"
  ObjName="Modify Online Stat"

  VariableLinks(1)=(ExpectedType=class'SeqVar_Int',LinkDesc="Stat Values")
}

키즈멧 예제

KismetOnlineSubsystem_11.png


Steam Works 전용 키즈멧 노드


SeqAct_RefreshAchievements

이 (업적 새로고침) 키즈멧 시퀸스 액션은, 클라이언트가 그 업적을 Steamworks 에서 읽어들여 강제 업데이트를 시도합니다.

KismetOnlineSubsystem_02.png

함수

  • InternalOnActivated 내부 활성화시 - 이 함수가 호출되면, 델리게이트에 바인딩되고 OnlineSubsystem.PlayerInterface.ReadAchievements() (업적 읽기 함수)가 호출됩니다.
  • InternalOnReadAchievementsComplete 내부 업적 읽기 완료시 - OnlineSubsystem.PlayerInterface.ReadAchievements() (업적 읽기 함수)가 Steamworks 에서 업적 읽기를 마쳤을 때 호출되는 함수입니다. FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료됨 함수)를 호출합니다.
  • CleanUp 청소 - 바인딩된 델리게이트를 모두 청소하는 함수입니다.

실행 흐름

InternalOnActivated() (내부 활성화시 함수)가 호출되면, 먼저 OnlineSubsystem.PlayerInterface 의 델리게이트에 바인딩됩니다. 이 델리게이트는 온라인 서브시스템이 Steamworks 에서 업적 읽기를 마치면 호출됩니다. 그 후 OnlineSubsystem.PlayerInterface.ReadAchievements() (업적 읽기 함수)가 호출됩니다. 업적 읽기가 끝나면, FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료됨 함수)를 호출하는 InternalOnReadAchievementsComplete() (내부 업적 읽기 완료시 함수)가 호출됩니다.

CleanUp() (청소 함수)가 호출되면, 바인딩된 델리게이트가 전부 해제됩니다.

UnrealScript

SeqAct_RefreshAchievements.uc
/**
 * Refresh Achievements 액션은 모든 업적을 새로고치는 데 사용됩니다.
 *
 * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
 */
class SeqAct_RefreshAchievements extends SeqAct_OnlineSubsystemBase
`if(`notdefined(USE_STEAMWORKS))
  abstract
  HideDropDown;
`else
  ;

/**
 * 이 시퀸스 액션이 로컬 유저 번호를 붙여 어떤 작업을 해야 할 때 호출됩니다.
 */
protected function InternalOnActivated()
{
  local OnlineSubSystem OnlineSubSystem;

  OnlineSubSystem = class'GameEngine'.static.GetOnlineSubSystem();
  if (OnlineSubSystem != None && OnlineSubSystem.PlayerInterface != None)
  {
    // 델리게이트를 바인딩합니다.
    OnlineSubSystem.PlayerInterface.AddReadAchievementsCompleteDelegate(ProcessingPlayers[0].LocalUserNum, InternalOnReadAchievementsComplete);

    // 모든 업적을 읽습니다.
    OnlineSubsystem.PlayerInterface.ReadAchievements(ProcessingPlayers[0].LocalUserNum);
  }
}

/**
 * 비동기 업적 읽기가 끝나면 호출됩니다.
 *
 * @param TitleId 읽기 대상이었던 타이틀 ID 입니다. (0 은 현재 타이틀)
 */
function InternalOnReadAchievementsComplete(int TitleId)
{
  FinishedProcessPlayerIndex(true);
}

/**
 * 가비지 콜렉션이 일어날 수 있도록 이 키즈멧 노드가 모든 변수나 델리게이트 리퍼런스를 청소해야 하면 호출됩니다.
 *
 * @param    IsExiting      빠져나가는 중이라면 모든 것을 청소합니다.
 */
function CleanUp(optional bool IsExiting)
{
  local OnlineSubsystem OnlineSubsystem;
  local int i, InPlayerIndex;

  // 온라인 서브시스템을 구하고 모든 델리게이트 바인딩을 지웁니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem != None && OnlineSubsystem.PlayerInterface != None)
  {
    // 분할 화면 로컬 플레이어 최대치는 4 입니다.
    for (i = 0; i < 4; ++i)
    {
      // 플레이어 인덱스를 구합니다.
      InPlayerIndex = class'UIInteraction'.static.GetPlayerIndex(i);
      if (InPlayerIndex >= 0)
      {
        // 델리게이트를 비웁니다.
        OnlineSubsystem.PlayerInterface.ClearReadAchievementsCompleteDelegate(InPlayerIndex, InternalOnReadAchievementsComplete);
      }
    }
  }

  // 딱 빠져나가는 경우라면 super 를 호출합니다.
  Super.CleanUp(IsExiting);
}
`endif

defaultproperties
{
  ObjName="Refresh Achievements"
}


SeqAct_ResetAchievements

이 (업적 리셋) 키즈멧 시퀸스 액션은 플레이어의 저장된 업적 및/또는 통계를 리셋시키는 데 사용됩니다. Steamworks 온라인 서브시스템과 직접 바인딩되는 액션이니, 디버깅 용으로만 사용해야 합니다.

KismetOnlineSubsystem_03.png

함수

  • InternalOnActivated 내부 활성화시 - 호출되면 OnlineSubsystemSteamworks.ResetStats() (통계 리셋 함수)를 호출합니다.

정의

  • ResetAchievements 업적 리셋 - 참이면 업적도 리셋시킵니다.

실행 흐름

InternalOnActivated() (내부 활성화시 함수)가 호출되면, OnlineSubsystemSteamworks.ResetStats() (통계 리셋 함수)가 호출됩니다. 그 후 FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료됨 함수)가 호출됩니다.

UnrealScript

SeqAct_ResetAchievements.uc
/**
 * Reset Achievements 액션은 플레이어가 얻은 업적 전부를 리셋시키는 데 사용됩니다.
 *
 * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
 */
class SeqAct_ResetAchievements extends SeqAct_OnlineSubsystemBase
`if(`notdefined(USE_STEAMWORKS))
  abstract
  HideDropDown;
`else
  ;

// 참이면 업적도 리셋됩니다. 거짓이면 통계만 리셋됩니다.
var() const bool ResetAchievements;

/**
 * 이 시퀸스 액션이 로컬 유저 번호를 붙여 어떤 작업을 해야 할 때 호출됩니다.
 */
protected function InternalOnActivated()
{
  local OnlineSubsystemSteamworks OnlineSubsystemSteamworks;
  local bool bWasSuccessful;

  OnlineSubsystemSteamworks = OnlineSubsystemSteamworks(class'GameEngine'.static.GetOnlineSubsystem());
  if (OnlineSubsystemSteamworks != None)
  {
    bWasSuccessful = OnlineSubsystemSteamworks.ResetStats(ResetAchievements);
  }
  else
  {
    bWasSuccessful = false;
  }

  FinishedProcessPlayerIndex(bWasSuccessful);
}
`endif

defaultproperties
{
`if(`isdefined(USE_STEAMWORKS))
  ResetAchievements=true
`endif
  ObjName="Reset Achievements/Stats"
}

키즈멧 예제

KismetOnlineSubsystem_12.png


SeqAct_ReadOnlineStat

이 (온라인 통계 읽기) 키즈멧 액션은 온라인 통계를 읽어 나중에 키즈멧 안에서 사용하도록 할 수 있습니다.

KismetOnlineSubsystem_04.png

KismetOnlineSubsystem_09.jpg

함수

  • InternalOnActivated 내부 활성화시 - 부모 클래스 SeqAct_OnlineSubsystemBase 에 의해 호출됩니다. 그러면 Steamworks 에서 통계를 읽는 호출을 시작합니다.
  • InternalOnReadOnlineStatsComplete 내부 온라인 통계 읽기 완료시 - Steamworks 에서 통계를 다 읽으면 호출됩니다. 최종적으로는 FinishedProcessPlayerIndex() 를 호출합니다.
  • CleanUp 청소 - 이 키즈멧 시퀸스 액션이 사용한 델리게이트나 오브젝트 리퍼런스를 청소하는 함수입니다.

정의

  • EReadStatsMethod 통계 읽기 방식 열거형 - 다양한 통계 읽기 방식을 정의하는 열거형입니다.
  • SLinkedVariableName 링크된 변수 이름 구조체 - 읽고자 하는 통계에 대한 ID, 붙은 키즈멧 변수로 링크되는 이름이 담긴 구조체입니다.
  • OnlineStatsReadClass 온라인 통계 읽기 클래스 - 온라인 통계를 읽는 데 사용할 클래스입니다.
  • ReadStatsMethod 통계 읽기 방법 - EReadStatsMethod 의 변수 선언입니다.
  • StartRankIndex 시작 랭크 인덱스 - 랭크 통계 읽기 메서드를 사용하는 경우에 사용할 시작 랭크 인덱스입니다.
  • RowCount 줄 수 - 랭크 통계 읽기나 플레이어 주변 랭크 메서드 사용시 읽어들일 줄 수입니다.
  • LinkedVariableNames 링크 변수 이름 - 결과를 출력할 링크 변수입니다.
  • Rank 랭크 - 키즈멧 변수에 매핑되는 랭크 변수입니다.

실행 흐름

InternalOnActivated() (내부 활성화시 함수)가 호출되면 먼저 OnlineStatsReadClass (온라인 통계 읽기 클래스)에 정의된 클래스에 따라서 OnlineStatsRead (온라인 통계 읽기 클래스)의 인스턴스를 만듭니다. 그리고서 OnlineStatsRead (온라인 통계 읽기)를 비우기 위해 OnlineSubsystem.StatsInterface.FreeStats() (통계 해제 함수)가 호출됩니다. 그런 다음 델리게이트가 바인딩되고, 온라인 통계 읽기 요청을 합니다. 온라인 통계를 읽는 방식은 ReadStatsMethod (통계 읽기 방식) 변수로 정의됩니다. 여기서 OnlineSubsystem.StatsInterface.ReadOnlineStatsForFriends(), OnlineSubsystem.StatsInterface.ReadOnlineStatsByRank(), OnlineSubsystem.StatsInterface.ReadOnlineStatsByRankAroundPlayer(), OnlineSubsystem.StatsInterface.ReadOnlineStats() (친구/순위/플레이어 주변 순위/전체 온라인 통계 읽기 함수) 중 하나를 호출합니다. 온라인 통계 읽기가 끝나면 InternalOnReadOnlineStatsComplete() (내부 온라인 통계 읽기 완료시) 함수가 호출되는데, 이 함수는 통계를 구하여 붙은 키즈멧 시퀸스 변수에다 출력하는 작업을 담당합니다. Rank (랭크)도 여기서 설정됩니다. 매핑된 통계 프로퍼티를 반대편으로 복사하기 위해 PopulateLinkedVariableValues() (링크 변수 값 채우기 함수)가 호출됩니다. 그 후 FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료됨 함수)가 호출됩니다.

CleanUp() (청소 함수)가 호출되면, 바인딩되거나 오브젝트 인스턴스가 있는 델리게이트는 전부 해제됩니다.

UnrealScript

SeqAct_ReadOnlineStat.uc
/**
 * Read Online Stat 액션은 온라인 통계를 읽는 데 사용됩니다.
 *
 * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
 */
class SeqAct_ReadOnlineStat extends SeqAct_OnlineSubsystemBase
// ==========
// GameCenter
// ==========
`if(`isdefined(USE_GAMECENTER))
  abstract
  HideDropdown;
`else
  ;
//
enum EReadStatsMethod
{
  RST_ReadAll<DisplayName=Read all stats>,
  RST_ReadFriendsOnly<DisplayName=Read stats of friends>,
  RST_ReadByRank<DisplayName=Read stats by rank>,
  RST_ReadByRankAroundPlayer<DisplayName=Read stats around player>
};

//
struct SLinkedVariableName
{
  var() int StatId;
  var() Name LinkedVariableName;
};

// 통계를 읽는 데 사용할 클래스입니다.
var() class<OnlineStatsRead> OnlineStatsReadClass;
// 통계 읽기 메서드입니다.
var() EReadStatsMethod ReadStatsMethod;
// 랭킹 통계 읽기 메서드를 사용하는 경우, 사용할 시작 랭크 인덱스 입니다.
var() int StartRankIndex;
// 랭킹 통계 읽기나 플레이어 근처 랭킹 메서드를 사용하는 경우에 몇 줄이나 읽을지 입니다.
var() int RowCount;
// 출력시킬 링크 변수 입니다.
var() array<SLinkedVariableName> LinkedVariableNames;

// 온라인 통계 읽기 오브젝트의 인스턴스를 만듭니다.
var protected OnlineStatsRead OnlineStatsRead;
// 키즈멧 변수에 매핑되는 랭크 변수입니다.
var int Rank;

/**
 * 이 시퀸스 액션이 로컬 유저 번호를 붙여 어떤 작업을 해야 할 때 호출됩니다.
 */
protected function InternalOnActivated()
{
  local OnlineSubSystem OnlineSubSystem;
  local array<UniqueNetId> PlayerIds;

  // 온라인 통계 읽기 클래스가 없으면 중단합니다.
  // 열 ID 가 없으면 중단합니다.
  if (OnlineStatsReadClass == None || LinkedVariableNames.Length <= 0)
  {
    return;
  }

  // 통계를 요청합니다.
  OnlineSubSystem = class'GameEngine'.static.GetOnlineSubSystem();
  if (OnlineSubSystem != None && OnlineSubSystem.StatsInterface != None)
  {
    // 온라인 통계 인스턴스를 만듭니다.
    OnlineStatsRead = new () OnlineStatsReadClass;
    if (OnlineStatsRead != None)
    {
      // 통계를 해제합니다.
      OnlineSubsystem.StatsInterface.FreeStats(OnlineStatsRead);

      // 온라인 통계 읽기 델리게이트를 바인딩합니다.
      OnlineSubsystem.StatsInterface.AddReadOnlineStatsCompleteDelegate(InternalOnReadOnlineStatsComplete);

      switch (ReadStatsMethod)
      {
      // 친구 통계만 읽습니다.
      case RST_ReadFriendsOnly:
        OnlineSubsystem.StatsInterface.ReadOnlineStatsForFriends(ProcessingPlayers[0].LocalUserNum, OnlineStatsRead);
        break;

      // 랭킹별 통계만 읽습니다.
      case RST_ReadByRank:
        OnlineSubsystem.StatsInterface.ReadOnlineStatsByRank(OnlineStatsRead, StartRankIndex, RowCount);
        break;

      // 플레이어 주변 통계만 읽습니다.
      case RST_ReadByRankAroundPlayer:
        OnlineSubsystem.StatsInterface.ReadOnlineStatsByRankAroundPlayer(ProcessingPlayers[0].LocalUserNum, OnlineStatsRead, RowCount);
        break;

        // 모든 통계를 읽습니다.
      case RST_ReadAll:
      default:
        PlayerIds.AddItem(ProcessingPlayers[0].LocalNetId);
        OnlineSubsystem.StatsInterface.ReadOnlineStats(PlayerIds, OnlineStatsRead);
        break;
      }
    }
  }
}

/**
 * 관련된 파티에 지난 통계 읽기가 끝났음을 알립니다.
 *
 * @param bWasSuccessful 참이면 비동기 작업이 에러 없이 끝난 것이고, 거짓이면 에러가 생긴 것입니다.
 */
function InternalOnReadOnlineStatsComplete(bool bWasSuccessful)
{
  local int i;
  local SeqVar_Int SeqVar_Int;
  local SeqVar_Float SeqVar_Float;

  if (OnlineStatsRead != None)
  {
    // 처리중인 플레이어에 대한 통계 ID 를 출력합니다.
    if (LinkedVariableNames.Length > 0)
    {
      for (i = 0; i < LinkedVariableNames.Length; ++i)
      {
        if (LinkedVariableNames[i].LinkedVariableName != '' && LinkedVariableNames[i].LinkedVariableName != 'None')
        {
          // 정수 통계를 시퀸스 변수로 복사합니다.
          ForEach LinkedVariables(class'SeqVar_Int', SeqVar_Int)
          {
            if (SeqVar_Int.VarName == LinkedVariableNames[i].LinkedVariableName)
            {
              OnlineStatsRead.GetIntStatValueForPlayer(ProcessingPlayers[0].LocalNetId, LinkedVariableNames[i].StatId, SeqVar_Int.IntValue);
            }
          }

          // 실수 통계를 시퀸스 변수로 복사합니다.
          ForEach LinkedVariables(class'SeqVar_Float', SeqVar_Float)
          {
            if (SeqVar_Float.VarName == LinkedVariableNames[i].LinkedVariableName)
            {
              OnlineStatsRead.GetFloatStatValueForPlayer(ProcessingPlayers[0].LocalNetId, LinkedVariableNames[i].StatId, SeqVar_Float.FloatValue);
            }
          }
        }
      }
    }

    // 순위를 출력합니다.
    Rank = OnlineStatsRead.GetRankForPlayer(ProcessingPlayers[0].LocalNetId);
    // 링크 변수를 채웁니다.
    PopulateLinkedVariableValues();
  }

  // 플레이어 처리 완료입니다.
  FinishedProcessPlayerIndex(bWasSuccessful);
  // 온라인 통계 읽기로의 리퍼런스를 제거합니다.
  OnlineStatsRead = None;
}

/**
 * 가비지 콜렉션이 일어날 수 있도록 이 키즈멧 노드가 모든 변수나 델리게이트 리퍼런스를 청소해야 하면 호출됩니다.
 *
 * @param    IsExiting      빠져나가는 중이라면 모든 것을 청소합니다.
 */
function CleanUp(optional bool IsExiting)
{
  local OnlineSubsystem OnlineSubsystem;
  local int i, InPlayerIndex;

  // 온라인 서브시스템을 구하고 모든 델리게이트 바인딩을 지웁니다.
  OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem();
  if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None)
  {
    // 분할 화면 로컬 플레이어 최대치는 4 입니다.
    for (i = 0; i < 4; ++i)
    {
      // 플레이어 인덱스를 구합니다.
      InPlayerIndex = class'UIInteraction'.static.GetPlayerIndex(i);
      if (InPlayerIndex >= 0)
      {
        // 델리게이트를 비웁니다.
        OnlineSubsystem.StatsInterface.ClearReadOnlineStatsCompleteDelegate(InternalOnReadOnlineStatsComplete);
      }
    }
  }

  // 온라인 통계 읽기 오브젝트를 비웁니다.
  OnlineStatsRead = None;

  // 딱 빠져나가는 경우라면 super 를 호출합니다.
  Super.CleanUp(IsExiting);
}
`endif

defaultproperties
{
  ObjName="Read Online Stat"

// ==========
// SteamWorks
// ==========
`if(`isdefined(USE_STEAMWORKS))
  StartRankIndex=1
  RowCount=50
`endif

  VariableLinks(1)=(ExpectedType=class'SeqVar_Int',LinkDesc="Stat Values",bWriteable=true)
  VariableLinks(2)=(ExpectedType=class'SeqVar_Int',LinkDesc="Rank",bWriteable=true,PropertyName=Rank)
}

키즈멧 예제

KismetOnlineSubsystem_13.png


Game Center 전용 키즈멧 노드


SeqAct_Mobile_ShowAchievementsUI

이 키즈멧 시퀸스 액션이 활성화되면, Game Center 의 Achievements UI 를 불러옵니다. 보통 게임을 자동으로 일시정지시킵니다.

KismetOnlineSubsystem_05.png

함수

  • InternalOnActivated 내부 활성화시 - 이 함수가 호출되면 OnlineSubSystem.PlayerInterfaceEx.ShowAchievementsUI() (업적 UI 표시 함수)를 호출합니다.

실행 흐름

부모 클래스 SeqAct_OnlineSubsystemBase (온라인 서브시스템 베이스 시퀸스 액션)에서 InternalOnActivated() (내부 활성화시 함수)가 호출되면, OnlineSubSystem.PlayerInterfaceEx.ShowAchievementsUI() (업적 UI 표시 함수)를 호출합니다. 이이서 FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료됨 함수)를 호출합니다.

UnrealScript

SeqAct_Mobile_ShowAchievementsUI.uc
/**
 * Mobile Show Achievements UI 액션은 업적 UI 를 표시하는 데 사용됩니다.
 *
 * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
 */
class SeqAct_Mobile_ShowAchievementsUI extends SeqAct_OnlineSubsystemBase
`if(`notdefined(USE_GAMECENTER))
  abstract
  HideDropDown;
`else
  ;

/**
 * 이 시퀸스 액션이 로컬 유저 번호를 붙여 어떤 작업을 해야 할 때 호출됩니다.
 */
protected function InternalOnActivated()
{
  local OnlineSubsystem OnlineSubsystem;

  // 온라인 서브시스템을 구합니다.
  OnlineSubSystem = class'GameEngine'.static.GetOnlineSubsystem();
  // 온라인 서브시스템과 플레이어 인터페이스 ex 가 접근가능한지 검사합니다.
  if (OnlineSubSystem != None && OnlineSubSystem.PlayerInterfaceEx != None)
  {
    OnlineSubSystem.PlayerInterfaceEx.ShowAchievementsUI(ProcessingPlayers[0].LocalUserNum);
    FinishedProcessPlayerIndex(true);
  }
  else
  {
    FinishedProcessPlayerIndex(false);
  }
}
`endif

defaultproperties
{
  ObjName="Show Achievements UI"
}

키즈멧 예제

KismetOnlineSubsystem_14.png


SeqAct_Mobile_ShowLeaderboardsUI

이 키즈멧 시퀸스 액션이 활성화되면, Game Center 의 Leaderboards UI 를 불러옵니다. 보통 게임을 자동으로 일시정지시킵니다.

KismetOnlineSubsystem_06.png

함수

  • InternalOnActivated 내부 활성화시 - 이 함수가 호출되면 OnlineStatsReadClass (온라인 통계 읽기 클래스) 값에 따라 OnlineStatsRead (온라인 통계 읽기 클래스)의 인스턴스를 만듭니다. 그 후 Game Center 의 순위표 UI 를 불러오기 위해 OnlineSuppliedUIInterface.ShowOnlineStatsUI() (온라인 통계 UI 표시 함수)가 호출됩니다. UI 가 닫힐 때 InternalOnShowOnlineStatsUIComplete (내부 온라인 통계 UI 표시 완료시) 를 호출하는 델리게이트도 바인딩됩니다.
  • InternalOnShowOnlineStatsUIComplete 내부 온라인 통계 UI 표시 완료시 - 순위표 UI 가 닫히면 호출됩니다.
  • CleanUp 청소 - 바인딩된 델리게이트와 오브젝트 리퍼런스를 모두 해제하기 위해 호출됩니다.p all bound delegates and object references.

정의

  • OnlineStatsReadClass 온라인 통계 읽기 클래스 - 온라인 통계를 읽는 데 사용하고자 하는 OnlineStatsRead 클래스입니다.

실행 흐름

부모 클래스 SeqAct_OnlineSubsystemBase (온라인 서브시스템 베이스 시퀸스 액션)에서 InternalOnActivated() (내부 활성화시 함수)가 호출되면 OnlineStatsReadClass (온라인 통계 읽기 함수)에 정의된 OnlineReadStats (온라인 통계 읽기) 클래스의 인스턴스를 만듭니다. 순위표 UI 가 닫히는 것을 감지하기 위해 델리게이트가 바인딩됩니다. 그 후 순위표 UI 를 읽고 표시하기 위해 OnlineSuppliedUIInterface.ShowOnlineStatsUI() (온라인 통계 UI 표시 함수)가 호출됩니다. 그리고 FinishedProcessPlayerIndex() (플레이어 인덱스 처리 완료됨 함수)가 호출됩니다.

InternalOnShowOnlineStatsUIComplete() (내부 온라인 통계 UI 표시 완료시 함수)가 호출되면, "Closed" 출력이 활성화됩니다.

CleanUp() (청소 함수)가 호출되면, 바인딩된 델리게이트가 전부 해제됩니다.

UnrealScript

SeqAct_Mobile_ShowLeaderboardsUI.uc
/**
 * Mobile Show Leaderboards UI 액션은 순위표 UI 를 표시하는 데 사용됩니다.
 *
 * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved.
 */
class SeqAct_Mobile_ShowLeaderboardsUI extends SeqAct_OnlineSubsystemBase
`if(`notdefined(USE_GAMECENTER))
  abstract
  HideDropDown;
`else
  ;

var() const class<OnlineStatsRead> OnlineStatsReadClass;

/**
 * 이 시퀸스 액션이 로컬 유저 번호를 붙여 어떤 작업을 해야 할 때 호출됩니다.
 */
protected function InternalOnActivated()
{
  local OnlineSubsystem OnlineSubsystem;
  local OnlineSuppliedUIInterface OnlineSuppliedUIInterface;
  local array<UniqueNetId> PlayerIds;
  local OnlineStatsRead OnlineStatsRead;
  local int i;

  // 클래스가 없으면 중단합니다.
  if (OnlineStatsReadClass == None)
  {
    return;
  }

  // 온라인 서브시스템을 구합니다.
  OnlineSubSystem = class'GameEngine'.static.GetOnlineSubsystem();
  // 온라인 서브시스템이 접근가능한지 확인합니다.
  if (OnlineSubSystem != None)
  {
    // 모든 처리중인 플레이어 배열에서 PlayerIds 배열을 만듭니다.
    for (i = 0; i < ProcessingPlayers.Length; ++i)
    {
      PlayerIds.AddItem(ProcessingPlayers[i].LocalNetId);
    }

    // 온라인 제공 UI 인터페이스를 구합니다.
    OnlineSuppliedUIInterface = OnlineSuppliedUIInterface(OnlineSubSystem.GetNamedInterface('SuppliedUI'));
    if (OnlineSuppliedUIInterface != None)
    {
      // 온라인 통계 읽기 클래스의 인스턴스를 만듭니다.
      OnlineStatsRead = new () OnlineStatsReadClass;
      if (OnlineStatsRead != None)
      {
        // 온라인 통계 UI 델리게이트에 바인딩합니다.
        OnlineSuppliedUIInterface.AddShowOnlineStatsUICompleteDelegate(InternalOnShowOnlineStatsUIComplete);

        // 온라인 통계 UI 를 표시합니다.
        OnlineSuppliedUIInterface.ShowOnlineStatsUI(PlayerIds, OnlineStatsRead);
        FinishedProcessPlayerIndex(true, true);
      }
    }
  }
  else
  {
    FinishedProcessPlayerIndex(false);
  }
}

/**
 * 제공된 통계 UI 가 닫힐 때 발동되는 델리게이트 입니다.
 */
function InternalOnShowOnlineStatsUIComplete()
{
  // 넷째 출력을 활성화시킵니다.
  ForceActivateOutput(4);
}

/**
 * 가비지 콜렉션이 일어날 수 있도록 이 키즈멧 노드가 모든 변수나 델리게이트 리퍼런스를 청소해야 하면 호출됩니다.
 *
 * @param    IsExiting      빠져나가는 중이라면 모든 것을 청소합니다.
 */
function CleanUp(optional bool IsExiting)
{
  local OnlineSubsystem OnlineSubsystem;
  local OnlineSuppliedUIInterface OnlineSuppliedUIInterface;

  Super.CleanUp(IsExiting);

  if (IsExiting)
  {
    // 온라인 서브시스템을 구합니다.
    OnlineSubSystem = class'GameEngine'.static.GetOnlineSubsystem();
    // Check that the online subsystem is accessible
    if (OnlineSubSystem != None)
    {
      // 온라인 제공 UI 인터페이스를 구합니다.
      OnlineSuppliedUIInterface = OnlineSuppliedUIInterface(OnlineSubSystem.GetNamedInterface('SuppliedUI'));
      if (OnlineSuppliedUIInterface != None)
      {
        OnlineSuppliedUIInterface.ClearShowOnlineStatsUICompleteDelegate(InternalOnShowOnlineStatsUIComplete);
      }
    }
  }
}
`endif

defaultproperties
{
  ObjName="Show Leaderboards UI"

  OutputLinks(4)=(LinkDesc="Closed")
}

키즈멧 예제

KismetOnlineSubsystem_15.png

내려받기