diff --git a/report/document.tex b/report/document.tex index 5057139..173a258 100644 --- a/report/document.tex +++ b/report/document.tex @@ -28,11 +28,17 @@ \tableofcontents \clearpage -\section{Projet} -% Expliquer ce qu'est geometry dash 3d +\section[Projet]{Explication du projet} +Le but du projet est de créer un jeu du type +\href{https://fr.wikipedia.org/wiki/Geometry_Dash}{Geometry Dash} en vue 3D. +Geometry Dash est un jeu de plateforme dans lequel le joueur dirige un cube +glissant sur le sol et qui doit sauter pour éviter des obstacles. + +Le but de \textbf{ce} jeu est de faire le plus gros score, chaque seconde +passée dans le jeu ajoute 1 point au compteur, le score étant affiché en haut de l'écran. \section[Rendu 3D]{Rendu 3D avec THREE.JS} -La \autoref{fig:expls} schématise le rendu complet d'une scène du jeu +La \autoref{fig:expls} schématise grossièrement comment est rendu le jeu. \begin{figure}[h] \centering @@ -41,12 +47,180 @@ La \autoref{fig:expls} schématise le rendu complet d'une scène du jeu \label{fig:expls} \end{figure} +L'environnement du jeu s'occupe de tout. Elle garde en mémoire la liste des +éléments présents dans le jeu : +\begin{itemize} + \item Joueur (le cube) + \item Ennemis (les piques) +\end{itemize} + +Elle lance l'animation de ces derniers chacun son tour dans la fonction +\texttt{animate}, qui est appelée juste avant de rendre la scène (cf. \autoref{cod:envupdate}). + +\begin{figure}[h] + \centering + \begin{minipage}{0.3\textwidth} + \begin{minted}[autogobble,linenos]{js} + update = (demo) => { + if (this.animate(demo)) { + return true; + } + this.render(); + + return false; + }; + \end{minted} + \end{minipage} + \caption{fonction \texttt{update}, \texttt{Env.js}} + \label{cod:envupdate} +\end{figure} + + \section[Logique]{Logique du jeu} +Lorsque l'on joue, on a l'impression que le personnage avance sur un plan +infini. Ce n'est pas exactement ce qui se passe. + +Le joueur est un cube fixe, il ne fait que sauté et tourné sur lui-même. +Le plan est fixe, ce qui bouge sont les piques ennemis. + +\subsection[Génération procédurale]{Génération du terrain} +Le sol est un plan simple sur lequel une texture en dégradé est appliquée, +du blanc (proche de la caméra) au noir. Il y a une lumière pour appuyer l'effet +3D de la scène (il y a donc les ombres des objets projetés sur le sol). + +Le monde est généré via la fonction \texttt{generateRandomMap} de la classe +\texttt{Env}. Elle permet de générer $n$ obstacles pour le joueur placés devant +le joueur. Quand les obstacles passent derrière le joueur et sortent de l'écran, +ils sont téléportés devant le joueur en étant déplacé, ils ne sont pas justes +replacés à leur position initiale, créant une carte infinie. La taille du pique est choisie de façon aléatoire. + +\begin{figure}[h] + \centering + \begin{minipage}{0.7\textwidth} + \begin{minted}[autogobble,linenos]{js} + const spade = new Spade( + Math.random() * 0xffffff, + Math.round(Math.random()) ? Size.little : Size.big, + startDelta + (index - 1) * 10 + ); + \end{minted} + \dots lors de l'animation des piques\dots + \begin{minted}[autogobble,linenos,firstnumber=last]{js} + if (ennemy.data.position.x <= -10) { + ennemy.data.position.x = ennemy.startPos + Math.random() * 20; + } + \end{minted} + \end{minipage} + \caption{Création d'un pique et retéléportation lors de la sortie de l'écran, \texttt{Env.js}} + \label{cod:spade} +\end{figure} + \subsection{Saut du joueur} -TODO! +Dans le cas d'un joueur humain, on écoute via \texttt{addEventListener} +l'événement de la touche \textit{espace} pressée. Dans le cas de la démo, on utilise +un évènement interne \textit{nommé \texttt{jumpKey}}. + +Les deux événements sont enregistrés et lanceront la fonction de saut. + +\begin{figure}[h] + \centering + \begin{minipage}{0.7\textwidth} + \begin{minted}[autogobble,linenos]{js} + if (demo) { + addEventListener("jumpKey", (e) => player.controlUser(e.detail)); + } else { + addEventListener("keypress", player.controlUser); + } + \end{minted} + \end{minipage} + \caption{Enregistrement des événements de saut, \texttt{Game.js}} + \label{cod:eventjump} +\end{figure} + +La fonction \texttt{controlUser} définit le point de saut maximal ainsi que la +rotation du joueur finale. + +\begin{figure}[h] + \centering + \begin{minipage}{0.7\textwidth} + \begin{minted}[autogobble,linenos]{js} + controlUser = (key) => { + if (key.code == "Space" && !this.movementData.state) { + this.movementData.changeRotation( + this.data.rotation.y - Math.PI / 2 + ); + this.movementData.changeJump( + 3 * this.data.position.y + Math.PI / 2 + ); + this.movementData.changeState(); + } + }; + \end{minted} + \end{minipage} + \caption{Définition du saut max et de la rotation finale, \texttt{Player.js}} + \label{cod:defjumprot} +\end{figure} + +Les coordonnées de transition (le saut et la descente) ainsi que l'angle sont calculées lors de l'animation du joueur, dans la fonction \texttt{animation} de la +classe \texttt{Player}. + +\subsubsection[Démo]{Cas de la démonstration} +On vérifie si un ennemi est devant lors de l'animation du joueur, si c'est le +cas alors on saute. + +\begin{figure}[h] + \centering + \begin{minipage}{0.7\textwidth} + \begin{minted}[autogobble,linenos]{js} + if (demo) { + listEnnemies.forEach((ennemy) => { + const pos = ennemy.position.x - joueur.position.x; + // Aléatoire entre 0.5 (proche du pique) et 2 (loin du pique) + if (pos > 0 && pos < Math.random() * 1.5 + 0.5) { + // Événement "jumpKey" + dispatchEvent(jumpDemo); + } + }); + } + \end{minted} + \end{minipage} + \caption{Vérification IA : saut automatique, \texttt{Player.js}} + \label{cod:ia} +\end{figure} + +\subsection{Collisions} +À chaque frame du jeu, le joueur vérifie qu'il n'entre pas en collision avec +les ennemies via du +\href{https://threejs.org/docs/#api/en/core/Raycaster}{ray casting}. + +\begin{figure}[h] + \centering + \begin{minipage}{0.7\textwidth} + \begin{minted}[autogobble,linenos]{js} + const rc = new THREE.Raycaster( + this.data.position, + directionVector.normalize() + ); + + // Contient les éléments en collisions avec le joueur + const collisionResults = rc.intersectObjects(listEnnemies, false); + if ( + collisionResults.length > 0 && + collisionResults[0].distance < directionVector.length() + ) { + return true; + } + return false; + \end{minted} + \end{minipage} + \caption{Vérification des collisions, \texttt{Player.js}} + \label{cod:raycast} +\end{figure} \subsection{Fin de jeu} -TODO! +Le jeu se termine dès que le joueur entre en collision avec un ennemi. Quand +c'est le cas, un menu de fin apparaît au-dessus du jeu, invitant le joueur à +rejouer. Peu importe si c'est une démo ou non. \newpage \appendix