Séparer la physique et la logique du jeu du code de l'interface utilisateur

12

Je travaille sur un jeu de puzzle simple basé sur des blocs.

Le jeu consiste à déplacer des blocs dans la zone de jeu, c'est donc une simulation physique triviale. Mon implémentation, cependant, est à mon avis loin d'être idéale et je me demande si vous pouvez me donner des conseils sur la façon de mieux le faire.

J'ai divisé le code en deux domaines: la logique du jeu et l'interface utilisateur, comme je l'ai fait avec beaucoup de jeux de puzzle:

  • La logique du jeu est responsable des règles générales du jeu (par exemple, le système de règles formel des échecs)
  • L'interface utilisateur affiche la zone de jeu et les pièces (par exemple, échiquier et pièces) et est responsable des animations (par exemple, le mouvement animé des pièces d'échecs)

La logique de jeu représente l'état du jeu sous forme de grille logique, où chaque unité correspond à la largeur / hauteur d'une cellule sur la grille. Ainsi, pour une grille de largeur 6, vous pouvez déplacer un bloc de largeur 2 quatre fois jusqu'à ce qu'il entre en collision avec la frontière.

L'interface utilisateur prend cette grille et la dessine en convertissant les tailles logiques en tailles de pixels (c'est-à-dire en la multipliant par une constante). Cependant, comme le jeu n'a pratiquement aucune logique de jeu, ma couche de logique de jeu [1] n'a pas grand-chose à faire à part la détection de collision. Voici comment ça fonctionne:

  1. Le joueur commence à faire glisser un morceau
  2. L'interface utilisateur demande la logique du jeu pour la zone de mouvement légale de cette pièce et permet au joueur de la faire glisser dans cette zone
  3. Le joueur lâche un morceau
  4. L'interface utilisateur accroche la pièce à la grille (afin qu'elle soit à une position logique valide)
  5. L'interface utilisateur indique à la logique du jeu la nouvelle position logique (via des méthodes de mutation, que je préfère éviter)

Je ne suis pas très content de ça:

  • J'écris des tests unitaires pour ma couche logique de jeu, mais pas l'interface utilisateur, et il s'est avéré que tout le code délicat se trouve dans l'interface utilisateur: empêcher la pièce d'entrer en collision avec les autres ou la frontière et l'enclencher sur la grille.
  • Je n'aime pas le fait que l'interface utilisateur raconte la logique du jeu sur le nouvel état, je préfère qu'il appelle une movePieceLeft()méthode ou quelque chose comme ça, comme dans mes autres jeux, mais je ne suis pas allé loin avec cette approche, car la logique du jeu ne sait rien du glissement et de la capture qui sont possibles dans l'interface utilisateur.

Je pense que la meilleure chose à faire serait de se débarrasser de ma couche logique de jeu et de mettre en place une couche physique à la place. J'ai quelques questions à ce sujet:

  1. Une telle couche physique est-elle courante, ou est-il plus courant que la couche logique de jeu le fasse?
  2. Est-ce que l'accrochage à la grille et au code de déplacement de pièces appartiendrait à l'interface utilisateur ou à la couche physique?
  3. Une telle couche physique fonctionnerait-elle généralement avec des tailles de pixels ou avec une sorte d'unité logique, comme ma couche logique de jeu?
  4. J'ai vu une fois une détection de collision basée sur des événements dans la base de code d'un jeu, c'est-à-dire que le joueur faisait simplement glisser la pièce, l'interface utilisateur le rendait docilement et notifiait le système physique, et le système physique appelait une méthode onCollision () sur la pièce une fois qu'une collision est détectée. Quoi de plus commun? Cette approche ou demander d'abord la zone de mouvement légale?

[1] couche n'est probablement pas le bon mot pour ce que je veux dire, mais le sous-système semble exagéré et la classe est erronée, car chaque couche peut être composée de plusieurs classes.


la source
Ma réponse vous a-t-elle aidé, besoin de plus d'informations?
Will Marcouiller
S'il vous plaît, votez et acceptez la réponse si cela a aidé ou parlez de vos problèmes de mise en œuvre d'une telle architecture ou quelque chose, mais tenez-nous informés de votre situation afin que nous puissions être d'une meilleure aide. =)
Will Marcouiller

Réponses:

3

Je vais essayer de répondre à cette question car je peux comprendre ce que vous demandez, bien que je n'ai pas beaucoup d'expérience en développement de jeux car je suis encore en train d'apprendre.

Comme je le vois, vous devez séparer le code GUI de votre logique de jeu et des objets de domaine, c'est-à-dire les pièces du puzzle. Ce sont en effet trois couches distinctes - et oui, layerc'est un terme approprié à mon avis. Il est souvent utilisé pour expliquer les concepts de séparation de chaque niveau d'objets d'un système en sous-systèmes indépendants les uns des autres.

En ce qui concerne la programmation orientée objet, chaque objet doit être une classe. Ainsi, chaque pièce de votre puzzle se composera d'une classe en soi, et donc du plateau de jeu. Le plateau de jeu doit contenir X pièces de puzzle en fonction de sa taille et de sa capacité de mouvement que vous souhaitez donner au joueur.

Alors, voici mes réflexions sur le sujet - j'espère que cela vous aidera:

  1. La couche GUI: Cette couche doit contenir des méthodes uniquement pour afficher les pièces sur le plateau de jeu afin de permettre l'interaction entre le jeu lui-même et le joueur;
  2. La couche Game Controller: est responsable des entrées du joueur. C'est la couche qui devrait indiquer à une pièce de se déplacer dans les différentes directions et demander au plateau de jeu s'il y aura des collisions lors du mouvement, etc.
  3. La couche Business: cette couche doit contenir toutes vos classes commerciales, c'est-à-dire les pièces de votre jeu et l'objet plateau de jeu qui contient les pièces du puzzle.

Dans cette architecture, vous auriez la couche GUI montrer au joueur de l'état du jeu, l'emplacement de chaque pièce du puzzle. La couche GUI serait alors chargée d'obtenir les entrées du joueur et de les transmettre à une couche sous-jacente de contrôleur de jeu, qui serait alors responsable de la détection des collisions. S'il n'y en a pas, alors la pièce peut être commandée pour se déplacer dans cette direction d'entrée. Pour ce faire, il suffit d'appeler cette pièce MoveLeft,MoveRight, etc. méthodes pour faire bouger la pièce. Vous pourriez aussi bien faire savoir au plateau de jeu quelle pièce vous voulez déplacer, puis il ordonne par lui-même le mouvement de la pièce, et la pièce se déplace ensuite dans la direction demandée. Cette architecture facilite le test de chaque morceau de code dans les différentes couches, puis vous permet d'effectuer des tests unitaires, des tests d'intégration et des tests fonctionnels.

Je sais que cela peut sembler un peu déroutant pour la vue, et Dieu merci, si ce n'est pas le cas! Si vous avez besoin de plus de détails et d'aide, n'hésitez pas à demander, je serai heureux de faire de mon mieux, même si je suis un débutant dans le développement de jeux.

Merci d'avoir lu! =)

Will Marcouiller
la source
2
Ce qui ressemble beaucoup à Model View Controller.
tenpn
Pour être honnête, toute description de l'interface d'abstraction de l'implémentation sous-jacente ressemblera à Model View Controller. Mais c'est parce que la terminologie et les techniques utilisées sont les mêmes (s'éloigner des détails de l'implémentation et séparer l'interface utilisateur de l'implémentation). Mais cela suppose un environnement très simple. Il y a plusieurs couches ici et il y a des contrôleurs dans chaque couche. Séparer la vue du modèle ne suffit pas ici, vous devez également séparer le contrôle d'interaction utilisateur du contrôle de jeu.
MrCranky
Je me demande ce qui est si complexe ici, puisque nous déplaçons des pièces d'un puzzle sur un plateau de jeu délimité. Les contrôles sont obtenus à partir de l'interface graphique, ce qui est un bon moyen, et passent à une couche d'abstraction, c'est-à-dire le contrôleur de jeu. Le contrôleur de jeu vérifie les collisions lors du mouvement, puis indique à la pièce de se déplacer si aucune collision n'est détectée. La pièce du puzzle se déplace ensuite correctement dans la direction demandée et révèle sa nouvelle position grâce à sa Positionfonction d'obtention de propriété, de sorte que le contrôleur de jeu nécessite maintenant l'interface graphique pour afficher cette pièce déplacée à cette nouvelle position.
Will Marcouiller
heh, j'ai blogué sur "MVC" dans le développement de jeux il y a quelques mois. Oui, c'est un bon concept de passerelle pour ceux qui ne sont pas initiés au développement de jeux: codecube.net/2010/08/xna-for-the-everyday-developer
Joel Martinez
1
Désolé pour l'acceptation tardive, j'ai malheureusement été distrait du projet pendant un moment. J'aime vraiment l'approche du contrôleur, je l'adopterai!