Mapping et Hammer

Inputs et outputs dans Hammer : relier un bouton à une porte

Inputs et outputs dans Hammer : les champs de l’onglet Outputs, un exemple bouton et porte, !activator, logic_relay et le débogage avec ent_fire.

Dans Hammer, tu relies les entités entre elles par des outputs et des inputs : un output d’une entité déclenche un input d’une autre, et la cible est trouvée par son nom (targetname). Pour un bouton qui ouvre une porte, tu donnes un nom à la porte et tu ajoutes au func_button l’output OnPressed avec l’input Open de la porte. Une connexion de ce genre casse le plus souvent à cause d’un nom mal tapé ou utilisé deux fois, d’une cible qui a été supprimée, ou de deux outputs qui arrivent au même moment.

Comment outputs et inputs s’articulent

Un output est un événement qu’une entité signale : un bouton a été pressé, une porte est complètement ouverte, quelqu’un est entré dans un trigger. Un input est une commande qu’une entité accepte, par exemple Open, Close ou Kill. Les outputs et les inputs dont dispose une classe sont définis dans le FGD.

Chaque output déclenché est placé dans une file d’événements à l’intérieur du moteur et délivré après le délai que tu as réglé. C’est seulement à ce moment-là que le moteur cherche la cible par son nom. Deux informations voyagent avec lui : l’activator, en général l’entité qui a lancé la chaîne (pour un bouton, le joueur), et le caller, l’entité dont l’output se déclenche.

Tu définis le nom dans le champ Name des Object Properties (Alt+Enter). Une entité sans nom peut envoyer des outputs, mais elle ne peut être ciblée que par son classname ou par une cible spéciale comme !activator. Les bases de l’éditeur sont expliquées dans débuter avec Hammer Editor.

Les champs de l’onglet Outputs

Dans l’onglet Outputs des Object Properties, tu crées une connexion avec Add.... Dans le Hammer fourni avec GMod, les champs de saisie et les colonnes de la liste portent des libellés légèrement différents :

ChampColonneSignification
My output namedMy OutputL’événement de cette entité qui déclenche la connexion
Targets entities namedTarget EntityNom de la cible, ou bien un classname, un wildcard ou une cible spéciale
Via this inputTarget InputLa commande que la cible exécute
With a parameter override ofParameterValeur pour l’input, s’il en attend une
After a delay in seconds ofDelayTemps d’attente entre l’événement et la livraison
Fire once onlyOnceLa connexion est supprimée après s’être déclenchée une fois

Hammer++ remplace la case à cocher par le champ Limit to this many fires ( -1 = infinite ) et la colonne « Refires ». L’onglet Inputs montre le sens inverse, c’est-à-dire toutes les connexions qui pointent vers cette entité. Mark saute à l’autre extrémité.

Exemple : un bouton ouvre une porte puis la referme

  1. Construis la porte sous forme de brush, appuie sur Ctrl+T et choisis func_door. Nomme-la door_1.
  2. Règle Move Direction (Pitch Yaw Roll) sur la direction d’ouverture et Delay Before Reset (-1 stay) sur -1. La porte reste alors ouverte.
  3. Dans l’onglet Flags, décoche Touch Opens. Il est coché par défaut et ouvrirait la porte au contact.
  4. Construis le bouton sous forme de brush et transforme-le en func_button. Le flag Use Activates est déjà activé. Avec Delay Before Reset (-1 stay) réglé sur 5, il ne peut pas être pressé de nouveau avant au moins cinq secondes.
  5. Dans l’onglet Outputs du bouton, ajoute deux connexions :
My Output   Target Entity   Target Input   Parameter   Delay
OnPressed   door_1          Open                       0.00
OnPressed   door_1          Close                      5.00

La porte s’ouvre immédiatement et se referme cinq secondes après l’appui. Ça marche aussi sans la deuxième connexion : une porte dont Delay Before Reset vaut 5 se referme toute seule. La façon d’appeler un ascenseur selon le même schéma est expliquée dans l’article sur les ascenseurs et les trains.

Cibles spéciales et briques logiques

À la place d’un nom, le champ de cible peut contenir une cible spéciale :

CibleSignification
!activatorL’entité qui a lancé la chaîne, par exemple le joueur au bouton
!callerL’entité dont l’output se déclenche
!selfComme cible, identique à !caller ; comme paramètre, l’entité qui reçoit l’input
!playerLe joueur ; en multijoueur, le premier à avoir rejoint le serveur
!pickerL’entité sous le viseur, prévue pour le débogage

Sur un serveur, !player n’est donc presque jamais le bon choix. Utilise !activator pour le joueur qui a déclenché la chaîne, ou le classname player pour tout le monde. Les wildcards ne fonctionnent qu’à la fin : door_* correspond à tous les noms qui commencent par door_. Un motif comme *_door ne fonctionne pas, car le moteur ignore tout ce qui suit le *. Un classname comme func_door est lui aussi une cible valide, sauf si une entité porte exactement ce texte comme nom.

Quelques entités logiques permettent d’en faire des circuits plus grands :

  • logic_relay regroupe plusieurs outputs sous un seul nom : input Trigger, output OnTrigger. Disable interrompt la chaîne, CancelPending abandonne les événements du relais encore en attente. Tant que des outputs retardés sont en attente, il ne peut pas être déclenché de nouveau, sauf si le flag Allow fast retrigger est activé.
  • logic_timer déclenche OnTimer au rythme de son Refire Interval.
  • math_counter compte avec Add et Subtract et déclenche OnHitMax ou OnHitMin dès qu’une limite est atteinte.
  • trigger_multiple est un volume avec OnStartTouch, OnEndTouch et OnTrigger. Les flags et Filter Name, avec un filter_activator_name ou un filter_activator_class, définissent qui peut le déclencher.

Déboguer en jeu

Avant la compilation, Map > Check for Problems (Alt+P) liste les entités dont les connexions sont invalides. En jeu, ces commandes console t’aident :

developer 2
ent_messages_draw 1
ent_fire door_1 Open
ent_fire !picker Kill
  • developer 2 écrit chaque output et chaque input dans la console. dumpeventqueue liste les événements qui attendent encore la fin de leur délai.
  • ent_messages_draw 1 affiche les mêmes messages dans le monde 3D, au niveau des entités.
  • ent_fire <target> <input> [parameter] [delay] déclenche un input à la main. Tu peux ainsi tester la cible indépendamment de ce qui la déclenche.

Teste en solo ou en tant qu’hôte, car d’après le wiki de Valve, un client connecté à un serveur ne peut pas utiliser ent_fire. Si la console réclame les cheats, tape d’abord sv_cheats 1. Avec developer 2, les lignes de la console suivent le modèle ci-dessous. Le bloc est assemblé à partir des formats de message de la server.dll de GMod, avec les noms de l’exemple et un temps inventé. Ce n’est pas l’enregistrement d’une vraie session :

(12.50) output: (func_button,button_1) -> (door_1,Open)()
(12.50) input button_1: door_1.Open()
unhandled input: (Open) -> (door_2), from (func_button,button_1); target entity not found

La première ligne est l’output qui se déclenche, la deuxième sa livraison. C’est la troisième qu’il faut repérer : elle apparaît quand aucune entité ne porte le nom de la cible au moment de la livraison, dans cette illustration door_2.

Erreurs typiques

  • Nom manquant ou mal tapé : Hammer affiche la cible en rouge, et « Check for Problems » signale la connexion. N’y utilise pas « Fix » à l’aveugle : d’après le texte de l’erreur, Hammer supprime les connexions fautives au lieu de les réparer. Les wildcards et les classnames apparaissent eux aussi en rouge alors qu’ils fonctionnent en jeu.
  • Nom utilisé deux fois : Hammer affiche la cible en gras. L’input part vers toutes les entités qui portent ce nom. C’est voulu pour deux battants de porte, pas pour une porte copiée dans la pièce voisine.
  • Cible supprimée : Kill supprime une entité avec ses enfants dans la hiérarchie de parents. Les outputs retardés qui arrivent ensuite tombent dans le vide, et l’entité supprimée ne déclenche plus aucun nouvel output. Il se passe la même chose avec !activator si le joueur quitte le serveur avant.
  • Courses de timing : Deux outputs avec le même délai arrivent dans le même tick. Ne te fie pas à leur ordre. Si B dépend de A, donne un petit délai à B. Le wiki de Valve utilise au moins 0,02 seconde dans un tel cas.

Les outputs peuvent aussi déclencher une lua_run, qui exécute alors du code Lua. Le Map Scanner trouve ce genre d’entités à risque dans un BSP. D’autres vérifications sont listées dans l’article sur la vérification d’un BSP avant publication.

Questions fréquentes

Un output peut-il s’adresser à plusieurs entités à la fois ?

Oui. L’input part vers chaque entité à laquelle la cible correspond : un nom partagé, un wildcard comme door_* ou un classname.

Quelle est la différence entre OnPressed et OnIn sur un func_button ?

OnPressed se déclenche dès que le bouton est pressé. OnIn ne se déclenche qu’une fois que le bouton a atteint sa position finale enfoncée, et OnOut quand il est revenu à sa position de départ.

Puis-je modifier des outputs pendant que le jeu tourne ?

Oui, avec l’input AddOutput. Le format est <output> <target>:<input>:<parameter>:<delay>:<max times to fire>. Le FGD le qualifie de « very dangerous, use with care », et les circuits qui se recâblent eux-mêmes en jeu sont difficiles à suivre par la suite.