Перейти до основного вмісту
Приклади

Цю сторінку ще не перекладено — показано англійську версію.

Таблиця лідерів у Tanks

У Tanks — навчальній арені зі сторінки Перші кроки — поки немає таблиці лідерів (leaderboard). У цьому уроці ми додамо щотижневу таблицю фрагів. Кроків три: оголосити таблицю, надсилати в неї фраги з хука і читати її на клієнті. Кожен крок посилається на сторінку модуля, якому належить те, чим ви на цьому кроці користуєтеся.

Оголосіть таблицю

Таблиця — це оголошення (declaration). Воно визначає, за яким полем (field) будують рейтинг, як поєднуються повторні надсилання, коли таблицю скидають і хто може в неї надсилати. Aggregation.Increment додає кожне надсилання до поточної суми, тож один фраг — одне очко. Submit.ServerOnly — типове значення: воно закриває таблицю для клієнтів, тому потрапити в неї можна лише так, як на кроці 2.

tanks-weekly-kills — фраги за спаданням, очки підсумовуються, скидання щопонеділка, надсилає лише сервер
[Leaderboard("tanks-weekly-kills")]
public static class WeeklyKills
{
    public static Owner Owner = Owner.Player;
    public static Aggregation Agg = Aggregation.Increment;
    public static Reset Reset = Reset.Weekly(DayOfWeek.Monday);
    public static Submit Submit = Submit.ServerOnly;

    [Rank(1, Sort.Descending)] public static int Kills;
}
@Leaderboard('tanks-weekly-kills')
export class WeeklyKills {
  static owner = Owner.Player;
  static agg = Aggregation.Increment;
  static reset = Reset.weekly(DayOfWeek.Monday);
  static submit = Submit.ServerOnly;

  @rank(1, Sort.Descending) static kills: number;
}
@leaderboard("tanks-weekly-kills")
class WeeklyKills:
    owner = Owner.PLAYER
    agg = Aggregation.INCREMENT
    reset = Reset.weekly(DayOfWeek.MONDAY)
    submit = Submit.SERVER_ONLY

    kills: int = rank(1, Sort.DESCENDING)
Незабаром — Go

Як оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.

USTRUCT(PSLeaderboard = (Name = "tanks-weekly-kills", Owner = "Player", Aggregation = "Increment",
                        Reset = "Weekly:Monday", Submit = "ServerOnly"))
struct FWeeklyKills
{
    GENERATED_BODY()

    UPROPERTY(PSRank = (Order = 1, Sort = "Descending")) int32 Kills;
};
// declarations compile into the same pushed model — playserv push from the UE project or CI
Unity C# is the same C# API here — ті самі атрибути C#; клієнт на Unity читає таблицю на кроці 3, але надсилати в неї не може
[Leaderboard("tanks-weekly-kills")]
public static class WeeklyKills
{
    public static Owner Owner = Owner.Player;
    public static Aggregation Agg = Aggregation.Increment;
    public static Reset Reset = Reset.Weekly(DayOfWeek.Monday);
    public static Submit Submit = Submit.ServerOnly;

    [Rank(1, Sort.Descending)] public static int Kills;
}

Завантажте оголошення командою playserv push — і в панелі з’явиться порожня таблиця, для якої вже заплановано цикл зі скиданням у понеділок. Осі, яких ви не задали, лишаються з типовими значеннями. Повний перелік осей — власник, ключ сортування, поля для показу, правила турніру — є на сторінці Leaderboards.

Розклад — за UTC

Щотижневе скидання відбувається в понеділок о 00:00 UTC, а не за часовим поясом гравців.

Надсилайте фраги з хука

У Tanks життя танка вже завершує поріг HP, оголошений на самому танку: коли HP падає до нуля, спрацьовує перехід death, а одразу після нього платформа викликає хук. Хук — це хмарна функція з типізованими входом і виходом, тож щоб надіслати фраг, у ній достатньо одного рядка.

Хук [After] на вичерпання hp зараховує один фраг тому, хто знищив танк
[After(Stats.Depleted, stat: "hp")]
public static Task SubmitKill(StatEvent e) =>
    PlayServ.Leaderboards.Submit("tanks-weekly-kills", e.By.PlayerId,
        kills: 1, idempotencyKey: e.Id);
export const submitKill = after(Stats.depleted, { stat: 'hp' }, (e: StatEvent) =>
  PlayServ.leaderboards.submit('tanks-weekly-kills', e.by.playerId,
    { kills: 1, idempotencyKey: e.id }));
@after(stats.depleted, stat="hp")
async def submit_kill(e: StatEvent):
    await playserv.leaderboards.submit("tanks-weekly-kills", e.by.player_id,
        kills=1, idempotency_key=e.id)
Незабаром — Go

Як оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.

Виконується поза рушієм

Хук — це хмарна функція: він працює на платформі, а не в рушії. Напишіть його на C#, TypeScript або Python, а ваш код на Unreal підпишеться на подію rank changed, яку він породжує. У планах — функції, написані в самому рушії: графи Blueprint і C++ (його транслюватимуть у скрипт, який перевіряє платформа). Їх аналізуватимуть і завантажуватимуть, як будь-яке оголошення, а виконувати їх і далі буде платформа.

Виконується поза рушієм

Хук — це хмарна функція: він працює на платформі, а не в рушії. Напишіть його на C#, TypeScript або Python, а ваш код на Unity підпишеться на подію rank changed, яку він породжує.

e.By — нападник, якого шкода вже несе в собі, тож окремо відстежувати, хто в кого стріляв, не потрібно. e.Id — ідентифікатор самої події (event), і таблиці з Increment потрібен саме він як ключ ідемпотентності: якщо подію про фраг доставлять повторно, її зарахують один раз, а не двічі. Точки виклику хуків, гарантії порядку і правила вето описано на сторінці Extensibility. Поріг, за яким спрацьовує хук, — це пресет характеристики (stat) на танку, а рядок із рахунком, який записує хук, — звичайні дані, до яких можна робити запити.

Читайте таблицю на клієнті

Для всього інтерфейсу вистачає двох запитів: верхівка таблиці та вікно навколо локального гравця (player) — п’ять рядків вище, п’ять нижче і ваш власний. Обидва повертають записи з позицією, кількістю фрагів та ім’ям для показу, і їх можна одразу прив’язати до списку. Підписка (subscription) оновлює таблицю на екрані, поки триває матч, але надсилає лише позицію локального гравця.

Топ-20, п’ять рядків навколо мене і жива підписка на мою позицію
var top    = await playserv.Leaderboards.Top("tanks-weekly-kills", 20);
var around = await playserv.Leaderboards.AroundMe("tanks-weekly-kills", 5);

playserv.Leaderboards.OnRankChanged("tanks-weekly-kills", r => UpdateHud(r));
const top    = await playserv.leaderboards.top('tanks-weekly-kills', 20);
const around = await playserv.leaderboards.aroundMe('tanks-weekly-kills', 5);

playserv.leaderboards.onRankChanged('tanks-weekly-kills', (r) => updateHud(r));
top    = await playserv.leaderboards.top("tanks-weekly-kills", 20)
around = await playserv.leaderboards.around_me("tanks-weekly-kills", 5)

playserv.leaderboards.on_rank_changed("tanks-weekly-kills", lambda r: update_hud(r))
Незабаром — Go

Як оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.

Client->Leaderboards->Of<FWeeklyKills>()->Get(
    TPSOnResult<FPSBoard*>::CreateWeakLambda(this, [this](const TPSResult<FPSBoard*>& Result)
    {
        if (!Result.HasValue()) { return; }
        OnBoard(Result.Value());
    }));

// in OnBoard(FPSBoard* Board):
Board->Entries->Select().Page(20).Then(
    TPSOnResult<TPSPage<FPSLeaderboardEntry>>::CreateWeakLambda(this, [this](const TPSResult<TPSPage<FPSLeaderboardEntry>>& Top)
    {
        if (!Top.HasValue()) { return; }
        Hud->ShowTop(Top.Value().Rows);
    }));

Board->Entries->SelectAround(MyPlayerId, /*Radius*/ 5,
    TPSOnResult<TArray<FPSLeaderboardEntry>>::CreateWeakLambda(this, [this](const TPSResult<TArray<FPSLeaderboardEntry>>& Around)
    {
        if (!Around.HasValue()) { return; }
        Hud->ShowWindow(Around.Value());
    }));

TPSSubscription MyRank = Board->Subscribe->Mine(
    [this](const FPSLeaderboardEntry& Mine) { UpdateHud(Mine); });
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.
Unity C# is the same C# API here — працює в Unity без змін, зі згенерованими типами
var top    = await playserv.Leaderboards.Top("tanks-weekly-kills", 20);
var around = await playserv.Leaderboards.AroundMe("tanks-weekly-kills", 5);

playserv.Leaderboards.OnRankChanged("tanks-weekly-kills", r => UpdateHud(r));

Понеділкове скидання закриває цикл, а не видаляє його, тож таблицю минулого тижня можна прочитати за її міткою — тим самим викликом Top з аргументом cycle:. Природний четвертий крок — хук, що видає нагороди, коли цикл закривається. Його описано на сторінці Leaderboards.

Що далі

  • Leaderboards — осі, цикли, турніри і хук перед надсиланням, що обмежує підозрілі результати.
  • Extensibility — усі точки виклику хуків по порядку і правила вето.
  • Entity Presets — поріг характеристики, що на кроці 2 зарахував фраг.
  • Аптечки в Tanks — другий приклад на Tanks: два оголошення й один хук.
  • Перші кроки — арена Tanks, яку розширює цей урок.
  • Основні поняття — словник, на який спирається кожна сторінка модуля.