Saltar al contenido principal
El modelo de tu juego

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

Entity presets

Un preset es un paquete con nombre de aspectos de entidad —datos, estados, RPC, eventos, hooks— armado para un caso concreto del juego. Puedes aplicar un preset tal cual, ajustar sus números o derivar el tuyo. Aplicarlo agrega aspectos a tu tipo; no coloca tu tipo por debajo de nada: un preset no es un módulo y no tiene nada propio de lo que heredar. Las estadísticas, las habilidades (ability), los proyectiles, las tablas de botín (drop table) y los objetos del mundo son cinco presets, no cinco subsistemas: la misma declaración, la misma sincronización, el mismo orden de hooks.

Cuándo usarlo

  • Algo en tu juego tiene números que se mantienen dentro de sus límites, se regeneran y disparan una transición al llegar a ellos.
  • Una acción necesita costo, tiempo de recarga (cooldown), fases y efectos, y debe poder lanzarse con un solo verbo del cliente.
  • Algo sale volando, y su impacto debe evaluarse de forma justa para un tirador con lag.
  • El botín debe salir de probabilidades ponderadas que se reproduzcan exactamente cuando un jugador reclama por un drop.
  • El mapa tiene mobiliario —puertas, botones, trampas, objetos destructibles— con estados que deben sobrevivir a que alguien se una a mitad de la ronda.
  • No uses presets cuando una entidad es solo datos sincronizados. Declara los campos y listo.

Quién hace qué

ActorQué hace en esta página
schema-authordeclara estadísticas, habilidades, proyectiles, tablas de botín y objetos del mundo
room-ownerajusta los números de los presets, tira las tablas de botín y crea objetos del mundo
playerlanza habilidades, dispara, recoge botín e interactúa con objetos

En resumen

Deriva Crate de dos presets incluidos y luego crea cada caja con una línea, ajustada a 250 HP
// derived once, in the schema
[Entity("crate", Persistence = Persistence.Runtime, Presets = new[] { Preset.WorldObjects, Preset.Stats })]
public class Crate { [Stat(Max = 100, AtMin = "broken")] public Stat Hp; }

// then one line per crate, on the room host
var crate = await room.Create<Crate>(at: pos, tune: c => c.Hp.Max = 250);
// derived once, in the schema
@Entity('crate', { persistence: Persistence.Runtime, presets: [Preset.WorldObjects, Preset.Stats] })
export class Crate { @Stat({ max: 100, atMin: 'broken' }) hp: Stat; }

// then one line per crate, on the room host
const crate = await room.create<Crate>({ at: pos, tune: (c) => { c.hp.max = 250; } });
# derived once, in the schema
@entity("crate", persistence=Persistence.RUNTIME, presets=[Preset.WORLD_OBJECTS, Preset.STATS])
class Crate:
    hp = stat(max=100, at_min="broken")

# then one line per crate, on the room host
crate = await room.create(Crate, at=pos, tune={"hp.max": 250})
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.

// declarations compile into the same pushed model — playserv push from the UE project or CI
UCLASS(PSEntity = (Name = "crate", Persistence = "Runtime", Presets = "world-objects, stats"))
class UCrate : public UObject
{
    GENERATED_BODY()

    UPROPERTY(PSStat = (Max = 100, AtMin = "broken")) FPSStat Hp;
};

// then one line per crate, on the room host (dedicated server / master-client)
Room->Entities->Of<UCrate>()->Create(FPSIdempotencyKey(CrateId),
    [SpawnPosition](UCrate& Crate) { Crate.Position = SpawnPosition; });   // Position — from the world-objects preset
// 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 — la misma declaración de C#; crear es algo que hace el host de la sala, y un cliente de Unity ve llegar la caja
// derived once, in the schema
[Entity("crate", Persistence = Persistence.Runtime, Presets = new[] { Preset.WorldObjects, Preset.Stats })]
public class Crate { [Stat(Max = 100, AtMin = "broken")] public Stat Hp; }

// then one line per crate, on the room host
var crate = await room.Create<Crate>(at: pos, tune: c => c.Hp.Max = 250);

El modelo

Un preset no introduce conceptos nuevos. Todo lo que agrega se puede expresar con lo que ya ofrece la entidad: aspectos, máquinas de estados, hooks, persistencia.

Qué aporta cada preset y dónde se ajusta.

PresetQué le da el contratoDónde se ajusta
statsun aspecto de características numéricas con límites, regeneración y modificadores, más un hook al alcanzar un umbral: 0 HP se convierte en una transición de la máquina de estados, no en un if en tu códigola declaración del campo; los números se pueden seguir editando en vivo en el panel
abilitiesun aspecto con un conjunto de habilidades, una máquina con las fases de aplicación, y costo y tiempo de recargala declaración de la habilidad
projectilesun tipo con persistencia runtime, un aspecto de balística y un evento de impactola declaración del proyectil: cambiar un solo atributo cambia el modelo de vuelo
dropsun aspecto de tabla de botín con pesos y un hook después de la muertelas entradas de la tabla y sus pesos
world objectsuna máquina de estados de un objeto interactivo y un aspecto con la condición de interacciónla declaración del preset, o por instancia al crearla
inventoryun tipo con dueño con una ref a un artículo del catálogo, un aspecto de pila con incremento y un tope por dueño con desbordamiento declaradola declaración del tipo

Inventory también tiene su propia página.

Qué se cumple en todo preset.

SiempreQué es
dónde viveen una entidad, como aspectos: sus datos se sincronizan como cualquier otro dato, sus estados son estados de la entidad, sus RPC son RPC de la entidad y sus hooks se ejecutan en el orden de hooks de la entidad
el ajustees configuración en vivo y no requiere un nuevo deploy; por eso el panel muestra en un mismo árbol el límite de una estadística, un tiempo de recarga y el peso de un drop
derivar el tuyoes composición, no herencia: Crate es world-objects más stats, y lo derivado sigue siendo aspectos sobre una entidad

Errores

  • Declarar una estadística, una habilidad, un proyectil, una tabla de botín o un objeto del mundo es un acto de esquema: fn o adm. Una clave de jugador o de cliente que lo intente recibe forbidden, y no queda nada declarado ni declarado a medias. Es un rechazo distinto de los que un jugador encuentra dentro de una llamada que sí tenía permitido hacer —en tiempo de recarga, sin con qué pagar, sin el item:key.bronze—, y cada uno de ellos trae su propio código.

El resto de los rechazos de un preset son los de entidad. Dos son propios de los presets:

  • Un preset rellenado a medias es un fallo de validación al desplegar. Un preset trae un conjunto coherente: media declaración se rechaza antes de publicarse, en lugar de comportarse de forma extraña en una partida.
  • A un preset no se le puede marcar con una propiedad que contradice su propia mecánica: un aspecto para el que el cliente no tiene reglas no se puede declarar predecible, y eso también se detecta al desplegar.

Límites

Los topes son los de entidad: aspectos por tipo, máquinas por tipo, el tamaño de la instancia almacenada, la frecuencia de cambios en una instancia. El único que declara el propio preset es el tope por dueño de un preset con dueño, con uno de tres comportamientos en el límite y sin valor predeterminado: refuse · redirect hacia un contenedor de dueño declarado · discard with event. Las cifras llegarán con el capítulo sobre los límites de la plataforma.