36 Commits

Author SHA1 Message Date
6d53865281 Merge pull request 'Rapport' (#53) from Rapport into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #53
Reviewed-by: Mat_02 <diletomatteo@gmail.com>
2023-05-20 23:36:13 +02:00
58810fff0c corrections
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-05-20 23:37:22 +02:00
a98c3cd3e1 Last modification by tonitch
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-05-20 14:17:42 +02:00
939f8566cd latex syntax
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-05-20 13:11:30 +02:00
03beebaeaf escape on the menu quit the game
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 12:45:52 +02:00
c7fe7853b4 Merge pull request 'restructuration des fichiers' (#84) from restructuration into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #84
Reviewed-by: Mat_02 <diletomatteo@gmail.com>
2023-05-20 12:32:10 +02:00
35c7ecf471 Merge pull request 'Random rotation to generated maps' (#83) from randomPieceRotation into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #83
Reviewed-by: Mat_02 <diletomatteo@gmail.com>
2023-05-20 12:31:53 +02:00
Mat
229782c772 Corriger qlq faute vu sur relecture
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-05-19 23:28:27 +02:00
Mat
2d27786e48 Make conclusion and correct some accent
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-05-19 23:06:18 +02:00
Mat
3bcfdfc084 Add some explain about MapGenerator
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-05-19 22:12:17 +02:00
Mat
878caf1c99 Add Apport Positifs et négatifs 2023-05-19 20:25:58 +02:00
0bb327abb2 restructuration des fichiers
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-05-19 16:44:45 +02:00
621be3af64 removing smap from the console
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-19 16:30:02 +02:00
Mat
9671f8ec53 Add Point forts
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-05-19 15:32:50 +02:00
Mat
2d4462735a Add Point faibles dans le rapport 2023-05-19 15:32:50 +02:00
7467abfd11 Random rotation to generated maps
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-05-19 14:55:48 +02:00
59fd891fd9 minor corrections
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-05-19 14:46:17 +02:00
e6853ab3d3 Parser de fichier + Consignes de base
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-05-19 10:20:11 +02:00
ff5ed0c85f correction fautes 2023-05-19 10:20:11 +02:00
0330303129 Avancement global
- Section Personelle
- Generateur de niveaux
- Organisation
2023-05-19 10:20:11 +02:00
0354679969 File Parser 2023-05-19 10:20:11 +02:00
714e904d1c Base de Rapport 2023-05-19 10:20:10 +02:00
d52d1cab77 Clean the pieces position when restarting the game
All checks were successful
continuous-integration/drone/push Build is passing
without that, the old pieces where still in game and this would break
the gameplay
2023-05-19 00:32:44 +02:00
9bc66ad6b1 Solve the position problem
All checks were successful
continuous-integration/drone/push Build is passing
The position was set to (70,76) for all floating pieces because it was
the value received.
nowk, if thes values are given (for the characters F & L as specified in
the specification in the rapport), the piece is set in a floating state
(position = null)
2023-05-19 00:32:26 +02:00
41be423f94 Draw the piece at their places at the start of the level
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-19 00:32:12 +02:00
774c594cb8 avoid too bright colors
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-05-18 19:46:19 +02:00
fed275ba09 Make a Button to load the previous game done by the player in case if he want to do it one more time(#73)
All checks were successful
continuous-integration/drone/push Build is passing
Make a Button to load the previous game done by the player in case if he want to do it one more time

Co-authored-by: Mat <diletomatteo@gmail.com>
Reviewed-on: #73
Reviewed-by: Debucquoy Anthony <d.tonitch@gmail.com>
2023-05-18 19:31:47 +02:00
8c8dacadd0 Bug Correction, Load Button linked
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-18 19:24:01 +02:00
a726019b18 lock fullscreen
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-05-18 19:11:43 +02:00
391d94afbe Make a screen when the player finish the level (#64)
All checks were successful
continuous-integration/drone/push Build is passing
Make a screen when the level is finish and also change the button play for select a difficulty

Co-authored-by: Mat <diletomatteo@gmail.com>
Co-authored-by: Anthony Debucquoy <debucquoy.anthony@gmail.com>
Reviewed-on: #64
Reviewed-by: Debucquoy Anthony <d.tonitch@gmail.com>
2023-05-18 19:03:53 +02:00
7bd43062d0 Make a Button to load the previous game (#68)
All checks were successful
continuous-integration/drone/push Build is passing
A simple button to load a previous game

Co-authored-by: Mat <diletomatteo@gmail.com>
Reviewed-on: #68
Reviewed-by: Debucquoy Anthony <d.tonitch@gmail.com>
2023-05-18 18:36:25 +02:00
4c185f0a81 saveLevel (#69)
All checks were successful
continuous-integration/drone/push Build is passing
Co-authored-by: Anthony Debucquoy <debucquoy.anthony@gmail.com>
Reviewed-on: #69
Reviewed-by: Mat_02 <diletomatteo@gmail.com>
2023-05-18 18:32:16 +02:00
ac6c8611e7 Adding all levels + linking buttons
All checks were successful
continuous-integration/drone/push Build is passing
to create new level just do

`gradle -q --console plain -PmainClass=school_project.Parsers.FileParserFactory run`

Co-authored-by: Anthony Debucquoy <debucquoy.anthony@gmail.com>
Reviewed-on: #56
Reviewed-by: Mat_02 <diletomatteo@gmail.com>
2023-05-17 23:42:44 +02:00
ff167b4d0f deleting piece position when clicked to avoid fantom pieces
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-17 23:41:55 +02:00
34ea408202 stop piece screen overflow
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-17 22:38:00 +02:00
c9fdb4a7db removing println to stop spamming the console
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-17 20:43:20 +02:00
50 changed files with 549 additions and 73 deletions

1
.gitignore vendored
View File

@ -53,3 +53,4 @@ build
.idea/
.settings/
*.slevel

2
JournalDeBord/rapport/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
rapport.aux
rapport.toc

Binary file not shown.

View File

@ -0,0 +1,278 @@
\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 quadrimèstre, 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 - je le pense - maintenant plus apte à nous confronter à ce genre d'objectifs. Tant au niveau personel qu'en tant que groupe.
Il va sans dire que comme pour tout projets, notre chemin a été semé d'embuches. En l'occurence, 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 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és d'accomplir une série d'objectifs propre à 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écessaire 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]{ Chaques cellules 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 chaques 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]{ Chaques 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 chaques pièces 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 flotante (n'est pas placée dans la carte.), les octets contenant les caractères F puis L (0x464C) remplacent les coordoné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 parser 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 system est que nous pouvons facilement ajouter d'autres parser de fichier dans le futurs.
\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 choisi une difficultée. En fonction de la difficultée choisie la grandeur du plateau de jeu sera différente.
Si la difficulté choisie est facile ou moyenne, alors un curseur parcour les extrémités du niveau.
Ce curseur sélectionne aléatoirement les cellules qui seront gardés ou non.
Grâce à ça, la forme du plateau n'est pas trop carrée.
Nous nous sommes basé sur le même principe pour le niveau de difficulté difficile mais en plus d'une taille encore plus grande,
le curseur parcours 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és. c'est pourquoi une boucle vérifie chaques cellules et la supprime si celle si 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 parcours des cases de la carte préalablement conçue de manière aléatoire.
Pour chaques cases, l'algorithme teste pour chaques cases de la pièce, si l'espace est disponible.
Si ca 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.
Ainsi qu'il est plus difficile d'appliquer des textures et dessins - à l'image du jeu de base - sur les pièces.
Malgrés tout, avec nos nombreux test, ce générateur de niveaux nous satisfait vraiment bien et la difficultée des niveaux coorespond 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 classe, et de la cohérence entre elles.
Toutes les autres classes (présentes dans le package \verb|Scenes|) sont des sous classe de Parents.
Celà permet de les afficher grace à 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 vera 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 boutons "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 choisir, 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'écrant.
Par la suite, les pièces sont dispérsées à la gauche de l'écrant et sont rendu disponible à l'utilisateurs.
Ces pièces sont déplacables à l'aide de la souris.
Si elles sont lachées sur un emplacement disponible du plateau, alors elle vont s'alligner 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èces 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}
Personellement, ce projet m'a permis de me plonger dans la conception d'un format de fichier personnalisé.
C'est une chôse que je n'avais pas encore fait jusqu'à maintenant.
Et malgré mes efforts pour prévoir un maximum de choses à l'avance afin d'éviter de devoir
modifier ma spécification pendant le dévelopement. Je me suis vite rendu compte que je n'avais pas pensé
à tout et que je devrais changer des choses 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 prend bonne notes.
\subsection{Matteo}
Il est clair que je peux tirer plusieurs enseignement 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 bibliotheque JavaFx.
De plus, durant la réalisation du projet, comme dit précédemment nous avons utilisé plusieurs outils et dont la plus grande découverte pour moi :
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 partie 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'autre améliorations sont toujours possible !
En effet l'idée de rajouter une histoire, des trophées, un easter egg, des pièces spéciales ou un encore un 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}

View File

@ -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
}

View File

@ -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();

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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")));
}
}

View File

@ -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);
}

View File

@ -6,9 +6,6 @@ import javafx.scene.paint.Paint;
import java.util.ArrayList;
import java.util.Random;
import static javafx.scene.paint.Color.WHITE;
import static javafx.scene.paint.Color.color;
/**
* Represent a Piece in the game.
* Every Piece should be contained in a Map Object.
@ -24,9 +21,10 @@ public class Piece extends Shape{
public Piece(boolean[][] matrix) {
super(matrix);
Random rand = new Random();
color = new Color((rand.nextDouble()%190), (rand.nextDouble()%190), (rand.nextDouble()%190), 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){
color = p;
}

View File

@ -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;
}
}

View 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")));
}
}

View File

@ -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);
}
}
}

View File

@ -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")));
}
}

View File

@ -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

View File

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -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");
}

View File

@ -0,0 +1 @@
SMS<03><13>SME

View File

@ -0,0 +1 @@
SMS<04><>"<22>"<22>"p1<70>SME

View File

@ -0,0 +1 @@
SMS<05><><EFBFBD><EFBFBD>"<22>1<EFBFBD><13>2<EFBFBD>"p3<70><33>SME

View File

@ -0,0 +1 @@
SMS?<3F><><EFBFBD><EFBFBD><13>"<22>3<EFBFBD><33><13>"<22>2<EFBFBD>3<EFBFBD><33>SME

View File

@ -0,0 +1 @@
SMSf<><66><EFBFBD>< 1<>"<22>"p<11>&<26><>"<22>"<22>"<22><13>SME

View File

@ -0,0 +1 @@
SMS<05><><EFBFBD>"<22>1<EFBFBD><11>"p#<23><12>!<21>SME

View File

@ -0,0 +1 @@
SMS<05><><EFBFBD><EFBFBD>"<22><13><11>"p3<70><33>3o<33>SME

View File

@ -0,0 +1 @@
SMS<06><><EFBFBD><EFBFBD><EFBFBD>3<><33>3<EFBFBD><33>1<EFBFBD>1<EFBFBD>#<23>#<23>SME

View File

@ -0,0 +1 @@
SMS<06><><EFBFBD><EFBFBD><EFBFBD><13>3<EFBFBD><33>3o<33>4<34>"<22>SME

View File

@ -0,0 +1 @@
SMS<01>1<>SME

View File

@ -0,0 +1 @@
SMS<05><><EFBFBD><EFBFBD><12>3<EFBFBD><33>"<22>2<EFBFBD>3<EFBFBD><33>SME

View File

@ -0,0 +1 @@
SMS<05><><EFBFBD><EFBFBD>"p<13><12>"<22>#<23>#<23>#<23>SME

View File

@ -0,0 +1 @@
SMS<05><><EFBFBD><EFBFBD>2<><13>"<22>#<23>#<23>BSME

View File

@ -0,0 +1 @@
SMSv<><76><EFBFBD>#<23><11><11>#<23>#<23>BSME

View File

@ -0,0 +1 @@
SMS<06><><EFBFBD><EFBFBD><EFBFBD>#<23>#|!<21>#\#<23>2<EFBFBD>2<EFBFBD>BSME

View File

@ -0,0 +1 @@
SMS<06><><EFBFBD><EFBFBD><EFBFBD>#<23>#<23>!<21>!<21>#<23>4<34>3o<33>SME

View File

@ -0,0 +1 @@
SMS<07><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<>B#|3?<3F>3<EFBFBD><33>#<23>&<26><>SME

View File

@ -0,0 +1 @@
SMSy<><79>y<EFBFBD>B<>!<21>B<EFBFBD>$<24>#<23>#<23>SME

View File

@ -0,0 +1 @@
SMS<03><>#<23><13>SME

View File

@ -0,0 +1 @@
SMS<07><><EFBFBD>}<7D>߀ $<24>3ۀ2<DB80><11><11>"<22>2x"p2<70>"<22>2<EFBFBD>SME

View File

@ -0,0 +1 @@
SMS<04><>#<23>#<23><11>1<EFBFBD>SME

View File

@ -0,0 +1 @@
SMS<>p<11><11>1<EFBFBD>#<23>#<23>SME

View File

@ -0,0 +1 @@
SMS<>3<><33><13>1<EFBFBD>SME

View File

@ -0,0 +1 @@
SMS3<>2<><13>"<22>SME

View File

@ -0,0 +1 @@
SMS<05><><EFBFBD><EFBFBD>2<>"<22>"<22>"p1<70><11>1<EFBFBD>"pSME

View File

@ -0,0 +1 @@
SMS<05><>߀1<>1<EFBFBD>3<EFBFBD><33>#<23>"<22>SME