Esta página ainda não foi traduzida: mostrando a versão em inglês.
Prediction & Lag Compensation
Predição para a frente e compensação para trás, sobre dados que carregam o momento real do evento. O cliente responde na hora, o servidor continua certo, e os acertos são julgados na linha do tempo do atirador.
Quando usar
- O input precisa parecer instantâneo mesmo com latência, e o servidor continua autoritativo: prevê para a frente e reconcilia quando diverge.
- Os acertos precisam ser julgados na linha do tempo do atirador:
ResolveAtretrocede as hitboxes até o tick de visualização informado. - Arcos de mira e marcadores de impacto precisam bater com o resultado: cliente e servidor calculam a mesma
Trajectory. - Campos críticos para o jogo nunca podem sofrer rollback: declare o que é previsto e o que espera o servidor.
- O rubber banding precisa de ajuste: janelas por entidade, tolerâncias e telemetria de erros de predição.
- Dispensável quando a latência não atrapalha. Jogos por turnos ou lentos funcionam bem só com os deltas do Data & Subscriptions.
Quem faz o quê
| Ator | O que faz nesta página |
|---|---|
schema-author | declara quais campos são previstos e quais são só autoritativos; define a janela de predição |
room-owner | resolve acertos sobre um estado histórico; retrocede o mundo |
player | prevê e reconcilia o movimento; assina as correções |
A quais salas isso se aplica
Este módulo roda onde a plataforma avança a simulação: nas salas (room) declaradas com Host = "Backend". Se o seu próprio servidor de jogo é dono da simulação (PlayServ como metaservidor), movimento, colisões e predição ficam do lado da engine, e esta página descreve a alternativa hospedada pela plataforma, não uma exigência. Veja Quem executa o tick.
Resumo
A palavra cobre três mecanismos distintos, com autoridades e modos de falha diferentes, e cada um tem o seu próprio artigo:
| Mecanismo | O que faz | Onde roda | Quando erra |
|---|---|---|---|
| Prever o próprio movimento | aplica o modelo declarado ao seu próprio input sem esperar o servidor | o cliente | uma correção, reaplicada e suavizada |
| Como mostrar outros jogadores | desenha as outras entidades entre os estados que chegam | o cliente | um tranco visível |
| Lag Compensation | retrocede os alvos até o momento que o atirador viu | o servidor | alguém morre injustamente |
Quatro presets, e “no prediction” é um deles.
| Preset | Prevê o seu | Compensa | Suaviza os outros |
|---|---|---|---|
| shooter | sim | numa janela de mais ou menos um segundo e meio | sim |
| arcade | sim | não | sim |
| observer | não | não | sim |
| no prediction | não | não | não — o estado chega da autoridade com uma janela de interpolação declarada |
Jogos por turnos, de estratégia e a maioria dos títulos para celular não querem predição nenhuma; ao declarar “no prediction”, você diz ao cliente que mostre o estado como ele é, sem adivinhar.
O módulo não tem modelo de movimento, tabela de reações, geometria nem janela de histórico próprios. Eles pertencem, respectivamente, a Locomotion, Collision, Map e Entity. A predição os aplica antes ou os lê de trás para a frente; nunca declara uma segunda cópia.
Tank: campos previstos, Hp só autoritativo, janela de 8 ticks para a frente / 64 para trás[Entity("tank")]
[Prediction(ForwardTicks = 8, MaxRewindTicks = 64)]
public class Tank
{
[Sync, Predicted] public Vector3 Position; // rolls back and replays
[Sync, Predicted] public Vector3 Velocity;
[Stat(Max = 100), AuthoritativeOnly] public Stat Hp; // never predicted
}@Entity('tank')
@Prediction({ forwardTicks: 8, maxRewindTicks: 64 })
export class Tank {
@Sync() @Predicted() position!: Vector3; // rolls back and replays
@Sync() @Predicted() velocity!: Vector3;
@Stat({ max: 100 }) @AuthoritativeOnly() hp: Stat; // never predicted
}@entity("tank")
@prediction(forward_ticks=8, max_rewind_ticks=64)
class Tank:
position: Vector3 = sync(predicted=True) # rolls back and replays
velocity: Vector3 = sync(predicted=True)
hp = stat(max=100, authoritative_only=True) # never predictedAinda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
UCLASS(PSEntity = "tank", PSPrediction = (ForwardTicks = 8, MaxRewindTicks = 64))
class UTank : public UObject
{
GENERATED_BODY()
UPROPERTY(PSSync = (Predicted = "true")) FVector3f Position; // rolls back and replays
UPROPERTY(PSSync = (Predicted = "true")) FVector3f Velocity;
UPROPERTY(PSStat = (Max = 100, AuthoritativeOnly = "true")) FPSStat Hp; // never predicted
};
// declarations compile into the same pushed model — playserv push from the UE project or CI[Entity("tank")]
[Prediction(ForwardTicks = 8, MaxRewindTicks = 64)]
public class Tank
{
[Sync, Predicted] public Vector3 Position; // rolls back and replays
[Sync, Predicted] public Vector3 Velocity;
[Stat(Max = 100), AuthoritativeOnly] public Stat Hp; // never predicted
}A resolução com compensação de lag responde “onde estava todo mundo quando este tiro foi disparado”:
ResolveAt(shooterViewTick) retrocede as hitboxes até o que o atirador via[After(Projectiles.HitReported)]
public static void Validate(HitReport hit) =>
hit.ResolveAt(hit.ShooterViewTick); // rewinds hitboxes, sub-tick interpolatedexport const validate = after(Projectiles.hitReported, (hit: HitReport) =>
hit.resolveAt(hit.shooterViewTick)); // rewinds hitboxes, sub-tick interpolated@after(projectiles.hit_reported)
def validate(hit: HitReport):
hit.resolve_at(hit.shooter_view_tick) # rewinds hitboxes, sub-tick interpolatedAinda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
Um hook é uma cloud function: ele executa na plataforma, não na engine. Escreva em C#, TypeScript ou Python — o Unreal assina os eventos resultantes. Funções escritas na engine estão planejadas: grafos de Blueprint e C++ (traduzido para um alvo de script validado pela plataforma). Os dois são analisados e publicados como qualquer declaração; a execução continua na plataforma.
Um hook é uma cloud function: ele executa na plataforma, não na engine. Escreva em C#, TypeScript ou Python — o Unity assina os eventos resultantes.
Previsão de trajetória, compartilhada por servidor e cliente (arcos de mira, marcadores de impacto). A previsão é uma operação deste módulo, extrapolada contra o conjunto de obstáculos do mapa, então os dois lados desenham o mesmo arco a partir dos mesmos dados de entrada:
Trajectory: uma previsão que considera colisões, compartilhada pelo servidor e pela prévia da miravar arc = room.Prediction.Trajectory(from, velocity, steps: 30); // collision-awareconst arc = room.prediction.trajectory(from, velocity, { steps: 30 }); // collision-awarearc = room.prediction.trajectory(origin, velocity, steps=30) # collision-awareAinda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
// collision-aware: the arc the platform itself would walk
Room->Prediction->Trajectories->Get(LaunchPosition, LaunchVelocity, /*Steps*/ 30,
TPSOnResult<FPSTrajectory>::CreateWeakLambda(this, [this](const TPSResult<FPSTrajectory>& Result)
{
if (!Result.HasValue()) { return; }
DrawArc(Result.Value());
}));
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.var arc = room.Prediction.Trajectory(from, velocity, steps: 30); // collision-awareO modelo
A previsibilidade é declarada em um aspecto — e um aspecto que só a autoridade altera, por regras que o cliente não tem, não pode ser marcado como previsível: isso é uma falha de validação no deploy, não uma surpresa em runtime.
O que se declara.
| Declara | O que é |
|---|---|
aspectos previsíveis | quais o cliente pode calcular à frente da autoridade |
limiar de divergência | abaixo dele, a correção é suavizada; acima, o estado da autoridade é aceito como chega. Declarado e managed — não é um ajuste que o designer mexe todo dia |
modo de exibição de entidades remotas | interpolação entre os estados que chegaram, ou extrapolação |
atraso de interpolação | quanto a exibição dos outros fica atrasada; é declarado, não ajustado no olho |
janela de extrapolação | além dela, a entidade é marcada como desatualizada e a extrapolação para |
janela de compensação | até onde um retrocesso pode voltar; é managed |
O que é retrocedido e o que, de propósito, não é.
| O que é | |
|---|---|
retrocedido | as posições e orientações dos alvos, e a geometria dos obstáculos dinâmicos quando o tipo os declara históricos |
não retrocedido | o estado de vida — quem morreu não revive para levar o tiro —, além de posse, pontuação e inventário |
a regra por trás da divisão | a decisão é tomada no passado; o efeito é aplicado no presente |
| Sempre | O que é |
|---|---|
o estado autoritativo informa o input que viu | ele traz o número do último input aplicado, e é isso que torna a reconciliação exata, e não aproximada |
divergência | é observável: o cliente sabe que a predição foi corrigida, em vez de ir se desviando em silêncio |
um momento de visualização | é uma alegação, não um fato: o momento que o ator diz ter visto. Além da janela, a plataforma recusa em vez de extrapolar: extrapolar em silêncio é um presente para o trapaceiro, que só precisaria enviar um momento mais antigo |
um retrocesso não garante reprodutibilidade em ponto flutuante | a mesma restrição que vale em todo o resto do contrato |
um anel de histórico, dois consumidores | a reconciliação e as consultas com compensação de lag leem a mesma trilha de histórico instantâneo da entidade. O retrocesso fica aqui, e não nos módulos retrocedidos: o anel restaura as poses do tick em disputa, e então as colisões respondem à pergunta de sobreposição de sempre sobre essas poses — sem histórico próprio e sem saber nada de “tick de visualização” |
client tick: apply input locally (predict) → buffer it → send, tick-stamped
server tick: step the same movement model → authoritative state → delta out
client recv: authoritative state for tick T → if divergence beyond tolerance:
rewind to T → replay buffered inputs T+1..now → smooth
Só os campos declarados como previstos sofrem rollback; um desvio mínimo é suavizado, e uma divergência real volta no tempo e reaplica os inputs.
Erros
- Momento de visualização fora da janela é um conflito: envie um atual.
- Pedido de histórico fora da janela também é um conflito.
- Dois defeitos de declaração são pegos no deploy: uma janela de compensação maior que o buffer de histórico e um aspecto marcado como previsível sem que o cliente tenha regras para prevê-lo. Nenhum dos dois chega a uma partida ao vivo.
- Duas coisas não são erros, e as duas são observáveis. Um buffer de input cheio suspende a predição até a confirmação, em vez de descartar inputs em silêncio. E uma divergência acima do limiar faz o estado da autoridade ser aceito como chega: essa é a correção declarada, não uma falha.
Limites
Os números chegam com o capítulo de limites da plataforma; o que acontece em cada teto já está definido.
- Janela de compensação: além dela, recusa, nunca extrapolação.
- Janela de extrapolação para os outros: a entidade é marcada como desatualizada e a extrapolação para.
- Buffer de inputs não confirmados: a predição é suspensa até a confirmação; inputs nunca são descartados em silêncio.
- Profundidade do histórico para um retrocesso: no mínimo a janela de compensação, verificado no deploy.
- Taxa de ações com momento de visualização: rate limit com prazo.
- Entidades previstas ao mesmo tempo por cliente: acima do teto, a predição não é feita, e isso é uma degradação declarada, não uma recusa.