Saltar al contenido principal
El juego en vivo

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

Collision

Vincula un transform al mapa de obstáculos y declara qué pasa al haber contacto. El módulo Collision se ejecuta dentro de la simulación de la plataforma. Declaras cuerpos, capas y respuestas, y te suscribes a los contactos.

Cuándo usarlo

  • Las entidades en movimiento tienen que resolver los contactos del lado del servidor (deslizarse, detenerse, rebotar) sin una rutina de desvío escrita a mano.
  • El gameplay reacciona al contacto: los objetos recolectables se recogen al superponerse y los volúmenes de activación disparan la máquina de estados de una entidad.
  • El módulo Locomotion y los proyectiles necesitan una resolución por barrido contra el conjunto de obstáculos del mapa.
  • Las vistas previas de colocación o el apuntado necesitan consultas del tipo “¿cabe esto aquí?” y de superposición de volúmenes.
  • No lo uses si nada se encuentra físicamente. Para un gameplay de solicitud y respuesta sobre registros basta con Data & Subscriptions.

Quién hace qué

ActorQué hace en esta página
room-ownerdeclara cuerpos, capas y respuestas; consulta superposiciones y contactos

A qué salas se aplica

Este módulo se ejecuta donde la plataforma avanza la simulación: en las salas (room) declaradas con Host = "Backend". Si tu propio servidor de juego es dueño de la simulación (PlayServ como metaservidor), el movimiento, las colisiones y la predicción se quedan del lado del motor, y esta página describe la alternativa alojada en la plataforma, no un requisito.

En resumen

La forma, la capa y lo que pasa al haber contacto se declaran en el propio cuerpo — nada declara pares de capas desde otro lugar:

Un Body con forma de cápsula en el tanque, capa vehicles: se desliza por las paredes, atraviesa los objetos recolectables y con las cajas decide en cada contacto
[Entity("tank")]
public class Tank
{
    [Sync] public Vector3 Position;

    [Body(Shape.Capsule, Radius = 0.6f, Layer = "vehicles")]
    [CollidesWith("walls", Response.Slide)]
    [CollidesWith("pickups", Response.Pass)]        // reported, motion passes through
    public Body Body;
}
@Entity('tank')
export class Tank {
  @Sync() position!: Vector3;

  @Body({ shape: 'capsule', radius: 0.6, layer: 'vehicles' })
  @CollidesWith('walls', Response.Slide)
  @CollidesWith('pickups', Response.Pass)        // reported, motion passes through
  body: Body;
}
@entity("tank")
class Tank:
    position: Vector3 = sync()

    body = collision.body(shape="capsule", radius=0.6, layer="vehicles",
                           collides_with=[
                               ("walls", Response.SLIDE),
                               ("pickups", Response.PASS),   # reported, motion passes through
                           ])
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.

// the declaration rides inside the engine's own reflection macros, in the specifier position —
// UHT reads it from the header text, and the member is a reflected property at the same time
UCLASS(PSEntity = "tank")
class UTank : public UObject
{
    GENERATED_BODY()

    UPROPERTY(PSSync)
    FVector3f Position;

    // walls slide, pickups report the contact and let motion pass through —
    // multi-entry values are one quoted list (a specifier value is a single token)
    UPROPERTY(PSBody = (Shape = "Capsule", Radius = "0.6", Layer = "vehicles"),
              PSCollidesWith = "walls:Slide, pickups:Pass")
    FPSBody Body;
};
// 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 con la versión base 2021.3 — aquí no hay sintaxis de C# 12
[Entity("tank")]
public class Tank
{
    [Sync] public Vector3 Position;

    [Body(Shape.Capsule, Radius = 0.6f, Layer = "vehicles")]
    [CollidesWith("walls", Response.Slide)]
    [CollidesWith("pickups", Response.Pass)]        // reported, motion passes through
    public Body Body;
}

Un contacto es un evento, y los módulos se suscriben a él. No hay hook sobre un contacto: para cuando existe, el paso ya lo resolvió, así que no queda nada que rechazar. Si un estudio necesita otras reglas, sobrescribe las comprobaciones de admisibilidad y de trayectoria con su propia implementación — consulta Extensibility — y la respuesta sigue declarada.

El modelo

Qué declara un cuerpo.

Qué se declaraQué es
shapeuna primitiva de un conjunto cerrado — esfera, cápsula, caja — con dimensiones declaradas. No se ofrecen mallas arbitrarias: es la misma restricción, y por la misma razón, que en el modelo del mundo del servidor en el mapa
dónde viveen un aspecto, junto con el transform: esa es la unidad de política, y el cuerpo corre la misma suerte que su transform
cómo se comprueba su trayectoriastepwise: se comprueba la posición final del paso; es rápido. swept: se comprueba el segmento entre posiciones; es más costoso, pero descarta el efecto túnel (tunneling) dentro de un paso. Se declara; la implementación nunca lo elige según la velocidad
en qué áreas participadentro de qué volúmenes se cuenta el cuerpo
su relación con el modelo artísticono se exige ninguna: un cuerpo es una simplificación, y puede diferir del modelo artístico dentro de límites declarados

Un cuerpo stepwise rápido atraviesa obstáculos delgados

stepwise solo comprueba dónde termina un paso, así que un cuerpo rápido puede atravesar un obstáculo delgado. swept comprueba el segmento entre posiciones y descarta el efecto túnel dentro de un paso. Si un proyectil puede atravesar una pared o no es una propiedad de tu juego, y la declaras tú.

La respuesta se declara por par — el tipo de transitabilidad de un obstáculo × un tipo de cuerpo — y sale de un conjunto cerrado:

RespuestaQué significa
stopel movimiento se detiene en la última posición admisible
slideel movimiento continúa a lo largo del obstáculo con la componente que sea admisible
bouncela dirección se refleja y la velocidad se multiplica por un coeficiente declarado
dampel movimiento continúa con la velocidad multiplicada por una fracción declarada
passel obstáculo no afecta el movimiento, pero el contacto sigue siendo observable
cease to existla entidad deja de existir — un proyectil contra una pared

Los coeficientes son valores declarados, no se calculan a partir de masas y materiales — este contrato no tiene ni lo uno ni lo otro.

Qué se cumple en toda comprobación.

SiempreQué es
cada partiene una respuesta: un par sin respuesta es un defecto de declaración, que se rechaza en el deploy en lugar de aparecer en pleno combate
la tabla de respuestasel cliente la puede leer: es la misma tabla con la que calcula la autoridad, así que un cliente y un servidor con la misma declaración responden igual a un mismo contacto
reproducible dentro de una autoridad, no entre plataformasla misma entrada en el mismo orden da el mismo resultado dentro de un proceso y un build. No se prometen resultados idénticos bit a bit entre plataformas y builds distintos
simultaneidadse declara: cuando dos cuerpos en movimiento chocan dentro de un mismo paso, el orden de resolución está declarado y es determinista. No pueden servir de base para ese orden el recorrido del almacenamiento, el orden en que llegó la entrada ni el azar
un contacto, un hechoun contacto entre dos cuerpos lo observan ambos lados como un único hecho con un único identificador, no como dos eventos independientes
los puntos de extensión están en el paso, no en el contactoantes del paso se puede cambiar el transform; después, solo queda observar. Si se sobrescriben las comprobaciones de admisibilidad y de trayectoria, esa versión también tiene que estar disponible para el cliente
el módulono mueve nada por sí mismo: responde si una posición es admisible y cuál es la respuesta; aplicarla le corresponde al desplazamiento
un contacto es un eventopor eso los módulos se suscriben en lugar de acoplarse: la máquina de estados de una trampa vincula una transición a la entrada en un volumen de activación, el botín se recoge al superponerse y los proyectiles resuelven los impactos con el barrido de este módulo

Errores

  • “Inadmisible” es una respuesta, no un error, e indica cuál de tres motivos aplica: fuera de los límites, ocupado por un obstáculo estático u ocupado por el cuerpo de otra entidad. El cliente reacciona distinto a cada uno: dar la vuelta, rodear o esperar.
  • Las fallas de declaración aparecen en el deploy, nunca en el primer contacto: un cuerpo con una forma fuera del conjunto cerrado, un cuerpo en un aspecto sin transform y un par sin respuesta declarada se rechazan al desplegar.
  • Si no se encuentra la entidad o la ubicación, la respuesta es not found, y repetir no sirve de nada.
  • Si se supera la frecuencia de comprobaciones, la respuesta cae en la categoría de rate limit, con el plazo antes del cual no tiene sentido reintentar.

Límites

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

  • Cuerpos en una sala: declarar uno más se rechaza como conflicto; nunca se eliminan cuerpos existentes para hacer lugar.
  • Contactos por paso: el excedente nunca se descarta en silencio. O se rechaza el paso, o el orden de corte está declarado.
  • Tamaño de un cuerpo frente al paso de la cuadrícula del mapa: se rechaza en el deploy, porque un cuerpo más pequeño que el paso del campo de alturas se cae a través del terreno.
  • Áreas en las que puede estar un cuerpo a la vez: el exceso se rechaza en el deploy.
  • Frecuencia de comprobaciones por actor: un rate limit con plazo.
  • Cuerpos en una respuesta a “¿quién está en esta área?”: se truncan según un orden declarado, y el indicador de truncamiento es obligatorio.