\chapter{Jeu de plateformes coopératif} Les jeux de plateformes sont un des premiers genres à avoir émergé dans le monde du jeu vidéo. Ils consistent à faire progresser un personnage en le faisant aller de plateforme en plateforme, d'où leur nom. Le troisième jeu étudié a un principe original, inspiré des jeux de plateformes, de coopération et de réflexion. Il s'agira pour deux joueurs de faire traverser deux balles aimantées à travers un niveau, certains éléments du décor étant eux aussi aimantés. La conception du jeu s'appuiera sur cet élément physique pour proposer des niveaux sous forme de casse-têtes, dans lesquels les deux participants devront s'entraider et réfléchir pour parvenir à la fin. \section{Principes} Deux joueurs doivent s'entraider pour faire avancer leur balle à travers des niveaux. Les joueurs exploitent pour ce faire les mécanismes physiques définis dans la section \ref{section:ptf-joueurs}. Le jeu est constitué d'une suite de niveaux, chaque niveau étant une grille de blocs en deux dimensions. Ces grilles sont définies en avance par le programmeur. Les blocs interagissent avec les balles comme défini dans les sections \ref{section:ptf-joueurs} et \ref{section:ptf-blocs}. Les joueurs valident un niveau en faisant parvenir toutes les balles dans une zone d'arrivée prédéfinie. Ils passent ainsi au niveau suivant. Le but du jeu est de terminer tous les niveaux. \subsection{Joueurs} \label{section:ptf-joueurs} Les deux joueurs contrôlent chacun une balle. Ces balles ont pour propriétés leur position dans le plan, leur vitesse et leur charge électrique. Les balles évoluent par interactions avec le joueur et par interactions physiques. Un joueur peut interagir avec sa balle de trois manières~: il peut lui donner de la vitesse vers la gauche de la fenêtre, la droite de la fenêtre, ou inverser la polarité de la balle. Le joueur ne peut pas faire « sauter » sa balle. L'évolution des balles, en dehors du contrôle des joueurs, est conditionnée par les phénomènes physiques suivants~: \begin{itemize} \item une force de gravité qui agit en tout point et attire les balles vers le bas, le haut, la gauche ou la droite de la fenêtre. L'orientation de cette force peut être modifiée par les actions d'une balle en cours de jeu~; \item une force de réaction qui agit de telle sorte que les balles ne puissent pas traverser les blocs~; \item des forces de frottements lorsque la balle se situe au contact d'un bloc. L'intensité de cette force varie en fonction des types de blocs~; \item une force d'interaction coulombienne entre les éléments du jeu qui sont polarisés (c'est-à-dire les balles et certains blocs tels que définis en section \ref{section:ptf-blocs}). \end{itemize} \subsection{Caméra} À tout moment les deux balles peuvent se situer n'importe où dans le niveau. Il faut toutefois faire en sorte qu'elles soient toutes deux visibles à tout moment. Pour ce faire, la caméra est centrée sur la position moyenne des deux balles si celles-ci sont suffisament proches. Sinon, l'écran est divisé en deux et chaque partie est centrée sur chacune des balles. \subsection{Blocs} \label{section:ptf-blocs} Le niveau est une grille de blocs. Les blocs possèdent une position sur la grille déterminée par une paire d'entiers et une charge électrique qui peut être annulée pour que le bloc n'attire aucun objet. Certains blocs peuvent modifier le sens de la gravité lorsqu'une balle rentre en collision avec eux. Enfin, les blocs sont statiques et ne sont pas soumis à la physique du jeu. \section{Modélisation} Les balles sont modélisées par une classe \texttt{Ball}. Cette classe est dotée des propriétés \texttt{position}, \texttt{velocity}, \texttt{mass} et \texttt{charge}. Le vecteur \texttt{position} représente la position de la balle dans le plan. Le vecteur \texttt{velocity} représente la vitesse de la balle. Le flottant \texttt{mass} représente la masse de la balle, qui sera utilisée dans le calcul de l'accélération. Enfin, le flottant \texttt{charge} représente sa charge électrique. Les blocs sont modélisés par une classe \texttt{Block} munie des propriétés \texttt{position} et \texttt{charge} qui représentent la position du bloc sur la grille de jeu et sa charge. La charge du bloc est nulle s'il n'est pas polarisé. La classe sera étendue pour représenter des types de blocs spécialisés. Enfin, une classe principale \texttt{Engine} est chargée de coordonner les éléments du jeu et d'organiser le dessin des \emph{frames}. Elle est dotée d'un tableau à deux dimensions d'instances de \texttt{Block} qui représente la grille de jeu, d'un tableau à une dimension d'instances de \texttt{Ball} qui stocke toutes les balles dans le niveau, et d'une horloge qui mesure le temps écoulé entre chaque \emph{frame}. La figure \ref{fig:ptf-uml-diagram} présente les classes utilisées pour la modélisation. Les méthodes de ces classes sont détaillées dans la section suivante. \begin{figure}[h!] \centering \input{./figures/ptf-uml-diagram} \caption{Diagramme des classes utilisées} \label{fig:ptf-uml-diagram} \end{figure} \section{Algorithmes} \label{section:ptf-algos} \subsection{Physique} Les balles sont équipées de propriétés représentant leur position, leur vitesse, leur masse et leur charge comme vu dans la section précédente. On utilise l'intégration explicite d'Euler pour calculer la nouvelle position de chaque balle à chaque \emph{frame}. \cite{ptf-euler} Cette méthode a l'avantage d'être simple à implémenter et rapide. \begin{description} \item[\texttt{Engine::update(delta)}] L'algorithme reçoit le temps écoulé depuis la dernière \emph{frame}. Il calcule les forces à appliquer à chaque balle, puis appelle la procédure \texttt{Ball::update(forces, delta)} en conséquence. \begin{enumerate} \item Pour chaque \texttt{ball} dans \texttt{balls}. \item Initialiser un vecteur \texttt{forces} au vecteur nul. \item Ajouter le vecteur $(0, g)$, $(0, -g)$, $(g, 0)$, $(-g, 0)$ selon la direction de la gravité au vecteur \texttt{forces}. \item Si la touche pour faire aller la balle \texttt{ball} à gauche est enfoncée, ajouter le vecteur $(-m, 0)$ au vecteur \texttt{forces}. \item Si la touche pour faire aller la balle \texttt{ball} à droite est enfoncée, ajouter le vecteur $(m, 0)$ au vecteur \texttt{forces}. \item Pour chaque autre élément polarisé, appliquer une force d'attraction portée par la droite passant par les deux éléments et de norme $c \times \frac{\mathtt{charge}_1 \times \mathtt{charge}_2}{\mathtt{distance}^2}$. \item Gérer les collisions et les frottements. \item Appeler \texttt{Ball::update(forces, delta)} sur \texttt{ball}. \end{enumerate} \item[\texttt{Ball::update(forces, delta)}] L'algorithme reçoit le vecteur somme de toutes les forces appliquées à la balle et un flottant qui contient le temps écoulé depuis la dernière \emph{frame}. Il calcule la position suivante de la balle. \begin{enumerate} \item $\mathtt{acceleration} := \frac{\mathtt{forces}}{\mathtt{mass}}$. \item Ajouter $\mathtt{acceleration} \times \mathtt{delta}$ à \texttt{velocity}. \item Ajouter $\mathtt{velocity} \times \mathtt{delta}$ à \texttt{position}. \end{enumerate} \end{description} Les constantes $g$, $m$ et $c$ devront être définies et ajustées au cours de la conception du jeu pour que la simulation paraisse naturelle. \subsection{Dessin} La scène du jeu est composée de trois couches, l'une pour l'arrière-plan du jeu, une autre pour la grille de blocs, et la dernière pour les balles. Ces couches sont représentées sur les figures \ref{fig:ptf-layering-exploded} et \ref{fig:ptf-layering-merged}. Chaque objet susceptible d'être dessiné à l'écran possède une méthode \texttt{draw()}. \begin{description} \item[\texttt{Ball::draw()}] Dessine la balle à sa position sur l'écran. \item[\texttt{Block::draw()}] Dessine le bloc à sa position sur l'écran. \item[\texttt{Engine::draw()}] Cet algorithme du moteur appelle les différentes fonctions de dessin dans un ordre spécifique pour que les trois couches de la scène du jeu soient correctement affichées. \begin{enumerate} \item Dessiner l'arrière-plan du jeu. \item Dessiner la grille de blocs en appelant \texttt{Block::draw()} sur les blocs de \texttt{blocks} qui sont visibles à l'écran. \item Dessiner le premier-plan en appelant \texttt{Ball::draw()} sur les balles de \texttt{balls} qui sont visibles à l'écran. \end{enumerate} \end{description} \begin{figure}[p!] \centering \input{./figures/ptf-layering-exploded} \caption{Vue explosée des trois couches de rendu du jeu} \label{fig:ptf-layering-exploded} \end{figure} \begin{figure}[p!] \centering \input{./figures/ptf-layering-merged} \caption{Une configuration similaire à celle de la figure \ref{fig:ptf-layering-exploded}, vue de face} \label{fig:ptf-layering-merged} \end{figure} \subsection{Moteur} \begin{description} \item[Initialisation du moteur \texttt{Engine}]\hfill \begin{enumerate} \item Créer et configurer la fenêtre d'affichage du jeu. \item Initialiser \texttt{clock} à zéro. \item Tant que la fenêtre est ouverte~: \begin{enumerate} \item traiter tous les événements relatifs à la fenêtre (notamment l'appui sur une touche, la fermeture, le redimensionnement)~; \item calculer le temps écoulé \texttt{delta} depuis la dernière \emph{frame} et réinitialiser \texttt{clock} à zéro~; \item appeler l'algorithme \texttt{Engine::update(delta)}~; \item appeler l'algorithme \texttt{Engine::draw()}. \end{enumerate} \end{enumerate} \end{description} \section{Spécifications} \subsection{Version initiale} On choisira le langage C++, qui prend en charge le paradigme objet et dont la syntaxe est enseignée dans le cursus. On utilisera la librairie SFML pour son A.P.I. simple et puissante. Les deux joueurs partageront le clavier d'une même machine. On utilisera le type \texttt{sf::Vector2d} pour ce qui est vectoriel (position, vitesse, accélération, forces). Les tableaux utiliseront le type standard \texttt{std::vector}. Le type \texttt{sf::Clock} sera utilisé pour l'horloge du moteur. On choisira entre des flottants simple précision ou double précision en fonction des besoins. Des recherches supplémentaires sont nécessaires notamment concernant les algorithmes de détection et de réaction aux collisions. \cite{ptf-collision-detection, ptf-collision-response} \subsection{Améliorations possibles} L'algorithme physique utilise la méthode explicite d'Euler. Cette méthode engendre une erreur de simulation d'autant plus grande que l'espacement entre les \emph{frames} est élevé, ce qui signifie que la physique du jeu se comportera différemment selon les performances de la machine. On pourra opter pour des méthodes plus précises comme l'intégration de Verlet \cite{ptf-verlet} ou la méthode de Runge-Kutta classique. \cite{ptf-rk4} On pourra faire en sorte que le jeu s'exécute en réseau entre deux machines, une pour chaque joueur. Cela requièrera une synchronisation de la simulation physique et la mise au point d'un protocole de communication entre les deux machines. \section{Organisation} Dans un premier temps le moteur physique et graphique devra être conçu sur la base des algorithmes fournis dans le rapport (hormis l'algorithme de collision, qui devra faire l'objet de plus de recherches). Durant la mise au point de ce moteur, un niveau de test sera créé permettant l'appréciation et l'ajustement des variables physiques. On y consacrera 40 heures au total. Ce niveau de test permettra également l'essai de différents mécanismes de \emph{gameplay.} Ces éléments seront en parallèle intégrés dans les niveaux finaux. La conception et le test des niveaux se fera sur 80 heures. Dans le même temps, l'univers graphique, notamment les textures, la décoration de l'interface, ainsi que la musique et les bruitages du jeu seront réalisés. Après la réalisation des éléments graphiques, on pourra concevoir l'interface du jeu. On y consacrera 50 heures. Enfin, les tests finaux du jeu s'effecturont sur 5 heures. On pourra éventuellement demander l'aide de personnes extérieures pour ces essais. La rédaction du rapport s'effectuera en continu pendant la création du jeu. La figure~\ref{fig:ptf-gantt} présente un diagramme de Gantt résumant la répartition du travail. \begin{figure}[h!] \centering \input{./figures/ptf-gantt} \caption{Développement du jeu sur 40 heures} \label{fig:ptf-gantt} \end{figure}