Cette page n’est pas encore traduite : la version anglaise est affichée.
Collision
Liez un transform à la carte des obstacles et déclarez ce que produit un contact. Les collisions sont calculées dans la simulation de la plateforme. Vous déclarez des corps, des couches et des réponses, puis vous vous abonnez aux contacts.
Quand l’utiliser
- Des entités en mouvement doivent résoudre leurs contacts côté serveur (glisser, s’arrêter, rebondir) sans routine de déviation écrite à la main.
- Le gameplay réagit au toucher : un objet se ramasse dès qu’on le chevauche, un volume déclencheur fait avancer la machine à états d’une entité.
- Locomotion et les projectiles ont besoin d’une résolution continue (swept) contre les obstacles de la carte.
- Un aperçu de placement ou un ciblage doit savoir « est-ce que ça tient ici ? » et interroger le chevauchement de volumes.
- Inutile quand rien ne se rencontre physiquement. Un gameplay requête/réponse sur des enregistrements relève simplement de Data & Subscriptions.
Qui fait quoi
| Acteur | Ce qu’il fait sur cette page |
|---|---|
room-owner | déclare les corps, les couches et les réponses ; interroge les chevauchements et les contacts |
Les salles concernées
Ce module fonctionne là où la plateforme fait avancer la simulation : dans les salles (room) déclarées Host = "Backend". Si c’est votre propre serveur de jeu qui possède la simulation (PlayServ en métaserveur), le mouvement, les collisions et la prédiction restent dans le moteur, et cette page décrit l’alternative hébergée par la plateforme, pas une obligation.
En bref
La forme, la couche et l’effet d’un contact se déclarent tous sur le corps lui-même — aucune déclaration à distance de paires de couches :
Body capsule sur le tank : couche vehicles — il glisse le long des murs, traverse les objets à ramasser, et les caisses se décident contact par contact[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
])La déclaration par attributs en Go n’est pas encore tranchée (O-1). Les exemples arriveront quand la forme d’écriture sera choisie.
// 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[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 contact est un événement, et les modules s’y abonnent. Il n’existe pas de hook sur un contact : au moment où il existe, le pas de simulation l’a déjà résolu, il n’y a donc plus rien à refuser. Si un studio a besoin d’autres règles, il remplace les vérifications d’admissibilité et de trajectoire par sa propre implémentation — voir Extensibility — et la réponse reste déclarée.
Le modèle
Ce que déclare un corps.
| Déclare | Ce que c’est |
|---|---|
shape | une primitive tirée d’un ensemble fermé — sphère, capsule, boîte — aux dimensions déclarées. Pas de maillage arbitraire : même contrainte, et pour la même raison, que pour le modèle du monde côté serveur dans la carte |
où il vit | sur un aspect, avec le transform : c’est l’unité de politique, et un corps partage le sort de son transform |
comment sa trajectoire est vérifiée | stepwise : on vérifie la position finale du pas ; c’est rapide. swept : on vérifie le segment entre les positions ; c’est plus coûteux, et l’effet tunnel est exclu à l’intérieur d’un pas. Le mode se déclare ; une implémentation ne le choisit jamais d’après la vitesse |
les zones auxquelles il participe | les volumes dans lesquels il est compté |
son rapport au modèle graphique | aucun n’est exigé : un corps est une simplification, et il peut s’écarter du modèle graphique dans des limites déclarées |
Un corps stepwise rapide traverse les obstacles fins
stepwise ne vérifie que l’endroit où finit un pas : un corps rapide peut donc traverser un obstacle fin. swept vérifie le segment entre les positions et exclut l’effet tunnel à l’intérieur d’un pas. Qu’un projectile puisse traverser un mur est une propriété de votre jeu, et c’est à vous de la déclarer.
La réponse se déclare sur une paire — un type de franchissabilité d’obstacle × un type de corps — et elle est tirée d’un ensemble fermé :
| Réponse | Ce que cela signifie |
|---|---|
stop | le mouvement s’arrête à la dernière position admissible |
slide | le mouvement continue le long de l’obstacle, selon la composante admissible |
bounce | la direction est réfléchie et la vitesse multipliée par un coefficient déclaré |
damp | le mouvement continue, la vitesse multipliée par une fraction déclarée |
pass | l’obstacle n’affecte pas le mouvement, mais le contact reste observable |
cease to exist | l’entité prend fin — un projectile contre un mur |
Les coefficients sont des valeurs déclarées, pas des résultats calculés à partir de masses et de matériaux : ce contrat ne connaît ni les unes ni les autres.
Ce qui vaut pour chaque vérification.
| Toujours | Ce que c’est |
|---|---|
chaque paire | a une réponse : une paire manquante est un défaut de déclaration, refusé au déploiement plutôt que découvert en plein combat |
la table des réponses | est lisible par le client : c’est la table même avec laquelle calcule l’autorité, si bien qu’un client et un serveur qui partagent une déclaration répondent de la même façon à un même contact |
reproductible au sein d'une autorité, pas entre plateformes | la même entrée dans le même ordre donne le même résultat au sein d’un processus et d’un build. Des résultats identiques au bit près sur des plateformes et des builds différents ne sont pas promis |
simultanéité | elle est déclarée : quand deux corps en mouvement se percutent pendant un même pas, l’ordre de résolution est déclaré et déterministe. L’ordre de parcours du stockage, l’ordre d’arrivée des entrées et le hasard ne peuvent pas en être le fondement |
un contact, un fait | un contact entre deux corps est observable par les deux côtés comme un fait unique doté d’un identifiant unique, et non comme deux événements indépendants |
les points d'extension sont sur le pas, pas sur le contact | avant le pas, on peut modifier le transform ; après, on ne fait qu’observer. Une surcharge des vérifications d’admissibilité et de trajectoire doit aussi être disponible pour le client |
le module | ne déplace rien lui-même : il répond si une position est admissible et quelle est la réponse ; l’appliquer revient au module Locomotion |
un contact est un événement | c’est pourquoi les modules s’abonnent au lieu d’être couplés : la machine à états d’un piège lie une transition à l’entrée dans un volume déclencheur, le butin se ramasse au chevauchement, et les projectiles résolvent leurs impacts grâce au balayage de ce module |
Erreurs
- « Inadmissible » est une réponse, pas une erreur, et elle précise laquelle des trois raisons s’applique : hors des limites, occupé par un obstacle statique, ou occupé par le corps d’une autre entité. Un client réagit différemment à chacune : faire demi-tour, contourner ou attendre.
- Les défauts de déclaration se révèlent au déploiement, jamais au premier contact : un corps dont la forme sort de l’ensemble fermé, un corps sur un aspect sans transform, une paire sans réponse déclarée — tous sont refusés au déploiement.
- L’entité ou l’emplacement est introuvable : la réponse est not found, et réessayer ne sert à rien.
- La fréquence de vérification est dépassée : la réponse relève de la catégorie rate limit, avec l’échéance avant laquelle réessayer ne sert à rien.
Limites
Les chiffres arriveront avec le chapitre sur les limites de la plateforme ; ce qui se passe à chaque plafond est déjà fixé.
- Corps dans une salle : en déclarer un de plus est refusé comme un conflit ; on ne supprime jamais des corps existants pour faire de la place.
- Contacts par pas : l’excédent n’est jamais écarté en silence. Soit le pas est refusé, soit l’ordre de coupure est déclaré.
- Taille d’un corps par rapport au pas de la grille de la carte : refusée au déploiement, car un corps plus petit que le pas du champ de hauteurs passe à travers le terrain.
- Zones dans lesquelles un corps peut se trouver : un excédent est refusé au déploiement.
- Fréquence de vérification par acteur : une limitation de débit, avec une échéance.
- Corps dans une réponse « qui est dans cette zone » : la liste est tronquée selon un ordre déclaré, et l’indicateur de troncature est obligatoire.