티어 리그 #

리그 플레이를 사용하여 더욱 매력적인 소셜 및 경쟁 게임플레이 경험을 느낄 수 있습니다.

리그는 각 순위표가 리그 계층의 특정 “티어"를 나타내는 일련의 연결된 순위표 역할을 합니다. 리그 “시즌"이 시작될 때 모든 플레이어는 첫 번째(하위) 티어에서 시작합니다. 플레이어가 특정 티어의 다른 모든 플레이어와 경쟁하기를 원하는 플레이 기간이 구현됩니다. 각 플레이 기간이 끝나면 순위표 상단에 있는 특정 플레이어 집합은 다음 티어로 승격되고 순위표 하단에 있는 특정 집합은 하위 티어로 강등됩니다.

플레이어에게는 각 플레이 기간이 끝날 때 보상이 제공됩니다. 보상의 양과 종류는 티어와 순위에 따라 결정되며 이에 따라 리그 기간 동안 플레이어의 경쟁과 참여가 유도됩니다.

리그는 특정 게임과 요구 사항에 맞게 사용자 지정할 수 있습니다. 주어지는 보상은 게임 조건에 따라 다르며 플레이어의 리그 가입비 지불을 결정할 수 있습니다. 원하는 승격 및 강등 논리를 사용하여 플레이어 티어 수를 무제한으로 만들 수 있습니다. 이는 백분율(또는 기타 측정 지표)을 기반으로 각 순위표에 승격 및 강등 영역을 만들거나 각 플레이 기간 종료 후에 승격 및 강등될 지정된 수의 플레이어(예: 상위 및 하위 10명)일 수 있습니다.

아래의 예에서는 매주 플레이 기간이 끝난 후 상위 3명의 플레이어가 승격되고 하위 3명의 플레이어가 강등되는 2티어 리그를 사용합니다. 게임 내 통화 보상의 가치는 리그의 하위 티어와 상위 티어 사이에 두 배의 차이가 납니다.

티어 순위표 생성 #

매주 재설정되는 하위 및 상위 티어에 대해 하나씩 리그에 대해 연결된 두 개의 순위표를 만들어 보겠습니다.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Create the two tiers of leaderboards
bottomTierId := "bottom-tier"
topTierId := "top-tier"
authoritative := true
sortOrder := "desc"
operator := "inc"
resetSchedule := "0 0 * * 1"
metadata := make(map[string]interface{})
nk.LeaderboardCreate(ctx, bottomTierId, authoritative, sortOrder, operator, resetSchedule, metadata)
nk.LeaderboardCreate(ctx, topTierId, authoritative, sortOrder, operator, resetSchedule, metadata)
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Create the two tiers of leaderboards
let bottomTierId = "bottom-tier";
let topTierId = "top-tier";
let authoritative = true;
let sortOrder = nkruntime.SortOrder.DESCENDING;
let operator = nkruntime.Operator.INCREMENTAL;
let resetSchedule = "0 0 * * 1";
let metadata = {};
nk.leaderboardCreate(bottomTierId, authoritative, sortOrder, operator, resetSchedule, metadata);
nk.leaderboardCreate(topTierId, authoritative, sortOrder, operator, resetSchedule, metadata);
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-- Create the two tiers of leaderboards
local bottom_tier_id = "bottom-tier"
local top_tier_id = "top-tier"
local authoritative = true
local sort = "desc"
local operator = "best"
local reset = "0 0 * * 1"
local metadata = {}
nk.leaderboard_create(bottom_tier_id, authoritative, sort, operator, reset, metadata)
nk.leaderboard_create(top_tier_id, authoritative, sort, operator, reset, metadata)

승격 및 강등 #

매주 플레이 기간이 끝나면 하위 티어의 상위 3명의 플레이어를 상위 티어로 승격합니다. 이에 따라, 상위 티어의 하위 3명의 플레이어를 하위 티어로 강등합니다.

순위표 승격 및 강등
순위표 승격 및 강등

처음에는 모든 플레이어가 최하위 티어에 있으므로 강등을 수행하기 전에 최상위 티어로 승격한 플레이어가 충분한지 먼저 확인해야 합니다.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Register leaderboard reset function to handle promotions and relegations
initializer.RegisterLeaderboardReset(func(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, leaderboard *api.Leaderboard, reset int64) error {
  // We're only interested in our top/bottom tier leaderboards so return if it isn't them
  if leaderboard.Id != topTierId && leaderboard.Id != bottomTierId {
    return nil
  }

  // Get all leaderboard records (assuming the tier has no more than 10,000 players)
  records, _, _, _, _ := nk.LeaderboardRecordsList(ctx, leaderboard.Id, []string{}, 10000, "", reset)

  // If leaderboard is top tier and has 10 or more players, relegate bottom 3 players
  if leaderboard.Id == topTierId && len(records) >= 10 {
    for _, record := range records[len(records)-3:] {
      // Relegate record owner by copying their record into the bottom tier and deleting their current top tier record
      nk.LeaderboardRecordWrite(ctx, bottomTierId, record.OwnerId, record.Username.Value, record.Score, record.Subscore, nil, nil)
      nk.LeaderboardRecordDelete(ctx, topTierId, record.OwnerId)
    }
  }

  // If leaderboard is bottom tier and has 10 or more players, promote top 3 players
  if leaderboard.Id == bottomTierId && len(records) >= 10 {
    for _, record := range records[0:2] {
      // Promote record owner by copying their record into the top tier and deleting their current bottom tier record
      nk.LeaderboardRecordWrite(ctx, topTierId, record.OwnerId, record.Username.Value, record.Score, record.Subscore, nil, nil)
      nk.LeaderboardRecordDelete(ctx, bottomTierId, record.OwnerId)
    }
  }

  return nil
})
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Register leaderboard reset function to handle promotions and relegations
let leaderboardReset: nkruntime.LeaderboardResetFunction = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, leaderboard: nkruntime.Leaderboard, reset: number) {
  // We're only interested in our top/bottom tier leaderboards so return if it isn't them
  if (leaderboard.id !== topTierId && leaderboard.id !== bottomTierId) {
    return;
  }

  // Get all leaderboard records (assuming the tier has no more than 10,000 players)
  let result = nk.leaderboardRecordsList(leaderboard.id, null, 10000, null, reset);

  // If leaderboard is top tier and has 10 or more players, relegate bottom 3 players
  if (leaderboard.id === topTierId && result.records.length >= 10) {
    // Relegate record owner by copying their record into the bottom tier and deleting their current top tier record
    result.records.slice(result.records.length-3).forEach(function (r) {
      nk.leaderboardRecordWrite(bottomTierId, r.ownerId, r.username, r.score, r.subscore, null, null);
      nk.leaderboardRecordDelete(topTierId, r.ownerId);
    });
  }

  // If leaderboard is bottom tier and has 10 or more players, promote top 3 players
  if (leaderboard.id === topTierId && result.records.length >= 10) {
    // Promote record owner by copying their record into the top tier and deleting their current bottom tier record
    result.records.slice(0, 3).forEach(function (r) {
      nk.leaderboardRecordWrite(topTierId, r.ownerId, r.username, r.score, r.subscore, null, null);
      nk.leaderboardRecordDelete(bottomTierId, r.ownerId);
    });
  }
};
initializer.registerLeaderboardReset(leaderboardReset);
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-- Register leaderboard reset function to handle promotions and relegations
local function leaderboardReset(ctx, leaderboard, reset)
  -- We're only interested in our top/bottom tier leaderboards so return if it isn't them
  if leaderboard.id ~= bottom_tier_id and leaderboard.id ~= top_tier_id then
    return
  end

  -- Get all leaderboard records (assuming the tier has no more than 10,000 players)
  local records, _, _, _ = nk.leaderboard_records_list(leaderboard.id, {}, 10000)

  -- If leaderboard is top tier and has 10 or more players, relegate bottom 3 players
  if leaderboard.id == top_tier_id and #records >= 10 then
    -- Relegate record owner by copying their record into the bottom tier and deleting their current top tier record
    for i=3, 1, -1 do
      local record = records[#records-i]
      nk.leaderboard_record_write(bottom_tier_id, record.owner, record.username, record.score, record.subscore, {})
      nk.leaderboard_record_delete(top_tier_id, record.owner)
    end
  end

  -- If leaderboard is bottom tier and has 10 or more players, promote top 3 players
  if leaderboard.id == bottom_tier_id and #records >= 10 then
    -- Promote record owner by copying their record into the top tier and deleting their current top tier record
    for i=1, 3, 1 do
      local record = records[i]
      nk.leaderboard_record_write(top_tier_id, record.owner, record.username, record.score, record.subscore, {})
      nk.leaderboard_record_delete(bottom_tier_id, record.owner)
    end
  end
end
nk.register_leaderboard_reset(leaderboardReset)

보상 #

순위표 보상
순위표 보상

또한 매주 플레이 기간이 끝나면 모든 플레이어에게 해당 티어에 따라 그리고 티어 내 순위에 맞게 보상합니다.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Register leaderboard reset function to handle promotions and relegations
initializer.RegisterLeaderboardReset(func(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, leaderboard *api.Leaderboard, reset int64) error {
  // Relegation/Promotion code as above...

  // Distribute rewards based on player's tier
  for _, record := range records {
    reward := int64(100)

    // Increase reward for top tier players
    if leaderboard.Id == topTierId {
      reward = 500
    }

    changeset := map[string]int64 {
      "coins": reward,
    }

    nk.WalletUpdate(ctx, record.OwnerId, changeset, nil, true)
  }

  return nil
})
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Register leaderboard reset function to handle promotions and relegations
let leaderboardReset: nkruntime.LeaderboardResetFunction = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, leaderboard: nkruntime.Leaderboard, reset: number) {
  // Relegation/Promotion code as above...

  // Distribute rewards based on player's tier
  result.records.forEach(function (r) {
    let reward = 100;

    // Increase reward for top tier players
    if (leaderboard.id === topTierId) {
      reward = 500;
    }

    let changeset = {
      coins: reward
    };

    nk.walletUpdate(r.ownerId, changeset, null, true);
  });
};
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
-- Register leaderboard reset function to handle promotions and relegations
local function leaderboardReset(ctx, leaderboard, reset)
  -- Relegation/Promotion code as above...

  -- Distribute rewards based on player's tier
  for i=1, #records do
    local record = records[i]
    local reward = 100

    -- Increase reward for top tier players
    if leaderboard.id == top_tier_id then
      reward = 500
    end

    local changeset = {
      coins = reward
    }

    nk.wallet_update(record.owner, changeset, {}, true)
  end
end

예제 파일 #

이 리그에 대한 전체 예제 파일 다운로드: