Pular para o conteúdo principal
O jogo ao vivo

Esta página ainda não foi traduzida: mostrando a versão em inglês.

Map

O mundo estático: limites, terreno, obstáculos e “para onde as coisas podem ir?”. O modelo físico é, de propósito, muito mais simples que o visual: primitivas com uma base e uma altura, camadas com regras e uma única consulta de posição válida que todos os outros módulos reutilizam.

Quando usar

  • Você precisa de um mundo estático (limites, terreno, obstáculos) que o servidor possa consultar, e não só renderizar.
  • Spawns, drops e decorações precisam cair em lugares válidos: uma única consulta RandomPosition, baseada em regras, sem atalhos.
  • As arenas devem ser geradas de novo a cada partida: um Seed declarado reproduz o mesmo mapa num relatório de bug.
  • Caixas e paredes quebram e voltam: objetos destrutíveis com HP e timers de respawn.
  • Bots e projéteis precisam de respostas de raycast e de linha de visão contra o conjunto de obstáculos.
  • Dispensável quando o mundo é puramente visual e nenhum código de servidor pergunta para onde as coisas podem ir.

Quem faz o quê

AtorO que faz nesta página
schema-authordeclara mapas, primitivas de obstáculo, objetos destrutíveis, camadas e suas regras
room-ownervincula um mapa a uma sala (room); pede posições de spawn; faz raycasts
operatorcoloca ou remove obstáculos e camadas pelo painel

Resumo

O layout arena declarado: seed e limites, terreno, rochas, caixas com respawn, uma camada de regras
[Map("arena", Seed = 42, Bounds = "160x160")]
public static class Arena
{
    [Terrain(HeightNoise = 0.3f)]                 public static Terrain Height;  // 3D height field
    [Scatter("rock", Count = 40, MinSpacing = 6)] public static ObstacleSet Rocks;
    [Destructible("crate", Count = 12, Hp = 100, RespawnAfter = "30s")] public static ObstacleSet Crates;
    [Layer("ground", NotInside = "water")]        public static Layer Ground;
}
// or: Maps.Named("arena-caves-v3")  — authored in the panel or loaded from an asset
@Map('arena', { seed: 42, bounds: '160x160' })
export class Arena {
  @Terrain({ heightNoise: 0.3 }) height: Terrain;                   // 3D height field
  @Scatter('rock', { count: 40, minSpacing: 6 }) rocks: ObstacleSet;
  @Destructible('crate', { count: 12, hp: 100, respawnAfter: '30s' }) crates: ObstacleSet;
  @Layer('ground', { notInside: 'water' }) ground: Layer;
}
// or: Maps.named('arena-caves-v3')  — authored in the panel or loaded from an asset
@Map("arena", seed=42, bounds="160x160")
class Arena:
    height = terrain(height_noise=0.3)                  # 3D height field
    rocks = scatter("rock", count=40, min_spacing=6)
    crates = destructible("crate", count=12, hp=100, respawn_after="30s")
    ground = layer("ground", not_inside="water")
# or: maps.named("arena-caves-v3")  — authored in the panel or loaded from an asset
Em breve — Go

Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.

USTRUCT(PSMap = (Name = "arena", Seed = 42, Bounds = "160x160"))
struct FArena
{
    GENERATED_BODY()

    UPROPERTY(PSTerrain = (HeightNoise = "0.3"))                             FPSTerrain Height;  // 3D height field
    UPROPERTY(PSScatter = (Obstacle = "rock", Count = 40, MinSpacing = 6))   FPSObstacleSet Rocks;
    UPROPERTY(PSDestructible = (Obstacle = "crate", Count = 12, Hp = 100,
                                RespawnAfter = "30s"))                       FPSObstacleSet Crates;
    UPROPERTY(PSStratum = (Name = "ground", NotInside = "water"))            FPSStratum Ground;
};
// or: PS::Maps::Named(TEXT("arena-caves-v3")) — authored in the panel or loaded from an asset
// declarations compile into the same pushed model — playserv push from the UE project or CI
Unity C# is the same C# API here — os mesmos atributos compilam na Unity, na base 2021.3 — sem sintaxe do C# 12 aqui
[Map("arena", Seed = 42, Bounds = "160x160")]
public static class Arena
{
    [Terrain(HeightNoise = 0.3f)]                 public static Terrain Height;  // 3D height field
    [Scatter("rock", Count = 40, MinSpacing = 6)] public static ObstacleSet Rocks;
    [Destructible("crate", Count = 12, Hp = 100, RespawnAfter = "30s")] public static ObstacleSet Crates;
    [Layer("ground", NotInside = "water")]        public static Layer Ground;
}
// or: Maps.Named("arena-caves-v3")  — authored in the panel or loaded from an asset

Scatter e Destructible são geradores de posicionamento, e não sorteios em runtime. O gerador é resolvido quando a versão do mapa é publicada: as quarenta rochas viram quarenta primitivas declaradas, e a versão publicada carrega as primitivas, não a regra. Por isso, o mesmo Seed gera as mesmas quarenta rochas na partida, no replay e no relatório de bug — e os limites de geometria são verificados uma única vez, nesse conjunto resolvido, antes que a versão chegue a um ambiente.

A consulta que todo o resto faz:

RandomPosition: um spawn justo, no chão, longe dos jogadores, sem repetir
var spawn = map.RandomPosition(r =>
{
    r.Layer("ground");
    r.AwayFrom(players, minDistance: 12);
    r.NoRepeat(lastN: 3);
});
const spawn = map.randomPosition((r) => {
  r.layer('ground');
  r.awayFrom(players, { minDistance: 12 });
  r.noRepeat({ lastN: 3 });
});
spawn = map.random_position(rules=lambda r: (
    r.layer("ground"),
    r.away_from(players, min_distance=12),
    r.no_repeat(last_n=3),
))
Em breve — Go

Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.

// Dedicated-server host: place a spawn through the same rule-based query
Map->Positions->GetRandom({ .Stratum = PSKeys::Strata::Ground,
                            .AwayFrom = Players,
                            .MinDistance = 12.f,
                            .NoRepeatLastN = 3 },
    TPSOnResult<FVector>::CreateLambda([](const TPSResult<FVector>& Result)
    {
        if (!Result.HasValue()) { return; }
        PlaceSpawn(Result.Value());
    }));
// 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 — um build de cliente mestre executa a mesma consulta; um cliente comum recebe o spawn na posição resultante
var spawn = map.RandomPosition(r =>
{
    r.Layer("ground");
    r.AwayFrom(players, minDistance: 12);
    r.NoRepeat(lastN: 3);
});

O modelo

static layer — the published map versionone map instancea query — RandomPosition, a raycastan answer, true for a named momentdynamic obstacles brought by entities shared, immutable geometrythis instance only
Uma consulta lê a camada estática publicada mais os obstáculos dinâmicos de uma instância; a resposta vale para um momento nomeado

Duas camadas, declaradas por pessoas diferentes.

CamadaO que contém e quem declara
estáticaterreno com altura, primitivas de obstáculo, limites e locais — conteúdo criado pela equipe
dinâmicaobstáculos trazidos por entidades em runtime: portas, objetos destrutíveis, plataformas. Por isso, um objeto destrutível é uma entidade com ciclo de vida, estados e dono, e só vira mapa na parte em que traz um obstáculo: uma rocha estática é declarada no mapa, enquanto uma porta é uma entidade que traz um obstáculo. Esses obstáculos não têm ciclo de vida próprio aqui: o ciclo de vida é da entidade

O que um mapa declara.

DeclaraO que é
key e versionum mapa é conteúdo criado pela equipe: declarado em código, endereçado por key e versionado — a versão faz parte do que uma sala referencia. Alterar a geometria de uma versão lançada é proibido; uma edição é uma nova versão
terrenoum campo de alturas — uma grade regular com um passo declarado, e esse passo é um limite de precisão declarado; por isso, uma consulta de altura responde de acordo com ele, e não com exatidão. O terreno pode não existir: uma arena no vazio é válida
obstáculosum conjunto fechado de primitivas — caixa, esfera, cápsula, envoltória convexa (convex hull) com um limite declarado de vértices. Uma malha de triângulos arbitrária não é aceita, e é isso que torna possível verificar no servidor
tipo de passabilidade por obstáculointransponível · transponível para uma classe declarada · bloqueia apenas a linha de visão. A mesma primitiva serve como parede e como arbusto, e a diferença é declarada, e não modelada duas vezes
limiteso volume fora do qual uma posição é inadmissível
estratos do mundoestratos espaciais declarados dentro de um mapa — solo, subsolo, ar. São declarações de geometria e de endereçamento
locaislugares ou áreas nomeados — um ponto de spawn, uma zona de captura, um corredor. Um local responde onde, nunca o que acontece: não carrega lógica de jogo
gerador de posicionamentoopcionalmente, uma regra que produz primitivas — uma quantidade, um espaçamento mínimo, uma área, um seed. Ele é resolvido quando uma versão é publicada, de forma determinística a partir do seed, e daí em diante o mapa contém primitivas, e não uma regra

Um estrato do mundo e uma instância de mapa nunca são sinônimos.

O que é
estrato do mundouma declaração dentro do mapa — solo, subsolo, ar
instância de mapauma cópia em runtime independente do mapa publicado. As instâncias compartilham a mesma geometria publicada e imutável, mas têm obstáculos dinâmicos e listas de entidades independentes. Uma sala ocupa uma instância e pode selecionar estratos dentro dela

O que vale para toda consulta.

SempreO que é
um único cânone geométricotoda a geometria usa o cânone de coordenadas declarado pela plataforma, e a precisão de cada campo geométrico é declarada no próprio campo
o modelo do mundoé uma simplificação: a geometria do servidor não é o modelo artístico, nem precisa ser
a resposta nomeia a instância e o momentoa consulta é respondida a partir da camada estática do mapa mais os obstáculos dinâmicos da instância consultada, e declara o momento em que é verdadeira — os obstáculos dinâmicos mudam, então a resposta é um snapshot
os valoressão managed, e não seed: geometria não é ajuste diário, então uma edição feita pelo painel de administração é recusada, em vez de ser mantida em silêncio
movimento e contatonão são resolvidos aqui: o mapa responde o que é o espaço. Decidir se uma posição é admissível e qual é a resposta cabe às colisões; aplicar isso ao deslocamento cabe à movimentação

Erros

  • Um mapa, uma versão ou uma instância que não existe responde not found, assim como uma versão retirada: não adianta repetir.
  • Publicar geometria alterada sob uma versão existente é conflito: crie uma nova versão.
  • Falhas de publicação aparecem na declaração, no deploy, nunca em runtime: um mapa acima do limite de primitivas, uma envoltória convexa acima do limite de vértices e uma malha arbitrária como obstáculo são, todos, recusas de validação antes que qualquer coisa vá ao ar.
  • Uma consulta de altura fora dos limites não é erro: é a resposta declarada “fora dos limites”, e ela é distinguível de “dentro de um obstáculo”, porque num caso o cliente dá meia-volta e no outro contorna.
  • Excesso na taxa de consultas responde na categoria rate limit, com prazo.

Limites

Os números chegam com o capítulo de limites da plataforma; o que acontece em cada teto já está definido.

  • Primitivas de obstáculo por mapa, vértices da envoltória convexa, resolução do campo de alturas, tamanho dos limites, locais por mapa: o excesso em qualquer um deles é recusado na publicação, e não no momento da consulta.
  • Instâncias por mapa: criar mais uma é recusado como conflito; as instâncias existentes nunca são liberadas para abrir espaço.
  • Versões mantidas: a versão em descontinuação mais antiga é retirada, mas uma versão em uso por uma sala ao vivo nunca é.
  • Taxa de consultas ao espaço: rate limit com prazo.