update report
This commit is contained in:
parent
c5b5ce1fa0
commit
503dca347b
1 changed files with 179 additions and 5 deletions
|
@ -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
|
||||
|
|
Reference in a new issue