This document is about: QUANTUM 2
SWITCH TO

유니티 플러그인


Available in the Gaming Circle and Industries Circle
Circle

시작하는 방법

유니티 플러그인 설치

유니티 패키지를 다운로드하고 설치합니다(SDK에 대한 접근 권한이 부여되면 다운로드 링크를 받게 됩니다). Assets/Import package/Custom Package와 함께 사용할 때처럼 패키지를 임포트 합니다.

여기에는 게임 클라이언트 내에서 토너먼트를 운영하는 데 필요한 dll들이 포함되어 있습니다.

새로운 게임 생성하고 gameId 설정

모든 파일들을 게임/프로젝트로 가져오면 게임 ID를 만들어야 합니다. 이렇게 하려면 토너먼트 관리 화면에 로그인하여 새 게임 추가 버튼을 클릭하고 새 게임을 만들 때까지 마법사 단계를 진행하세요.

add game image

game id를 찾을 수 있는 Game settings/General을 오픈합니다.

get gameid image

다음은, 유니티 프로젝트에서, 임포트 한 파일 이름을 찾습니다: Plugins/Gimmebreak.Backbone/Resources/ 폴더에는 BackboneClientSetting.asset 이 있어야 합니다. 파일을 열고 아래에 표시된 game id를 복사합니다.

set gameid image

SDK 클라이언트 초기화

씬 객체에 BackboneManager 스크립트를 추가합니다. Unity의 Start()가 호출될 때 클라이언트가 초기화되도록 하려면 시작 시 초기화라고 표시된 상자를 선택합니다.

add backbone manager image
set backbone manager image
클라이언트를 수동으로 초기화하려는 경우(예: 인터넷 연결을 확인한 후) 나중에 명시적 호출을 통해 초기화할 수 있습니다.

C#

public class MyMonoBehaviour : MonoBehaviour
{
    //EXAMPLE 1, using callback
    public void ExplicitInitializeCall()
    {
        //initializing Backbone (TournamentSDK) client
        BackboneManager.Initialize()
            .ResultCallback((result) => {
                if(result)
                {
                    //success
                }
                else
                {
                    //fail
                }
            })
            .Run(this);
    }
    
    //EXAMPLE 2, using Unity coroutine
    public IEnumerator ExplicitInitializeCallCoroutine()
    {
        //some other code initialization
        //...
        //waiting for internet connectivity
        //...
        //initializing Backbone (TournamentSDK) client
        AsyncOperation<bool> asyncOperation = BackboneManager.Initialize();
        //wait until initialization is done
        yield return asyncOperation;
        //check result
        if(asyncOperation.ReturnValue)        
        {
            //success
        }
        else
        {
            //fail
        }
    }
}

BackboneManager.IsInitialized를 사용하여 클라이언트가 초기화되었는지를 확인할 수 있습니다.

사용자 로그인

관리 화면에서 로그인 제공자 사용

토너먼트 관리 화면을 열고 Game Settings/Authentication providers로 이동합니다. 게임과 관련된 공급자를 사용하도록 설정합니다. 사용하도록 설정된 각 공급자에 대한 필수 설정을 입력하고 저장합니다.

login providers image

사용자 로그인

로그인 공급자가 설정된 경우 사용자 로그인을 계속할 수 있습니다. 클라이언트 초기화 후 사용자의 로그인 여부를 확인할 수 있습니다. 그렇지 않은 경우 로그인 작업을 진행할 수 있습니다.

이 예에서는 Steam 인증을 사용합니다:

C#

private IEnumerator Start()
{
    //wait until backbone client is initialized
    while (!BackboneManager.IsInitialized)
    {
        yield return null;
    }
    //check if user is logged in
    if (!BackboneManager.IsUserLoggedIn)
    {
        //Obtain steam user name, auth session ticket, steam id from prefered steam
        //api library
        //...
        //login user using steam authentication provider
        var steamLogin = LoginProvider.Steam(true, userName, steamAuthCode, steamUserId);
        yield return BackboneManager.Client.Login(steamLogin);
    }
}

사용자가 Steam 로그인 공급자를 사용하여 로그인합니다. 사용자가 성공적으로 로그인하면 클라이언트 API와 상호 작용할 수 있습니다.

기본 토너먼트 운영

토너먼트 목록 얻기

모든 토너먼트 목록은 다음을 통해 찾을 수 있습니다:

C#

var allTournaments = BackboneManager.Client.Tournaments.TournamentList;

목록을 로드하거나 새로 고침하기 위해서는 (로그인 후 비어 있을 수 있음) BackboneManager.Client.LoadTournamentList() 오퍼레이션을 호출해야 합니다.

C#

 //load/refresh tournament list
 BackboneManager.Client.LoadTournamentList()
     //set finish callback
     .FinishCallback(() =>
     {
         //bind data after operation finishes
         BindData();
     })
     //run async operation on this MonoBehaviour
     .Run(this);

노트: 이 작업은 목록을 성공적으로 새로 고쳤는지 여부를 나타내는 bool 결과를 반환합니다. 이 작업이 너무 자주 호출되면 false가 반환됩니다. 현재 "허용된 새로 고침 제한"은 1분으로 설정됩니다. 당신은 등록하면 오퍼레이션 결과를 얻을 수 있습니다.

ResultCallback((result) => {}) 과 같이:

C#

 //load/refresh tournament list
 BackboneManager.Client.LoadTournamentList()
     //set finish callback
     .FinishCallback(() =>
     {
         //bind data after operation finishes
         BindData();
     })
     .ResultCallback((result) => {
         if(result)
         {
             //successful refresh/load
         }
         else
         {
             //not refreshed/loaded
         }
     })
     //run async operation on this MonoBehaviour
     .Run(this);

토너먼트 목록이 로드되면 토너먼트 클래스의 일부 속성이 채워지지 않습니다. 모든 토너먼트 데이터를 로드하기 위해서는 BackboneManager.Client.LoadTournament(tournamentId)를 호출해야 합니다. 토너먼트 클래스에는 모든 데이터가 로드되었는지 확인할 수 있는 HasAllDataLoaded 플래그가 포함되어 있습니다.

토너먼트 얻기

모든 토너먼트 데이터를 불러오고/새로고침 하기 위해서 BackboneManager.Client.LoadTournament(tournament)를 호출합니다. 이 오퍼레이션은 토너먼트가 성공적으로 불러오기/새로고침 되었는지를 나타내는 bool을 리턴합니다. 또한 토너먼트 클래스에는 모든 데이터가 이전에 이미 로드되었는지를 나타내주는 tournament.HasAllDataLoaded 속성이 포함되어 있습니다.

C#

//get first tournament in the list
var tournament = BackboneManager.Client.Tournaments.TournamentList[0];
//load/refresh all tournament data
BackboneManager.Client.LoadTournament(tournament)
    //set finish callback
    .FinishCallback(() =>
    {
        if(tournament.HasAllDataLoaded)
        {
            //all data has been loaded/refreshed
        }
    })
    //run async operation on this MonoBehaviour
    .Run(this);

토너먼트 가입

사용자가 이미 가입되어 있는지 확인하려면 토너먼트 속성 tournament.Invite를 확인하십시오. 이 값이 null인 경우 초대되지 않은 사용자이며 확인도 되지 않습니다. 초대를 사용할 수 있는지 확인하려면 사용자의 토너먼트 초대 상태를 나타내는 tournament.Invite.Status를 확인하세요.

C#

//get tournament
var tournament = BackboneManager.Client.Tournaments.GetTournamentById(tournamentId);
//check if user is signed up
if (tournament.Invite == null ||
    tournament.Invite.Status != TournamentUserStatus.Confirmed)
{
    //user is not signed up for tournament
}

토너먼트에 가입을 하기 위해서는 SignupForTournament(tournamentId)를 호출하세요.

C#

//sign up user for tournament
BackboneManager.Client.SignupForTournament(tournamentId)
    //set result callback
    .ResultCallback((result) =>
    {
        //check sign up result
        if (result.ProcessStatus != TournamentSignUpStatus.Ok)
        {
            LobbyAlertDialog.Show("Sign up process failed with status: " + result.ProcessStatus.ToString());
        }
    })
    //run async operation on this MonoBehaviour
    .Run(this);

이 작업은 등록 결과에 대한 정보가 포함된 InviteResult 객체를 리턴합니다. 관리 화면에 가입 프로세스를 위한 외부 서버가 설정된 경우(예: 통화, 아이템 등을 차감하는 경우) inviteResult.IsExternalSignupError는 이 작업 중에 문제가 발생했는지 여부를 나타냅니다. 또한 사용자 지정 서버에서 발생하는 모든 오류 메시지는 inviteResult.ErrorMessage에서 확인할 수 있습니다.

토너먼트 허브

토너먼트 허브 초기화

토너먼트 허브는 토너먼트의 사용자 정의 로비 역할을 합니다. 연결되면 경기 및 토너먼트 진행 상황 정보를 제공합니다. 토너먼트 허브 콜백을 받으려면 ITournamentHubCallbackHandler 인터페이스를 구현하세요.

C#

public void OnInitialized(ITournamentHubController controller)
{
    //tournament hub was initialized and provides controller
}

public void OnHubStatusChanged(TournamentHubStatus newStatus)
{
    //tournament hub status has changed
}

public void OnTournamentUpdate()
{
    //tournament data has been updated
}

public void OnHubMatchStatusChanged(TournamentHubMatchStatus newStatus)
{
    //joined tournament match status has changed
}

public void OnHubMatchUpdate()
{
    //joined tournament match data has been updated
}

토너먼트 허브를 초기화하기 위해서는 ITournamentHubCallbackHandler를 구현하는 객체인 콜백 핸들러가 존재하는 ConnectTournamentHub(callbackHandler, tournament);를 호출하세요.

C#

var tournament = BackboneManager.Client.Tournaments.GetTournamentById(tournamentId);
BackboneManager.Client.ConnectTournamentHub(this, tournament);

토너먼트 허브가 초기화되면 OnInitialized(ITournamentHubController controller)가 호출되고 사용자 관점에서 특정 토너먼트 흐름을 제어하는 데 사용되는 컨트롤러를 반환합니다(예: 사용자가 다음 경기를 치를 준비가 되었음을 나타냄).

토너먼트 허브 상태

OnHubStatusChanged(TournamentHubStatus newStatus)는 사용자 관점에서의 토너먼트 상태를 나타냅니다. 상태 변화에 따라서 UI가 상황에 맞게 반응해야 합니다.

C#

public void OnHubStatusChanged(TournamentHubStatus newStatus)
{        
    switch (newStatus)
    {
        case TournamentHubStatus.RegistrationClosed:
            //Registration is closed and has not been open yet. You can check
            //open time in tournament property 'RegistrationOpenTime'.
            break;
        case TournamentHubStatus.RegistrationOpening:
            //Registration is opening as 'RegistrationOpenTime' was reached but
            //confirmation from server is awaited.
            break;
        case TournamentHubStatus.RegistrationOpened:
            //Registration is opened and users can sign up for tournament.
            break;
        case TournamentHubStatus.RegistrationClosing:
            //Registration/Inivitation is closing as 'InvitationCloseTime' was reached
            //but confirmation from server is awaited.
            break;
        case TournamentHubStatus.WaitingForTournamentStart:
            //Registration is closed and tournament start confirmation from 
            //server is awaited.
            break;
        case TournamentHubStatus.Starting:
            //Tournament is starting as tournament 'Time' was reached but
            //confirmation from server is awaited.
            break;
        case TournamentHubStatus.Started:
            //Tournament has started. Get current phase from
            //'tournament.GetCurrentTournamentPhase()' containing user standings.
            break;
        case TournamentHubStatus.MatchInProgress:
            //User has match in progress he should be part of.
            //Get all match metadata from 'tournament.UserActiveMatch'. 
            break;
        case TournamentHubStatus.ResolvingPartiallyFilledMatch:
            //User active match was not filled in time. Awaiting confirmation from
            //server if match should be played.
            break;
        case TournamentHubStatus.ClosingOverdueMatch:
            //User active match reached a deadline and its due for closure.
            //Awaiting confirmation from server.
            break;
        case TournamentHubStatus.WaitingForUserReadyConfirmation:
            //User can proceed to next round of the tournament. Explicit confirmation
            //is requested by calling 'tournamentHubController.SetUserReady()'.
            break;
        case TournamentHubStatus.WaitingForNextPhase:
            //Tournament current phase is about to finish. Waiting for next phase to
            //start.
            break;
        case TournamentHubStatus.WaitingForTournamentToFinish:
            //User has finished all rounds in current phase or was already knocked
            //out of the tournament and waiting for tournament to finish.
            break;
        case TournamentHubStatus.Finishing:
            //Last round of last phase has reached deadline and all matches should
            //be finilized. Awaiting confirmation from server.
            break;
        case TournamentHubStatus.Finished:
            //Tournament has finished. Found out who won by looking at 
            //'tournament.Winner.Users'
            break;
    }
}

사용자의 활성 매치

토너먼트가 실행 중이고 사용자가 가입하면 모든 메타데이터와 상태가 포함된 Active match가 자동으로 제공됩니다. 사용자의 활성 경기는 토너먼트 허브에서 tournamentHub.Tournament.UserActiveMatch로 접근할 수 있습니다.

UserActiveMatch.Secret은 네트워크 룸/로비 비밀번호 또는 이름으로 사용할 수 있습니다. 이것은 특정 매치 항목에 참가할 수 있는 사용자에게만 배포됩니다.

UserActiveMatch.Status는 사용자가 다른 사용자/상대방을 기다리고 있는지, 게임이 진행 중일 때 특정 룸/로비에 연결해야 하는지 또는 사용자가 다른 매치를 진행해야 하는지 여부를 결정하는 데 사용됩니다.

C#

switch (tournamentHubController.Tournament.UserActiveMatch.Status)
{
    case TournamentMatchStatus.Created:
        //Match was successfuly created.
        break;
    case TournamentMatchStatus.WaitingForOpponent:
        //Match is not filled and opponents are still awaited to join. 
        break;
    case TournamentMatchStatus.GameReady:
        //Match game is ready to be played. Proceed to create game session that will
        //change status to 'GameInProgress'.
        //NB: users might still be checking in and connecting to room/lobby.
        //Client should wait until all parties are successfully connected and ready.
        //Only then procced to create game session.
        break;
    case TournamentMatchStatus.GameInProgress:
        //Match game is in progress, game session was created and it's in progress.
        //NB: user should be able to reconnect to ongoing game session.
        break;
    case TournamentMatchStatus.GameFinished:
        //Match game has finished as results were reported. If match requires more games
        //to be played per series (e.g. best of 3) proceed to create another game session
        //that will change status to 'GameInProgress' again.
        break;
    case TournamentMatchStatus.MatchFinished:
        //Match has finished as all games has been played (or deadline was reached).
        //It will be closed soon, user can proceed to another match.
        break;
    case TournamentMatchStatus.Closed:
        //Match was finalized and closed by server.
        break;
}

UserActiveMatch.Users[i].IsCheckedIn를 확인하여 상대가 준비되었는지 확인할 수 있습니다. tournamentHubController.RefreshActiveMatch()를 호출하여 UserActiveMatch 데이터를 새로고침할 수 있습니다

UserActiveMatch가 null이거나 완료 상태일 수 있습니다. 다른 매치를 요청하고 토너먼트를 진행하려면 사용자가 tournamentHubController.SetUserReady()를 명시적으로 호출해야 합니다. 사용자는 토너먼트 허브가 TournamentHubStatus.WaitingForUserReadyConfirmation 상태로 전환될 때 이 작업을 수행해야 합니다. 이 액션은 UI(예: 다음 경기 준비)로 나타낼 수도 있고, 토너먼트 허브가 들어갈 때 사용자 개입 없이 자동 수행될 수도 있습니다.

참여한 매치 인터페이스

게임에서 개인 게임/로비 생성 방법을 정의한 경우 매치 인터페이스를 구현하면 사용자 참가 및 선발 토너먼트 경기를 쉽게 관리할 수 있습니다.

로비 스크립트에서 ITournamentMatchCallbackHandler 인터페이스를 구현하거나 이를 구현하는 새 스크립트를 만듭니다. 다음은 로비 상태를 토너먼트 허브에 전달하는 간단한 방법입니다.

C#

public void OnJoinTournamentMatch(Tournament tournament, TournamentMatch match, ITournamentMatchController controller)
{
    //Callback from tournament hub passing tournament, match and controller object.
    //Use match data to join correct lobby/room.
    //User controller to inform tournament hub about changes in your lobby/room.
}

public bool IsConnectedToGameServerNetwork()
{
    //Check if client is successfully connected to your networking backend.
    //Return true if user is connected and ready to join lobby/room.
}

public bool IsUserConnectedToMatch(long userId)
{
    //Check if specific user is already connected to lobby/room.
    //Return true if user is connected.
}

public bool IsUserReadyForMatch(long userId)
{
    //Check if specific user is ready (e.g. moved to correct slot)
    //Return true if user is ready to start.
    //NB: local user that is not checked in for the match yet, will be checked in
    //only after returning true
}

public bool IsGameSessionInProgress()
{
    //Check if game session is already in progress for given tournament match.
    //Return true if game session is in progress.
}

public void OnLeaveTournamentMatch()
{
    //Callback from tournament hub informing user should leave joined lobby/room.
}

public void StartGameSession(IEnumerable<TournamentMatch.User> checkedInUsers)
{
    //Callback from tournament hub requesting game session to start immediately. Also
    //passing users that successfully checked in for current match.
    //Create tournament game session, and start your game.
    //This might be called multiple times until IsGameSessionInProgress returns true.
}

그런 다음 이 인터페이스를 구현하는 개체는 특정 일치 항목인 tournamentHubController.JoinTournamentMatch()에 참여할 때 토너먼트 허브 컨트롤러 메서드로 전달할 수 있습니다.

코드 예제:

C#

//Initialize tournament hub
BackboneManager.Client.ConnectTournamentHub(hubCallbackHandler, tournament);
//...
//Get hub controller 
public void OnInitialized(ITournamentHubController controller)
{
    hubController = controller
}
//...
//Check tournament hub is in "MatchInProgress" status
public void OnHubStatusChanged(TournamentHubStatus newStatus)
{        
    switch (newStatus)
    {
        case TournamentHubStatus.MatchInProgress:
        case TournamentHubStatus.ResolvingPartiallyFilledMatch:
            //...
            //Join users active match
            var match = hubController.Tournament.UserActiveMatch;
            hubController.JoinTournamentMatch(match, matchCallbackHandler);
            //...
            break;        
    }
}

노트: 또한 OnJoinTournamentMatch에 전달된 tournamentMatchController에서 reporting 메서드를 사용하는 것이 중요합니다. 연결된 로비/룸에서 특정 이벤트가 발생할 경우 이러한 컨트롤러 메소드를 호출합니다. 토너먼트 허브는 메타데이터를 새로 고쳐야 할 시기를 결정하기 위해 이러한 항목을 사용합니다. 그렇지 않으면 한 클라이언트가 일치 항목을 시작하지만 다른 클라이언트는 일치하지 않는 불일치가 발생할 수 있습니다(예: 다른 클라이언트는 사용자가 아직 체크인하지 않았다고 생각함).

Photon 룸 콜백을 사용하여 tournamentMatchController에 대한 변경 사항을 보고하는 예:

C#

//Photon callback when new player joined room
public void OnPlayerEnteredRoom(Player newPlayer)
{
    long userId;
    //extract user id from player custom properties
    if (this.tournamentMatchController != null &&
        TryGetPlayerBackboneUserId(newPlayer, out userId))
    {
        //report user who joined room
        this.tournamentMatchController.ReportJoinedUser(userId);
    }
}

//Photon callback when player disconnected from room
public void OnPlayerLeftRoom(Player otherPlayer)
{
    long userId;
    //extract user id from player custom properties
    if (this.tournamentMatchController != null &&
        TryGetPlayerBackboneUserId(otherPlayer, out userId))
    {
        //report user who disconnected from room
        this.tournamentMatchController.ReportDisconnectedUser(userId);
    }
}

//Photon callback when room properties are updated
public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
{
    if (this.tournamentMatchController != null)
    {
        //reporting status change will refresh match metadata
        this.tournamentMatchController.ReportStatusChange();
    }
}

//Photon callback when player properties are updated
public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
{
    if (this.tournamentMatchController != null)
    {
        //reporting status change will refresh match metadata
        this.tournamentMatchController.ReportStatusChange();
    }
}

토너먼트 게임 세션 & 결과 제출

토너먼트 설정에 따라 한 경기에 여러 게임 세션이 포함될 수 있습니다. 예를 들어, 1v1 매치가 "Best of 3" 시리즈로 설정된 경우 적어도 2개의 게임 세션을 실행해야 합니다. "게임 세션"은 매치 시리즈에서 단일 게임을 나타냅니다.

게임 세션 생성

게임 세션을 만들려면 CreateGameSession(users, userActiveMatchId, sessionType)을 호출하십시오. 리턴된 게임 세션 ID는 다른 클라이언트에 배포될 수 있습니다. 다른 후속 호출 시 동일한 게임 세션 ID가 리턴됩니다.

C#

//create game session only for checked in users
var checkedInUsers = userActiveMatch.Users.Where(user => user.IsCheckedIn);
//game session type can be used to identify specific game modes
//e.g. 0-default, 1-4player mode, 2-8player mode
var sessionType = 0;
//create game session
BackboneManager.Client.CreateGameSession(checkedInUsers, userActiveMatch.Id, sessionType)
    //set result callback
    .ResultCallback((gameSession) =>
    {
        //get game session id and distribute it to other clients
        //NB: this is up to developer, e.g. use custom room/lobby properties
        //or broadcast message
        var gameSessionId = gameSession.Id;
    })
    //run async operation on this MonoBehaviour
    .Run(this);

결과 제출

게임 세션이 종료된 후 데드라인에 도달하기 전에 결과를 보고해야 합니다. 경기가 둘 이상의 게임 세션(예: 베스트 3 시리즈)을 가지도록 설정된 경우 GetMatchNextGameDeadline(match)를 사용하여 현재 게임 세션의 이상적인 데드라인을 결정하여 다음 게임 세션을 아직 실행할 시간이 남아 있도록 할 수 있습니다.

C#

//get user active match
var userActiveMatch = tournament.UserActiveMatch;
//get ideal deadline for next game session
var deadline = tournament.GetMatchNextGameDeadline(userActiveMatch);

게임 세션 결과를 제출하려면 SubmitGameSession(gameSession) 호출을 사용하십시오. 게임 세션 개체는 CreateGameSession 호출에서 리턴된 분산 게임 세션 ID를 사용하여 모든 클라이언트에 생성되어야 합니다. 주어진 게임 세션의 포인트 배분을 결정할 유저 place를 설정하는 것이 중요합니다.

C#

//create game session only for checked in users
var matchUsers = userActiveMatch.Users.Where(user => user.IsCheckedIn).ToList();
//sort users based on your game session results, e.g. kills, deaths etc.
matchUsers.Sort((user1, user2) =>{
    //sort users from best to worst based on specific game rules
});
//create list for game session users
List<GameSession.User> gameSessionUsers = new List<GameSession.User>();
//loop through sorted users from best to worst
for(var i = 0; i < matchUsers.Count; i++)
{
    var userId = matchUsers[i].UserId;
    var teamId = matchUsers[i].TeamId;
    //add game session user with set final place in game session (more users
    //can have same placement if required)
    gameSessionUsers.Add(new GameSession.User(userId, teamId) { Place = (i + 1) });
}
//get user active match id
var matchId = userActiveMatch.Id;
//create game session using id that was obtained from 'CreateGameSession' call as
//well as passing tournament match id
GameSession gameSession = new GameSession(gameSessionId, 0, gameSessionUsers, matchId);
//set played date and game session duration
gameSession.PlayDate = DateTime.UtcNow;
gameSession.PlayTime = gameTime;
//submit game session to server
BackboneManager.Client.SubmitGameSession(gameSession)
    .ResultCallback((result) => {
        if (result)            
        {
            //game session was successfuly submitted
        }
    })
    .Run(this);
Back to top