This commit is contained in:
Mattéo Delabre 2016-04-19 12:34:47 +02:00
commit bc459fb901
31 changed files with 1132 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,128 @@
\begin{tikzpicture}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% ÉTATS DE JEU PRINCIPAUX %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spécifications de l'état de jeu abstrait "niveau"
% qui est une collection d'objets
\umlabstract{Level}{
name : string\\
background : sprite\\
music : string\\
camera : vue\\
gravity\_direction : direction\\
total\_time : entier\\
players : \texttt{[Player]}\\
objects : \texttt{[Object]}\\
zone : \texttt{[vecteur]}
}{
enable() : vide\\
processEvent(event) : vide\\
load(file : string) : vide\\
save(file : string) : vide\\
\umlvirt{frame() : vide}\\
draw() : vide\\
addObject(object : \texttt{Object})\\
removeObject(object : \texttt{Object})
}
% Spécifications de l'état de jeu "éditeur" qui permet
% de modifier des niveaux de jeu
\umlclass[x=-6]{Editor}{
selection : \texttt{[Object]}
}{
enable() : vide\\
processEvent(event) : vide\\
frame() : vide\\
select(objet: \texttt{Object}) : vide\\
selectAll() : vide\\
clearSelection() : vide\\
}
\umlinherit{Level}{Editor}
% Spécifications de l'état de jeu "jeu" qui permet
% de jouer aux niveaux créés
\umlclass[x=6]{Game}{
mode : mode\\
next\_frame\_time : temps\\
pending\_kill : \texttt{[Object]}\\
time\_left : flottant
}{
enable() : vide\\
processEvent(event) : vide\\
frame() : vide\\
update() : vide\\
kill(objet : \texttt{Object}) : vide
}
\umlinherit{Level}{Game}
% Spécifications des objets du jeu
\umlabstract[y=-6.5]{Object}{
acceleration : vecteur\\
velocity : vecteur\\
position : vecteur\\
}{
\umlvirt{getForces(game : \texttt{Game}) : vecteur}\\
\umlvirt{activate(game : \texttt{Game}, object : \texttt{Object}) : vecteur}\\
\umlvirt{getAABB() : boîte}\\
\umlvirt{getRadius() : flottant}\\
\umlvirt{draw(level : \texttt{Level}) : vide}
}
\umlunicompo{Level}{Object}
% Spécifications de l'objet "joueur"
\umlclass[x=-4,y=-11]{Player}{
player\_number : entier non-signé
}{
getForces(game : \texttt{Game}) : vecteur\\
draw(level : \texttt{Level}) : vide\\
activate(game : \texttt{Game}, object : \texttt{Object}) : vecteur\\
getAABB() : boîte\\
getRadius() : flottant\\
}
\umlinherit{Object}{Player}
% Spécifications de l'objet "bloc"
\umlclass[x=4,y=-11]{Block}{}{
draw(level : \texttt{Level}) : vide\\
prepareDraw(resources : \texttt{ResourceManager}) : vide\\
activate(game : \texttt{Game}, object : \texttt{Object}) : vecteur\\
getAABB() : boîte\\
getRadius() : flottant\\
}
\umlinherit{Object}{Block}
% Spécifications de l'objet "bloc de fin" permettant
% quand il est activé de terminer le niveau
\umlclass[x=-2,y=-14]{FinishBlock}{}{
prepareDraw(resources : \texttt{ResourceManager}) : vide\\
activate(game : \texttt{Game}, object : \texttt{Object}) : vecteur
}
\umlinherit{Block}{FinishBlock}
% Spécifications de l'objet "bloc de gravité" permettant
% quand il est activé de réorienter la gravité du niveau
\umlclass[x=6,y=-14]{GravityBlock}{
gravity\_direction : direction
}{
prepareDraw(resources : \texttt{ResourceManager}) : vide\\
activate(game : \texttt{Game}, object : \texttt{Object}) : vecteur
}
\umlinherit{Block}{GravityBlock}
% Spécifications de l'objet "bloc tueur" permettant
% quand il est activé par un joueur de le tuer
\umlclass[x=-2,y=-16.5]{KillBlock}{}{
prepareDraw(resources : \texttt{ResourceManager}) : vide\\
activate(game : \texttt{Game}, object : \texttt{Object}) : vecteur
}
\umlinherit{Block}{KillBlock}
% Spécifications de l'objet "bloc d'échange" permettant
% quand il est activé par un joueur d'échanger sa charge
\umlclass[x=6,y=-16.5]{SwitchBlock}{}{
prepareDraw(resources : \texttt{ResourceManager}) : vide\\
activate(game : \texttt{Game}, object : \texttt{Object}) : vecteur
}
\umlinherit{Block}{SwitchBlock}
\end{tikzpicture}

View File

@ -0,0 +1,88 @@
\begin{tikzpicture}
%%%%%%%%%%%%%%%%%%%%%%%
%% CLASSES GÉNÉRALES %%
%%%%%%%%%%%%%%%%%%%%%%%
% Spécifications du gestionnaire de jeu
\umlclass{Manager}{
window : fenêtre\\
resource\_manager : \texttt{ResourceManager}\\
clock : horloge\\
states : pile de \texttt{State}
}{
start() : vide\\
pushState(state : \texttt{State}) : vide\\
popState() : vide
}
% Spécifications du gestionnaire de ressources
\umlclass[x=4,y=-4.5]{ResourceManager}{
textures : dictionnaire \texttt{string -> texture}\\
fonts : dictionnaire \texttt{string -> police}
music : musique
}{
getTexture(name : string) : texture\\
getFont(name : string) : texture\\
getLevelPath(name : string) : string\\
playMusic(name : string) : vide\\
stopMusic() : vide
}
\umluniassoc{Manager}{ResourceManager}
% Spécfications d'un état de jeu
\umlabstract[x=-4,y=-4.5]{State}{
manager : \texttt{Manager}
}{
\umlvirt{enable() : vide}\\
\umlvirt{processEvent(event : événement) : vide}\\
\umlvirt{frame() : vide}
}
\umlunicompo{Manager}{State}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% ÉTATS DE JEU PRINCIPAUX %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spécifications de l'état de jeu abstrait "niveau"
% qui est une collection d'objets
\umlabstract[x=7,y=-11]{Level}{}{
\emph{Voir la figure suivante}
}
\umlinherit{State}{Level}
%%%%%%%%%%%%%%%%%%%%%%%%%%
%% ÉTATS DE JEU DU MENU %%
%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spécifications de l'état de jeu "menu"
% qui est le premier état au démarrage et permet
% d'afficher les différents choix de jeu
\umlclass[y=-11]{Menu}{
background : sprite\\
choices : \texttt{[string]}\\
actions : \texttt{[callback]}\\
selection : entier non-signé
}{
loadMainMenu() : vide\\
loadLevelMenu() : vide\\
loadEditorMenu() : vide\\
launchGame(path : string) : vide\\
launchEditor(path : string) : vide\\
launchRules() : vide\\
quit() : vide\\
enable() : vide\\
processEvent(event : événement) : vide\\
frame() : vide
}
\umlinherit{State}{Menu}
% Spécifications de l'état de jeu "règles" qui
% affiche l'image décrivant les règles
\umlclass[x=-7,y=-11]{Rules}{
background : sprite\\
}{
processEvent(event : événement) : vide\\
frame() : vide
}
\umlinherit{State}{Rules}
\end{tikzpicture}

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,72 @@
\begin{ganttchart}[
x unit=.4cm,
y unit title=0.5cm,
title height=1,
y unit chart=.6cm,
vgrid=true
]{0}{32}
% raccourci pour changer le style des barres
\newcommand{\ganttbstyle}[1]{\ganttset{bar/.append style={fill=#1}}}
\gantttitle{Développement du projet}{20}
\gantttitle{Rapport}{4}
\gantttitle{Préparation soutenance}{9}\\
\gantttitle{04/03}{4}
\gantttitle{11/03}{4}
\gantttitle{18/03}{4}
\gantttitle{25/03}{4}
\gantttitle{01/04}{4}
\gantttitle{09/04}{4}
\gantttitle{16/04}{4}
\gantttitle{23/04}{5}\\
\ganttgroup{Moteur physique}{0}{14}\\
\ganttbstyle{matred}
\ganttbar{Organisation des classes}{0}{6}\\
\ganttbstyle{matgreen}
\ganttbar{Gestion de l'affichage}{0}{8}\\
\ganttbstyle{matred}
\ganttbar{Implémentation des forces}{0}{8}\\
\ganttbar{Gestion des collisions}{4}{14}\\
\ganttbstyle{matblue}
\ganttbar{Tests du moteur}{4}{14}\\
\ganttgroup{Niveaux du jeu}{6}{32}\\
\ganttbstyle{matblue}
\ganttbar{Définition du format de fichier}{6}{8}\\
\ganttbstyle{matred}
\ganttbar{Lecture et écriture du format}{12}{16}\\
\ganttbar{Création de l'éditeur}{16}{21}\\
\ganttbar{Blocs spécialisés}{18}{21}\\
\ganttbstyle{matblue}
\ganttbar{Niveau de test}{19}{21}\\
\ganttbstyle{matgray}
\ganttbar{Conception des niveaux}{19}{32}\\
\ganttbar{Tests des niveaux}{19}{32}\\
\ganttgroup{Interface du jeu}{10}{32}\\
\ganttbstyle{matred}
\ganttbar{Découpage en états de jeu}{10}{14}\\
\ganttbstyle{matgreen}
\ganttbar{Création du menu}{12}{18}\\
\ganttbstyle{matred}
\ganttbar{Interface de l'éditeur}{16}{21}\\
\ganttbar{Menus gagné, perdu, pause}{24}{32}\\
\ganttgroup{Univers graphique}{8}{28}\\
\ganttbstyle{matgreen}
\ganttbar{Création des musiques}{8}{16}\\
\ganttbstyle{matgray}
\ganttbar{Fond du menu}{12}{20}\\
\ganttbar{Décor}{14}{21}\\
\ganttbstyle{matblue}
\ganttbar{Textures blocs}{18}{21}\\
\ganttbstyle{matred}
\ganttbar{Implémentation décor}{24}{28}\\
\ganttgroup{Gestion de projet}{21}{32}\\
\ganttbstyle{matgray}
\ganttbar{Rédaction du rapport}{21}{25}\\
\ganttbar{Tests pré-présentation}{25}{32}\\
\ganttmilestone{Présentation du jeu}{32}
\end{ganttchart}

View File

@ -0,0 +1,160 @@
\chapter{Analyse du projet}
\section{Découpage du projet}
Le jeu est constitué d'une suite de niveaux organisés de manière
semblable à ceux d'un jeu de plateformes. Ces niveaux contiennent des entités.
Les entités du jeu sont multiples : blocs, blocs spéciaux, joueurs,
éléments de décor. Ces entités -- ou objets, interagissent entre elles
par un certain nombre de phénomènes physiques « naturels ». Pour répondre
à ce besoin, \textbf{un moteur physique} est nécessaire. Le moteur physique est
chargé de gérer les forces s'appliquant aux objets du jeu, de répondre
aux collisions entre objets et de faire évoluer les objets en conséquence
des forces qui leur sont appliquées.
Plusieurs moteurs physiques en 2D existent déjà dans le langage que nous
avons choisi, notamment Box2D. \cite{analyse-box2d}
Nous avons choisi d'implémenter le moteur
physique du jeu par nous-mêmes pour répondre aux besoins particuliers
(notamment la force d'attraction) et car cela nous permet de mettre en
pratique les savoirs acquis au premier semestre dans le module
de physique générale.
Les \textbf{niveaux du jeu} sont constitués de ces entités et d'autres
métadonnées. Pour pouvoir éditer les niveaux, les sauvegarder et
y rejouer plus tard, il est nécessaire de pouvoir les stocker
en dehors de la mémoire. Nous avons pour ce faire choisi de définir
un format de fichier binaire permettant leur stockage sur le disque.
Des fonctions pour coder et décoder ce format devront être écrites.
Skizzle propose différents \textbf{états de jeu}, notamment, on peut à tout moment
se trouver dans l'éditeur, dans le jeu en lui-même ou sur la vue des règles.
Pour pouvoir accéder à ces états, nous devons créer un menu. L'ensemble
des états du jeu doit être abstrait pour pouvoir être géré dans la classe
principale. Certains états du jeu proposeront des éléments interactifs
(boutons, barres d'outils, zones de texte) qui doivent être implémentés.
Enfin, les différents \textbf{objets du jeu} sont représentés à l'écran en
dessinant des textures. Nous avons également choisi d'ajouter des musiques
au jeu pour le rendre plus convivial. D'autres éléments graphiques doivent
être créés, par exemple le fond du menu. Tous ces éléments sont regroupés
dans l'univers graphique du jeu.
\section{Découpage du code}
Nous avons choisi d'organiser notre code selon le paradigme objet. La plupart
du code est sorti en dehors du \texttt{main}, dont la seule fonction est d'instancier
la classe \texttt{Manager} qui gère de manière abstraite le jeu et de démarrer
le premier état du jeu~: le menu.
\subsection{États, gestion des états et des ressources}
Un état du jeu modélise un écran pouvant être affiché. Une classe
abstraite \texttt{State} chapeaute toutes les classes d'états et permet
de requérir l'implémentation d'une interface commune~:
\begin{itemize}
\item \texttt{enable()}~: cette méthode initialise l'état avant qu'il
commence à être affiché. L'état implémentant cette méthode doit
mettre en place les ressources globales utilisées comme la lecture
de la musique, le titre de la fenêtre, les éléments de l'interface
quand cette méthode est appelée~;
\item \texttt{processEvent(event)}~: cette méthode est appelée avec
un événement lorsque celui-ci est extrait par la SFML lors de la boucle
principale. L'état est censé décider s'il souhaite traiter cet événement
et, si oui, modifier ses variables en conséquence~;
\item \texttt{frame()}~: cette méthode est appelée lorsque l'état
doit dessiner une frame à l'écran. Pour éviter d'encombrer la boucle
principale, l'état doit dessiner sa frame le plus rapidement possible.
\end{itemize}
Les états suivants sont implémentés et descendent de la classe \texttt{State}~:
\texttt{Rules} pour afficher les règles du jeu, \texttt{Menu} pour afficher
le menu du jeu, \texttt{Level} pour afficher les niveaux (soit l'éditeur,
soit le jeu en lui-même).
On définit \texttt{Manager} la classe qui gère les éléments principaux du jeu.
Notamment, \texttt{Manager} maintient une pile d'états qui est initialisée
contenant une seule instance de la classe \texttt{Menu} et peut être
empilée ou dépilée par les états. Par exemple, le menu peut empiler
un nouvel état instance de \texttt{Rules} pour « démarrer » la vue affichant
les règles. En tout temps, l'état en haut de la pile est celui qui est actif
(il reçoit les événements et est dessiné).
La librairie SFML permet de charger les ressources comme la musique,
les images et les polices. Cependant, recharger ces ressources à chaque
utilisation serait inefficace. La classe \texttt{ResourceManager} permet
de mutualiser ces ressources~: les états lui demandent les ressources
à obtenir et le gestionnaire de ressources s'arrange pour ne charger
la ressource qu'à la première demande et à la garder en mémoire par la suite.
Le gestionnaire des ressources mutualise l'accès aux polices, textures
et à la lecture de la musique.
La figure \ref{fig:analyse-uml-state} résume les classes de gestion
d'états et de ressources présentées.
\newgeometry{left=1cm,top=2cm,bottom=2cm,right=1cm}
\thispagestyle{empty}
\begin{figure}[p!]
\centering
\input{figures/analyse-uml-state.tex}
\caption{Gestion des états et des ressources dans le jeu}
\label{fig:analyse-uml-state}
\end{figure}
\restoregeometry
\subsection{Niveau et objets}
La classe \texttt{Level} définit les niveaux, qui sont des collections
d'objets. Elle définit la méthode pour dessiner tous les objets d'un niveau,
le charger, le sauvegarder dans un fichier, ajouter ou supprimer des objets.
Elle ne définit pas la méthode \texttt{frame()} que tous les états doivent
implémenter, elle n'est donc pas un état en tant que tel.
Deux classes dérivent de \texttt{Level} : \texttt{Game} pour jouer aux
niveaux et \texttt{Editor} pour les éditer. L'abstraction en
\texttt{Level} permet d'éviter la duplication de code notamment en
ce qui concerne la gestion des objets contenus.
Les classes de niveaux manipulent des collections d'objets. Les objets
modélisent toutes les entités du jeu : les joueurs, les blocs et les
blocs spéciaux. Une classe abstrait les fonctionnalités de tous les objets,
\texttt{Object}.
Les classes \texttt{Block}, définissant l'apparence et le comportement
des blocs, et \texttt{Player}, définissant l'apparence et le comportement
des joueurs, descendent directement d'\texttt{Object}. Enfin, on définit
des blocs spéciaux, qui peuvent réaliser des actions particulières~:
\begin{itemize}
\item le bloc de gravité modifie la direction de la gravité
dans un niveau lorsqu'une entité entre en contact avec lui.
Il ne peut être activé qu'une seule fois par partie~;
\item le bloc changeur échange la polarité de l'entité
entrant en contact avec lui. Il ne peut être activé qu'une seule
fois par partie~;
\item le bloc tueur tue le joueur entrant en contact avec lui
et fait perdre la partie (le niveau passe en mode « perdu »)~;
\item le bloc d'arrivée tue le joueur entrant en contact et lorsqu'il
ne reste plus de joueurs fait gagner la partie (le niveau passe
en mode « gagné »).
\end{itemize}
La figure \ref{fig:analyse-uml-level} résume les classes de niveaux
et d'objets.
\newgeometry{left=1cm,top=2cm,bottom=2cm,right=1cm}
\thispagestyle{empty}
\begin{figure}[p!]
\centering
\input{figures/analyse-uml-level.tex}
\caption{Classes du niveau}
\label{fig:analyse-uml-level}
\end{figure}
\restoregeometry

View File

@ -0,0 +1,61 @@
\chapter{Conclusion}
\section{Perspectives}
Par manque de temps, de nombreuses perspectives d'amélioration n'ont pas pu
aboutir. Nous aurions pu améliorer l'interface graphique (ce qui est prévu
avant l'oral), faire plus de niveaux et donc ajouter des musiques pour ces
nouveaux niveaux, ou encore ajouter les menus pause, victoire et défaite.
\section{Conclusions}
\subsection{Fonctionnement de l'application}
Nous avons remarqué qu'un message d'erreur « l'application ne répond pas »
s'affiche dans certaines circonstances que nous n'avons pas réussi à isoler,
bien que le programme réponde toujours.
Lors de la rotation de la caméra à la suite d'un changement dans la direction
de la gravité, la caméra ne réalise pas toujours le mouvement le plus rapide
et peut effectuer une rotation à 270 degrés au lieu de 90 degrés dans certaines
circonstances.
Dans certains cas les blocs déplaçables, si placés en trop grandes quantités,
se traversent. De plus, l'algorithme de collisions étant discret, un objet
ayant une vitesse trop élevée peut passer à travers un autre.
Enfin, la force d'attraction ne réagit pas tout à fait comme nous l'avions
imaginée initialement : elle est moins souple. Cela est probablement lié
à un mauvais ajustement dans les constantes du moteur physique. Cette
force fonctionne malgré tout.
Cependant, les menus ainsi que les retours aux menus fonctionnent bien,
l'affichage est fluide, l'éditeur est fonctionnel et comporte de nombreuses
fonctionnalités intuitives. Les ressources ne posent pas de problème,
les textures se chargent et les musiques se déclenchent correctement
et bouclent comme prévu.
Globalement, le jeu est fonctionnel et ne pose problème que dans des
cas très particuliers, il reste tout de même stable malgré le temps
limité que nous avions pour régler les problèmes.
\subsection{Fonctionnement du groupe de travail}
Le principal problème du groupe fut le sens des priorités. Nous nous sommes
attardés sur des détails avant que le tout soit fonctionnel, ce qui a
perturbé notre gestion du temps. Malgré cela, nous avons réussi à nous
organiser pour gagner en efficacité en partageant les tâches les plus
chronophages.
Le fait de refaire fréquemment le diagramme de Gantt nous a permis de
rattraper en partie le retard que nous avions pris. Au niveau de la
communication, nous n'avons eu aucun problème car nous nous
retrouvions très souvent sur Skype ainsi qu'à l'université afin
de discuter de tout problème et optimiser nos chances de trouver
rapidement une solution.
Malgré une gestion du temps perturbée, notre organisation et notre
communication nous ont permis de compenser les défauts du groupe,
et ainsi d'avancer à un rythme convenable.
Ainsi, grâce à notre détermination et notre coopération, nous avons
su franchir les difficultés et ainsi proposer un jeu fonctionnel et amusant.

View File

@ -0,0 +1,89 @@
\chapter{Développement}
\section{Moteur physique}
Cette partie du projet est basée en partie sur les algorithmes du pré-rapport.
L'affichage est géré par des fonctions draw() permettant de boucler
sur les différents objets afin de mettre à jour le dessin affiché en
fonction de leur position. Il fallut également gérer la caméra avec
l'objet view de la SFML, et l'adapter au redimensionnement de la
fenêtre. Enfin, pour un meilleur rendu graphique, nous avons chargé
et appliqué des textures aux objets (textures provisoires lors de
cette étape).
Lors de l'implémentation des forces et des collisions, nous avons
rencontré quelques difficultés, notamment au niveau des collisions.
En effet, grâce aux tests du moteur dans différentes situations,
nous avons pu repérer des erreurs de collisions. Cela mena à la
création d'une nouvelle classe Object réunissant les balles et les
blocs dans un même tableau et définissant les propriétés communes
des objets. L'implémentation des collisions est basée sur un
solveur de collisions discret. Nous avons finalement passé plus de
temps que prévu sur cette partie du projet.
\cite{develop-collision-solver,develop-collision-stability,develop-game-loop}
\section{Niveaux du jeu}
Tout d'abord, il fallut définir le format des fichiers de niveaux. Pour cela, nous avons au préalable fait des recherches sur les normes de base, puis nous avons listé tout ce qu'il fallait sauvegarder dans le niveau. Il fallut ensuite lire et écrire le format. Afin de pouvoir créer des niveaux
plus facilement, mais également permettre aux utilisateurs de jouer
à leurs propres niveaux, nous avons choisi de créer un éditeur de
niveaux en mode graphique. Cela nous prit plus de temps que prévu,
c'est pourquoi il nous restait peu de temps pour la création de
niveaux. Nous avons donc réfléchi à des niveaux courts mais
demandant de la réflexion afin de les rendre intéressants
malgré le manque de temps.
\section{Interface du jeu}
Afin de mieux gérer l'affichage entre les menus, l'éditeur et les
niveaux, nous avons décidé de découper le jeu en états. Nous avons
créé une classe abstraite gérant tous ces états, ainsi qu'une
classe par état : Menu pour le menu principal, Rules pour les
règles du jeu, Editor pour l'éditeur de niveaux et Game pour
le jeu, ainsi qu'une classe abstraite Level pour gérer les niveaux.
Le menu principal se base ainsi sur ce découpage. Chaque option
renvoie à un état différent ou modifie l'état du menu si cela
envoie sur un autre menu (choix de niveaux).
L'éditeur est la partie la plus interactive du jeu. En sus des
nombreux raccourcis clavier et souris décrits dans le manuel
d'utilisation, une boîte à outils est présente sur le côté
et permet de sélectionner le type d'objet à placer dans le niveau.
Cependant, il reste certaines lacunes au niveau de l'interface,
notamment au niveau des composants interfaçant avec l'utilisateur.
Par exemple, faute de temps, nous n'avons pas pu implémenter de
moyen de modifier le nom du niveau modifié depuis l'éditeur,
de modifier la texture de fond ou la musique jouée. Ces modifications
doivent être faites « à la main » dans le fichier du niveau.
Par ailleurs, lorsque l'on gagne, perd ou met en pause le niveau,
cet état est bien sauvegardé mais aucun élément d'interface n'a
pu être implémenté à temps pour permettre à l'utilisateur par
exemple de passer au niveau suivant ou de recommencer. Pour le moment,
l'état est affiché en texte dans la console pour s'assurer
que le changement d'état est bien fonctionnel.
Nous espérons pouvoir ajouter ces fonctionnalités en nous basant
sur la librairie SFGUI d'ici à la soutenance, car elles nous
paraissent importantes. \cite{develop-sfgui}
\section{Univers graphique}
Nous avons décidé d'ajouter des musiques au jeu afin de le rendre
plus vivant. Pour cela, nous avons fait appel à un étudiant de
l'IUT de Montpellier, Maxime \textsc{Petitjean}, pour aider Maëlle
à créer des musiques pour chaque niveau ainsi que pour le menu
à l'aide du logiciel \emph{Ableton.}
Nous avons également décidé de créer nos propres textures pour un
rendu du jeu plus personnel. Après les dessins préalablement
réalisés par Maëlle sur papier, Rémi s'est chargé de les numériser
en repassant les contours pour créer une image vectorielle sur
Inkscape. Puis Maëlle les a colorées avec le même logiciel,
pendant que Rémi se chargeait des textures des blocs que nous
avions au préalable définies à l'oral en groupe.
Nous n'avons pas eu le temps d'implémenter le décor avant le rendu
du code, mais il est prévu que cela soit fait avant le passage à l'oral.

View File

@ -0,0 +1,84 @@
\chapter{Introduction}
Dans le cadre du module Projet C.M.I. du second semestre de L1,
nous avons développé en équipe un jeu vidéo nommé « Skizzle ».
Notre groupe est composé de trois personnes~: Maëlle \textsc{Beuret},
Rémi \textsc{Cérès} et Mattéo \textsc{Delabre}.
L'objectif de ce projet est la création d'un jeu vidéo fonctionnel.
Le jeu mobilise les bases d'algorithmique apprises au premier semestre ainsi
que nos connaissances et recherches personnelles. La création de ce jeu nous
a permis de renforcer nos capacités de travail en collaboration, en
communication et en gestion de projet en général.
Le développement du projet s'est déroulé sur une période d'un mois
et une semaine~: du vendredi 4 mars 2016 au lundi 11 avril 2016 inclus.
Chaque vendredi, lors de la séance de trois heures consacrée au projet,
les trois membres de l'équipe se réunissent pour résumer le travail effectué
la semaine passée et planifier celui de la semaine à venir.
\section{Choix du jeu}
Nous avons d'abord réalisé une étude comparative entre trois jeux
vidéos possibles. Pour ces jeux vidéos, le choix était libre. Notre choix
s'est porté sur un jeu avec un principe original inspiré des jeux de
plates-formes, de coopération et de réflexion. Nous l'avons appelé
« Skizzle », à mi-chemin entre l'anglais \emph{skill} et \emph{puzzle.}
Le jeu se joue à deux joueurs. Chaque joueur est affecté à une balle qu'il
contrôle par le clavier. Le but est pour ces joueurs de faire traverser leur
balle à travers des niveaux prédéfinis. Pour ce faire, les joueurs doivent
utiliser différents phénomènes physiques et constructions mises en place
à la fois dans le niveau et dans le moteur physique du jeu.
Les niveaux se présentent sous la forme de casse-têtes courts à difficulté
progressive. Les deux joueurs doivent souvent réfléchir et s'entraider
pour pouvoir parvenir à la fin. Ce n'est que lorsque les deux joueurs
franchissent la ligne d'arrivée que la partie est gagnée.
\section{Cahier des charges}
Le jeu doit fonctionner sur tous les systèmes courants (Linux, OS X, Windows).
À l'ouverture du jeu, un menu doit permettre d'orienter le joueur vers
les différents états de jeu disponibles, notamment le jeu ou l'éditeur.
Avant d'ouvrir le jeu, on doit pouvoir choisir le niveau à jouer. Avant
d'ouvrir l'éditeur, on doit pouvoir choisir le niveau à éditer ou si l'on
veut créer un nouveau niveau.
Le jeu se joue à deux joueurs qui incarnent chacun une balle contrôlable au clavier.
Une partie doit se présenter sous la forme d'un niveau où les joueurs
ont une position initiale et une position à atteindre pour gagner.
Chaque partie est limitée en temps. La durée limite est définie selon le niveau.
Les mécanismes physiques à implémenter sont la force d'attraction chargée
(coulombienne), la force de gravité, les forces de frottement et les
collisions entre objets. La force de gravité est appliquée selon un vecteur
de norme constante mais de direction et sens modifiables selon les
conditions du niveau.
La caméra des joueurs doit être centrée sur la position intermédiaire
des deux joueurs et doit s'orienter dans la direction inverse
de la direction actuelle de la gravité, en tout temps.
Chaque niveau est composé d'objets. Chaque objet possède une masse,
un coefficient de frottement statique et dynamique, un coefficient
de restitution, une charge, une position, une vitesse et un calque d'affichage
(couche). Un objet est soit une balle d'un joueur soit un bloc. Un bloc
peut être neutre ou posséder une particularité.
Les blocs particuliers sont définis complètement dans la section
\ref{sec:manuel-objets}.
Dans chaque niveau est définie une zone de jeu. Cette zone est un polygone
contrôlé par un nombre arbitraire de points. Si un objet avec une masse
non-infinie se trouve en dehors de cette zone (notamment un joueur
ou bien un bloc déplaçable), il doit être immédiatement tué. S'il s'agissait
d'un joueur, la partie se termine.
Les niveaux doivent être éditables par un éditeur. L'éditeur permet
de créer un nouveau niveau ou d'éditer un niveau existant. Il permet
de placer des objets prédéfinis sélectionables depuis la barre d'outils.
Il permet notamment de situer les positions initiales des joueurs
et du(des) bloc(s) d'arrivée. On doit pouvoir modifier la polarité des objets
depuis l'éditeur. Le niveau modifié doit pouvoir être sauvegardé. On doit
pouvoir tester le niveau en cours d'édition depuis l'éditeur sans perdre
son travail. On doit pouvoir modifier la taille de la zone de jeu.

View File

@ -0,0 +1,234 @@
\chapter{Manuel d'utilisation}
\section{Menu du jeu}
\subsection{Menu principal}
Au lancement du jeu le menu principal apparaît. La figure
\ref{fig:manuel-menu-principal} montre une copie d'écran du menu.
Le menu est composé de quatre boutons.
\begin{figure}[h]
\centering
\includegraphics[width=13cm]{figures/manuel-menu-principal.png}
\caption{Menu principal}
\label{fig:manuel-menu-principal}
\end{figure}
\begin{description}
\item[Bouton jouer]
Il permet d'accéder au menu de sélection des niveaux.
\item[Bouton règles du jeu]
Il démarre la vue affichant les règles du jeu.
\item[Bouton éditeur]
Il permet d'accéder au menu permettant de créer un niveau
ou d'en éditer un existant.
\item[Bouton quitter]
Il quitte le jeu.
\end{description}
\subsection{Menu de sélection du niveau}
\begin{figure}[h]
\centering
\includegraphics[width=13cm]{figures/manuel-menu-jouer.png}
\caption{Menu de sélection du niveau}
\label{fig:manuel-menu-selection}
\end{figure}
Le menu de sélection des niveaux, dont une copie d'écran est présentée
en figure \ref{fig:manuel-menu-selection}, se compose de deux types de boutons.
Les boutons niveaux permettent au joueur de choisir un niveau à jouer et
le bouton retour pour revenir vers le menu principal.
\subsection{Menu de l'éditeur}
\begin{figure} [!h]
\centering
\includegraphics[width=13cm]{figures/manuel-menu-editeur.png}
\caption{Menu de l'éditeur}
\label{fig:manuel-menu-editeur}
\end{figure}
Le menu de l'éditeur, dont une copie d'écan est présentée en figure
\ref{fig:manuel-menu-editeur}, se compose de trois types de boutons.
Créer un nouveau niveau pour démarrer la création d'un niveau à partir
de rien, éditer un niveau déjà existant, et retour qui renvoie vers
le menu principal.
Dans les menus, les touches \fbox{$\uparrow$}, \fbox{$\downarrow$} ou
le passage de la souris sur un des boutons permettent de séléctionner un
élément du menu. Le bouton actuellement sélectionné est souligné.
La touche \fbox{Entrée} ou un clic avec la souris permettent de valider le choix.
Les touches \fbox{$\longleftarrow$} et \fbox{Échap} permettent de revenir
au menu précédent.
\section{Objets}
\label{sec:manuel-objets}
\newcommand{\objectsymbol}[1]{
\includegraphics[width=23px]{figures/manuel-#1.png}
}
\newcommand{\describeobject}[2]{
\noindent
\begin{minipage}{.08\textwidth}
\objectsymbol{#1}
\end{minipage}
\begin{minipage}{.9\textwidth}
#2
\end{minipage}
}
Les niveaux se composent d'une collection d'objets qui
interagissent entre eux par les lois physiques ou par
l'action du joueur, selon les types d'entités. Nous
définissons dans cette section les comportements
des différents objets.
\subsection{Balles}
\describeobject{ball}{
Les balles sont des objets controlés par les joueurs. Chaque joueur
est affecté à une balle, identifiée de manière unique par son numéro.
}
\subsubsection{Polarité}
\describeobject{charge-neg}{
Les objets de couleur bleue repoussent tous ceux de la même couleur
et attirent les objets de couleur rouge.
}
\describeobject{charge-pos}{
Les objets de couleur rouge repoussent tous ceux de la même couleur
et attirent les objets de couleur bleue.
}
\subsubsection{Blocs}
\describeobject{block}{
Le bloc de base ne réalise aucune action particulière, il sert
uniquement à délimiter des zones dans le niveau.
}
\describeobject{movable-block}{
La caisse est un bloc qui peut être déplacé par les joueurs
ou une autre caisse. Elle active les blocs de gravité comme les joueurs.
}
\describeobject{gravity-block}{
Le bloc de gravité modifie le sens de la gravité du niveau lorsqu'un joueur
ou une caisse l'active en rentrant en collision avec lui. Ce bloc n'est
activable qu'une seule fois par partie.
}
\describeobject{switch-block}{
Le bloc inverseur inverse la charge des balles qui l'activent
en rentrant en collision avec lui. Ce bloc n'est activable qu'une
seule fois par partie.
}
\describeobject{kill-block}{
Le bloc tueur fait perdre la partie si un des deux joueurs l'active en
rentrant en collision avec lui.
}
\describeobject{finish-block}{
Le bloc d'arrivée fait gagner la partie lorsque tous les joueurs
l'ont activé en rentrant en collision avec lui.
}
\section{Jeu}
Les joueurs ne peuvent déplacer leurs balles que vers la gauche ou la droite.
Le joueur 1 utilise \fbox{$\rightarrow$} pour se déplacer vers la droite et
\fbox{$\leftarrow$} pour se déplacer vers la gauche. Le joueur 2 utilise
\fbox{D} pour se déplacer vers la droite et \fbox{Q} pour se déplacer
vers la gauche.
Les joueurs peuvent mettre le jeu en pause avec la touche \fbox{Échap},
ou bien quitter le niveau en appuyant sur \fbox{Espace}.
La partie peut être perdue pour trois raisons~: si un joueurs a
activée un bloc tueur, si un joueur est sortie de la zone de jeu
ou si le temps s'est écoulé totalement.
\section{Éditeur}
\subsection{Gestion des objets}
La figure \ref{fig:manuel-editeur-example} montre un exemple de niveau
en édition dans l'éditeur. On peut sélectionner le type d'objet à placer
en cliquant à la souris dans la barre latérale droite.
Un clic gauche sur une zone libre permet de placer le type d'objet
actuellement sélectionné. Le maintien du clic gauche en déplaçant la
souris permet de placer plusieurs objets à la fois.
\begin{figure}[h]
\centering
\includegraphics[width=13cm]{figures/manuel-editor.png}
\caption{Exemple de niveau en cours d'édition dans l'éditeur}
\label{fig:manuel-editeur-example}
\end{figure}
Pour modifier la charge d'un objet, le curseur doit être placé sur
celui-ci. La touche \fbox{Ctrl} doit être enfoncée tout en faisant
glisser la mollette de la souris, ou bien en faisant glisser deux
doigts sur le pavé tactile vers le haut ou le bas. L'objet change
de couleur en conséquence.
La sélection d'un objet se fait en cliquant sur celui-ci. Pour sélectionner
plusieurs objets, on maintient \fbox{Ctrl} et on clique sur les objets.
On peut effectuer une sélection rectangulaire en maintenant la touche
\fbox{Shift} et en faisant glisser la souris sur la zone à sélectionner,
en maintenant le clic gauche enfoncé. Lorsqu'un objet est sélectionné, sa
bordure devient rouge, comme montré dans la figure \ref{fig:manuel-selection}.
\begin{figure}[h]
\centering
\includegraphics[width=25px]{figures/manuel-selected-block.png}
\caption{Exemple d'objet sélectionné}
\label{fig:manuel-selection}
\end{figure}
La suppression d'un objet s'effectue en cliquant dessus avec le bouton
droit de la souris. Pour supprimer tous les objets actuellement sélectionnés,
il suffit d'appuyer sur la touche \fbox{Suppr}.
\subsection{Compte à rebours}
Le compte à rebours se situe en haut au centre de le fenêtre. Durant la partie,
s'il arrive à 0, les joueurs meurent et la partie est perdue.
La valeur du compte à rebours peut être modifiée dans l'éditeur en cliquant
sur les flèches se situant à coté, ou en glissant la molette en gardant la
souris sur le compteur.
\subsection{Zone de jeu}
La zone jouable est représentée dans l'éditeur par un polygone
rouge composé de quatre points. Chacun de ces points peuvent être
déplacés en cliquant dessus afin de modifier la taille et la
forme de la zone.
\subsection{Gestion de la caméra}
Le déplacement de la caméra s'effectue en déplaçant la souris vers
une bordure de la fenêtre. La molette de la souris peut être utilisée
pour un défilement vertical, ou horizontal si la touche \fbox{Shift}
est enfoncée.
\subsection{Commandes générales}
Il est possible de tester le niveau à tout moment en appuyant sur la
touche \fbox{Espace}. Pour revenir à l'édition il suffit d'appuyer à
nouveau sur \fbox{Espace}.
Afin de sauvegarder le niveau il est nécessaire de réaliser la
combinaison de touches suivantes : \fbox{Ctrl} + \fbox{S}. (en mode édition).
Si vous ne sauvegardez pas vos changements, ils seront perdus.
Pour quitter léditeur et revenir au menu la touche \fbox{Échap} doit être utilisée.

View File

@ -0,0 +1,62 @@
\chapter{Organisation du projet}
\section{Organisation du travail}
Lors de la réunion initiale les tâches ont été réparties selon les capacités
de chacun. En dehors des réunions, chaque membre du groupe a travaillé en
autonomie, en communiquant son avancement au reste du groupe.
Les réunions suivantes étaient consacrées au passage en revue des modifications
effectuées par chacun, à l'analyse des tâches restant à effectuer et à leur
distribution entre les membres du groupe, toujours selon leurs capacités
et leur disponibilité.
La répartition finale des tâches telles qu'elles sont été accomplies
est présentée dans la figure \ref{fig:organisation-gantt}.
Certaines parties du développement nécessitaient plusieurs personnes et
étaient ainsi partagées entre certains membres du groupe. La conception
et les tests des niveaux, le fond des menus ainsi que les décors furent
réalisés par Rémi et Maëlle, et la gestion de projet par tout le groupe.
Tout au long de la réalisation du projet, nous communiquions par Skype
afin de s'informer de l'avancement et réfléchir à des solutions lorsqu'un
problème était rencontré.
\newgeometry{left=2cm,top=1.5cm,bottom=1.5cm,right=2cm}
\thispagestyle{empty}
\begin{figure}[p!]
\centering
\input{figures/organisation-gantt.tex}
\caption{
Diagramme de la répartition des tâches. En vert, les tâches
affectées à Maëlle~; en bleu, les tâches affectées à Rémi~;
en rouge, les tâches affectées à Mattéo~; en gris, les
tâches résolues en groupe
}
\label{fig:organisation-gantt}
\end{figure}
\restoregeometry
\section{Outils de développement}
Tout le code du programme est centralisé sur un dépôt GitHub et les membres
du groupe utilisent Git pour synchroniser leur travail, travailler
indépendamment, vérifier le travail des autres ou récupérer
d'anciennes versions.
\cite{organisation-github-skizzle}
Pour le développement du jeu, nous avons choisi le C++ car il s'agit du
langage que nous apprenons cette année~: d'une part, nous possédons
donc des acquis avec de langage, et d'autre part ce projet nous permet
de fixer nos connaissances du C++. Une autre raison est la quantité
importante de documentation sur le web, de nombreuses bibliothèques
et d'une communauté importante.
Nous avons fait le choix de la SFML comme librairie graphique et d'utiliser
la spécification C++11 qui apporte des fonctionnalités non-négligeables
(notamment concernant la gestion de la mémoire avec les pointeurs intelligents).
\cite{organisation-smartptr}
Pour écrire le code, nous avons utilisé différents éditeurs de texte
(atom et gedit). Nous compilons notre programme avec le compilateur g++.
Pour faciliter la compilation, nous avons utilisé CMake.

View File

@ -0,0 +1,41 @@
@online{organisation-smartptr,
author = "University of Michigan",
title = "Using C++11s Smart Pointers",
howpublished = "\url{http://goo.gl/nwsKu2}"
}
@online{organisation-github-skizzle,
author = "Membres du groupe",
title = "Dépôt {GitHub} du projet",
howpublished = "\url{https://goo.gl/9AaOZu}"
}
@online{analyse-box2d,
author = "Erin Catto",
title = "Box2D : A 2D Physics Engine for Games",
howpublished = "\url{http://goo.gl/uTnXH4}"
}
@online{develop-collision-solver,
author = "Randy Gaul",
title = "ImpulseEngine"
howpublished = "\url{https://goo.gl/UvOGk4}",
}
@online{develop-collision-stability,
author = "Allen Chou",
title = "Game Physics: Stability Slops",
howpublished = "\url{http://goo.gl/jOaEGA}"
}
@online{develop-game-loop,
author = "Entropy Interactive",
title = "The Game Loop",
howpublished = "\url{http://goo.gl/cin0jt}"
}
@online{develop-sfgui,
author = "Stefan Schindler",
title = "SFGUI",
howpublished = "\url{http://goo.gl/2fwBku}"
}

Binary file not shown.

View File

@ -0,0 +1,113 @@
\documentclass{report}
% Graphismes
\usepackage[pdftex]{graphicx}
\usepackage{lmodern}
\usepackage{tikz}
\usepackage{tikz-uml}
\usepackage{pgfgantt}
\usepackage{lscape}
\usepackage{rotating}
% Couleurs personnalisées
\definecolor{matred}{RGB}{239,83,80}
\definecolor{matgreen}{RGB}{156,204,101}
\definecolor{matblue}{RGB}{41,182,246}
\definecolor{matyellow}{RGB}{255,235,59}
\definecolor{matblack}{RGB}{33,33,33}
\definecolor{matgray}{RGB}{224,224,224}
\definecolor{matbrown}{RGB}{78,52,46}
% Packages basiques
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{geometry}
\usepackage{wrapfig}
\usepackage[french]{babel}
\usepackage{hyperref}
% Noms personnalisés
\addto{\captionsfrench}{\renewcommand{\bibname}{Webographie}}
% En-têtes et pieds de pages personnalisés
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhf{}
\lhead{Maëlle \textsc{Beuret}, Rémi \textsc{Cérès}, Mattéo \textsc{Delabre}}
\rhead{Projet L1 -- C.M.I.}
\cfoot{\thepage}
\begin{document}
% Page de garde
\begin{titlepage}
\centering
% Titre de la formation
\textsc{\LARGE Projet L1 -- C.M.I.}\\
\vspace{.25cm}
{\Large Spécialité informatique}
\vspace{3cm}
% Titre du projet
\rule{\linewidth}{0.5mm}\\
\vspace{.4cm}
{\huge\bfseries Skizzle}\\
\vspace{.2cm}
\rule{\linewidth}{0.5mm}
\vspace{2cm}
% Author and supervisor
\begin{minipage}{0.495\textwidth}
\begin{flushleft}\large
Maëlle \textsc{Beuret}\\
Rémi \textsc{Cérès}\\
Mattéo \textsc{Delabre}
\end{flushleft}
\end{minipage}
\begin{minipage}{0.495\textwidth}
\begin{flushright}\large
\textbf{Année :} 2015 -- 2016\\
\textbf{Soutenu le :} 29/04/2016
\end{flushright}
\end{minipage}
\vfill
% Logos
\newcommand{\logo}[2]{
\raisebox{-.5\height}{
\includegraphics[scale=#2]{#1}
}
}
\logo{figures/frontpage-logo-um.png}{0.5}
\logo{figures/frontpage-logo-fds.png}{0.25}
\logo{figures/frontpage-logo-cgi.png}{0.05}
\logo{figures/frontpage-logo-figure.png}{1}
\end{titlepage}
% Table des matières
\setcounter{tocdepth}{1}
\tableofcontents
\setlength{\parskip}{0.4cm plus4mm minus3mm}
% Sous-parties
\include{./parts/intro}
\include{./parts/organisation}
\include{./parts/analyse}
\include{./parts/develop}
\include{./parts/manuel}
\include{./parts/conclu}
% Bibliographie
\cleardoublepage
\phantomsection
\addcontentsline{toc}{chapter}{Webographie}
\bibliographystyle{unsrt}
\bibliography{rapport}
\end{document}