Comment puis-je mettre à jour les paramètres d'affichage à partir d'un écran Options sans redémarrer?

9

Je suis en train de créer un RPG 2D en C ++ 11 avec Allegro 5 et boost.

Mon objectif est de mettre à jour mes paramètres de jeu d'une manière ou d'une autre lorsqu'une option est modifiée dans le menu Options. Je ne veux pas forcer l'utilisateur à redémarrer mon jeu. D'autres jeux ne nécessitent pas de redémarrage lors du changement de résolution ou du passage du mode plein écran au mode fenêtré, donc mon jeu ne devrait pas non plus. Veuillez voir une vue simplifiée du système ci-dessous.

Veuillez noter que je ne souhaite pas nécessairement appeler directement mon objet Game depuis l'écran OptionsScreen. La ligne pointillée sert simplement à illustrer l'effet que j'essaie d'obtenir; pour provoquer en quelque sorte une mise à jour du jeu lorsqu'une option est modifiée dans une autre partie du système.

Explication détaillée

Le ScreenManager contient une liste de tous les GameScreenobjets existants. Ce seront différents écrans du jeu, y compris des popups. Cette conception adhère plus ou moins à l' échantillon de gestion du jeu Etat en C # / XNA .

Le ScreenManagercontient une référence à mon Gameobjet. L' Gameobjet initialise et modifie les paramètres du jeu. Si je veux changer la résolution, passer en plein écran ou couper le volume, je le ferais en Gameclasse.

Cependant, OptionsScreen ne peut actuellement pas accéder à la classe Game. Voir le schéma ci-dessous:

Un GameScreen peut signaler trois événements onFinished, onTransitionStartet onTransitionEnd. Il n'y en a pas onOptionsChangedparce qu'un seul écran fait ça. ScreenManager ne peut pas configurer la gestion des événements pour cela car il gère tous les écrans en tant que GameScreens.

Ma question est, comment puis-je changer ma conception afin qu'une modification dans le menu Options ne nécessite pas un redémarrage, mais est immédiatement modifiée? Je demanderais de préférence que mon Gameobjet soit mis à jour une fois que le bouton Appliquer est cliqué.

IAE
la source
Cette question (même si elle est liée au jeu) ressemble parfaitement à programmers.stackexchange.com car elle concerne plus la conception OO générale que le jeu réel
bughi
Où avez-vous obtenu des graphiques aussi impressionnants?
joltmode
@psycketom: Le premier est de Visio 2013 et le second est généré à partir du code par VS2012. J'espère que cela pourra aider!
IAE

Réponses:

1

D'après ce que j'ai vu, l'approche la plus simple est de lire un fichier d'options au démarrage pour déterminer les paramètres d'affichage actuels; puis, lorsque votre écran d'options s'affiche, chargez toutes les options actuelles à partir d'un fichier.

Lorsque les modifications sont finalisées via un bouton applyou ok, elles sont enregistrées dans un fichier. Si des modifications affectent l'affichage, informez l'utilisateur que le jeu doit être redémarré pour qu'il prenne effet.

Lorsque le jeu est redémarré, les (désormais nouveaux) paramètres d'affichage sont à nouveau lus à partir du fichier.

--ÉDITER--

Et ... ça aurait aidé si j'avais remarqué cette dernière phrase. Vous ne voulez pas avoir à redémarrer. Rend les choses un peu plus difficiles en fonction de votre implémentation et de votre bibliothèque graphique principale.

IIRC, Allegro dispose d'un appel de fonction qui vous permet de modifier les paramètres d'affichage à la volée. Je ne connais pas encore Allegro 5, mais je sais que vous pourriez le faire en 4.

Casey
la source
Je ne pense pas que le problème soit lié à Allegro et à ses capacités. "Ma question est, comment puis-je changer ma conception afin qu'un changement dans le menu Options ne nécessite pas un redémarrage, mais est changé immédiatement?" Le redémarrage est dû au fait que "OptionsScreen ne peut actuellement pas accéder à la classe de jeu" et doit les charger à partir du fichier de paramètres si je comprends bien.
ClassicThunder
@Casey: Bonjour Casey! :) Oui, j'utilise un fichier de configuration pour stocker les données d'affichage pertinentes, mais je veux pouvoir éviter le redémarrage. C'est un petit jeu, et d'autres jeux peuvent changer la résolution sans avoir à redémarrer, donc je ne vois pas pourquoi je devrais forcer l'utilisateur à redémarrer avec le mien. C'est un problème d'utilisabilité. Bien que je puisse simplement appeler les fonctions de disponsation allegro, j'essaie de rester OOP et de ne pas mélanger mes appels graphiques simplement parce que je peux; Je ne considère pas ce bon design.
IAE
J'ai mis en évidence le bit "sans redémarrage" afin que les autres ne se déclenchent pas sur la même dernière phrase. Un fait aussi important n'aurait pas dû arriver à la fin, je m'excuse):
IAE
@SoulBeaver Qui a dit que vous devez le faire pour ne pas redémarrer? C'est une chose viable d'exiger cela, en fait, cela devient de plus en plus courant de nos jours. Certaines versions récentes (XCOM: Enemy Unknown, Skyrim, etc.) de jeux à gros budget nécessitent un redémarrage. (le fait qu'ils soient sur Steam peut être le coupable en raison de sa synchronisation d'options côté serveur mais n'y allons pas ...)
Casey
1
@Casey: Vrai, et pour certaines options, je peux voir pourquoi c'est même nécessaire, mais pour des choses comme le plein écran / fenêtré ou la résolution d'écran? Je n'ai jamais vu de jeu nécessitant un redémarrage pour ces paramètres. Ma question de base est: pourquoi devrais-je forcer mon utilisateur à redémarrer alors que le jeu peut modifier automatiquement les paramètres?
IAE
1

C'est ce que je fais pour mon jeu. J'ai 2 fonctions distinctes pour initialiser des trucs, 'init' et 'reset'. Init n'est appelé qu'une seule fois au démarrage et fait des choses qui ne dépendent d'aucun paramètre, comme le chargement des ressources principales. La réinitialisation fait des choses comme la présentation de l'interface utilisateur en fonction de la résolution de l'écran, elle est donc appelée à chaque fois que les paramètres changent.

init();
bool quit = false;
while( !quit )
{
    createWindow();
    reset();

    bool settings_changed = false;
    while( !quit && !settings_changed )
    {
        ... main loop
        // set quit=true if user clicks 'quit' in main menu
        // set settings_changed=true if user clicks 'apply' in options
    }

    destroyCurrentWindow();
}

Je ne connais pas Allegro, mais ma réponse est assez générale, donc j'espère que cela vous aidera, vous ou quelqu'un d'autre, avec un problème similaire.

DaleyPaley
la source
Voilà une solution intéressante. J'essayais d'avoir un contrôle serré sur chaque fois qu'une réinitialisation est appelée, mais vous le faites indépendamment d'un changement. C'est certainement quelque chose que je pourrais implémenter, et je vais y réfléchir, merci!
IAE
1

Sans gâcher votre architecture actuelle, je vois deux façons. Tout d'abord, vous pouvez stocker un pointeur vers l' Gameinstance dans la OptionsScreenclasse. Deuxièmement, vous pourriezGame classe récupère les paramètres actuels dans un intervalle donné, disons toutes les secondes.

Pour réellement s'adapter aux nouveaux paramètres, la Gameclasse doit implémenter une sorte de fonctions de réinitialisation qui récupère les paramètres actuels et les réinitialise en fonction de ceux-ci.

Pour une solution propre, vous avez besoin d'un gestionnaire global d'une sorte, c'est donc plus d'efforts à mettre en œuvre. Par exemple, un système d'événements ou un système de messagerie. Il est très utile de laisser les classes communiquer sans ces liaisons fortes telles que l'agrégation ou la composition, etc.

Avec un gestionnaire d'événements global, le OptionsScreenpourrait simplement déclencher globalement un événement redessiné qui Games'est enregistré pour écouter auparavant.

En règle générale, vous pouvez implémenter une classe de gestionnaire stockant les événements et les rappels en les écoutant dans une carte de hachage. Vous pouvez ensuite créer une seule instance de ce gestionnaire et lui transmettre des pointeurs à vos composants. L'utilisation de C ++ plus récent est assez simple car vous pouvez l'utiliser std::unordered_mapcomme carte de hachage et std::functionpour stocker des rappels. Il existe différentes approches que vous pouvez utiliser comme clé. Par exemple, vous pouvez rendre la chaîne du gestionnaire d'événements basée, ce qui rend les composants encore plus indépendants. Dans ce cas, vous utiliseriez std::stringcomme clé dans la carte de hachage. Personnellement, j'aime cela et ce n'est certainement pas un problème de performance, mais la plupart des systèmes d'événements traditionnels fonctionnent avec des événements en tant que classes.

danijar
la source
Bonjour danijar. J'utilise déjà boost :: signaux pour mettre en place un système de gestion d'événements rudimentaire. La ligne forte entre le GameScreen et le ScreenManager est en fait cette gestion des événements. Avez-vous des ressources détaillant l'architecture d'un gestionnaire d'événements global? Plus de travail ou pas, cela pourrait conduire à une implémentation propre.
IAE
Bien que je n'aie pas lu de recherche à ce sujet, j'ai mis en place un gestionnaire d'événement événementiel mondial pour mon propre petit moteur. Si la mise en œuvre vous intéresse, je vous enverrai un lien. J'ai mis à jour ma réponse pour couvrir plus de détails.
danijar
1

Eh bien, c'est un cas spécifique du modèle Observer.

Il existe une solution qui implique des rappels. C'est la meilleure façon de le faire si vous voulez un couplage lâche, et je pense que c'est aussi le plus propre. Cela n'impliquera aucun gestionnaire mondial ou singletons.

Fondamentalement, vous aurez besoin d'une sorte de SettingsStore. Là, vous stockez les paramètres. Lorsque vous créez un nouvel écran, ils auront besoin d'un pointeur vers le magasin. Dans le cas du, OptionsScreenil modifiera lui-même certains des paramètres. Dans le cas duGameScreen il suffit de les lire. Ainsi, dans votre jeu, vous ne créeriez qu'une seule instance, qui sera transmise sur tous les écrans qui en nécessitent une.

Maintenant, cette SettingsStoreclasse aura une liste de notifiables. Ce sont des classes qui implémentent une certaine ISettingChangedinterface. L'interface serait simple et contient la méthode suivante:

void settingChanged(std::string setting);

Ensuite, dans votre écran, vous implémenterez la logique de chaque paramètre qui vous tient à cœur. Ensuite, ajoutez - vous au magasin pour être informé: store->notifyOnChange(this);. Lorsqu'un paramètre est modifié, le rappel est appelé avec le nom du paramètre. La nouvelle valeur de réglage peut ensuite être récupérée à partir du SettingsStore.

Maintenant, cela peut être encore augmenté avec les idées suivantes:

  • Utilisez une classe qui stocke les chaînes de paramètres - peut être même les SettingsStore(chaînes const) afin d'empêcher le copier-coller des chaînes.
  • Encore mieux, au lieu d'une chaîne, utilisez une énumération pour spécifier les paramètres que vous avez
  • Vous pouvez même spécifier les anciennes et les nouvelles valeurs comme arguments dans le rappel, mais ce serait plus compliqué, car vous pouvez avoir des chaînes ou des valeurs int, ou autre chose. C'est à vous.
Timotei
la source
0

Lisez vos paramètres d'un fichier dans des variables. Demandez à votre gestionnaire d'écran de savoir si l'écran d'où il vient vient de l'écran Options, et si tel est le cas, rechargez vos paramètres à partir des variables. Lorsque l'utilisateur quitte votre jeu, réécrivez les paramètres des variables dans le fichier.

131nary
la source