순위표 #

대결, 친구, 클랜 및 데이터를 저장하는 방법을 설정하였습니다. 모든 항목을 결합하여 상위권 플레이어의 순위표를 표시하여 경쟁을 조성합니다.

순위표
순위표

순위표 설정 #

각각의 생성하려고 하는 순위표에 대해서 leaderboardCreate을(를) 실행합니다. 이 함수는 여러 매개 변수를 사용합니다:

  • ID/이름
  • 순위표가 권한 보유에 해당할 경우 부울(해당되는 경우, 서버를 통해서만 순위표를 업데이트할 수 있습니다.)
  • 정렬 순서(SortOrder.ASCENDING 또는 SortOrder.DESCENDING)
  • 점수 집계를 위한 연산자
    • Operator.BEST: 제출된 가장 큰 점수를 사용함
    • Operator.SET: 가장 최근에 제출된 점수를 사용함
    • Operator.INCR: 제출된 점수를 총계에 추가함
  • 재설정되지 않는 순위표에 대한 CRON 표현 또는 null의 일정을 재설정합니다.
  • 순위표 내에서 저장할 메타 데이터

Pirate Panic의 경우, 모든 플레이어로부터 역대 가장 높은 점수를 표시하는 글로벌 순위표를 설정합니다:

main.ts

1
2
3
4
5
6
7
const id = "global_leaderboard";
const authoritative = false;
const metadata = {};
const scoreOperator = nkruntime.Operator.BEST;
const sortOrder = nkruntime.SortOrder.DESCENDING;
const resetSchedule = null;
nk.leaderboardCreate(id, authoritative, sortOrder, scoreOperator, resetSchedule, metadata);

순위표에서 새로운 레코드를 수락할 수 있습니다.

순위표에 추가 #

대결이 끝날 때마다 플레이어의 점수를 순위표에 기록합니다. 대결이 끝날 때마다 클라이언트는 서버로 RPC 요청을 보내 최종 게임 행위를 처리합니다:

match.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
interface MatchEndRequest { // Create a structure to align the payload to
    matchId : string;
    placement: MatchEndPlacement;
    time : number;
    towersDestroyed : number;
}


const rpcHandleMatchEnd: nkruntime.RpcFunction = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, payload: string): string {
    ... // Add gems to wallet, calculate score, etc. etc.
    let request : MatchEndRequest = JSON.parse(payload);
    let score = calculateScore(request.placement == MatchEndPlacement.Winner, request.towersDestroyed, request.time);
    nk.leaderboardRecordWrite(globalLeaderboard, ctx.userId, ctx.username, score);
    ...
}

점수를 계산하는 방법은 사용자와 게임의 메커니즘에 따라 다릅니다. 이 예시와 같이 누가 이겼는지, 승리까지 시간이 얼마나 걸렸는지, 타워를 몇 개 파괴했는지 등을 고려할 수 있습니다. 이러한 모든 정보는 클라이언트에서 페이로드로 전달됩니다.

점수를 계산한 뒤에 표시할 사용자 ID와 사용자 이름과 leaderboardRecordWrite(으)로 숫자를 전달하면 순위표 엔진에서 나머지 작업을 처리합니다.

또한, WriteLeaderboardRecordAsync를 사용하여 Unity 측에서 이 작업을 수행할 수 있습니다.

순위표 기록 나열 #

새로운 순위표 기록을 처리할 수 있도록 서버를 설정했고, 이제 플레이어가 서버에 저장된 항목을 볼 수 있도록 설정합니다. 이 작업은 ListLeaderboardRecordsAsync을(를) 사용하여 클라이언트 측에서 완료할 수 있습니다:

LeaderboardsMenuUI.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[SerializeField] private int _recordsPerPage = 100;
...
public async void ShowGlobalLeaderboards(string cursor = null)
{
    // Fetch all records from the leaderboard "global"
    IApiLeaderboardRecordList records = await _connection.Client.ListLeaderboardRecordsAsync(_connection.Session, "global", ownerIds: null, expiry: null, _recordsPerPage, cursor);

    SetLeaderboardsCursor(records, ShowGlobalLeaderboards);
    ...
}

위의 예시와 같이, SetLeaderboardsCursor은(는) 다음과 같이 표시됩니다:

LeaderboardsMenuUI.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private void SetLeaderboardsCursor(IApiLeaderboardRecordList records, Action<string> caller)
{
    if (records.PrevCursor != null) {
        _prevPageButton.interactable = true;
        _prevPageButton.onClick.RemoveAllListeners();
        _prevPageButton.onClick.AddListener(() => caller(records.PrevCursor));
    } else {
        _prevPageButton.interactable = false;
    }
    if (records.NextCursor != null) {
        _nextPageButton.interactable = true;
        _nextPageButton.onClick.RemoveAllListeners();
        _nextPageButton.onClick.AddListener(() => caller(records.NextCursor));
    } else {
        _nextPageButton.interactable = false;
    }
}

여기서는 ListLeaderboardRecordsAsync에 의해 반환된 records의 목록에 PrevCursorNextCursor의 함수가 표시되고, 이전 또는 다음 페이지(다음 페이지가 있는 경우)에서 레코드를 얻습니다.

플레이어가 레코드를 쉽게 찾을 수 있도록 해당 함수를 버튼과 연결합니다. PrevCursor와(과) NextCursor 모두 기존의 ListLeaderboardRecordsAsync 호출에서 설정한 페이지 별 레코드를 준수하고, 레코드의 다음 페이지를 유지하도록 records 개체를 업데이트합니다.

이 레코드를 표시할 수 있도록 records에서 레코드마다 foreach 루프를 사용할 수 있습니다:

LeaderboardsMenuUI.cs

1
2
3
4
5
6
7
8
9
foreach (IApiLeaderboardRecord record in records)
{
    string username = record.Username;
    if (localId == record.OwnerId)
    {
        username += " (You)";
    }
    ...
}

각각의 record에는 Username, OwnerId, Score와 옵션으로 제공되는 메타 데이터에 저장된 사용자 지정 필드가 저장됩니다.

순위표 필터링 #

친구 또는 클랜을 보기 위해서 글로벌 순위표에 필터링을 적용할 수 있습니다.

이전 섹션에서는 모든 항목을 얻기 위해서 ownerIds을(를) null의 상태로 유지하였습니다. 이제, 이 매개 변수를 사용하여 특정 소유자에 대한 항목만 얻을 수 있습니다.

예를 들어, ListFriendsAsync을(를) 사용하여 모든 친구를 불러옵니다:

1
var friends = await _connection.Client.ListFriendsAsync(_connection.Session);

모든 사용자 ID를 추출하여 목록에 저장할 수 있습니다:

1
List<string> ids = friends.Friends.Select(x => x.User.Id).ToList();

마지막으로, ids 목록을 필터로 전달하고, 해당 목록에 있는 사용자들만 records에 표시됩니다:

1
IApiLeaderboardRecordList records = await _connection.Client.ListLeaderboardRecordsAsync(_connection.Session, "global", ids, null, 1, cursor);

여기서는 같은 순위표(global)에 있는 레코드만 사용하지만, ids을(를) ownerIds(으)로 전달하여 페이지 당 레코드 숫자를 1(으)로 설정합니다.

이 작업과 유사하게, 클랜 구성원의 점수만 표시하도록 순위표에 필터를 적용할 수 있습니다.

1
2
3
4
var users = await _connection.Client.ListGroupUsersAsync(_connection.Session, _userClan.Group.Id, null, 1, null);
IEnumerable<string> ids = users.GroupUsers.Select(x => x.User.Id);

IApiLeaderboardRecordList list = await _connection.Client.ListLeaderboardRecordsAsync(_connection.Session, "global", ids, null, 1, cursor);

다음 주제 #

알림

Related Pages