分级联赛 #

联赛游戏可以提供更具吸引力的社交和竞争游戏体验。

联赛的作用是一组关联的排行榜,其中每个排行榜代表您的联赛层次结构的特定“层级”。在联赛“赛季”开始时,所有玩家都从第一层(最底层)开始。理想情况下,在游戏周期内,玩家需要与其特定层级内的所有其他人竞争。每个游戏周期结束时,排行榜顶部的部分玩家会晋升到较高的层级,而排行榜底部的部分玩家会降级到较低的层级。

玩家将在每个游戏周期结束时收到奖励。奖励的数量和种类取决于他们在游戏周期内的等级和排名,这有助于推动玩家在整个联赛期间的竞争和参与。

联赛可以根据您的特定游戏和需求进行定制。给予的奖励取决于您的游戏的财物状况,您可以决定让玩家支付加入联赛的入场费。您可以使用任何所需的晋级和降级逻辑创建无限数量的玩家层级。您可以按照百分比(或其他指标)为每个排行榜创建晋级区和降级区,也可以确定在每个比赛时段后要晋级和降级的玩家数量(例如前10名和后10名)。

在以下示例中,我们使用了两个层级的联赛,即在每周比赛周期后,前三名玩家晋级,最后三名玩家降级。在联赛的最底层级和最高层级之间,游戏中的货币奖励翻倍。

创建层级排行榜 #

在这里,我们为联赛创建了两个关联的排行榜,最底层级和最高层级各一个排行榜,每周都会重置。

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)

晋级和降级 #

每周比赛周期后,我们让前三名玩家晋级到最高层级。相应地,也让最后三名玩家降级。

排行榜晋级和降级
排行榜晋级和降级

请记住,开始时所有玩家都处于最低层级,所以我们必须先检查是否有足够的玩家晋级到最高层级,然后执行降级。

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

示例文件 #

下载此联赛的完整示例文件: