Saltar al contenido principal
El juego en vivo

Esta página aún no está traducida: se muestra la versión en inglés.

Map

El mundo estático: límites, terreno, obstáculos y “¿dónde puede ir cada cosa?”. El modelo físico es, a propósito, mucho más simple que el visual: primitivas con una huella y una altura, capas con reglas y una única consulta de posición válida que reutilizan todos los demás módulos.

Cuándo usarlo

  • Necesitas un mundo estático (límites, terreno, obstáculos) que el servidor pueda consultar, no solo renderizar.
  • Las apariciones, los objetos que caen y las decoraciones tienen que quedar en lugares válidos: una sola consulta RandomPosition basada en reglas, sin atajos.
  • Las arenas deben regenerarse en cada partida: un Seed declarado reproduce el mismo mapa en un reporte de bug.
  • Las cajas y los muros se rompen y vuelven: objetos destructibles con HP y temporizadores de reaparición.
  • Los bots y los proyectiles necesitan respuestas de raycast y de línea de visión contra el conjunto de obstáculos.
  • No lo necesitas si el mundo es puramente visual y ningún código del servidor pregunta dónde puede ir cada cosa.

Quién hace qué

ActorQué hace en esta página
schema-authordeclara mapas, primitivas de obstáculo, objetos destructibles, capas y sus reglas
room-ownervincula un mapa a una sala (room); pide posiciones de aparición; lanza raycasts
operatorcoloca o quita obstáculos y capas desde el panel

En resumen

La disposición arena declarada: semilla y límites, terreno, rocas, cajas que reaparecen y una capa de reglas
[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
Próximamente — Go

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(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 — los mismos atributos compilan en Unity, sobre la base 2021.3; aquí no hay sintaxis de C# 12
[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 y Destructible son generadores de colocación, no tiradas al azar en runtime. Un generador se resuelve cuando se publica la versión del mapa: las cuarenta rocas se convierten en cuarenta primitivas declaradas, y la versión publicada lleva las primitivas, no la regla. Por eso el mismo Seed da las mismas cuarenta rocas en la partida, en la repetición y en el reporte de bug — y los límites de geometría se verifican una sola vez, sobre ese conjunto resuelto, antes de que la versión llegue a un entorno.

La consulta que hacen todos los demás:

RandomPosition: una aparición justa sobre el suelo, lejos de los jugadores y sin repetirse
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),
))
Próximamente — Go

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.

// 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 — una compilación de cliente maestro ejecuta la misma consulta; un cliente común aparece en la posición resultante
var spawn = map.RandomPosition(r =>
{
    r.Layer("ground");
    r.AwayFrom(players, minDistance: 12);
    r.NoRepeat(lastN: 3);
});

El 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
Una consulta lee la capa estática publicada más los obstáculos dinámicos de una instancia; la respuesta vale para un momento concreto

Dos capas, declaradas por personas distintas.

CapaQué contiene y quién la declara
estáticaterreno con altura, primitivas de obstáculo, límites y ubicaciones: contenido de autor
dinámicaobstáculos que aportan las entidades en runtime: puertas, objetos destructibles, plataformas. Por eso un objeto destructible es una entidad con ciclo de vida, estados y dueño, y solo pasa a ser parte del mapa en lo que aporta como obstáculo: una roca estática se declara en el mapa; una puerta es una entidad que aporta uno. Estos obstáculos no tienen ciclo de vida propio aquí: el ciclo de vida es de la entidad

Qué declara un mapa.

DeclaraQué es
key y versionun mapa es contenido de autor: se declara en código, se identifica por key y tiene versiones; la versión es parte de lo que referencia una sala. Cambiar la geometría de una versión ya lanzada está prohibido; una edición es una versión nueva
terrenoun campo de alturas: una cuadrícula regular con un paso declarado, y ese paso es un límite de precisión declarado, así que una consulta de altura responde según él y no con exactitud. El terreno puede no existir: una arena en el vacío es válida
obstáculosun conjunto cerrado de primitivas: caja, esfera, cápsula, envolvente convexa con un límite de vértices declarado. Una malla de triángulos arbitraria no se acepta, y esa es la condición para que una verificación del lado del servidor sea posible
tipo de transitabilidad por obstáculointransitable · transitable para una clase declarada · solo bloquea la línea de visión. Una misma primitiva sirve como muro y como arbusto, y la diferencia se declara en vez de modelarse dos veces
límitesel volumen fuera del cual una posición no es admisible
estratos del mundoestratos espaciales declarados dentro de un mapa: suelo, subsuelo, aire. Son declaraciones de geometría y de direccionamiento
ubicacioneslugares o áreas con nombre: un punto de aparición, una zona de captura, un pasillo. Una ubicación responde dónde, nunca qué pasa: no lleva lógica de juego
generador de colocaciónopcionalmente, una regla que produce primitivas: una cantidad, una separación mínima, un área, una semilla. Se resuelve al publicar una versión, de forma determinista según la semilla, y desde ese momento el mapa contiene primitivas y no una regla

Un estrato del mundo y una instancia de mapa nunca son sinónimos.

Qué es
estrato del mundouna declaración dentro del mapa: suelo, subsuelo, aire
instancia de mapauna copia en runtime independiente del mapa publicado. Las instancias comparten la geometría publicada, que es inmutable, y tienen obstáculos dinámicos y listas de entidades independientes. Una sala ocupa una instancia y puede elegir estratos dentro de ella

Qué se cumple en toda consulta.

SiempreQué es
un único canon geométricotoda la geometría usa el canon de coordenadas declarado de la plataforma, y la precisión de cada campo (field) geométrico se declara en el propio campo
el modelo del mundoes una simplificación: la geometría del servidor no es el modelo artístico, ni tiene por qué serlo
una respuesta nombra su instancia y su momentouna consulta se responde con la capa estática del mapa más los obstáculos dinámicos de la instancia por la que se preguntó, y declara el momento en el que es cierta: los obstáculos dinámicos cambian, así que la respuesta es un snapshot
los valoresson managed, no seed: la geometría no se ajusta a diario, así que una edición desde la consola de administración se rechaza en lugar de guardarse sin avisar
el movimiento y el contactono se resuelven aquí: esto responde qué es el espacio; si una posición es admisible y cuál es la respuesta le corresponde a las colisiones, y aplicarla, al desplazamiento

Errores

  • Un mapa, una versión o una instancia que no se encuentra responde not found, y lo mismo una versión retirada: no tiene sentido repetir.
  • Publicar geometría modificada bajo una versión existente es un conflicto: crea una versión nueva.
  • Las fallas de publicación aparecen al declarar o al desplegar, nunca en runtime: un mapa por encima del límite de primitivas, una envolvente convexa por encima de su límite de vértices y una malla arbitraria como obstáculo son todos rechazos de validación antes de que se publique nada.
  • Una consulta de altura fuera de los límites no es un error: es la respuesta declarada “fuera de los límites”, y se distingue de “dentro de un obstáculo”, porque en un caso el cliente se da vuelta y en el otro lo rodea.
  • Superar la frecuencia de consultas responde en la categoría de límite de frecuencia, con un plazo.

Límites

Las cifras llegarán con el capítulo de límites de la plataforma; lo que pasa en cada tope ya está definido.

  • Primitivas de obstáculo por mapa, vértices de la envolvente convexa, resolución del campo de alturas, tamaño de los límites, ubicaciones por mapa: todos se rechazan al publicar, no al consultar.
  • Instancias por mapa: crear otra se rechaza como conflicto; las instancias existentes nunca se liberan para hacer lugar.
  • Versiones conservadas: se retira la versión obsoleta más antigua, y nunca una versión que usa una sala en vivo.
  • La frecuencia de consultas al espacio: un límite de frecuencia con un plazo.