Tout permuter
=Notes générales
- Le projet Game dans Visual Studio créera le fichier gamex86.dll.
- Le projet DoomDLL créera l'exécutable principal du jeu, Doom3.exe. J'ignore pourquoi le mot DLL se trouve dans le nom d'un projet qui sera compilé en .exe.
- Le projet d3xp dans la solution de Doom 3 serait pour l'extension Resurrection of Evil. Je crois que d3xp signifie Doom 3 expansion pack.Quand je fais une recherche dans tout le projet dans Visual C++, ce projet porte à confusion par rapport au projet Game que j'utilise principalement. On peut donc faire un clic droit dessus dans VC++, et sélectionner Décharger le projet. Cela ne le supprimera pas (il faut le faire explicitement dans l'explorateur Windows), mais la recherche dans toute la solution ignorera ce projet.
- id Tech 4 utilise certaines fonctions de la bibliothèque standard du langage C (voir neo/idlib/precompiled.h), mais les conteneurs
std
de C++ ne sont généralement pas utilisés chez id (sauf pour les outils de création comme l'éditeur de niveau). (Source) (en) - Il y a plusieurs endroits dans le code où on trouve des
typedef
dans les déclarations destruct
:typedef struct chose_s
{
...
} chose_t;Ceci sert à utiliser les structures sans avoir à toujours écrire
struct
devant le nom. Les suffixes (_s
et_t
) indiquent le type de l'objet auquel on fera référence.À certains endroits, le nom de la struct est omis ; les struct avec des typedef n'ont pas besoin de nom de base, à moins qu'elles ne doivent être déclarées d'avance (forward declaration). Ce sont surtout (peut-être exclusivement) les noms des typedef qui sont utilisés.
=Modifications
Compilation
-
Compilation avec Visual Studio 2010 Express
Ces notes se basent considérablement sur les directives de compilation du site Riot's House of Stuff ; le site n'étant pas disponible à l'heure actuelle, les notes sont publiées ici. Note importante : les instructions dans ce fichier texte nécessitent le fichier win_nanoafx.h.
Le but de la liste d'instructions suivante est surtout de clarifier les étapes en expliquant à quoi elles servent, au meilleur de mes connaissances.
À la base, Doom 3 ne compile qu'avec la version complète de Visual Studio, car il utilise la bibliothèque propriétaire MFC qui n'est distribuée qu'avec Visual Studio Professional, c'est-à-dire la version payante. Il faut donc effectuer quelques modifications au code pour supprimer les dépendances problématiques.
Une fois ces étapes achevées, les outils de développement de Doom 3 (éditeur de niveau, éditeur de GUI, etc.) seront omis à la compilation, et leurs commandes de console correspondantes ne seront pas reconnues. Évidemment, compiler ces outils ne sera pas nécessaire tant qu'on possède un exemplaire de Doom 3, qui les contient déjà.
- Si ce n'est pas déjà fait, récupérer le code de Doom 3 de la page GitHub officielle d'id Software.
- Selon le fichier README.txt (dans le dossier racine de Doom 3), le kit de développement DirectX de Microsoft doit être installé.
- Dans le dossier neo :
- Ouvrir _Release.props et enlever toutes les occurrences de
nafxcw.lib;
. - Ouvrir _Debug.props et enlever toutes les occurrences de
nafxcwd.lib;
.
Ces deux fichiers font partie de la bibliothèque MFC.
- Ouvrir _Release.props et enlever toutes les occurrences de
-
- Récupérer un fichier nommé win_nanoafx.h, anciennement disponible sur le site Riot's House of Stuff (qui n'est maintenant plus qu'un répertoire). Ce fichier contient les définitions de quelques classes (comme CComBSTR) qui sont utilisées par le moteur de jeu.
- Placer le fichier dans neo/sys/win32.
- Dans le même dossier, ouvrir win_shared.cpp, puis inclure win_nanoafx.h à la ligne 49 :
#include "win_nanoafx.h"
- Ouvrir neo/sys/win32/win_taskkeyhook.cpp et mettre en commentaire la ligne 37 :
#include <afxwin.h>
En général, les entêtes C++ dont le nom contient « AFX » font partie de MFC.
- Ouvrir neo/idlib/precompiled.h et mettre en commentaire la ligne 61 :
#include "../tools/comafx/StdAfx.h"
StdAfx.h est le nom standard pour un fichier qui contient tous les entêtes qui seront compilés d'avance. Les entêtes précompilés servent à accélérer la compilation du projet durant le développement ; par contre, ce fichier particulier n'est d'aucune utilité car il ne fait référence qu'aux entêtes MFC.
- Ouvrir neo/framework/BuildDefines.h et mettre en commentaire la ligne 96 :
#define ID_ALLOW_TOOLS
- Ouvrir neo/doom.sln, puis faire les changements suivants dans Visual C++ :
- Dans l'explorateur de solutions, supprimer ces dossiers (clic droit > Supprimer) :
- dlls (non disponible)
- exes (non disponible)
- libs (non disponible)
- Propriétés du projet DoomDLL (Toutes les configurations) > Répertoires VC++ :
- Dans Répertoires Include, ajouter le dossier include de l'SDK DirectX :
C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include
- Dans Répertoires de bibliothèques, ajouter le dossier de bibliothèques de l'SDK DirectX :
C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Lib/x86
- Dans Répertoires Include, ajouter le dossier include de l'SDK DirectX :
- Supprimer tous les dossiers dans DoomDLL/Tools, SAUF Compilers.
- Supprimer le dossier DoomDLL/Sys/RC. (Ce dossier contient le code pour les outils de création.)
- Dans l'explorateur de solutions, supprimer ces dossiers (clic droit > Supprimer) :
Pour l'erreur suivante : « Couldn't load default.cfg »
Le compilateur cherche dans le mauvais dossier pour trouver default.cfg. Le chemin où il cherche sera affiché quelques lignes plus haut dans la fenêtre de sortie.
L'idéal est de copier le dossier
base
de Doom 3 dans le dossier racine du projet.J'ai déjà tenté de changer le répertoire des ressources via la méthode suivante :
- Propriétés du projet DoomDLL (toutes les configurations) > Débogage > Arguments de la commande
- Dans l'instruction
+set fs_basepath
, changer le chemin pour celui du dossier racine du jeu, c'est-à-dire celui qui contient le dossier base :- pour déboguer dans Visual Studio, ce sera le dossier racine du projet
- pour démarrer le jeu de façon traditionnelle, ce sera le dossier où se trouvera le fichier Doom3.exe du mod (monmod/build/Win32/Release par défaut en mode Release)
Par contre, il semble que la valeur de
fs_basepath
ne soit pas affectée par la ligne de commande, du moins pas à la compilation (ou peut-être même à l'exécution de DOOM3.exe...).Notes :
- La variable
fs_basepath
, qui contient le chemin absolu vers le dossier des ressources du jeu, prend comme valeur par défaut le dossier courant (celui du projet et de l'exécutable). On peut vérifier sa valeur dans la console en tapantfs_basepath
. - La première fois que j'ai rencontré ce problème, j'ai tout simplement changé le nom du dossier racine de doom3.gpl à doom, et tout a fonctionné...
Pour l'erreur suivante : « error C2504: '_bstr_t' : classe de base non définie »
Le nouveau fichier, win_nanoafx.h, fait référence à la classe _bstr_t, qui est définie dans comutil.h.
Si on compile en mode « Dedicated Release » (comme c'est le cas par défaut), comutil.h ne sera pas inclus au projet par win_shared.cpp :
#ifndef ID_DEDICATED
#include <comdef.h>
#include <comutil.h>
#include <Wbemidl.h>#pragma comment (lib, "wbemuuid.lib")
#endifOn peut soit laisser tomber l'inclusion de win_nanoafx.h dans win_shared.cpp (ligne 49), soit compiler dans un autre mode.
Réglages généraux
-
Changer le nom de l'application
Fichier : framework/Licensee.h
Il suffit de trouver la constante
GAME_NAME
, puis modifier sa valeur. -
Choisir le GUI du menu principal
-
Commandes du jeu
- Les commandes de clavier sont définies dans base/DoomConfig.cfg (ou, si ce fichier n'existe pas, base/default.cfg).
- Les impulsions sont définies dans neo/framework/UsercmdGen.h.
- Les impulsions sont vérifiées dans idPlayer::PerformImpulse().
Modifications précises
-
Créer une CVar
- Déclarer la CVar dans neo/game/gamesys/SysCvar.cpp
- Déclarer la CVar avec
extern
dans neo/game/gamesys/SysCvar.h
-
Ajouter une nouvelle propriété aux windowDef
- Fichier : neo/ui/Window.hDéclarer la variable correspondante dans la classe idWindow. La variable devra être une occurrence d'une classe héritant d'idWinVar
- Fichier : neo/ui/Window.cpp
- Initialiser la variable dans idWindow::CommonInit()
- L'ajouter à
RegisterVars[]
(~ ligne 62). Les autres variables montrent comment indiquer le type de la propriété - Les mots-clés sont traités dans idWindow::GetWinVarByName()
-
Modifier les propriétés d'un matériau (texture)
Pour modifier les propriétés d'un matériau dans le code, il faut utiliser des identifiants particuliers pour les valeurs modifiables.
Par exemple, pour permettre au code C++ de modifier l'alpha (l'opacité) d'un matériau, il faudra donner la valeur
parm3
à la propriété alpha :custom/mon_materiau
{
// Première passe
{
...
alpha parm3
}
}Les paramètres de matériaux utilisés par la classe idMaterial sont indiqués dans l'énumération expRegister_t. Malheureusement, les noms des éléments sont vagues, et je n'ai trouvé aucune documentation précise qui explique clairement à quoi chaque élément fait référence.
Il semble que les quatre premiers paramètres sont les valeurs RVBA (0 = rouge, 1 = vert, 2 = bleu, 3 = alpha).
parm3
signifie simplement le paramètre #3, alpha, c'est-à-dire l'opacité.Ainsi, pour modifier la transparence du matériau :
- on récupère le matériau à l'aide du gestionnaire de déclarations (decl manager)
- on modifie la valeur alpha avec idRenderSystemLocal::SetColor4()
- on applique la matériau (dans cet exemple, la texture fera le quart de l'écran, et sera affichée dans le coin supérieur gauche)
// L'idéal serait de faire de monMateriau un membre de la classe et de l'initialiser dans le constructeur
const idMaterial *monMateriau = declManager->FindMaterial( "custom/mon_materiau" );
...
renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 0.5f ); // Rouge, vert, bleu, alpha (0,5)
renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH /2, SCREEN_HEIGHT /2, 0.0f, 0.0f, 1.0f, 1.0f, monMateriau );
=Récupération et échange d'informations
-
Afficher un message dans la console
La plupart des classes, sinon toutes, utilisent un objet de type idCommon (neo/framework/Common.h). L'objet se nomme
common
et a une portée globale.La méthode Printf() de cet objet fonctionne comme la méthode standard std::printf(), sauf qu'elle imprime le texte dans la console de Doom.
common->Printf( "Message" );
-
Créer un fichier et y insérer du texte
idStr texte( "Du texte.\n" );
fileSystem->WriteFile( "fichier.txt", texte.c_str(), texte.Length() );fichier.txt sera créé / modifié dans le dossier base.
fileSystem
est initialisé très tôt dans l'application, et peut donc servir presque partout.Pour inscrire plus d'une ligne :
idFile *fichier = fileSystem->OpenFileAppend( "nom_du_fichier.txt" );
fichier->Write( "Du texte.\n\n", 10 ); // 10 == nombre de caractères
fichier->Write( "D'autre texte.\n\n", 15 );
fileSystem->CloseFile( fichier ); -
Trouver le type d'une entité
Fichier : game/Entity.cpp
Fonction : idEntity::GetEntityDefName()
Note : Lorsqu'on veut comparer le nom d'une entité avec une chaîne de caractère, la chaîne doit être au format idStr :
if ( GetEntityDefName() == idStr( "func_securitycamera" ) ) // Fonctionne
et non un const char* :
if ( GetEntityDefName() == "func_securitycamera" ) // Ne fonctionne pas
-
Insérer une valeur du code C++ dans un GUI
Les GUI sont issus de la classe idUserInterfaceLocal. Il suffit d'appeler une des fonctions prévues dans le code :
- idUserInterfaceLocal::SetStateString()
- idUserInterfaceLocal::SetStateBool()
- idUserInterfaceLocal::SetStateInt()
- idUserInterfaceLocal::SetStateFloat()
Par exemple, pour insérer une clé nommée
chose
, avec la valeuraffaire
:// C++
objetGUI->SetStateString( "chose", "affaire" );Une fois ceci fait, le gui référencé sera en mesure de récupérer la valeur comme suit avec la syntaxe
gui::propriété
:// fichier .gui
windowDef Desktop
{
rect 0,0,640,480
menugui 1
backcolor 0, 0, 0, 0.5
text "gui::chose" // affaire
} -
Suite d'événements pour le déplacement du joueur (astuce incomplète)
J'ai noté ici des appels de fonction pertinents sur le déplacement du joueur à l'appui d'une touche. Incomplètes et d'origines variées, ces notes ne sont pas totalement cohérentes entre elles.
La classe usercmd_t représente l'ensemble des commandes récentes du joueur. La classe idPlayer contient un objet de ce type, nommé
usercmd
.- On récupère les instructions de l'utilisateur les plus récentes (un objet usercmd_t).-> idSessionLocal::RunGameTic() appelle idUsercmdGenLocal::GetDirectUsercmd() :
cmd = usercmdGen->GetDirectUsercmd();
-> GetDirectUsercmd() fait appel à idUsercmdGenLocal::MakeCurrent() :
// create the usercmd
MakeCurrent();Cette fonction est appelée à chaque image ; c'est elle qui met à jour
usercmd.impulse
dans la classe idPlayer.-> MakeCurrent() fait appel à idUsercmdGenLocal::KeyMove() :
// get basic movement from keyboard
KeyMove();
- RunGameTic() appelle ensuite idGameLocal::RunFrame() en lui passant l'objet usercmd_t
gameReturn_t ret = game->RunFrame( &cmd );
idPlayer::EvaluateControls() est aussi appelée à chaque image. Quand on appuie une touche d'impulsion, on exécute idPlayer::PerformImpulse().
Les impulsions sont des commandes avec des significations particulières (sélectionner une arme, recharger, entres autres). Voir neo/framework/UsercmdGen.h, ligne ~53 pour avoir une meilleure idée des impulsions enregistrées.
- On récupère les instructions de l'utilisateur les plus récentes (un objet usercmd_t).-> idSessionLocal::RunGameTic() appelle idUsercmdGenLocal::GetDirectUsercmd() :
=Problèmes de développement
-
Boîte de dialogue sous Visual C++ : Un ou plusieurs projets de la solution n'ont pas été correctement chargés.
Ce message d'erreur fait surface à cause d'une sous-section dans le fichier neo/doom.sln.
- Faire une copie de doom.sln (au cas où).
- Ouvrir doom.sln dans un éditeur de texte.
-
Supprimer la section
NestedProjects
:GlobalSection(NestedProjects) = preSolution
{49BEC5C6-B964-417A-851E-808886B57400} = {347D107C-D787-4408-A60D-86FA45997F9B}
{F46F5D4E-C1D4-4ADE-9FAA-5F0CE3AA07F1} = {347D107C-D787-4408-A60D-86FA45997F9B}
{6EA6406F-3E65-47D9-8246-D6660A81606F} = {003B01AB-152D-45C8-BF45-E5A035042D7F}
{49BEC5C6-B964-417A-851E-808886B57420} = {003B01AB-152D-45C8-BF45-E5A035042D7F}
{49BEC5C6-B964-417A-851E-808886B57430} = {1E2B3940-65F8-4D8F-9EEE-85E94EBBC6DF}
{49BEC5C6-B964-417A-851E-808886B574F1} = {1E2B3940-65F8-4D8F-9EEE-85E94EBBC6DF}
{0BC6FCC9-C65E-4B1F-9A58-0B9399987C9F} = {1E2B3940-65F8-4D8F-9EEE-85E94EBBC6DF}
EndGlobalSection