Esta página aún no está traducida: se muestra la versión en inglés.
Tabla de clasificación en Tanks
Tanks, la arena de ejemplo de Primeros pasos, todavía no tiene tabla de clasificación (leaderboard). En esta lección le agregas una tabla semanal de eliminaciones en tres pasos: declarar la tabla, enviarle puntos desde el hook de eliminación y leerla en el cliente. Cada paso enlaza a la página del módulo al que pertenece lo que usa.
Declara la tabla
Una tabla es una declaración: dice qué campo (field) ordena la clasificación, cómo se combinan los envíos repetidos, cuándo se reinicia y quién puede enviarle puntos. Aggregation.Increment suma cada envío al total acumulado, así que una eliminación vale un punto. Submit.ServerOnly es el valor predeterminado y cierra la tabla a los clientes; por eso el paso 2 es la única forma de entrar en ella.
tanks-weekly-kills — eliminaciones en orden descendente, suma acumulada, reinicio los lunes, solo el servidor envía[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)Todavía no está decidido cómo declarar con atributos en Go (O-1). Los ejemplos llegarán cuando se elija la forma de escribirlo.
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;
}Súbela con playserv push y la tabla aparece vacía en el panel, con su ciclo de los lunes ya programado. Los ejes que no configuraste conservan sus valores predeterminados. La lista completa de ejes — dueño, clave de orden, campos visibles, reglas de torneo — está en Leaderboards.
Los horarios están en UTC
El reinicio semanal ocurre el lunes a las 00:00 UTC, no según la zona horaria de los jugadores (player).
Envía desde el hook de eliminación
En Tanks, una vida ya termina con el umbral de HP declarado en el tanque: cuando el HP llega a cero se dispara la transición death, y justo después la plataforma llama al hook. El hook es una función en la nube con entrada y salida tipadas, así que enviar una eliminación ocupa una sola línea.
[After] sobre el agotamiento de hp le envía una eliminación a quien la consiguió[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)Todavía no está decidido cómo declarar con atributos en Go (O-1). Los ejemplos llegarán cuando se elija la forma de escribirlo.
Un hook es una función en la nube: se ejecuta en la plataforma, no en el motor. Escríbelo en C#, TypeScript o Python; tu código de Unreal se suscribe al evento rank changed que resulta. Están previstas funciones escritas desde el motor: grafos de Blueprint y C++ (traducido a un script que valida la plataforma). Ambos se analizan y se suben como cualquier declaración; la ejecución sigue en la plataforma.
Un hook es una función en la nube: se ejecuta en la plataforma, no en el motor. Escríbelo en C#, TypeScript o Python; tu código de Unity se suscribe al evento rank changed que resulta.
e.By es el atacante que viene con el daño, así que no hace falta llevar la cuenta de quién le disparó a quién. e.Id es el id del propio evento, y pasarlo como clave de idempotencia es justo lo que necesita una tabla Increment: si el evento de eliminación se entrega de nuevo, cuenta una vez, no dos. El punto de enganche del hook, las garantías de orden y el contrato de veto están en Extensibility; el umbral que lo dispara es un preset de estadística (stat) del tanque, y la fila de puntaje que escribe son datos comunes que puedes consultar.
Lee la tabla en el cliente
Con dos lecturas cubres toda la interfaz: la parte superior de la tabla y la ventana alrededor del jugador local — cinco filas arriba, cinco abajo y la tuya. Las dos devuelven entradas ordenadas por posición, con eliminaciones y nombre visible, listas para mostrarse en una lista. Una suscripción mantiene el panel al día mientras dura la partida, y solo entrega la posición del jugador 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))Todavía no está decidido cómo declarar con atributos en Go (O-1). Los ejemplos llegarán cuando se elija la forma de escribirlo.
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));El reinicio del lunes cierra el ciclo en lugar de borrarlo, así que la tabla de la semana pasada se puede seguir leyendo por su etiqueta: es la misma llamada Top con un argumento cycle:. El cuarto paso natural es un hook de recompensa al cerrar el ciclo, que se describe en Leaderboards.
Siguientes pasos
- Leaderboards — los ejes, los ciclos, los torneos y el hook previo al envío que pone un tope a los puntajes sospechosos.
- Extensibility — todos los puntos de enganche de hooks, en orden, con el contrato de veto.
- Entity Presets — el umbral de estadística que disparó la eliminación en el paso 2.
- Botiquines en Tanks — el otro ejemplo de Tanks: dos declaraciones y un hook.
- Primeros pasos — la arena de Tanks que amplía esta lección.
- Conceptos básicos — el vocabulario que da por sentado cada página de módulo.