Cette page n’est pas encore traduite : la version anglaise est affichée.
Un classement dans Tanks
Tanks, l’arène d’exemple de Premiers pas, n’a pas de classement (leaderboard). Cette leçon lui ajoute un classement hebdomadaire des éliminations, en trois étapes : déclarer le classement, y soumettre (submit) les éliminations depuis un hook, puis le lire dans le client. Chaque étape renvoie à la page du module dont relève ce qu’elle utilise.
Déclarer le classement
Un classement est une déclaration : elle dit quel champ détermine le rang, comment se combinent plusieurs soumissions, quand le classement est réinitialisé et qui a le droit d’y soumettre. Aggregation.Increment ajoute chaque soumission au total en cours : une élimination vaut un point. Submit.ServerOnly, la valeur par défaut, ferme le classement aux clients ; c’est ce qui fait de l’étape 2 la seule voie d’entrée.
tanks-weekly-kills — éliminations par ordre décroissant, cumulées, réinitialisation le lundi, soumission par le serveur uniquement[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)La déclaration par attributs en Go n’est pas encore tranchée (O-1). Les exemples arriveront quand la forme d’écriture sera choisie.
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[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;
}Publiez-le avec playserv push : le classement apparaît dans le panneau, vide, avec son cycle du lundi déjà planifié. Les axes que vous n’avez pas définis gardent leur valeur par défaut. La liste complète des axes — propriétaire, clé de tri, champs affichés, règles de tournoi — se trouve sur Leaderboards.
Les horaires sont en UTC
La réinitialisation hebdomadaire a lieu le lundi à 00:00 UTC, pas dans le fuseau horaire des joueurs.
Soumettre depuis le hook d’élimination
Tanks met déjà fin à une vie grâce au seuil de PV déclaré sur le tank : à zéro PV, la transition death se déclenche, puis la plateforme appelle le hook. Ce hook est une fonction cloud, typée en entrée comme en sortie ; soumettre une élimination y tient en une ligne.
[After] sur l’épuisement des PV soumet une élimination au nom du tireur[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)La déclaration par attributs en Go n’est pas encore tranchée (O-1). Les exemples arriveront quand la forme d’écriture sera choisie.
Un hook est une fonction cloud : il s’exécute sur la plateforme, pas dans le moteur. Écrivez-le en C#, TypeScript ou Python ; votre code Unreal s’abonne à l’événement rank changed qui en résulte. Des fonctions écrites dans le moteur sont prévues : des graphes Blueprint et du C++ (traduit vers une cible de script validée par la plateforme). Les deux seront analysés et publiés comme n’importe quelle déclaration, et l’exécution restera sur la plateforme.
Un hook est une fonction cloud : il s’exécute sur la plateforme, pas dans le moteur. Écrivez-le en C#, TypeScript ou Python ; votre code Unity s’abonne à l’événement rank changed qui en résulte.
e.By est l’attaquant transmis avec les dégâts : inutile de tenir soi-même le compte de qui a tiré sur qui. e.Id est l’identifiant de l’événement lui-même. Un classement Increment en a besoin comme clé d’idempotence : un événement d’élimination livré deux fois ne compte qu’une fois. Le point d’accroche, les garanties d’ordre et le contrat de veto sont décrits sur Extensibility. Le seuil qui déclenche le hook est un preset de statistique du tank, et la ligne de score qu’il écrit, ce sont des données ordinaires, que vous pouvez interroger.
Lire le classement dans le client
Deux lectures suffisent à toute l’interface : le haut du classement et la fenêtre autour du joueur local — cinq lignes au-dessus, cinq en dessous, plus la vôtre. Les deux renvoient des entrées classées, avec les éliminations et le nom affiché, prêtes à lier à une liste. Pendant le match, un abonnement (subscription) tient l’affichage à jour ; il ne transmet que le rang du joueur local.
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))La déclaration par attributs en Go n’est pas encore tranchée (O-1). Les exemples arriveront quand la forme d’écriture sera choisie.
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.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));La réinitialisation du lundi clôt le cycle au lieu de l’effacer : le tableau de la semaine passée reste lisible par son libellé, avec le même appel Top et un argument cycle:. Un hook de récompense à la clôture du cycle serait la quatrième étape naturelle ; elle est décrite sur Leaderboards.
Pour aller plus loin
- Leaderboards — les axes, les cycles, les tournois et le hook de pré-soumission qui plafonne les scores suspects.
- Extensibility — tous les points d’accroche des hooks, dans l’ordre, avec le contrat de veto.
- Entity Presets — le seuil de statistique qui a déclenché l’élimination à l’étape 2.
- Des trousses de soin dans Tanks — l’autre exemple Tanks : deux déclarations et un hook.
- Premiers pas — l’arène Tanks que cette leçon enrichit.
- Concepts clés — le vocabulaire que chaque page de module suppose acquis.