Compare commits
38 Commits
delete_whi
...
master
Author | SHA1 | Date | |
---|---|---|---|
512fcc069d | |||
7a5a4a093e | |||
6d53865281 | |||
58810fff0c | |||
a98c3cd3e1 | |||
939f8566cd | |||
03beebaeaf | |||
c7fe7853b4 | |||
35c7ecf471 | |||
229782c772 | |||
2d27786e48 | |||
3bcfdfc084 | |||
878caf1c99 | |||
0bb327abb2 | |||
621be3af64 | |||
9671f8ec53 | |||
2d4462735a | |||
7467abfd11 | |||
59fd891fd9 | |||
e6853ab3d3 | |||
ff5ed0c85f | |||
0330303129 | |||
0354679969 | |||
714e904d1c | |||
d52d1cab77 | |||
9bc66ad6b1 | |||
41be423f94 | |||
774c594cb8 | |||
fed275ba09 | |||
8c8dacadd0 | |||
a726019b18 | |||
391d94afbe | |||
7bd43062d0 | |||
4c185f0a81 | |||
ac6c8611e7 | |||
ff167b4d0f | |||
34ea408202 | |||
c9fdb4a7db |
1
.gitignore
vendored
1
.gitignore
vendored
@ -53,3 +53,4 @@ build
|
||||
|
||||
.idea/
|
||||
.settings/
|
||||
*.slevel
|
||||
|
2
JournalDeBord/rapport/.gitignore
vendored
Normal file
2
JournalDeBord/rapport/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
rapport.aux
|
||||
rapport.toc
|
BIN
JournalDeBord/rapport/rapport.pdf
Normal file
BIN
JournalDeBord/rapport/rapport.pdf
Normal file
Binary file not shown.
274
JournalDeBord/rapport/rapport.tex
Normal file
274
JournalDeBord/rapport/rapport.tex
Normal file
@ -0,0 +1,274 @@
|
||||
\documentclass[12pt,a4paper]{article}
|
||||
|
||||
\usepackage{amsmath}
|
||||
\usepackage{listings}
|
||||
\usepackage{tikz}
|
||||
\usepackage{csquotes}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\title{Rapport du projet informatique 2023 \\ "Road to Master"}
|
||||
\author{Debucquoy Anthony \and Matteo Di Leto}
|
||||
\date{May 2023}
|
||||
|
||||
\maketitle
|
||||
\newpage
|
||||
|
||||
\tableofcontents
|
||||
\newpage
|
||||
|
||||
|
||||
\section*{Introduction}
|
||||
|
||||
Lors de ce deuxième quadrimestre, le projet Informatique proposé par notre université fut partie intégrante de notre emploi du temps.
|
||||
Régulièrement, nous nous sommes rassemblés pour nous organiser et trouver une direction dans laquelle nous voulions voir notre projet évoluer.
|
||||
Grâce aux objectifs fixés par nos enseignants, nous sommes - nous le pensons - maintenant plus aptes à nous confronter à ce genre d'objectifs, tant au niveau personnel qu'en tant que groupe.
|
||||
Il va sans dire que comme pour tout projet, notre chemin a été semé d'embûches. En l'occurrence, nous souhaitons faire part de l'abandon d'un de nos membre. Eddy Jiofak qui souhaite se réorienter.
|
||||
Nous lui souhaitons une bonne reconversion.
|
||||
|
||||
\section*{Objectifs}
|
||||
|
||||
Voici l'objectif fixé par nos enseignants. (document de consignes)
|
||||
|
||||
\begin{displayquote}
|
||||
|
||||
Le but final de ce projet est de réaliser une application graphique en Java permettant de
|
||||
jouer au jeu "Cats Organized Neatly". Ce jeu est un jeu de type "puzzle" où le joueur doit
|
||||
placer des pièces de formes différentes pour combler l'aire de jeu. L'application devra permettre
|
||||
de sauvegarder et charger une partie et de créer des niveaux automatiquement.
|
||||
|
||||
\end{displayquote}
|
||||
|
||||
\newpage
|
||||
|
||||
\section{Organisation}
|
||||
|
||||
Lors de nos rassemblement pour le projet, toutes les idées émises se sont retrouvés sur un blog afin de pouvoir y accéder de manière efficace.
|
||||
Ce blog nous sert également à garder une trace de l'évolution du projet.
|
||||
|
||||
\subsection{Choix}
|
||||
|
||||
\begin{itemize}
|
||||
\item{le VCS \verb|git| pour garder une trace de l'avancement du projet. Avec comme remote une instance privée de gitea nous permettant de vérifier les MR/PR plus efficacement.}
|
||||
\item{Une instance de DroneCI permettant de vérifier que le projet soit toujours compilable et que les tests ne soient pas ratés sans que nous nous en rendions compte.}
|
||||
\item{Javafx, comme recommandé par nos enseignants.}
|
||||
\item{Les pièces et niveaux sont stockées sous forme de matrice de booléen}
|
||||
\item{Un parser de fichiers efficace et donnant des fichier légers.}
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection*{Shapes}
|
||||
|
||||
Les pièces ainsi que la carte sont représentés par une matrice de booléen \verb|boolean[][]|.
|
||||
Nous avons donc une classe \verb|shape|, parent de \verb|Map| et de \verb|Piece| dans lequel nous stockons notre matrice.
|
||||
Ensuite, \verb|Map| Contient une liste de \verb|Piece|. Ces pièces contiennent une position représentée par la classe \verb|Vec2|.
|
||||
Cette position est la position du carré supérieur gauche dans la \verb|Map|.
|
||||
Avec toutes ces informations nous avons le nécessaire pour le moteur du jeu.
|
||||
|
||||
Il est facilement possible de manipuler la carte et les pièces. Il nous suffit alors de faire correspondre l'affichage avec
|
||||
ces différentes classes. Ce qui est entrepris par la classe \verb|GameUI|.
|
||||
|
||||
Le tout est géré par la classe \verb|Controller| qui permet de choisir entre l'affichage d'un menu ou d'une partie en cours.
|
||||
|
||||
% \subsection{Difficultés}
|
||||
|
||||
\section{Points Forts}
|
||||
|
||||
\subsection{Parser de fichiers}
|
||||
|
||||
Pour la rétention des niveaux, plusieurs possibilités s'offraient à nous. Nous avons alors décidé d'accomplir une série d'objectifs propres à notre projet avec un parser de fichiers dédié.
|
||||
Nous voulions que ce parser accomplisse les objectifs suivants:
|
||||
\begin{itemize}
|
||||
\item{Les données du niveau seront encapsulées dans un header/footer pour laisser la possibilité d'enregistrer plus d'informations (images/musiques) dans un seul fichier dans le futur.}
|
||||
\item{La taille du fichier devra être aussi petite que possible, tout en gardant les informations nécessaires au bon fonctionnement du jeu.}
|
||||
\item{Il sera possible d'enregistrer l'état d'une partie en cours.}
|
||||
\end{itemize}
|
||||
|
||||
Ce parser est implémenté par la classe \verb|BinaryParser|.
|
||||
|
||||
\subsubsection*{spécification}
|
||||
|
||||
\begin{description}
|
||||
\item[Header/Footer]{ Les données du niveau commencent par les 3 \emph{caractères} 'S', 'M', 'S' (ou \verb|0x534D53|) et se terminent par les 3 \emph{caractères} 'S', 'M', 'E' (ou \verb|0x534D45|)}
|
||||
\item[Taille de carte]{ Le premier octet des données représente la largeur de la carte, le second sa hauteur.}
|
||||
\item[Forme de la carte]{ Chaque cellule de la carte est représenté par un 1 ou un 0. Le 1 représente un emplacement libre, un 0 une cellule vide.
|
||||
La forme de la carte peut alors être répartie sur un nombre indéterminé d'octets.
|
||||
Nous pouvons déterminer ce nombre grace à
|
||||
$$\frac{\text{largeur} * \text{hauteur } (+1 \text{ si multiple de } 8)}{8}$$
|
||||
en division entière.}
|
||||
\item[Nombre de pièce]{ L' (les) octet(s) qui forme(nt) la carte représente le nombre de pièce}
|
||||
\item[Pour chaque pièces]{
|
||||
\
|
||||
\begin{description}
|
||||
\item[Taille de la pièce]{La taille est représentée sur un seul octet sous forme de nibble\footnote{https://en.wikipedia.org/wiki/Nibble}. La première partie du nibble est la largeur. La seconde partie du nibble est la hauteur }
|
||||
\item[Forme de la pièce]{ Chaque cellules de la pièce est représentée par un 1 ou un 0. La manière de le représenter et exactement la même que pour la forme de la carte }
|
||||
\end{description}
|
||||
Dans le cas où le fichier sauvegarde l'état de la partie, à la fin, et pour chaque pièce dans le même ordre que l'apparition des pièces:
|
||||
\begin{description}
|
||||
\item[Position de la pièce]{2 octets par pièces, 1 octet pour sa position en x et 1 octet pour sa position en y.
|
||||
Dans le cas où la pièce est flottante (n'est pas placée dans la carte.), les octets contenant les caractères F puis L (0x464C) remplacent les coordonnées}
|
||||
\end{description}
|
||||
}
|
||||
\end{description}
|
||||
|
||||
\subsubsection*{Exemple}
|
||||
|
||||
Voici le 'hexdump' du niveau 11
|
||||
|
||||
\begin{verbatim}
|
||||
53 4d 53 05 05 e7 ff ff 80 06 33 ff 80 22 f0 22 |SMS.......3.."."|
|
||||
b0 22 70 22 b0 11 80 53 4d 45 |."p"...SME|
|
||||
\end{verbatim}
|
||||
|
||||
Représente une carte de la forme :
|
||||
|
||||
\begin{tikzpicture}
|
||||
\filldraw[blue] (0,0) -- (0,5) -- (3,5) -- (3,4) -- (5,4) -- (5,0) -- cycle;
|
||||
\draw[step=1cm,gray] (0, 0) grid (5,5);
|
||||
\end{tikzpicture}
|
||||
|
||||
Avec les pièces :
|
||||
|
||||
\begin{tikzpicture}
|
||||
\fill[red] (1,1) rectangle (4,4);
|
||||
\fill[red] (5,1) rectangle (7,3);
|
||||
\fill[red] (8,1) -- (8,3) -- (9,3) -- (9,2) -- (10,2) -- (10,1) -- cycle;
|
||||
\fill[red] (11,1) -- (11,2) -- (12,2) -- (12,3) -- (13,3) -- (13,1) -- cycle;
|
||||
\fill[red] (1,-2) -- (1,0) -- (2,0) -- (2,-1) -- (3,-1) -- (3,-2) -- cycle;
|
||||
\fill[red] (4, -2) rectangle (5, -1);
|
||||
\draw[step=1cm,gray] (0.5, -2.5) grid (13.5, 4.5);
|
||||
\end{tikzpicture}
|
||||
|
||||
En plus de ce parser, et dans le cas où ce premier ne serait pas capable de stocker certaine carte (par exemple si une pièce mesure plus de 15x15),
|
||||
nous avons également implémenté un parser très simple en utilisant l'interface \verb|Serialize| de java. Ce parser est implémenté et fonctionnel,
|
||||
mais n'est pas utilisé dans le projet à l'heure actuelle.
|
||||
|
||||
Ces deux parseurs implémentent l'interface \verb|FileParser|.
|
||||
|
||||
Finalement, La classe \verb|FileParserFactory| permet une utilisation simple de ces parseurs. Celle-ci contient deux fonctions statiques.
|
||||
\begin{itemize}
|
||||
\item{\verb|Map loadMapFromFile(File)|}
|
||||
permet de retourner la Map d'un fichier chargé.
|
||||
\item{\verb|void saveFileFromMap(File, Map)|}
|
||||
permet de sauvegarder une map dans un fichier.
|
||||
\end{itemize}
|
||||
|
||||
Dans le cas d'une sauvegarde ou d'un chargement, le parser est choisi en fonction de l'extension de fichier ('.level', '.slevel', .'serialized', '.sserialized').
|
||||
|
||||
L'avantage de ce système est que nous pouvons facilement ajouter d'autres parser de fichiers dans le futur.
|
||||
|
||||
\subsection{Générateur de niveaux}
|
||||
|
||||
Le générateur de niveaux a été conçu de sorte à proposer 3 difficultés différentes:
|
||||
|
||||
\begin{itemize}
|
||||
\item{Niveau Facile}
|
||||
\item{Niveau Moyen}
|
||||
\item{Niveau Difficile}
|
||||
\end{itemize}
|
||||
|
||||
L'algorithme derrière est le même. En voici le principe :
|
||||
|
||||
\subsubsection*{Gestion du plateau}
|
||||
|
||||
Le joueur choisit une difficulté. En fonction de la difficulté choisie, la grandeur du plateau de jeu sera différente.
|
||||
Si la difficulté choisie est facile ou moyenne, alors un curseur parcourt les extrémités du niveau.
|
||||
Ce curseur sélectionne aléatoirement les cellules qui seront gardées ou non.
|
||||
Grâce à ça, la forme du plateau n'est pas trop carrée.
|
||||
|
||||
Nous nous sommes basés sur le même principe pour le niveau de difficulté difficile mais en plus d'une taille encore plus grande,
|
||||
le curseur parcourt les extrémités avec une profondeur de 2 afin de faire varier la carte plus grande.
|
||||
Cela introduit le problème des cases isoléss. C'est pourquoi une boucle vérifie chaque cellule et la supprime si celle-ci est isolée.
|
||||
|
||||
\subsubsection*{Gestion des pièces}
|
||||
|
||||
Peu importe la difficulté du niveau, voici le fonctionnement :
|
||||
|
||||
Une taille maximum des pièces a été fixée au préalable à 3x3.
|
||||
Par la suite, un curseur parcourt des cases de la carte préalablement conçue de manière aléatoire.
|
||||
Pour chaque case, l'algorithme teste pour chaque case de la pièce, si l'espace est disponible.
|
||||
Si ça n'est pas le cas, alors la pièce est modifiée afin de faire correspondre la pièce et la carte.
|
||||
|
||||
L'avantage de cette méthode est que les niveaux sont tous très différents.
|
||||
Les désavantages sont que, par malchance, il est possible d'avoir énormément de piece 1x1.
|
||||
il est aussi plus difficile d'appliquer des textures et dessins - à l'image du jeu de base - sur les pièces.
|
||||
|
||||
Malgré tout, avec nos nombreux tests, ce générateur de niveaux nous satisfait vraiment bien et la difficulté des niveaux correspond bien aux attentes.
|
||||
|
||||
\subsection{Interface graphique}
|
||||
L'interface graphique du jeu tient sur 5 classes différentes.
|
||||
|
||||
\subsubsection*{Controller}
|
||||
Classe principale. Elle s'occupe de la gestion des autres classes et de la cohérence entre elles.
|
||||
Toutes les autres classes (présentes dans le package \verb|Scenes|) sont des sous classe de Parents.
|
||||
Cela permet de les afficher grâce à la méthode statique \verb|switchRoot|.
|
||||
C'est aussi le point d'entrée du programme.
|
||||
|
||||
\subsubsection*{MenuAccueil}
|
||||
Classe s'occupant de générer la page d'accueil du jeu.
|
||||
C'est-à-dire la première page que verra l'utilisateur.
|
||||
Cette page permet d'accéder aux niveaux du jeu de base, mais également de générer les niveaux aléatoires.
|
||||
De plus un dernier bouton "Load Game" permet de revenir sur la dernière partie en cours du joueur.
|
||||
|
||||
\subsubsection*{MenuLevel}
|
||||
Classe s'occupant d'afficher les niveaux proposés dans le jeu, et qui,
|
||||
en fonction du jour choisi, change les boutons et les niveaux disponibles.
|
||||
|
||||
\subsubsection*{ScreenLevelFinish}
|
||||
Classe qui s'affiche à l'écran dès que le joueur a fini le niveau.
|
||||
Celle-ci propose également de réessayer le niveau ou de retourner au menu principal.
|
||||
|
||||
\subsubsection*{GameUI}
|
||||
Classe s'occupant de l'affichage d'un niveau.
|
||||
S'occupe dans un premier temps d'afficher le plateau au milieu de l'écran.
|
||||
Par la suite, les pièces sont dispersées à la gauche de l'écran et sont rendues disponibles à l'utilisateur.
|
||||
Ces pièces sont déplaçables à l'aide de la souris.
|
||||
Si elles sont lâchées sur un emplacement disponible du plateau, alors elles vont s'aligner avec ses cellules.
|
||||
Pour se faire, nous regardons si le centre d'une cellule (la plus en haut à gauche) est présente dans une cellule du plateau.
|
||||
Ensuite, on vérifie que toutes les cellules de la pièce ont bien la place dans le plateau pour se poser. Si c'est le cas, alors la pièce est posée.
|
||||
|
||||
\section{Points Faibles}
|
||||
|
||||
Bien que l'interface graphique permet de naviguer de manière fluide dans le jeu, il y a clairement un manque d'animation et de dynamisme
|
||||
\emph{i.e}: transition de page à une autre, apparition des boutons ainsi qu'un manque de musique.
|
||||
Toujours concernant l'affichage l'adaptation à la taille de l'écran peut être revue.
|
||||
|
||||
De plus suite à la perte de notre membre nous n'avons pas su gérer la partie du projet qui concerne le design/textures des pièces ainsi que l'histoire jeu préparée auparavant.
|
||||
|
||||
\section{Apports Positifs et négatifs}
|
||||
|
||||
\subsection{Anthony}
|
||||
|
||||
Personnellement, ce projet m'a permis de me plonger dans la conception d'un format de fichier personnalisé.
|
||||
C’est un exercice que je n’avais pas encore fait jusqu’a maintenant. Malgré mes efforts pour prévoir un maximum de choses à l’avance afin d’éviter de devoir modifier ma spécification pendant le développement, je me suis vite rendu compte que je n’avais pas pensé à tout et que je devrais changer des étapes pour pouvoir arriver à mes fins. Je pense que ce parser de fichier est vraiment améliorable mais je suis relativement fier du résultat.
|
||||
|
||||
J'ai pu présenter ce parser à Dr Quoitin qui a pu me conseiller sur différentes approches à ce problème.
|
||||
J'en prends bonne note.
|
||||
|
||||
\subsection{Matteo}
|
||||
|
||||
Il est clair que je peux tirer plusieurs enseignements grâce à la réalisation de notre projet.
|
||||
Tout d'abord, j'ai pu en apprendre beaucoup plus concernant la P.O.O en java, mais aussi j'en ai appris
|
||||
d'avantage sur l'utilisation de la bibliothèque JavaFx.
|
||||
|
||||
De plus, durant la réalisation du projet, comme dit précédemment, nous avons utilisé plusieurs outils dont la plus grande découverte pour moi est
|
||||
Git qui révèle son intérêt pour les travaux de groupes et qui, selon moi, se révélera tout aussi essentiel pour mes futurs projets
|
||||
scolaires ainsi que professionnels.
|
||||
|
||||
\section{conclusion}
|
||||
|
||||
En conclusion, nous pouvons séparer notre travail en trois parties différentes:
|
||||
|
||||
\begin{enumerate}
|
||||
\item Le parser de fichier (gestion sauvegarder/charger partie)
|
||||
\item Logique du jeu (Map,Vec2,Shapes)
|
||||
\item Liaison à l'UI (Javafx)
|
||||
\end{enumerate}
|
||||
|
||||
Malgré notre travail concentré sur le bon fonctionnement du jeu avec un parser suivant nos objectifs, une utilisation de la P.O.O de manière très efficace
|
||||
ainsi qu'une approche correcte de l'utilisation du framework Javafx, d'autres améliorations sont toujours possibles!
|
||||
En effet, l'idée d'ajouter une histoire, des trophées, un Easter egg, des pièces spéciales ou un encore une table de score basée sur le temps,
|
||||
reste possible afin de rendre notre jeu encore plus complet.
|
||||
|
||||
En conclusion, notre jeu a encore plein de possibilité afin d'être encore plus complet et amusant!
|
||||
|
||||
\end{document}
|
@ -29,7 +29,7 @@ dependencies {
|
||||
|
||||
application {
|
||||
// Define the main class for the application.
|
||||
mainClass = 'school_project.Controller'
|
||||
mainClass = project.hasProperty("mainClass") ? project.getProperty("mainClass") : 'school_project.Controller'
|
||||
}
|
||||
|
||||
javafx {
|
||||
@ -41,3 +41,7 @@ tasks.named('test') {
|
||||
// Use JUnit Platform for unit tests.
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
run{
|
||||
standardInput = System.in
|
||||
}
|
||||
|
@ -4,11 +4,15 @@ import javafx.application.Application;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.stage.Stage;
|
||||
import school_project.Menu.MenuAccueil;
|
||||
import school_project.Scenes.GameUI;
|
||||
import school_project.Scenes.MenuAccueil;
|
||||
import school_project.Parsers.FileParserFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@ -19,6 +23,7 @@ public class Controller extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws IOException {
|
||||
new File("save.slevel");
|
||||
stage = primaryStage;
|
||||
screen_size = new Vec2(
|
||||
(int) Screen.getPrimary().getBounds().getWidth(),
|
||||
@ -28,9 +33,6 @@ public class Controller extends Application {
|
||||
stage.setTitle("ROAD TO MASTER YOU");
|
||||
|
||||
// Full Screen mode
|
||||
stage.setFullScreen(true);
|
||||
stage.setFullScreenExitHint("");
|
||||
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
|
||||
|
||||
root = new MenuAccueil();
|
||||
|
||||
@ -41,7 +43,31 @@ public class Controller extends Application {
|
||||
|
||||
public static void switchRoot(Parent root){
|
||||
Scene scene = new Scene(root);
|
||||
if(root instanceof GameUI){
|
||||
scene.setOnKeyPressed(event ->{
|
||||
GameUI game = (GameUI) root;
|
||||
if(event.getCode() == KeyCode.ESCAPE){
|
||||
try {
|
||||
FileParserFactory.saveFileFromMap(new File("save.slevel"), game.getLevel());
|
||||
switchRoot(new MenuAccueil());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if(root instanceof MenuAccueil){
|
||||
scene.setOnKeyPressed(event ->{
|
||||
if(event.getCode()==KeyCode.ESCAPE){
|
||||
stage.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
stage.setScene(scene);
|
||||
|
||||
stage.setFullScreen(true);
|
||||
stage.setFullScreenExitHint("");
|
||||
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
launch();
|
||||
|
@ -124,4 +124,13 @@ public class Map extends Shape{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the position of each pieces in the map to null
|
||||
*/
|
||||
public void resetPiecesPositions(){
|
||||
for (Piece p : pieces) {
|
||||
p.setPosition(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,9 @@ public class MapGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
ret.addPiece(new Piece(shape));
|
||||
Piece piece = new Piece(shape);
|
||||
piece.RotateRight(rand.nextInt(4));
|
||||
ret.addPiece(piece);
|
||||
}
|
||||
|
||||
//generate pieces
|
||||
|
@ -1,42 +0,0 @@
|
||||
package school_project.Menu;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import school_project.Controller;
|
||||
public class MenuAccueil extends BorderPane {
|
||||
|
||||
|
||||
public MenuAccueil(){
|
||||
super();
|
||||
//create all the objet that i need
|
||||
Button Play = new Button("Play");
|
||||
Button SelectLevel= new Button("Select Level");
|
||||
Button Trophy = new Button("Trophy");
|
||||
Label Title = new Label("Welcome to Road to Master");
|
||||
//set up all the Button where i need
|
||||
setTop(Title);
|
||||
setLeft(Play);
|
||||
setRight(SelectLevel);
|
||||
setBottom(Trophy);
|
||||
Title.setFont(Font.font(20));
|
||||
Title.setTextFill(Color.GOLD);
|
||||
setAlignment(Title, Pos.CENTER);
|
||||
setAlignment(Play,Pos.CENTER);
|
||||
setAlignment(SelectLevel,Pos.CENTER);
|
||||
setAlignment(Trophy,Pos.CENTER);
|
||||
setPadding(new Insets(20,60,20,60));
|
||||
SelectLevel.setOnAction(event -> Controller.switchRoot(new MenuLevel(1)));
|
||||
getStyleClass().add("BorderPane");
|
||||
getStylesheets().add(String.valueOf(getClass().getResource("StyleMenuAcceuil.css")));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -114,7 +114,8 @@ public class BinaryParser implements FileParser {
|
||||
ret[piece_index] = new Piece(_piece_matrix);
|
||||
|
||||
if(saved_data){
|
||||
Vec2 _piece_pos = new Vec2(pieces_positions[piece_index*2], pieces_positions[piece_index*2 + 1]);
|
||||
Vec2 _piece_pos = (pieces_positions[piece_index*2] == 70 && pieces_positions[piece_index*2+1] == 76) ?
|
||||
null : new Vec2(pieces_positions[piece_index*2], pieces_positions[piece_index*2 + 1]);
|
||||
ret[piece_index].setPosition(_piece_pos);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,8 @@ public class Piece extends Shape{
|
||||
public Piece(boolean[][] matrix) {
|
||||
super(matrix);
|
||||
Random rand = new Random();
|
||||
color = new Color(rand.nextDouble(), rand.nextDouble(), rand.nextDouble(), 1);
|
||||
// the %.8 is there to avoid pieces that are too "white" so that we can see them
|
||||
color = new Color(rand.nextDouble()%.8, rand.nextDouble() %.8, rand.nextDouble()%.8, 1);
|
||||
}
|
||||
|
||||
public void setColor(Paint p){
|
||||
|
@ -1,9 +1,14 @@
|
||||
package school_project;
|
||||
package school_project.Scenes;
|
||||
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import school_project.Menu.MenuAccueil;
|
||||
|
||||
import school_project.Controller;
|
||||
import school_project.Map;
|
||||
import school_project.Piece;
|
||||
import school_project.Scenes.ScreenLevelFinish;
|
||||
import school_project.Utils.MatrixShape;
|
||||
import school_project.Vec2;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
@ -12,8 +17,11 @@ public class GameUI extends Group{
|
||||
public final static int SPACE_SIZE = 5;
|
||||
private final Vec2 piece_pos_click = new Vec2();
|
||||
|
||||
private Map level;
|
||||
|
||||
public GameUI(Map level) throws FileNotFoundException {
|
||||
super();
|
||||
this.level = level;
|
||||
|
||||
MatrixShape grid = new MatrixShape(level);
|
||||
|
||||
@ -28,15 +36,20 @@ public class GameUI extends Group{
|
||||
for (Piece p : level.getPieces()) {
|
||||
MatrixShape _piece = new MatrixShape(p);
|
||||
|
||||
if(piece_space.y + _piece.boundary_size.y >= Controller.screen_size.y){
|
||||
column++;
|
||||
piece_space.y = SPACE_SIZE;
|
||||
piece_space.x = (SEGMENT_SIZE*3 + SPACE_SIZE*4 )* column;
|
||||
}
|
||||
|
||||
_piece.setLayoutX(piece_space.x);
|
||||
_piece.setLayoutY(piece_space.y);
|
||||
|
||||
piece_space.y += _piece.boundary_size.y;
|
||||
|
||||
if(piece_space.y >= Controller.screen_size.y){
|
||||
column++;
|
||||
piece_space.y = SPACE_SIZE;
|
||||
piece_space.x = (SEGMENT_SIZE*3 + SPACE_SIZE*4 )* column;
|
||||
if(p.getPosition() != null){
|
||||
_piece.setLayoutX(grid.getLayoutX() + p.getPosition().y * (SEGMENT_SIZE+SPACE_SIZE));
|
||||
_piece.setLayoutY(grid.getLayoutY() + p.getPosition().x * (SEGMENT_SIZE+SPACE_SIZE));
|
||||
}
|
||||
|
||||
// Pieces Events
|
||||
@ -58,6 +71,7 @@ public class GameUI extends Group{
|
||||
_piece.setOnMouseReleased(event -> {
|
||||
if(event.getButton() != MouseButton.PRIMARY)
|
||||
return;
|
||||
p.setPosition(null);
|
||||
if(event.getSceneX() > grid.getLayoutX() && event.getSceneX() < grid.getLayoutX() + grid.boundary_size.x
|
||||
&& event.getSceneY() > grid.getLayoutY() && event.getSceneY() < grid.getLayoutY() + grid.boundary_size.y )
|
||||
{
|
||||
@ -66,18 +80,21 @@ public class GameUI extends Group{
|
||||
(int) (_piece.getLayoutY() + (SEGMENT_SIZE+SPACE_SIZE)/2 - grid.getLayoutY())/(SEGMENT_SIZE+SPACE_SIZE),
|
||||
(int) (_piece.getLayoutX() + (SEGMENT_SIZE+SPACE_SIZE)/2 - grid.getLayoutX())/(SEGMENT_SIZE+SPACE_SIZE)
|
||||
);
|
||||
System.out.println(level.placePiece(p, piece_position_in_grid) + piece_position_in_grid.toString());
|
||||
level.placePiece(p, piece_position_in_grid);
|
||||
if(p.getPosition() != null){
|
||||
_piece.setLayoutX(grid.getLayoutX() + p.getPosition().y * (SEGMENT_SIZE+SPACE_SIZE));
|
||||
_piece.setLayoutY(grid.getLayoutY() + p.getPosition().x * (SEGMENT_SIZE+SPACE_SIZE));
|
||||
}
|
||||
if(level.gameDone()){
|
||||
Controller.switchRoot(new MenuAccueil());
|
||||
Controller.switchRoot(new ScreenLevelFinish(level));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getChildren().add(_piece);
|
||||
}
|
||||
}
|
||||
|
||||
public Map getLevel() {
|
||||
return level;
|
||||
}
|
||||
}
|
88
app/src/main/java/school_project/Scenes/MenuAccueil.java
Normal file
88
app/src/main/java/school_project/Scenes/MenuAccueil.java
Normal file
@ -0,0 +1,88 @@
|
||||
package school_project.Scenes;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import school_project.Controller;
|
||||
import school_project.MapGenerator;
|
||||
import school_project.Parsers.FileParserFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MenuAccueil extends StackPane {
|
||||
public MenuAccueil(){
|
||||
super();
|
||||
//create all the objet that I need
|
||||
ChoiceBox<String> SlctDifficulty = new ChoiceBox<>();
|
||||
SlctDifficulty.getItems().addAll("Easy", "Medium", "Difficult");
|
||||
|
||||
Label RdmLvl = new Label("Random Level : ");
|
||||
Button LoadLvl = new Button("Load game");
|
||||
Button SelectLevel= new Button("Select Level");
|
||||
|
||||
Label Title = new Label("Welcome to Road to Master");
|
||||
SlctDifficulty.setOnAction(event -> {
|
||||
String choosediff = SlctDifficulty.getSelectionModel().getSelectedItem();
|
||||
switch (choosediff) {
|
||||
case "Easy":
|
||||
try {
|
||||
Controller.switchRoot(new GameUI(MapGenerator.generate(MapGenerator.Difficulty.Easy)));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
break;
|
||||
case "Medium":
|
||||
try {
|
||||
Controller.switchRoot(new GameUI(MapGenerator.generate(MapGenerator.Difficulty.Medium)));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
break;
|
||||
case "Difficult":
|
||||
try {
|
||||
Controller.switchRoot(new GameUI(MapGenerator.generate(MapGenerator.Difficulty.Difficult)));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
//set up all the Button where I need
|
||||
|
||||
getChildren().addAll(Title,SlctDifficulty,SelectLevel,RdmLvl,LoadLvl);
|
||||
RdmLvl.setFont(Font.font(25));
|
||||
RdmLvl.setTextFill(Color.GOLD);
|
||||
Title.setFont(Font.font(40));
|
||||
Title.setTextFill(Color.RED);
|
||||
setAlignment(Title, Pos.TOP_CENTER);
|
||||
setAlignment(LoadLvl,Pos.BOTTOM_CENTER);
|
||||
setAlignment(SlctDifficulty,Pos.CENTER_LEFT);
|
||||
setAlignment(SelectLevel,Pos.CENTER_RIGHT);
|
||||
setAlignment(RdmLvl, Pos.CENTER_LEFT);
|
||||
|
||||
setMargin(RdmLvl,new Insets(0,0,0,100));
|
||||
setMargin(SlctDifficulty,new Insets(0,0,0,300));
|
||||
setMargin(SelectLevel,new Insets(0,300,0,0));
|
||||
setMargin(Title,new Insets(200,0,0,0));
|
||||
setMargin(LoadLvl,new Insets(0,0,200,0));
|
||||
|
||||
SelectLevel.setOnAction(event -> Controller.switchRoot(new MenuLevel(1)));
|
||||
LoadLvl.setOnAction(event -> {
|
||||
try {
|
||||
Controller.switchRoot(new GameUI(FileParserFactory.loadMapFromFile(new File("save.slevel"))));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
getStyleClass().add("BorderPane");
|
||||
getStylesheets().add(String.valueOf(getClass().getResource("StyleMenuAcceuil.css")));
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package school_project.Menu;
|
||||
package school_project.Scenes;
|
||||
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
@ -7,6 +7,10 @@ import javafx.scene.layout.ColumnConstraints;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.RowConstraints;
|
||||
import school_project.Controller;
|
||||
import school_project.Parsers.FileParserFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MenuLevel extends GridPane {
|
||||
private int StartLevel;
|
||||
@ -60,30 +64,37 @@ public class MenuLevel extends GridPane {
|
||||
//It's here that I put all buttons where I need (base on column not row)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 1; j < 5; j++) {
|
||||
Button levelButton = new Button("level "+(StartLevel));
|
||||
levelButton.setOnAction(event -> {
|
||||
try {
|
||||
String levelName = ((Button)event.getSource()).getText().replace(" ", "") + ".level";
|
||||
GameUI level = new GameUI(FileParserFactory.loadMapFromFile(new File(Controller.class.getResource("levels/" + levelName).getFile())));
|
||||
Controller.switchRoot(level);
|
||||
} catch (IOException e) {
|
||||
System.out.println("Le niveau " + StartLevel + "n'existe pas.");
|
||||
}
|
||||
});
|
||||
if(i==0){
|
||||
Button Level = new Button("level "+(StartLevel));
|
||||
StartLevel+=3;
|
||||
add(Level,i,j);
|
||||
setHalignment(Level,HPos.CENTER);
|
||||
add(levelButton,i,j);
|
||||
setHalignment(levelButton,HPos.CENTER);
|
||||
if(j==4){
|
||||
StartLevel-=11;
|
||||
}
|
||||
}
|
||||
else if(i==1&&j!=4) {
|
||||
Button Level = new Button("level "+(StartLevel));
|
||||
StartLevel += 3;
|
||||
add(Level, i, j);
|
||||
setHalignment(Level,HPos.CENTER);
|
||||
add(levelButton, i, j);
|
||||
setHalignment(levelButton,HPos.CENTER);
|
||||
if (j == 3) {
|
||||
StartLevel -=8;
|
||||
|
||||
}
|
||||
}
|
||||
else if(i==2&&j!=4){
|
||||
Button Level = new Button("level "+(StartLevel));
|
||||
StartLevel+=3;
|
||||
add(Level,i,j);
|
||||
setHalignment(Level,HPos.CENTER);
|
||||
add(levelButton,i,j);
|
||||
setHalignment(levelButton,HPos.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package school_project.Scenes;
|
||||
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.text.Font;
|
||||
import school_project.Controller;
|
||||
import school_project.Map;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
public class ScreenLevelFinish extends StackPane {
|
||||
|
||||
public ScreenLevelFinish(Map lastlevel){
|
||||
super();
|
||||
Button retry = new Button("Retry Level");
|
||||
Label CongraMess = new Label(" LEVEL DONE GREAT JOB ");
|
||||
CongraMess.setFont(Font.font(40));
|
||||
Button BckMenu = new Button("Back to Menu");
|
||||
BckMenu.setFont(Font.font(25));
|
||||
Button ChooseLvl = new Button("Choose level");
|
||||
ChooseLvl.setFont(Font.font(25));
|
||||
|
||||
BckMenu.setOnAction(event -> Controller.switchRoot(new MenuAccueil()));
|
||||
ChooseLvl.setOnAction(event -> Controller.switchRoot(new MenuLevel(1)));
|
||||
retry.setOnAction(event -> {
|
||||
try {
|
||||
lastlevel.resetPiecesPositions();
|
||||
Controller.switchRoot(new GameUI(lastlevel));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
getChildren().addAll(BckMenu,ChooseLvl,CongraMess,retry);
|
||||
setAlignment(retry,Pos.BOTTOM_CENTER);
|
||||
setAlignment(BckMenu, Pos.CENTER_RIGHT);
|
||||
setAlignment(ChooseLvl, Pos.CENTER_LEFT);
|
||||
setAlignment(CongraMess, Pos.TOP_CENTER);
|
||||
setMargin(BckMenu, new Insets(0,300,0,0 ));
|
||||
setMargin(ChooseLvl,new Insets(0,0,0,300));
|
||||
setMargin(CongraMess,new Insets(300,0,0,0));
|
||||
setMargin(retry,new Insets(0,0,300,0));
|
||||
|
||||
getStyleClass().add("StackPane");
|
||||
getStylesheets().add(String.valueOf(getClass().getResource("StyleMenuAcceuil.css")));
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import javafx.scene.layout.Pane;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import school_project.*;
|
||||
import school_project.Scenes.GameUI;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 237 KiB |
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 423 KiB After Width: | Height: | Size: 423 KiB |
@ -5,4 +5,7 @@
|
||||
.GridPane{
|
||||
-fx-background-image: url("Background-select-level.jpg");
|
||||
-fx-background-position:right;
|
||||
}
|
||||
.StackPane{
|
||||
-fx-background-image: url("BackGround-LvlFinish.jpg");
|
||||
}
|
@ -0,0 +1 @@
|
||||
SMSààSME
|
@ -0,0 +1 @@
|
||||
SMS<04><>"<22>"<22>"p1<70>SME
|
@ -0,0 +1 @@
|
||||
SMSÿÿÿ€"°1àà2ü"p3í€SME
|
@ -0,0 +1 @@
|
||||
SMS?ÿÿýðà"°3í€à"ð2ü3í€SME
|
@ -0,0 +1 @@
|
||||
SMSfÿÿŸ< 1à"°"p€&ÿð"ð"ð"°àSME
|
@ -0,0 +1 @@
|
||||
SMSÿÿð"°1à€"p#üÀ!ÀSME
|
@ -0,0 +1 @@
|
||||
SMS˙˙˙€"đŕ€"p3í€3o€SME
|
@ -0,0 +1 @@
|
||||
SMSÿÿÿÿð3ÿ€3í€1à1à#ü#üSME
|
BIN
app/src/main/resources/school_project/levels/level18.level
Normal file
BIN
app/src/main/resources/school_project/levels/level18.level
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
SMSó˙˙óŕŕ3ü€3o€4đ"°SME
|
@ -0,0 +1 @@
|
||||
SMSà1àSME
|
@ -0,0 +1 @@
|
||||
SMSÿÿÿ€À3í€"ð2ø3ü€SME
|
BIN
app/src/main/resources/school_project/levels/level21.level
Normal file
BIN
app/src/main/resources/school_project/levels/level21.level
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
SMSÿÿÿ€"pàÀ"°#ü#è#èSME
|
@ -0,0 +1 @@
|
||||
SMSÿÿÿ€2Üà"Ð#è#¼BSME
|
@ -0,0 +1 @@
|
||||
SMSv˙˙€#쀀#ě#ĽBSME
|
@ -0,0 +1 @@
|
||||
SMSÿÿÿÿð#¼#|!À#\#ü2ô2äBSME
|
BIN
app/src/main/resources/school_project/levels/level26.level
Normal file
BIN
app/src/main/resources/school_project/levels/level26.level
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
SMSÿÿÿÿð#ø#´!À!À#¼4ð3o€SME
|
@ -0,0 +1 @@
|
||||
SMS<07><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<>B#|3?<3F>3<EFBFBD><33>#<23>&<26><>SME
|
@ -0,0 +1 @@
|
||||
SMSy<><79>y<EFBFBD>B<>!<21>B<EFBFBD>$<24>#<23>#<23>SME
|
@ -0,0 +1 @@
|
||||
SMS<03><>#<23><13>SME
|
@ -0,0 +1 @@
|
||||
SMS<07><><EFBFBD>}<7D>߀$<24>3ۀ2<DB80><11><11>"<22>2x"p2<70>"<22>2<EFBFBD>SME
|
@ -0,0 +1 @@
|
||||
SMS<04><>#<23>#<23><11>1<EFBFBD>SME
|
@ -0,0 +1 @@
|
||||
SMS÷p€€1à#ü#üSME
|
@ -0,0 +1 @@
|
||||
SMSÿ3ÿ€à1àSME
|
@ -0,0 +1 @@
|
||||
SMS3ÿ2üà"°SME
|
@ -0,0 +1 @@
|
||||
SMSÿÿÿ€2ü"°"°"p1à€1à"pSME
|
@ -0,0 +1 @@
|
||||
SMSÿÿ߀1à1à3ÿ€#ü"°SME
|
Loading…
Reference in New Issue
Block a user