D'accord, formulé de cette façon, je suis sûr que cela semble obscur, mais je ferai de mon mieux pour décrire mon problème.
Tout d'abord, laissez-moi vous expliquer ce qui ne va pas avec mon vrai code source.
Je fais un jeu de plateforme, et voici les composants actuels de mon jeu: un niveau , qui tient sa propre grille de tuiles et aussi les feuilles de tuiles et une tuile , qui garde juste le nom de sa feuille de tuiles et son index dans le feuille de tuile. Tout cela fait partie d'un projet de jeu XNA distinct, nommé GameLib.
Voici comment mes niveaux sont formatés dans un fichier texte:
x x . . . . .
. . . b g r .
. . . . . . .
X X X X X X X
Où .
représente une tuile vide, b
représente une plate-forme bleue, X
représente un bloc avec une couleur aléatoire, etc.
Au lieu de faire tout le travail de chargement d'un niveau dans la classe Level , j'ai décidé de profiter de l'extension du pipeline de contenu et de créer un LevelContentPipelineExtension.
Dès que j'ai commencé, j'ai vite réalisé que je ne voulais pas coder en dur 20+ lignes du genre:
if (symbol == 'b')
return new Tile(/*...*/);
Donc, dans l'espoir de centraliser au moins toute la création de contenu dans une seule classe, j'ai créé une classe statique, appelée LevelContentCreation , qui contient les informations sur le symbole qui crée quoi et tout ce genre de choses. Il contient également une liste de tous les chemins d'accès aux actifs des feuilles de tuiles, pour les charger plus tard. Cette classe fait partie de mon projet de bibliothèque de jeux GameLib.
Le problème est que j'utilise cette classe statique LevelContentCreation à la fois dans ma classe Level (pour charger les feuilles de tuiles réelles) et dans mon extension de pipeline de contenu (pour savoir quel symbole représente quelle tuile) ...
Et voici à quoi ressemble ma classe LevelContentCreation :
public static class LevelContentCreation
{
private static Dictionary<char, TileCreationInfo> AvailableTiles =
CreateAvailableTiles();
private static List<string> AvailableTileSheets { get; set; }
/* ... rest of the class */
}
Maintenant, puisque la classe LevelContentCreation fait partie de la bibliothèque de jeux, l'extension de pipeline de contenu essaie de l'utiliser mais l'appel à CreateAvailableTiles () ne se produit jamais et je ne peux pas charger un objet Level à partir du pipeline de contenu.
Je sais que c'est parce que le pipeline de contenu s'exécute avant la compilation réelle ou quelque chose comme ça, mais comment puis-je résoudre le problème?
Je ne peux pas vraiment déplacer la classe LevelContentCreation vers l'extension de pipeline, car alors la classe Level ne serait pas en mesure de l'utiliser ... Je suis assez perdu et je ne sais pas quoi faire ... Je connais ces sont de très mauvaises décisions de conception, et j'aimerais que quelqu'un me pointe dans la bonne direction.
Merci pour votre précieux temps.
Si quelque chose n'est pas assez clair ou si je dois fournir plus d'informations / code, veuillez laisser un commentaire ci-dessous.
Un peu plus d'informations sur mes projets:
- Jeu - Projet de jeu XNA Windows. Contient des références à: Engine, GameLib et GameContent
- GameLib - Projet de bibliothèque de jeux XNA. Contient des références à: Moteur
- GameContent - Projet de contenu XNA. Contient des références à: LevelContentPipelineExtension
- Moteur - Projet de bibliothèque de jeux XNA. Pas de références.
- LevelContentPipelineExtension - Extension de pipeline de contenu XNA. Contient des références à: Engine et GameLib
Je ne connais presque rien de XNA 4.0 et je suis un peu perdu sur la façon dont le contenu fonctionne maintenant. Si vous avez besoin de plus d'informations, dites-le-moi.
Réponses:
Dans les cas où vous utilisez des singletons, des classes statiques ou des variables globales, 99% du temps ce que vous devriez vraiment utiliser est un service de jeu . Les services de jeu sont utilisés lorsque vous souhaitez qu'une instance d'une classe soit disponible pour toutes les autres classes, mais le couplage étroit des autres options vous donne un goût amusant. Les services n'ont pas non plus les problèmes d'instanciation que vous avez vus, ils sont plus réutilisables et ils peuvent être moqués (si vous vous souciez des tests unitaires automatisés) .
Pour enregistrer un service, vous devez donner à votre classe sa propre interface. Appelez ensuite
Game.Services.AddService()
. Pour utiliser ultérieurement ce service dans une autre classe, appelezGame.Services.GetService()
.Dans votre cas, votre classe ressemblerait à ceci:
À un moment donné au début du programme, vous devrez enregistrer votre service auprès de
Game.Services
. Je préfère le faire au début deLoadContent()
, mais certaines personnes préfèrent enregistrer chaque service dans le constructeur de cette classe.Maintenant, chaque fois que vous souhaitez accéder à votre
LevelContentCreation
classe dans une autre classe, vous devez simplement faire ceci:En remarque, ce paradigme est connu sous le nom d' inversion de contrôle (IoC) et est également largement utilisé en dehors du développement de XNA. Le framework le plus populaire pour l'IoC dans .Net est Ninject , qui a une syntaxe plus agréable que les méthodes Game.Services:
Il prend également en charge l' injection de dépendance , une forme d'IoC qui est légèrement plus complexe, mais beaucoup plus agréable à utiliser.
[Edit] Bien sûr, vous pouvez aussi obtenir cette belle syntaxe avec XNA:
la source
En fait, j'ai trouvé un moyen de résoudre le problème: j'ai supprimé la classe statique et en ai fait une classe normale. De cette façon, je suis sûr que les bonnes déclarations sont exécutées au bon moment.
J'aurais aimé ne pas avoir perdu votre temps ainsi, mais j'ai eu l'idée de le faire en lisant les commentaires / réponses.
Merci quand même.
la source
if
/switch
qui renvoient simplement différents types, vous voulez probablement que ces valeurs fassent partie de la classe à la place. Dans votre cas, je créerais uneTileType
classe, qui contient des informations (texture, lettre de fichier de sauvegarde, etc.) sur chaque type de tuile. Ensuite, pour obtenir unTileType
extrait d'une lettre, recherchez simplement dans votre liste deTileTypes
, ou si vous êtes préoccupé par les performances, stockez un statiqueDictionary<char, TileType>
dans laTileType
classe (vous pouvez le remplir automatiquement dans leTileType
constructeur).Tile
(classe qui représente une seule tuile affichée à l'écran) ferait référence à une seuleTileType
. Si un nombre suffisant de vos tuiles ont un comportement unique qui nécessite un code spécial, vous souhaiterez plutôt utiliser une hiérarchie de classes.Jetez un œil à ceci: http://msdn.microsoft.com/en-us/library/bb447745.aspx
Pour la disposition des niveaux, je viens de mettre une fonction dans ma classe Level appelée "LoadFromFile (string name)". Parce que si vous souhaitez ajouter ultérieurement la prise en charge de cartes personnalisées dans votre jeu, vous devrez de toute façon enregistrer et charger les niveaux à partir de votre propre format de fichier.
Vous pouvez également créer un importateur de contenu, à charger à partir des ressources incluses et également à partir des fichiers fournis par l'utilisateur. Regardez cette page pour plus d'informations sur la création d'un importateur
http://msdn.microsoft.com/en-us/library/bb447743.aspx
la source