Classes d’id Tech 4 (neo/game)

Description des fichiers dans le dossier neo/game.

  • Chaque section prend le nom des deux fichiers C++ (.h et .cpp) auquelle elle fait référence. Ainsi, Actor indique les fichiers Actor.h et Actor.cpp.
  • Certaines sections ajouteront l'extension pour signaler un fichier unique, tel Game.h.
  • Une note « + description » indique que le fichier contient une description détaillée pour une ou plusieurs classes.
  • Une classe entre parenthèses indique qu'elle est héritée : SousClasse (ClasseDeBase).
  • Les hypothèses incertaines seront marquées clairement ; elles sont probables, mais pas confirmées.

Sous-dossiers : ai, anim, gamesys, script.

=Actor

Animations, champ de vision, attaque et dégâts, bruits de pas, entre autres. La classe idPlayer hérite d'idActor.

  • idActor::Event_SetState() trouve la fonction de script avec le nom spécifié et modifie la variable membre idealState (qui sera lue par UpdateScript()).
  • idActor::GetScriptFunction() prend comme paramètre le nom d'une fonction, et un objet function_t.
  • idActor::SetState( const char * ) trouve la fonction de script avec le nom spécifié et appelle idActor::SetState( const function_t * ).
  • idActor::UpdateScript() est appelée chaque image. Elle appelle idActor::SetState() lorsque sa variable membre idealState change (ce qui n'arrive que dans SetState(), Event_SetState() et Event_SetNextState()).

Hypothèse : tous les personnages vivants (y compris le joueur) se servent de cette classe ; peut-être aussi certains éléments de l'environnement.

=AF

AF : articulated figure (ragdoll). Ce fichier s'occupe de la physique pour les créatures vivantes (voir l'hypothèse).

Par exemple, lorsqu'un ennemi est tué, il fait référence à la fonction idAF::ApplyImpulse() pour déterminer comment son corps sera projeté par le coup mortel. Si on multiplie le vecteur impulse par 10, il sera projeté 10 fois plus fort lorsqu'il sera tué.

Hypothèse : la physique ragdoll n'est peut-être utilisée que lorsque le personnage est tué (et n'a peut-être aucune influence tant qu'il est vivant).

=AFEntity

C'est ici que se trouve idAFAttachment ; je crois que cette classe gère la physique pour les membres du corps des ennemis (et du joueur), c'est-à-dire les attachements.

Cette classe utilise notamment la fonction ApplyImpulse() qui, selon toute apparence, provoque la réaction de douleur de l'ennemi. Cette fonction n'est appelée qu'après deux ou trois coups réussis.

Si par exemple le vecteur impulse est multiplié par 100, alors après quelques coups, la victime sera projetée rapidement vers l'arrière (sans nécessairement mourir), car l'attachement, lorsqu'il est projeté, amène bien sûr le reste du corps avec lui.

Il y a aussi plusieurs autres classes qui font usage des ragdoll : idAFEntity_Generic, idAFEntity_WithAttachedHead, idAFEntity_Vehicle, entre autres.

=BrittleFracture

La vitre, ou tout autre surface pouvant être fracturée. Lorsqu'elle reçoit quelques coups, elle éclate en plusieurs morceaux, avec la fonction idBrittleFracture::Shatter(). La classe est reliée à l'entité func_fracture.

=Camera

Ce fichier contient trois classes : idCamera (abstraite), et ses sous-classes idCameraView et idCameraAnim. Lorsqu'une occurrence d'idCameraView est créée, elle appelle sa fonction Spawn(), qui elle-même appelle implicitement idCamera::Spawn(). À noter que l'implémentation d'idCamera ne contient aucun code ; peut-être a-t-elle servi à des tests durant le développement.

Notes :

  • idCameraView correspond à l'entité func_cameraview dans DarkRadiant

    Note : lorsqu'une vue de caméra est envoyée à un GUI, le modèle sur lequel ce GUI est affiché subira une rotation, selon l'angle de vue de la caméra. Cette rotation est causée par le code suivant, dans neo/game/Entity.cpp :

    void idEntity::Event_UpdateCameraTarget( void )
    {
        ...
        SetAxis(dir.ToMat3());
        ...
    }

    Cette ligne sert peut-être aux caméras qui ont elles-mêmes une propriété cameraTarget, qui fait référence à une autre entité. Par contre, ça fait subir la rotation de la vue à tous les écrans qui sont reliés à la caméra via cette propriété. Il suffit de mettre l'appel à SetAxis() en commentaire pour résoudre le problème.

  • idCameraAnim est une caméra qui se déplace en suivant un chemin (spline)

=EndLevel

Fichiers désuets.

La classe idTarget_EndLevel correspond à l'entité target_endlevel dans DarkRadiant, mais cette version n'est pas utilisée, car EndLevel.h et EndLevel.cpp ne sont pas inclus dans le projet Game.

La définition et l'implémentation finales seraient plutôt dans Target.h et Target.cpp, où la classe hérite d'idTarget.

=Entity

Classes :

  • signalList_t
  • idEntity
  • idAnimatedEntity

idEntity

La classe de base pour toutes les entités. Elle s'occupe de la création, la mise à jour, les sons, etc. des entités.

Chaque entité a un objet idDict nommé spawnArgs ; c'est la liste des paramètres d'une entité au chargement de la map, c'est-à-dire les paramètres réglés pour l'objet dans l'éditeur de niveau (name, model, origin, angle, etc.).

  • idEntity::CheckDormant() est appelée à chaque frame. Elle appelle DoDormantTests() pour savoir si l'entité devrait être active. C'est ici que l'entité est activée, ce qui signifie qu'elle se mettra à agir suivant son comportement normal.Dans le cas de certains monstres, par exemple, ce comportement normal est de se promener dans une salle, ou se cacher, et attaquer le joueur quand il apparaît. Si le joueur n'a aucune chance d'interagir avec le monstre, celui-ci restera « dormant » et ne fera rien du tout, afin d'économiser les ressources de l'ordinateur.
  • idEntity::DoDormantTests() vérifie le drapeau fl.neverDormant, qui lit le spawnArg neverDormant de l'entité dans Spawn().
  • Je crois qu'idEntity::Event_UpdateCameraTarget() met à jour la vue de la caméra pour les écrans dans le jeu.Note : Cette fonction contient une ligne de code étrange ; il semble que la fonction récupère l'orientation de chaque caméra, afin de l'appliquer à toutes les entités qui y sont reliées (via la propriété cameratarget).La ligne se trouve dans une boucle :

    while( kv )
    {
    ...
    SetAxis(dir.ToMat3()); // Cette ligne provoque des rotations étranges
    break;
    ...
    }

    L'utilité de cette ligne est incertaine ; je l'ai donc mise en commentaire en vue d'empêcher les rotations. Aucun problème à date.

  • idEntity::SetModel( const char *modelname ) associe un modèle (statique) à une entité ; les modèles utilisés sont surtout des .ase et des .lwo.

    Note : dans certains cas, on peut y retrouver des références telles « grenadeExplosion.prt ». Or, il n'y a aucun fichier avec ce nom ; grenadeexplosion est un effet de particules défini dans particles/explosions.prt. Le processus de chargement d'effets de particules n'est pas tout à fait clair...

=Fx

Un objet idEntityFx crée des effets spéciaux en lisant un fichier .fx. On trouvera plusieurs fichiers avec cette extension dans le fichier de ressources base/pak000.pk4, dans le dossier fx.

=Game.h

Comme bien des classes non « locales », idGame est abstraite et sert uniquement d'interface pour un objet « local », à l'occurrence idGameLocal. Il y a un fichier Game_local.cpp, mais pas de Game.cpp.

=Game_local

Selon une revue du code source par Fabien Sanglard (en), il y aurait au démarrage de Doom une échange de pointeurs entre le moteur et le jeu. Cette échange se ferait à l'aide de la fonction globale GetGameAPI() (dans neo/game/Game_local.cpp).

idGameLocal est probablement la classe la plus importante de Doom 3. Elle hérite d'idGame, et contient plusieurs fonctions importantes qui relèvent de toutes les sections du moteur.

  • idGameLocal::FindEntity() retourne une entité avec le nom spécifié en paramètre (ou NULL).

Membres notables :

  • entities est un tableau de toutes les entités dans le niveau. La taille du tableau est 4096, à savoir le nombre maximal d'entités dans une scène ; si la map contient un nombre d'entités inférieur à cette limite, les enregistrements restants dans le tableau seront NULL.
  • time est le nombre de millisecondes écoulées depuis le début du niveau.

=Game_network

Ce fichier contient les définitions des méthodes d'idGameLocal qui servent à la communication par réseau.

=GameEdit

La classe idDragEntity permet au joueur, lorsque la CVar g_dragEntity a la valeur 1, de traîner des objets ou des cadavres, en suivant les lois de la physique : par exemple, on peut traîner les cadavres par leurs bras, leurs jambes, etc. Elle appelle sa fonction Update() à chaque image, que le joueur traîne un objet ou pas.

C'est la classe idCursor3D qui dessine les flèches jaune et rouge lorsqu'on traîne un objet, dans sa fonction Present(), qui « présente » les informations de débogage au moteur de rendu.

Grâce à la classe idEditEntities, on peut afficher des informations graphiques de débogage en réglant la CVar g_editEntityMode à une valeur de 1 à 7. Chaque valeur permet d'éditer un type d'entité différent : les lumières, les sons, les modèles articulés (articulated figures), les particules, etc.

=IK

Probablement la kinématique inverse. Je n'ai rien réussi à faire avec à date.

=Item

Les objets que le joueur peut ramasser. La plus importante fonction serait probablement idItem::Pickup(), qui joue un son et fait disparaître l'objet, entre autres, lorsqu'il est saisi.

=Light

Les lumières électriques dans les niveaux. Lorsqu'on détruit une des lumières fluorescentes par exemple, la méthode idLight::Killed() est appelée.

=Misc (+ description)

Un amas de petites classes, parmi lesquelles on peut trouver :

  • idActivator : un objet invisible qui peut activer une entité trigger.
  • idAnimated : les entités capables de bouger. Étrangement, les monstres ne semblent pas appeler idAnimated::StartRagdoll() lorsqu'ils sont tués.
  • idEarthQuake : un effet de secousse.
  • idFuncPortal : incertain. La description de l'entité associée à cette classe, func_portal, est Use to make triggerable portals. Par contre, son matériel est textures/editor/visportal, ce qui indique une association quelconque avec les vis portals.
  • idPathCorner : un chemin qui sera suivi par un monstre.
  • idPlayerStart : une position de départ potentielle pour le joueur.
  • idShaking : un objet sautillant pouvant contenir un modèle ; supporte l'entité func_shaking.
  • idSpawnableEntity : une entité extrêmement simple pouvant être utilisée pour indiquer un emplacement dans une map, ou contenir un modèle non interactif, entre autres.
  • idTextEntity : affiche du texte dans une map.

=Moveable

Les objets déplaçables, tels les petites boîtes et la plupart des petits objets pouvant être déplacés.

Selon mes tests, ces objets appellent la fonction idMoveable::Collide() chaque fois que l'objet entre en collision avec l'environnement (sol, murs) durant un mouvement. Cette fonction étant appelée plusieurs fois quand l'objet est déplacé, il est difficile de savoir si les collisions avec le joueur sont prises en compte comme les autres.

Les classes idBarrel et idExplodingBarrel, dans les mêmes fichiers, héritent de la classe idMoveable.

=Mover

Je crois que les classes dans ces fichiers servent à déplacer le joueur. On peut trouver ici :

  • idMover et sa sous-classe idElevator.
  • idMover_Binary et ses sous-classes idDoor et idPlat (idPlat est probablement l'ascenseur sans murs, qui ne consiste que d'une plateforme) ; des types d'objets qui ne peuvent bouger que dans deux sens.
  • idMover_Periodic et ses sous-classes idRotater, idBobber, idPendulum et idRiser ; probablement des classes dont le mouvement n'arrête jamais.

=MultiPlayerGame

La base pour les parties multijoueur.

Note : Les dossiers game et d3xp (l'expansion Resurrection of Evil) contiennent plusieurs fichiers avec les mêmes noms, mais ils ne sont pas tous identiques.

Les fichiers MultiplayerGame.h/cpp sont différents dans le dossier d3xp. La version d3xp vérifie plusieurs fois si les constantes CTF (Capture the Flag) et _D3XP sont définies, et exécute du code additionnel si c'est le cas.

=Player

Le joueur. Il y a deux classes qui sont définies ici : idInventory et idPlayer.

idInventory semble suivre la santé, les armes et l'armure du joueur, entre autres.

idPlayer est une classe énorme, composée de plusieurs propriétés et méthodes :

  • idPlayer::DrawHUD() dessine le HUD (curseur, santé, etc.).
  • idPlayer::EnterCinematic() semble garder le joueur en place avec la ligne : physicsObj.SetLinearVelocity( vec3_origin );
  • idPlayer::Killed() joue l'animation de mort du joueur avant de passer à l'écran Game over.
  • idPlayer::Move() gère l'animation (repos, course, etc.) et le déplacement du personnage.
  • idPlayer::PerformImpulse() est associée aux raccourcis clavier. Elle détecte les commandes (les « impulsions ») associées aux touches (et la souris) et appelle les fonctions correspondantes dans idPlayer. (Les impulsions sont définies dans neo/framework/UsercmdGen.h).
  • idPlayer::ShowTip() affiche un message dans le hud (hud.gui).
  • idPlayer::UpdateDeathSkin() : je ne sais pas exactement à quoi sert cette méthode, c'est peut-être utile pour le mode multijoueur.Note : Cette fonction risque de poser des problèmes. Lorsqu'on appelle UpdateDeathSkin( false ), les bras du joueur disparaissent à sa mort.
  • idPlayer::UpdateWeapon() : une des tâches de cette fonction est de déterminer si le joueur interagit avec un gui ou discute avec un allié, avant de lui permettre de tirer son arme (voir Weapon_Combat(), Weapon_GUI() et Weapon_NPC()).
  • idPlayer::Weapon_Combat() gère les états de l'arme du joueur : rechargement, changement d'arme, et coup de feu (dans l'ordre indiqué).
  • idPlayer::Weapon_GUI() est appelée lorsqu'on interagit avec un world gui.
  • idPlayer::Weapon_NPC() serait appelée lorsque le curseur est sur un allié ? (Hypothèse non confirmée.)
  • idPlayer::WeaponFireFeedback() agite la vue du joueur lorsqu'il tire.

Propriétés notables:

  • godmode permet l'invincibilité et peut être changée avec la commande de console god
  • noclip désactive toute collision avec l'environment et peut être modifiée avec la commande de console noclip

=PlayerIcon

Incertain, mais probablement l'icône du joueur en mode multijoueur. Dans PlayerIcon.h, il y a une énumération, playerIconType_t, qui contient trois valeurs, ICON_LAG, ICON_CHAT et ICON_NONE, qui laissent croire que la classe sert plutôt aux parties multijoueur en ligne.

=PlayerView

La classe idPlayerView se charge de la vue du personnage et de la plupart des choses qui peuvent l'affecter.

  • idPlayerView::SetPlayerEntity() sert à lier le joueur et la vue, pour qu'idPlayerView puisse infuencer la vue du joueur.
  • idPlayerView::DamageImpulse() projette la tête du côté lorsque le joueur est frappé.
  • idPlayerView::WeaponFireFeedback() est la fonction qui est appelée par idPlayer::WeaponFireFeedback().
  • idPlayerView::RenderPlayerView() affiche le jeu.

Objets notables :

  • La structure screenBlob_t servirait aux marques de griffe sur l'écran, et d'autres effets.

=Projectile

idProjectile se charge des balles qui sont tirées à chaque coup de feu ; le pistolet n'en a qu'une seule par coup, le fusil à pompe en a plusieurs. Elle a deux sous-classes, idGuidedProjectile et idBFGProjectile.

On trouve aussi dans ces fichiers d'autres petites classes reliées aux projectiles.

=Pvs

PVS = Potentially visible set, comme l'indique une ligne dans neo/game/Game_local.h :

idPVS pvs; // potential visible set

=SecurityCamera

Les caméras ; celles-ci peuvent afficher ce qu'elles voient sur des écrans.

  • idSecurityCamera::Killed() est appelée lorsque la caméra reçoit assez de dégats et « meurt ».
  • idSecurityCamera::Think() est appelée à chaque image, qu'elle soit en rotation ou non.
  • La fonction idSecurityCamera::StartSweep() débute la rotation de la caméra.

Note : Il y a un problème avec la vue de la caméra : elle semble toujours viser une direction totalement différente de son modèle. L'erreur se trouverait possiblement dans idSecurityCamera::Event_ContinueSweep()...

=SmokeParticles

L'utilité de ce fichier est nébuleuse, car en fait, il ne semble pas affecter la fumée dans le jeu ; même après avoir effacé le code dans toutes les méthodes de la classe, j'ai constaté que la fumée était toujours présente dans le jeu.

idSmokeParticles n'est pas une classe abstraite, et aucune autre classe n'en hérite.

=Sound

Une classe plutôt petite, idSound est la base de l'entité speaker. (Le système de son est construit dans neo/sound/snd_system.cpp.)

=Target

idTarget représente les entités dont le nom commence avec target_. Ces entités servent à toutes sortes de choses ; voici quelques exemples :

  • target_null : cible générique pour les caméras.
  • target_light_fadeout : pour faire s'éteindre lentement une lumière.
  • target_lock : verrouille ou débarre une porte.

Les entités target_ peuvent être activées par une entité de type trigger_ (voir ci-bas).

=Trigger

Les entités trigger exécutent une action lorsque quelque chose les touche.

Plusieurs classes héritent d'idTrigger :

  • idTrigger_Multi
  • idTrigger_EntityName
  • idTrigger_Timer
  • idTrigger_Count
  • idTrigger_Hurt
  • idTrigger_Fade
  • idTrigger_Touch

idTrigger_Multi

  • idTrigger_Multi::Event_Touch() est appelée lorsque le joueur entre dans la zone de l'objet trigger.
  • idTrigger_Multi::Event_Trigger() est appelée si l'objet trigger a une propriété target. Si cette propriété a comme valeur le nom d'une entité trigger_relay (ou peut-être un objet de type semblable), la propriété call de ce trigger_relay sera vérifiée pour une fonction de script à exécuter.

=Weapon

idweapon sert de base à toutes les armes : elle utilise l'énumération weaponStatus_t pour déterminer l'état du fusil (WP_OUTOFAMMO, WP_RELOAD, WP_HOLSTERED...) et elle comporte énormément de fonctions, dont BeginAttack() et EndAttack().

BeginAttack() est appelée lorsqu'on appuie sur le bouton gauche de la souris, et EndAttack(), quand on le relâche. Cela signifie que même pour une arme semi-automatique, le fusil ne cessera de tirer que lorsque le joueur lâche le bouton.

Note : BeginAttack() est appelée plusieurs fois à chaque coup de feu. Pour l'instant, j'ignore pourquoi.

=WorldSpawn

L'entité worldSpawn représente le niveau entier.

  • idWorldspawn::Spawn() règle la gravité (g_gravity) et réduit la propriété stamina (pm_stamina) du joueur à zéro si on est dans un des niveaux d'enfer.