Salut,
Quelques remarques
Screens
Découper par écran n’est pas une mauvaise idée, c’est ce qui s’apparente à des "Scenes" dans les moteurs de jeux. Cependant tes classes Screen sont de fausses classes, ce ne sont que des fonctions déguisées, tu pourrais faire la même chose en donnant l’index dans les arguments plutôt qu’utiliser les méthodes virtuelles. Et dans cette fonction, elles gèrent chacune l’initialisation puis la boucle. Je te propose plutôt de concevoir les Screen un peu comme tu as conçu les boutons. Ce sont des classes qui devrait pouvoir se dessiner, et réagir à un évènement. Ta boucle d’évènement se situerait au niveau du main et se propage dans l’écran courant, ça t’éviterai de dupliquer du code comme les évènements de la fenêtre (fermeture), la limite de rafraichissement etc…
Si tu as cette approche, il faut aussi que tu aie conscience que tu vas charger tes éléments vraisemblablement dans le constructeur, et donc le fait d’instancier tout tes écrans au début va tout mettre en RAM. Là que tu navigues entre 5 écrans c’est trois fois rien mais sur un jeu qui multiplie les tableaux genre un plateformer 2D, t’as pas envie de tout charger alors que tu te sers d’un seul tableau.
MVC
Effectivement, c’est pas du tout un MVC, désolé ^^'
Ce qui s’approche le plus d’un model dans ton archi est Game, qui contient la grille et est appelée par la vue ScreenGame pour être mis en forme. Dans cet échange, le modèle n’a pas à se préoccuper de la mise en forme, or ici il le mets sous forme de wstring pour faire plaisir à la vue. ça va rejoindre un point un peu plus bas, mais dans cette grille, chaque case n’a que trois états, vide/joueur1/joueur2, pourquoi représenter ces trois états par un wstring ou un char qui peuvent prendre bien plus de valeurs, qu’il faudra gérer ensuite comme des cas d’erreur, un enum me semble tout indiqué ici.
Game a aussi tout un tas de logique du jeu, je suppose que c’est pour ça qu’il a fini dans controller. Mais il ne fait pas le rôle de controller qui test la validité de l’action de la vue, la vue se charge elle même de savoir qu’est-ce qui est déjà joué, à quel joueur c’est etc…
Enfin hésites pas à plus découper ta vue en composants comme le bouton (et en fait c’est assez valable sur tout le code qui regroupe généralement trop de responsabilité aux mêmes endroits).
Inclusion
Inclut les headers qui ne viennent pas de ton code, tel que la lib sfml, avec les chevrons <>
. Ainsi on peut modifier les chemins de recherche pour pouvoir utiliser par exemple un gestionnaire de paquet.
Evite de naviguer dans l’arborescence de fichier "../model/machin.h", c’est à ta configuration d’indiquer à partir d’où chercher des headers et ainsi tu peux inclure "model/machin.h".
Déclaration de variable
Effectue la déclaration d’une variable au plus proche de son utilisation, avec une initialisation. ça te permettra d’avoir une initialisation qui a du sens, et une portée ainsi qu’une durée de vie minimale.
Linéarisation de matrice
Tu peux représenter un tableau de tableau [NB_ROW][NB_COLUMN] par un seul tableau [NB_ROW*NB_COLUMN] et accéder à [i][j] par [i*NB_COLUMN+j] quand ton nombre de colonne est fixe. Préférer les classes C++ pour les collections std::array
, std::vector
…
Ajouter de la sémantique (enums et constantes à la place de magic numbers)
Tu dois toujours de poser la question de quelle est la meilleure représentation de ton information, celle qui a le plus de sens. Tu as déjà eu le réflexe de mettre un tas de constante, mais tu as encore plein d’endroit où des nombres se baladent. Et il y a plusieurs données qui pourraient facilement devenir des enum pour des statuts ou l’index des écrans par exemples…
Pour les constante ça a déjà été dit mais préfère static constexpr qui type ta donnée.
Et cadeau, je te laisse le fichier que je me suis rajouté pour compiler ton projet. Il faudrait mieux gérer les ressources, je l’ai écrit juste pour pouvoir faire un xmake run, mais au moins tu vois que ce n’est pas très compliqué.
xmake.lua
add_rules("mode.debug", "mode.release")
set_languages("cxxlatest")
set_warnings("allextra")
add_requires("sfml")
target("OXO")
set_kind("binary")
add_packages("sfml")
add_includedirs(".")
add_files(
"main.cpp",
"model/*.cpp",
"view/*.cpp",
"controller/*.cpp"
)
add_extrafiles("src/**/*")
after_build(function (target)
local resdir = path.join(target:targetdir(), "src")
if not os.exists(resdir) then
os.cp("src", resdir)
end
end)
on_clean(function (target)
local resdir = path.join(target:targetdir(), "src")
os.rm(resdir)
end)