Compare commits

..

8 Commits

1550 changed files with 185 additions and 6045 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,207 +1,185 @@
# Projet Jetpack DinoAI ---
Comparaison de Différentes Intelligences Artificielles dans un Jeu Simple # Projet Jetpack DinoAI
Cahier des charges fonctionnelles Comparaison de Différentes Intelligences Artificielles dans un Jeu Simple
Par : Cahier des charges fonctionnelles
Victor Descamps Par :
Simon Catanese
Ethan Richard Victor Descamps
Simon Catanese
Tuteur : Ethan Richard
Florent Madelaine Tuteur :
fait le 18/11/2023 Florent Madelaine
> Les images sont présentes en local avec ce document s'il y a un problème d'affichage nous recommandons d'utiliser l'extension markdown viewer de Firefox fait le 18/11/2023
# Sommaire : > Les images sont présentes en local avec ce document s'il y a un problème d'affichage nous recommandons d'utiliser l'extension markdown viewer de Firefox
- [Contexte](#contexte) # Sommaire :
- - [Sujet](#sujet) - [Contexte](#Contexte-)
- - [Objectifs](#objectifs) - - [Sujet](#sujet-)
- - [Le Jeu](#le-jeu) - - [Objectifs](#objectifs-)
- - - [Le jeu de base](#le-jeu-de-base) - - [Le Jeu](#le-jeu-)
- - - [Notre jeu](#notre-jeu) - - - [Le jeu de base](#le-jeu-de-base-)
- [Les Bots](#les-bots) - - - [Notre jeu](#notre-jeu-)
- - [S) L'omnicient (neuronal)](#s-l-omnicient-neuronal-img-title-src-images-img3png-png-alt-loading-ag-353-data-align-inline-width-50) - [Les Bots](#les-bots-)
- [M) Lobservateur (neuronal)](#m-l-observateur-neuronal-img-title-src-images-img4png-png-alt-loading-ag-364-width-50) - - [S) Le sachant (Apprentissage)](#s-le-sachant-apprentissage--)
- [M) L'Itérateur (Procédural)](#m-l-itérateur-procédurall-img-title-src-images-img5png-png-alt-loading-ag-372-width-50) - [M) Lobservateur (Apprentissage)](#m-lobservateur-apprentissage-)
- [C) L'Itérateur Intelligent (Procédural & Neuronal)](#c-l-itérateur-intelligent-t-procédural-neuronal-img-title-src-images-img6png-png-alt-loading-ag-381-width-51) - [M) Le calculateur (Algorithme)](#m-le-calculateur-algorithme)
- [L'application :](#l-application) - [C) Le calculateur apprenant (Algorithme & IA)](#c-le-calculateur-apprenant-algorithme--ia-)
- - [Menu principal](#menu-principal) - [L'application :](#lapplication-)
- [Affichage des données](#affichage-des-données) - - [Menu principal](#menu-principal-)
- [Configuration d'une partie](#configuration-d-une-partie) - [Affichage des données](#affichage-des-données-)
- [Le jeu](#le-jeu-1) - [Configuration d'une partie](#configuration-dune-partie-)
- [Prévison dees Étapes de création](#prévision-des-étapes-de-création) - [Le jeu](#le-jeu--1)
# Contexte # Contexte
### Sujet : ### Sujet :
Florent Madelaine.  Florent Madelaine. 
Expérimentation Bots pour un ou des jeux.  Expérimentation Bots pour un ou des jeux. 
Programmer un jeu original avec idéalement des éléments qui en font un challenge pour la réalisation dIA (hasard, phase denchère changeant le payoff, multijoueur, combinatoire importante, ...). Il conviendra de choisir un jeu pour lequel il est possible de réduire la combinatoire du jeu de manière naturelle et de désactiver/activer des règles pour en étudier des variantes naturelles. Il faudra ensuite créer, améliorer et expérimenter avec divers algorithmes pour converger vers des bots offrant un challenge raisonnable pour un humain. Programmer un jeu original avec idéalement des éléments qui en font un challenge pour la réalisation dIA (hasard, phase denchère changeant le payoff, multijoueur, combinatoire importante, ...). Il conviendra de choisir un jeu pour lequel il est possible de réduire la combinatoire du jeu de manière naturelle et de désactiver/activer des règles pour en étudier des variantes naturelles. Il faudra ensuite créer, améliorer et expérimenter avec divers algorithmes pour converger vers des bots offrant un challenge raisonnable pour un humain.
### Objectifs : ### Objectifs :
Nous avons très rapidement opté pour faire différentes IA qui pourraient jouer toutes seules à un jeu simple. Nous sommes très intéressés par la notion de temps réel et de lapprentissage.  Nous avons très rapidement opté pour faire différentes IA qui pourraient jouer toutes seules à un jeu simple. Nous sommes très intéressés par la notion de temps réel et de lapprentissage. 
Notre objectif serait de comparer différentes méthodes danalyse du jeu sur des bots différents pour les comparer entre elles sur plusieurs facteurs comme le temps d'apprentissage ou l'efficacité de la méthode. Notre objectif serait de comparer différentes méthodes danalyse du jeu sur des bots différents pour les comparer entre elles sur plusieurs facteurs comme le temps d'apprentissage ou l'efficacité de la méthode.
Le livrable sera une application exécutable avec un .exe. Linterface doit permettre de lancer le jeu en choisissant les paramètres désirés. Lutilisateur pourra choisir de contrôler lavatar pour jouer lui-même ou bien déléguer la partie à lun des bots développés, puis éventuellement apporter des changements de règle au jeu. Le livrable sera une application exécutable avec un .exe. Linterface doit permettre de lancer le jeu en choisissant les paramètres désirés. Lutilisateur pourra choisir de contrôler lavatar pour jouer lui-même ou bien déléguer la partie à lun des bots développés, puis éventuellement apporter des changements de règle au jeu.
En fonction des paramètres choisis, lutilisateur peut jouer lui-même ou assister à la partie dun ou plusieurs bots en même temps. Chaque partie de chaque bot (ou du joueur) enregistre des données stockées au format json ou xml.  En fonction des paramètres choisis, lutilisateur peut jouer lui-même ou assister à la partie dun ou plusieurs bots en même temps. Chaque partie de chaque bot (ou du joueur) enregistre des données stockées au format json ou xml. 
Lutilisateur peut choisir un paramètre “Rush” privilégiant la vitesse de calculs à laffichage graphique, accélérant le jeu au maximum et réalisant des parties en boucle pour obtenir des données exploitables et comparables le plus vite possible. Lutilisateur peut choisir un paramètre “Rush” privilégiant la vitesse de calculs à laffichage graphique, accélérant le jeu au maximum et réalisant des parties en boucle pour obtenir des données exploitables et comparables le plus vite possible.
Le menu principal du jeu permet daccéder à une interface de visualisation des données collectées en jeu, affichables en graphiques et tableaux, permettant de comparer efficacement les performances des différents bots et du joueur humain en fonction des paramètres de partie choisis.  Le menu principal du jeu permet daccéder à une interface de visualisation des données collectées en jeu, affichables en graphiques et tableaux, permettant de comparer efficacement les performances des différents bots et du joueur humain en fonction des paramètres de partie choisis. 
# Le Jeu : # Le Jeu :
### Le jeu de base : ### Le jeu de base :
Jetpack Joyride est un jeu dit “Runner” car il consiste simplement à aller le plus loin possible tout en survivant aux dangers que le jeu lui oppose. Les règles du jeu sont simples, laissant le joueur décider de se propulser son avatar vers le haut ou de le laisser tomber pour faire bouger son personnage verticalement tandis que le terrain et donc ses obstacles avancent horizontalement vers lui de plus en plus vite. Jetpack Joyride est un jeu dit “Runner” car il consiste simplement à aller le plus loin possible tout en survivant aux dangers que le jeu lui oppose. Les règles du jeu sont simples, laissant le joueur décider de se propulser son avatar vers le haut ou de le laisser tomber pour faire bouger son personnage verticalement tandis que le terrain et donc ses obstacles avancent horizontalement vers lui de plus en plus vite.
![capture ecran partie type](images/img1PNG.PNG) ![capture ecran partie type](img1PNG.jpg)
( capture décran dune partie type ) ( capture décran dune partie type )
### Notre jeu : ### Notre jeu :
Pour faire jouer nos IA nous allons donc reproduire le jeu sur Unity en adaptant certains aspects pour faciliter la compréhension et lapprentissage de lIA : Pour faire jouer nos IA nous allons donc reproduire le jeu sur Unity en adaptant certains aspects pour faciliter la compréhension et lapprentissage de lIA :
- Les objets qui ajoutent des règles très situationnelles sont retirés (comme les véhicules ou bonus modifiant l'expérience interactive du jeu). - Les objets qui ajoutent des règles très situationnelles sont retirés (comme les véhicules ou bonus modifiant l'expérience interactive du jeu).
- Un système de score sera ajouté. La maximisation du score sera lobjectif de lIA au-delà du temps de survie. Le score aura une augmentation linéaire en fonction du temps de survie et pourra être augmenté en ramassant des pièces. Mais de mauvaises pièces peuvent apparaître et faire baisser le score. Les IA devront ainsi adapter leur chemin pour tenter de maximiser leur score quitte à prendre des risques. - Un système de score sera ajouté. La maximisation du score sera lobjectif de lIA au-delà du temps de survie. Le score aura une augmentation linéaire en fonction du temps de survie et pourra être augmenté en ramassant des pièces. Mais de mauvaises pièces peuvent apparaître et faire baisser le score. Les IA devront ainsi adapter leur chemin pour tenter de maximiser leur score quitte à prendre des risques.
<img title="" src="images/img2PNG.PNG" alt="interface du jeu" width="637" data-align="inline"> <img title="" src="img2PNG.jpg" alt="interface du jeu" width="637" data-align="inline">
- Le jeu original peut faire apparaître des missiles pouvant tuer le joueur. Ces missiles ne seront initialement pas implémentés, des tests nous permettront de savoir si ils sont un ajout viable au jeu après le développement du premier bot. - Le jeu original peut faire apparaître des missiles pouvant tuer le joueur. Ces missiles ne seront initialement pas implémentés, des tests nous permettront de savoir si ils sont un ajout viable au jeu après le développement du premier bot.
# #
## Les bots : ## Les bots :
nb. Pour comprendre la suite, il faut savoir que les IA fonctionnant par apprentissage nécessitent lapport dun certain nombre de données de sources définies pour fonctionner, données quelles apprennent à corréler par tâtonnement afin de trouver la meilleure approche et apporter les meilleures réponses. nb. Pour comprendre la suite, il faut savoir que les IA fonctionnant par apprentissage nécessitent lapport dun certain nombre de données de sources définies pour fonctionner, données quelles apprennent à corréler par tâtonnement afin de trouver la meilleure approche et apporter les meilleures réponses.
Notation MoSCoW: Notation MoSCoW:
Must Should Could Would Must Should Could Would
### S) L'omnicient (neuronal) <img title="" src="images/img3PNG.PNG" alt="loading-ag-353" data-align="inline" width="50"> ### S) Le sachant (Apprentissage) <img title="" src="img3PNG.jpg" alt="loading-ag-353" data-align="inline" width="50">
Cette IA avancera à laveugle en ayant en tête les positions de chaque objet sur l'écran. On sattends à ce que lIA comprenne que lobjet représentant son personnage ne doit pas sapprocher des objets néfastes entraînant la mort ou une baisse de score. Cette IA avancera à laveugle en ayant en tête les positions de chaque objet sur l'écran. On sattends à ce que lIA comprenne que lobjet représentant son personnage ne doit pas sapprocher des objets néfastes entraînant la mort ou une baisse de score.
### M) Lobservateur (neuronal) <img title="" src="images/img4PNG.PNG" alt="loading-ag-364" width="50"> ### M) Lobservateur (Apprentissage) <img title="" src="img4PNG.jpg" alt="loading-ag-364" width="50">
Cette IA ne connaît pas les coordonnées des objets à lécran. Lobservateur possède en revanche un champ de vision lui permettant de repérer sur certains angles la distance dobjets par rapport à lavatar. On s'attend à ce que lIA apprenne à éviter les objets néfastes sapprochant trop de lavatar.  Cette IA ne connaît pas les coordonnées des objets à lécran. Lobservateur possède en revanche un champ de vision lui permettant de repérer sur certains angles la distance dobjets par rapport à lavatar. On s'attend à ce que lIA apprenne à éviter les objets néfastes sapprochant trop de lavatar. 
### M) L'Itérateur (procédural) <img title="" src="images/img5PNG.PNG" alt="loading-ag-372" width="50"> ### M) Le calculateur (Algorithme) <img title="" src="img5PNG.jpg" alt="loading-ag-372" width="50">
Le bot Itérateur ne fonctionne pas par apprentissage mais par un algorithme. Au fil de la partie, l'itérateur doit calculer par itérations différents chemins en essayant de sapprocher du chemin le plus sûr possible. Par intervalle de temps, l'itérateur essaiera de prolonger le meilleur chemin quil a trouvé et testera des déviations possibles apportant un éventuel avantage sur lespérance de survie et dobtention de points. Le bot Calculateur ne fonctionne pas par apprentissage mais par un algorithme. Au fil de la partie, le calculateur doit calculer par itérations différents chemins en essayant de sapprocher du chemin le plus sûr possible. Par intervalle de temps, le calculateur essaiera de prolonger le meilleur chemin quil a trouvé et testera des déviations possibles apportant un éventuel avantage sur lespérance de survie et dobtention de points.
### C) L'Itérateur Intelligent t (procédural & neuronal) <img title="" src="images/img6PNG.PNG" alt="loading-ag-381" width="51"> ### C) Le calculateur apprenant (Algorithme & IA) <img title="" src="img6PNG.jpg" alt="loading-ag-381" width="51">
Bot optionnel fonctionnant comme l'Itérateur à la différence que lon essaiera dy implémenter un syteme neuronal qui devra permettre de faire de meilleurs choix de prolongement et de déviation de chemin afin daugmenter lefficacité et les performances du bot Itérateur. On s'attend à ce que le bot apprenne à trouver des chemins plus sûrs en prenant en compte les chemins déjà testés. Bot optionnel fonctionnant comme le calculateur à la différence que lon essaiera dy implémenter une IA qui devra faire de meilleurs choix de prolongement et de déviation de chemin afin daugmenter lefficacité et les performances du bot Calculateur. On s'attend à ce que lIA apprenne à trouver des chemins plus sûrs en prenant en compte les chemins déjà testés.
Dautres IA viendront si les idées et les deadlines nous le permettent. Dautres IA viendront si les idées et les deadlines nous le permettent.
# #
## LApplication : ## LApplication :
### Menu principal : ### Menu principal :
![image menu principal](images/img7PNG.PNG) ![image menu principal](img7PNG.jpg)
Permet de naviguer entre les deux parties majeures de lapplication.  Permet de naviguer entre les deux parties majeures de lapplication. 
Le bouton “Lancer le jeu” redirige vers un menu de configuration de la partie.  Le bouton “Lancer le jeu” redirige vers un menu de configuration de la partie. 
Le bouton “Consulter les données” redirige vers une interface de visualisation des données récoltées au long des parties enregistrées. Le bouton “Consulter les données” redirige vers une interface de visualisation des données récoltées au long des parties enregistrées.
### Affichage des données : ### Affichage des données :
Cette interface traite les données enregistrées automatiquement pendant toutes les parties jouées. Les bots utilisés et les règles de chaque partie permettent de différencier chaque ensemble de données. Cette interface traite les données enregistrées automatiquement pendant toutes les parties jouées. Les bots utilisés et les règles de chaque partie permettent de différencier chaque ensemble de données.
Les données traitées seront affichées sous forme de tableaux ou graphiques selon ce qui semble le plus adapté. Les données traitées seront affichées sous forme de tableaux ou graphiques selon ce qui semble le plus adapté.
Différents onglets permettent de filtrer laffichage et obtenir des informations plus précises sur certains aspects. Différents onglets permettent de filtrer laffichage et obtenir des informations plus précises sur certains aspects.
Lobjectif est de permettre à lutilisateur de pouvoir comparer facilement de multiples aspects de chaque bots de manière concrète et rigoureuse après avoir recueilli suffisamment de données. Lobjectif est de permettre à lutilisateur de pouvoir comparer facilement de multiples aspects de chaque bots de manière concrète et rigoureuse après avoir recueilli suffisamment de données.
Il est envisagé dimplémenter une fonctionnalité dexport des données en tableau Exel. Il est envisagé dimplémenter une fonctionnalité dexport des données en tableau Exel.
<img title="" src="images/img8PNG.PNG" alt="image affichage des données" data-align="center"> <img title="" src="img8PNG.jpg" alt="image affichage des données" data-align="center">
### Configuration dune partie : ### Configuration dune partie :
<img title="" src="images/img9PNG.PNG" alt="image configuration d'une partie" data-align="center"> <img title="" src="img9PNG.jpg" alt="image configuration d'une partie" data-align="center">
Cette interface permet à lutilisateur de configurer une partie ou une suite de parties auxquelles il fera participer un ou plusieurs bots ou bien lui-même.  Cette interface permet à lutilisateur de configurer une partie ou une suite de parties auxquelles il fera participer un ou plusieurs bots ou bien lui-même. 
Les règles du jeu peuvent être modifiées pour soumettre les bots à des conditions de jeu diverses. Les règles du jeu peuvent être modifiées pour soumettre les bots à des conditions de jeu diverses.
Des éléments comme les missiles ou les pièces peuvent être activés ou désactivés. Dautres paramètres peuvent être modifiés comme la vitesse maximale pouvant être atteinte par le jeu ou le nombre dessais maximums et la durée de session maximum (la session sarrête lorsquune des deux limites est atteinte). Des éléments comme les missiles ou les pièces peuvent être activés ou désactivés. Dautres paramètres peuvent être modifiés comme la vitesse maximale pouvant être atteinte par le jeu ou le nombre dessais maximums et la durée de session maximum (la session sarrête lorsquune des deux limites est atteinte).
Chaque partie peut comporter une seed aléatoire ou garder une seed constante. La seed constante peut être la seed par défaut, conservée dans les fichiers du jeu et non modifiée depuis le début du développement, ou bien une autre seed choisie par lutilisateur. La seed par défaut peut aussi être modifiée.  Chaque partie peut comporter une seed aléatoire ou garder une seed constante. La seed constante peut être la seed par défaut, conservée dans les fichiers du jeu et non modifiée depuis le début du développement, ou bien une autre seed choisie par lutilisateur. La seed par défaut peut aussi être modifiée. 
Enfin, le mode Rush permet de maximiser la vitesse du jeu pour accélérer la collecte de données et lapprentissage des bots. Ce mode de jeu remplace le rendu graphique par une console affichant en temps réel différentes informations. La vitesse du mode rush dépend entièrement de la puissance de lordinateur combinée à la complexité de calcul générée par les bots. Enfin, le mode Rush permet de maximiser la vitesse du jeu pour accélérer la collecte de données et lapprentissage des bots. Ce mode de jeu remplace le rendu graphique par une console affichant en temps réel différentes informations. La vitesse du mode rush dépend entièrement de la puissance de lordinateur combinée à la complexité de calcul générée par les bots.
Il est envisagé de permettre de choisir un niveau d'entraînement pour chaque bot.  Il est envisagé de permettre de choisir un niveau d'entraînement pour chaque bot. 
### Le jeu : ### Le jeu :
![image du jeu plus travaillé](images/img10PNG.PNG) ![image du jeu plus travaillé](img10PNG.jpg)
Lécran de jeu exécutera en temps réel la reproduction du jeu Jetpack Joyride en rendu graphique (ou console si le mode Rush est activé). Lécran de jeu exécutera en temps réel la reproduction du jeu Jetpack Joyride en rendu graphique (ou console si le mode Rush est activé).
Lavatar, représentant la célèbre mascotte officieusement officielle du BUT INFO, Dino, peut se déplacer de haut en bas. Le décor et les autres éléments du jeu se déplacent de droite à gauche. Lavatar, représentant la célèbre mascotte officieusement officielle du BUT INFO, Dino, peut se déplacer de haut en bas. Le décor et les autres éléments du jeu se déplacent de droite à gauche.
Dino doit se déplacer verticalement de manière à éviter les lasers et autres malus et attraper le plus de pièces possibles. Dino doit se déplacer verticalement de manière à éviter les lasers et autres malus et attraper le plus de pièces possibles.
Si plusieurs bots jouent ensemble, leurs avatars apparaîtront en transparence. Lavatar du joueur humain sera toujours opaque. Si plusieurs bots jouent ensemble, leurs avatars apparaîtront en transparence. Lavatar du joueur humain sera toujours opaque.
Si plusieurs parties sont configurées, chaque partie suivante commencera dès la fin de la partie en cours. Si plusieurs parties sont configurées, chaque partie suivante commencera dès la fin de la partie en cours.
A la fin de la dernière partie, lapplication changera dinterface pour afficher le menu de consultation de données ne comprenant que les données obtenues pendant la session jouée. A la fin de la dernière partie, lapplication changera dinterface pour afficher le menu de consultation de données ne comprenant que les données obtenues pendant la session jouée.**
### Prévision des Étapes de création
Nous avons décidé d'organiser le processus de création du projet en travaillant simultanément sur plusieurs axes afin de rester constamment informés de son évolution et d'éviter d'être dépassés, tout en permettant à chacun de commencer à travailler sur des aspects additionnels. Ainsi, nous avons structuré la création en plusieurs étapes :
Semaine 1-3 : Apprentissage des outils (Unity, C#, Python, ML Agent)
Semaine 3-4 : Conception de l'interface
Semaine 5-6 : Conception du jeu
Semaine 7-8 : Création de l'IA "Sachant"
Semaine 9-10 : Création de l'IA "Observateur"
Semaine 11-12 : Création de l'IA "Calculateur"
Semaine 13-14 : Création de l'IA "Calculateur Intelligent"
Semaine 15-16 : Collecte de données des IA
diagramme de Gantt disponible dans cahier des charges technique.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,72 +0,0 @@
Rapport de Réunion du 11 Octobre en visioconférence
Jour du choix du projet et de sa découverte , mise en place des outils et des droits pour les documents.
Réunion entre Simon, Victor et Ethan pour décider du jeu adopté et recherché .
Le choix fut de faire un jetpack Joyride en y incorporent un joueur bot au lieu du joueur ou plusieurs propositions de réponse à la demande fut trouvé .
Diverses maquettes de ses propositions 3 retenus :
-Lomniscient(Neuronal)
-Lobservateur(Neuronal)
-LItérateur(Procédural)
Réunion en visioconférence ensemble avec M.Florent Madelaine
Proposition des subjectivions précédente , usage de Unity pour la création du jeu, discutions autour des propositions ou lomniscient et lobservateur serait ceux que nous envisagions de faire en seule joueur bot ( lun deux) et la possibilité dajouter un joueur humain.
Proposition de M.Madelaine dincorporer les connaissances et lusage de MIN, et MAX appris durant nos cours .
Les trois IA cité précédemment fut retenus avec pour objectif de les ajouter et de pouvoir les comparer sur des chemins pré-générer ou procédural .
Rapport de Réunion du 18 Octobre en visioconférence 
Discutions entre Simon, Victor et Ethan sur les ressources pour aboutir à notre projet et des connaissances et outils nécessaires pour y parvenir . Apprendre Unity , sprites utilisé ou pour plus tard. Départ du projet sur unity .
Rapport de discutions du 7 Novembre
Discutions sur la mise en place du principe du Min , Max dans le projet , possibilité dajouter un système de score et des pièces malus déposé par min pour diminuer le score de max .
Rapport de Réunion du 20 Novembre en visioconférence
Mise en place et début des premier documents , cahier des charges , gant prévisionnel et mise en place des premiers assets sur le git.
Rapport de Réunion du 24 Novembre en présentiel sans Ethan
Réunion sur lavancement du projet , révision de la génération des niveaux
Rapport de Réunion du 28 Novembre en visioconférence
Révision des cahiers des charges fonctionnel et technique afin de les transmettre a M.Madelaine
Difficulté sur la mise en place de ces fichiers en markdown sur le git
Rapport de Discutions du 4 Décembre
Résolution du problème suivis dune vérification et refonte des documents avant dêtre envoyé .
Rapport de Réunion du 20 Décembre
Mise en place du système de raycaste sur le jeu , incorporation des missiles, obstacle et pièces .
Rapport de Réunion du 1er mars en présentiel sans Ethan
Réunion sur lavancement du projet et la gestions de travail.
Rapport de Réunion du 10 Mars en présentiel
Présentation du jeu avec une interface et un menu , dun système de pièces et missiles .
discutions autour des performances et de la vitesse du jeu et mise en place de date pour de futur réunions .
Rapport de Réunion du 19 Mars en visioconférence
Remise en route des taches attribués et créations des ressources pour créer les patterns
Rapport de Réunion du 25 Mars en présentiel
Réunion sur lavancement du projet,révision des documents disponibles et demandés.
Rapport de discutions du 2 Avril
Révision et recherche dutilité des formes des patterns en fonction de la vitesse du jeu.
Rapport de Réunion du 3 avril en présentiel avec M.Madelaine
Discutions sur lavancement du projet , des manques de documents sur le git , nécessite une explication pour déployer et utiliser le jeu .
Discussions sur la mise en place dune difficulté via une augmentation de la vitesse du jeu
vérifier les assets et les optimiser pour diminuer la taille du jeu
mise en place des documents de suivit et avoir des traces écrites des réunions.
Rapport de discutions du 24 Avril
Vérification des documents présent et remise en conformité
recherche dinformation sur loptimisation du jeu .
Rapport de discutions en visioconférence du 01 Mai
Mise en place dun modèle de données ( JSON) pour stocker les données
Mise en place de micros-services (plusieurs API).
Rapport de réunion en présentiel du 16 Mai
Refonte des documents existant.

View File

@ -1,223 +0,0 @@
# Projet Jetpack DinoAI
Comparaison de Différentes Intelligences Artificielles dans un Jeu Simple
Cahier des charges techniques
Par :
Victor Descamps
Simon Catanese
Ethan Richard
Tuteur :
Florent Madelaine
fait le 18/11/2023
# Sommaire :
- [Contexte](#1-contexte)
- - [Sujet](#sujet)
- [Technologies envisagées ](#2-technologies-envisagées)
- - [Plateforme de développement](#2-1-plateforme-de-développement)
- [Langage de programmation](#2-2-langage-de-programmation)
- [Utilisation de ML-Agents](#2-3-utilisation-de-ml-agents)
- [Modélisation des personnages et des environnements](#2-4-modélisation-des-personnages-et-des-environnements)
- [Contrôles du jeu](#2-5-contrôles-du-jeu)
- [Gestion du jeu](#2-6-gestion-du-jeu)
- [Tests et débogage](#2-7-tests-et-débogage)
- [Équipe de développement](#3-équipe-de-développement)
- [Les IA](#4-les-ia)
- - [L'omniscient (Neuronal)](#l-omniscient-neuronal-img-title-src-images-img3png-png-alt-loading-ag-445-data-align-inline-width-50)
- [L'observateur (Neuronal](#l-observateur-neuronal-img-title-src-images-img4png-png-alt-loading-ag-455-width-50)
- [L'itérateur(Procédural](#l-itérateur-procédural-img-title-src-images-img5png-png-alt-loading-ag-463-width-64)
- [L'itérateur intelligent (Procédural et neuronal)](#l-itérateur-intelligent-procédural-et-neuronal-img-title-src-images-img6png-png-alt-loading-ag-481-width-58)
- [Livrables](#5-livrables)
- [Bibliographie](#6-bibliographie)
# 1. Contexte
## Sujet :
Florent Madelaine. 
Expérimentation Bots pour un ou des jeux. 
Programmer un jeu original avec idéalement des éléments qui en font un challenge pour la réalisation dIA (hasard, phase denchère changeant le payoff, multijoueur, combinatoire importante, ...). Il conviendra de choisir un jeu pour lequel il est possible de réduire la combinatoire du jeu de manière naturelle et de désactiver/activer des règles pour en étudier des variantes naturelles. Il faudra ensuite créer, améliorer et expérimenter avec divers algorithmes pour converger vers des bots offrant un challenge raisonnable pour un humain.
# 2.Technologies envisagées :
## 2.1. Plateforme de développement
Le projet sera développé sur Unity, version 22.3.11f1.
Unity est un moteur de jeu gratuit et simple daccès offrant une grande liberté de création et une grande collection doutils divers.
De nombreux cours sont disponibles sur internet pour apprendre son utilisation. 
Unity est aussi compatible avec de nombreuses plateformes dont Windows, MacOs et Linux.
## 2.2. Langage de programmation
Le langage de programmation utilisé sera C# pour le développement dans l'environnement Unity. Il sagit du langage natif de la plateforme. 
C# est un langage de programmation objet très ressemblant au Java.
## 2.3. Utilisation de ML-Agents
ML-Agents sera intégré pour la conception et l'entraînement des intelligences artificielles. La version 2.0.1 de ML-Agents sera utilisée. 
ML agent est un asset (outil additionnel) interne à Unity qui permet d'encadrer un apprentissage neuronal. Les scripts d'IA seront développés en utilisant ML-Agents pour permettre aux bots d'apprendre à jouer au jeu de manière autonome. 
Les modèles neuronaux seront entraînés en utilisant des algorithmes d'apprentissage par renforcement.
## 2.4. Modélisation des personnages et des environnements
Des sprites 2D (images) seront créés pour représenter lavatar, les obstacles et l'environnement du jeu. 
Loutil de graphisme Photoshop sera majoritairement utilisé pour la conception des sprites.
## 2.5 Contrôles du jeu
Les contrôles du jeu seront mis en œuvre pour un joueur humain ainsi que pour les IA.
La seule commande permettra de modifier la position verticale du joueur tandis que le niveau défilera tout seul de droite à gauche.
## 2.6 Gestion du jeu
Un système de gestion de jeu sera mis en place, incluant le chargement du niveau, la gestion des collisions, la génération procédurale d'obstacles et la sauvegarde des scores et de données diverses.
Les données enregistrées durant lexécution de lapplication seront stockées dans des fichiers json ou xml dans le répertoire de lapplication.
## 2.7 Tests et débogage
Des scénarios de test exhaustifs seront élaborés pour chaque composant du jeu. 
Les fonctionnalités seront soumises à des tests unitaires. Des outils de débogage de Unity seront utilisés pour résoudre les problèmes potentiels.
# 3. Équipe de développement
- **Ethan, Simon et Victor portent tous les 3 le rôle de chef de projet et de développeur.** 
- **Simon sera également responsable des tests de lapplication**
- **Ethan sera aussi impliqué dans la documentation du projet**
- **Victor sera plus spécifiquement impliqué dans limplémentation des systèmes neuronaux dans le jeu.**
Loutil en ligne Trello nous servira à planifier les tâches. 
# 4.Les IA :
Les concepts dIA suivant sont expérimentaux et seront sujets à des changements suivant lavancée du développement du projet et de notre documentation sur la programmation de systèmes neuronaux.
Le rôle des bots est de décider à chaque instant T de faire monter ou de laisser descendre lavatar. Tous auront une source de données différentes leur permettant de prendre une décision.
## Lomniscient (Neuronal)<img title="" src="images/img3PNG.PNG" alt="loading-ag-445" data-align="inline" width="50">
Ce bot connaît un grand nombre de données brutes. Entre autres : 
- La vitesse de défilement du jeu,
- La position verticale du joueur,
- Les coordonnées des deux points des deux lasers les plus proches,
- Les coordonnées de tout autre danger proche,
- les coordonnées dune zone comportant des pièces
LIA devra réussir à faire les bonnes corrélations dans ce grand nombre de données pour prendre les bonnes décisions au cours du temps. Elle nobtiendra pas de données enrichies pouvant faciliter la prise de décision, ces données brutes seront son seul guide. 
![](images/img11PNG.PNG)
## Lobservateur (Neuronal)<img title="" src="images/img4PNG.PNG" alt="loading-ag-455" width="50">
Ce bot reçoit en temps réel des données de proximité de lavatar avec les éléments de lenvironnement et des données sur la nature de ces éléments.
Un ensemble de RayCast partant de lavatar dans des directions précises seront utilisés pour recueillir les données nécessaires. 
A la manière dun radar de voiture, lIA devra analyser sa proximité avec des obstacles puis prendra une décision pour ajuster sa trajectoire.
Lobservateur devra apprendre à éviter les objets néfastes sapprochant trop de lavatar et essayer de se rapprocher des pièces pour maximiser son score. 
Cinq RayCasts couvrent probablement un champ de vision suffisamment large et dense pour une analyse efficace de lenvironnement, mais des nombres différents seront essayés pour essayer de maximiser les performances du bot.
La longueur des RayCasts correspondra à la taille de lécran, et les premiers objets rencontrés dans leur trajectoire seront traités comme des données utilisables par le système neuronal de lobservateur. Leur distance par rapport à lavatar et leur nature (bordure, obstacle, bonus, malus) seront transmises au bot. 
![](images/img12PNG.PNG)
## LItérateur (Procédural)<img title="" src="images/img5PNG.PNG" alt="loading-ag-463" width="64">
Le bot itérateur ne fonctionne pas par apprentissage mais par itérations de calculs. 
Son fonctionnement consiste à calculer de nombreux chemins différents par itérations puis à sélectionner le meilleur chemin trouvé pour poursuivre la partie. À chaque intervalle , le bot pourra effectuer et maintenir une action (monter ou descendre) le temps de cet intervalle.  Lensemble des actions forme un chemin donné.
Le moteur de jeu va tester virtuellement les chemins à tester et le bot va itérer des calculs de suite dactions un certain nombre de fois. Les chemins proposés amenant à la collision avec un obstacle seront écartés. Le chemin sans collision et apportant le plus de points au score sera sélectionné et le bot recommencera une suite ditérations de calculs vers la moitié de ce chemin pour améliorer sa trajectoire. 
Si tous les chemins mènent à une collision avec un obstacle, le chemin retardant le plus la collision sera sélectionné. Lensemble des actions possibles dans une suite dintervalles représente un arbre binaire où chaque chemin est un parcours de la base de larbre vers une feuille. Litérateur procède à un parcours en profondeur de larbre pour trouver le meilleur chemin.
Nous envisageons dutiliser lapproche de l'algorithme glouton afin dobtenir un chemin satisfaisant sans dépenser trop de temps de calcul en parcourant larbre entier.Pendant le parcours de larbre, si un chemin testé mène à une collision avec un obstacle ou avec un bonus, litérateur reviendra avec une nouvelle suite dactions jusquà quelques itérations avant la collision puis procèdera à un nouveau parcours en profondeur à partir de cette nouvelle base pour essayer doptimiser son score et sa survie. ![loading-ag-468](images/img13PNG.PNG)
## L itérateur intelligent (Procédural et neuronal)<img title="" src="images/img6PNG.PNG" alt="loading-ag-481" width="58">
Bot optionnel que lon implémentera si le temps nous le permet. 
Nous navons pas encore de concept clair pour ce bot, nous chercherons une possible implémentation de réseau neuronal fonctionnant de pair avec le calcul itératif de chemins pour améliorer les performances du bot précédent.
# 5. Livrables
Le dépôt git du projet contiendra le build de la dernière version fonctionnelle de lapplication en plus des documentations annexes. Le projet Unity en lui-même est partagé entre les membres du PT sur la plateforme de collaboration officielle du Unity. Nous veillerons à inclure le code source du projet final dans le dépôt git en plus du build complet de lapplication.
# 6. Bibliographie :
Apprentissage du C# : 
Série de vidéos sur les aspects du C# dans la programmation sous Unity
[Learn C# BASICS in 10 MINUTES! - YouTube](https://www.youtube.com/watch?v=IFayQioG71A&list=PLzDRvYVwl53t2GGC4rV_AmH7vSvSqjVmz&pp=iAQB)
Documentation unity : 
Documentation officiel de la plateforme, utile pour tout besoin technique.
[https://docs.unity.com/](https://docs.unity.com/)
Documentation ML-Agents : 
Documentation officielle de ML-Agents qui nous servira  à implémenter des systèmes neuronaux.
[Toolkit Documentation - Unity ML-Agents Toolkit](https://unity-technologies.github.io/ml-agents/ML-Agents-Toolkit-Documentation/)

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 760 KiB

After

Width:  |  Height:  |  Size: 760 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

Binary file not shown.

View File

@ -1,264 +0,0 @@
using System;
using System.IO;
using UnityEngine;
using Newtonsoft.Json;
using static GameHandler;
using static LevelSpawner;
using static PlayerScript;
using static PlayersHandler;
namespace Assets.Scripts
{
public class DataBearer : SingletonMB<DataBearer>
{
#region PROPERTIES
public bool DataSaved { get; private set; }
public Attempt CurrentAttempt { get; private set; }
public SessionData Session
{
get
{
if (_session == null)
{
_session = GetSettings();
}
return _session;
}
private set
{
_session = value;
}
} private SessionData _session = null;
public GameState GameState { get; set; } = new();
public Players Players { get { return Session.Players; } }
public Rules Rules { get { return Session.Rules; } }
#endregion
#region VARIABLES
private GameHandler _gameHandler;
private ScoreHandler _scoreHandler;
private PlayersHandler _playerHandler;
private LevelSpawner _levelSpawner;
#endregion
#region EVENTS
public void RegisterAttempt_OnAttemptEnded(object sender, AttemptEventArgs eventArgs)
{
Session.Attempts.Add(CurrentAttempt);
CurrentAttempt = null;
} //
public void RegisterPlayer_OnPlayerInstanciated(object sender, PlayerEventArgs args)
{
var player = args.player;
var newPlayer = new RegisteredPlayer()
{
Name = player.Name,
Id = player.Id,
Human = player.IsHuman
};
if(!Session.RegisteredPlayers.Exists(rp => rp.Name == newPlayer.Name))
{
Session.RegisteredPlayers.Add(newPlayer);
}
player.OnBonusTouched += RecordBonus_OnBonusTouched;
player.OnObstacleTouched += RecordObstacle_OnObstacleTouched;
} //
public void CreateAttemptRecord_OnAttemptStarted(object sender, AttemptEventArgs args)
{
CurrentAttempt = args.attempt;
foreach(var player in Session.RegisteredPlayers)
{
PlayerAttempt playerAttempt = new PlayerAttempt()
{
PlayerName = player.Name
};
CurrentAttempt.PlayersAttempts.Add(player.Name, playerAttempt);
}
} //
public void RecordAttemptObstacle_OnPrefabSpawned(object sender, PrefabSpawnedEventArgs args)
{
LevelPrefab obstacle = new() {
PrefabName = args.pattern.name, //todo : prefab name has "(clone)" at the end
PrefabNumber = CurrentAttempt.Level.Count + 1,
TickTimeWhenTouched = _gameHandler.TickAttemptDuration
};
CurrentAttempt.Level.Add(obstacle);
} //
public void RecordObstacle_OnObstacleTouched(object sender, PrefabTouchedEventArgs args)
{
var prefab = args.objectTouched;
TouchedGameObject touchedGameObject = new()
{
FullObjectName = prefab.name, //todo : prefab name is laser... It should be full pattern name
ObjectTypeName = SortingLayer.IDToName(prefab.layer), // todo : <unknown layer>
GameSpeed = _gameHandler.MapSpeed,
PrefabName = prefab.name,
TickTimeWhenTouched = _gameHandler.TickAttemptDuration
};
CurrentAttempt.PlayersAttempts[args.playerName].ObstaclesTouched.Add(touchedGameObject);
RecordPlayerScore(args.playerId, args.playerName);
} //
public void RecordBonus_OnBonusTouched(object sender, PrefabTouchedEventArgs args)
{
var prefab = args.objectTouched;
TouchedGameObject touchedGameObject = new()
{
FullObjectName = prefab.name,
ObjectTypeName = SortingLayer.IDToName(prefab.layer),
GameSpeed = _gameHandler.MapSpeed,
PrefabName = prefab.name,
TickTimeWhenTouched = _gameHandler.TickAttemptDuration
};
CurrentAttempt.PlayersAttempts[args.playerName].BonusTouched.Add(touchedGameObject);
RecordPlayerScore(args.playerId, args.playerName);
} //
#endregion
#region ENDPOINTS
public void ResetSession()
{
_session = null;
}
public void SaveData()
{
string directoryPath = Path.Combine(Application.persistentDataPath, "SessionsData");
if (!Directory.Exists(directoryPath))// Crée le répertoire s'il n'existe pas déjà
{
Directory.CreateDirectory(directoryPath);
}
var existingFiles = Directory.GetFiles(directoryPath, "*.json");
int sessionNumber = existingFiles.Length + 1;
Session.SessionNumber = sessionNumber;
string jsonData = JsonConvert.SerializeObject(Session);
string fileName = $"Session_{sessionNumber}.json";
string filePath = Path.Combine(directoryPath, fileName);
File.WriteAllText(filePath, jsonData);
DataSaved = true;
Debug.Log($"Session saved to {filePath}");
}
public void RecordSessionTime(float time)
{
Session.SessionTime = time;
}
public void RecordSessionDate(DateTime date)
{
Session.Date = date;
}
/// <summary>
/// Record all players scores
/// </summary>
public void RecordPlayersScore()
{
Session.RegisteredPlayers.ForEach(p =>
{
RecordPlayerScore(p.Id, p.Name);
});
}
/// <summary>
/// Find and record a player score
/// </summary>
public void RecordPlayerScore(int playerId, string playerName)
{
int score = (int) _scoreHandler.GetPlayerScore(playerId);
var tickTime = _gameHandler.TickAttemptDuration;
GenericVector<int, int> ScoreByTime = new() { X = tickTime, Y = score };
CurrentAttempt.PlayersAttempts[playerName].PlayerScoresOverTicksTime.Add(ScoreByTime);
}
/// <summary>
/// Record a player final score
/// </summary>
public void RecordPlayerFinalScore(int score, string playerName)
{
CurrentAttempt.PlayersAttempts[playerName].FinalScore = score;
}
/// <summary>
/// Record the distance parcoured by the player
/// </summary>
public void RecordPlayerDistance(string playerName)
{
CurrentAttempt.PlayersAttempts[playerName].Distance = (int)_gameHandler.TotalDistance;
}
public void RecordPlayerAttemptDuration(string playerName)
{
CurrentAttempt.PlayersAttempts[playerName].AttemptDurationSeconds = _gameHandler.TimeAttemptDuration;
CurrentAttempt.PlayersAttempts[playerName].AttemptDurationTicks = _gameHandler.TickAttemptDuration;
}
#endregion
#region METHODS
private SessionData GetSettings()
{
SessionData session = new();
session.Players.Human = bool.Parse(PlayerPrefs.GetString(Settings.HumanPlayerSelected));
session.Players.OmnicientBot = bool.Parse(PlayerPrefs.GetString(Settings.OmnicientBotSelected));
session.Players.ObserverBot = bool.Parse(PlayerPrefs.GetString(Settings.ObserverBotSelected));
session.Players.IteratorBot = bool.Parse(PlayerPrefs.GetString(Settings.IteratorBotSelected));
session.Rules.ChangeSeedEachTry = bool.Parse(PlayerPrefs.GetString(Settings.ChangeSeedSetting));
session.Rules.UsesDefaultSeed = bool.Parse(PlayerPrefs.GetString(Settings.UseDefaultSeedSetting));
session.Rules.Lasers = bool.Parse(PlayerPrefs.GetString(Settings.LaserSettingToggle));
session.Rules.Missiles = bool.Parse(PlayerPrefs.GetString(Settings.MissileSettingToggle));
session.Rules.BonusCoins = bool.Parse(PlayerPrefs.GetString(Settings.CoinSettingToggle));
session.Rules.MalusCoins = bool.Parse(PlayerPrefs.GetString(Settings.BadCoinSettingToggle));
session.Rules.ScoreOnDistance = bool.Parse(PlayerPrefs.GetString(Settings.SurviveScoreSettingToggle));
session.Rules.RushMode = bool.Parse(PlayerPrefs.GetString(Settings.RushModeSettingToggle));
session.Rules.MaxDistance = PlayerPrefs.GetFloat(Settings.MaxDistanceSettingValue);
session.Rules.MaxAttempts = PlayerPrefs.GetInt(Settings.MaxTrySettingValue);
session.Rules.MaxSessionTime = PlayerPrefs.GetInt(Settings.MaxTimeSettingValue);
session.Rules.MaxSpeed = PlayerPrefs.GetFloat(Settings.MaxSpeedSettingValue);
session.Rules.StartSpeed = PlayerPrefs.GetFloat(Settings.StartSpeedSettingValue);
session.Rules.DifficultyMultiplier = PlayerPrefs.GetFloat(Settings.DifficultyMultiplierSettingValue);
return session;
}
#endregion
#region LIFECYCLE
protected override void Awake()
{
DataSaved = false;
Session = GetSettings();
base.Awake();
_gameHandler = GameHandler.Instance;
_scoreHandler = ScoreHandler.Instance;
_playerHandler = PlayersHandler.Instance;
_levelSpawner = LevelSpawner.Instance;
_gameHandler.OnAttemptEnded += RegisterAttempt_OnAttemptEnded;
_gameHandler.OnAttemptStarted += CreateAttemptRecord_OnAttemptStarted;
_playerHandler.OnPlayerInstanciated += RegisterPlayer_OnPlayerInstanciated;
_levelSpawner.OnPrefabSpawned += RecordAttemptObstacle_OnPrefabSpawned;
}
#endregion
}
}

View File

@ -1,164 +0,0 @@
using Assets.Scripts;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using static GameHandler;
public class GameController : SingletonMB<GameController>
{
#region PROPERTIES
public bool IsExiting { get; private set; }
public SessionData Session { get; private set; } = new SessionData();
#endregion
#region VARIABLES
private GameHandler _gameHandler;
private ScoreHandler _scoreHandler;
private LevelSpawner _LevelSpawner;
private PlayersHandler _playerHandler;
private DataBearer _dataBearer;
private int maxSessionTime;
private float currentSessionTime;
private int maxAttempts;
private int currentAttempts;
#endregion
#region EVENTS
public event EventHandler<SessionEventArgs> OnSessionEnded;
public class SessionEventArgs : EventArgs
{
public SessionData session;
public SessionEventArgs(SessionData session)
{
this.session = session;
}
}
private void ProcessNextAttempt_OnAttemptEnded(object sender, AttemptEventArgs args)
{
if(!IsExiting)
{
PrepareNextRound();
}
}
#endregion
#region METHODS
/// <summary>
/// Initialize a new round if there are more available attempts
/// </summary>
public void PrepareNextRound()
{
currentAttempts+=1;
if(maxAttempts == 0 || _dataBearer.Session.Attempts.Count < maxAttempts)
{
_gameHandler.InitAttempt();
}
else
{
ExitToMenu("Nombre maximum d'essais atteint");
}
}
public void ExitToMenu(string message)
{
IsExiting = true;
Debug.Log("Session END");
//StartCoroutine(ExitToMenuCoroutine(message));
_gameHandler.ResumeGame();
//yield return null;
if (!_gameHandler.AttemptEnding) _gameHandler.HaltAttempt(message);
Clean();
SceneManager.LoadScene("MainMenu");
_dataBearer.ResetSession();
}
//private IEnumerator ExitToMenuCoroutine(string message)
//{
// _gameHandler.ResumeGame();
// //yield return null;
// if(!_gameHandler.AttemptEnding) _gameHandler.HaltAttempt(message);
// _dataBearer.ResetSession();
// Clean();
// SceneManager.LoadScene("MainMenu");
//}
#endregion
#region LIFECYCLE
// Start is called before the first frame update
private void Start()
{
Debug.Log($"Start session for {Session.Rules.MaxAttempts} rounds");
_dataBearer.RecordSessionDate(DateTime.Now);
currentSessionTime = 0f;
PrepareNextRound();
}
// Awake is called before Start
protected override void Awake()
{
base.Awake();
_gameHandler = GameHandler.Instance;
_scoreHandler = ScoreHandler.Instance;
_playerHandler = PlayersHandler.Instance;
_dataBearer = DataBearer.Instance;
_gameHandler.OnAttemptEnded += ProcessNextAttempt_OnAttemptEnded;
maxAttempts = _dataBearer.Rules.MaxAttempts;
maxSessionTime = _dataBearer.Rules.MaxSessionTime;
currentAttempts = 0;
}
// Update is called once per frame
void Update()
{
currentSessionTime += Time.deltaTime;
_dataBearer.RecordSessionTime(currentSessionTime);
if (maxSessionTime > 0 && currentSessionTime >= maxSessionTime)
{
currentSessionTime = maxSessionTime;
_dataBearer.RecordSessionTime(currentSessionTime);
ExitToMenu("Temps <20>coul<75>");
}
}
//Unsubscribe events, save databearer;
private void OnDestroy()
{
Clean();
}
private void OnApplicationQuit()
{
Clean();
}
public void Clean()
{
_gameHandler.OnAttemptEnded -= ProcessNextAttempt_OnAttemptEnded;
if (!_dataBearer.DataSaved)
{
//datasaving logic
_dataBearer.RecordSessionTime(currentSessionTime);
_dataBearer.SaveData();
}
}
#endregion
}

View File

@ -1,299 +0,0 @@
using Assets.Scripts;
using System;
using System.Collections;
using Unity.VisualScripting;
using UnityEditor.Rendering;
using UnityEngine;
using static PlayersHandler;
/// <summary>
/// GameHandler gère le fonctionnement général d'une partie de jeu. Il se charge de faire apparaître les joueurs
/// </summary>
public class GameHandler : SingletonMB<GameHandler>
{
#region PROPERTIES
int Seed
{
get { return _seed; }
set
{
_seed = value;
UnityEngine.Random.InitState(value);
}
} private int _seed;
public float FrameDistance { get; private set; }
public float TotalDistance { get; private set; }
[SerializeField] public float MapSpeed { get; private set; }
public Attempt CurrentAttempt { get; private set; }
public bool MaxDifficultyReached { get; private set; }
public bool IsPaused { get; private set; } = false;
public bool HaveHumanPlayer { get; private set; } = false;
public bool AttemptEnding { get; private set; }
public int TickAttemptDuration { get; private set; }
public float TimeAttemptDuration {get; private set;}
#endregion
#region VARIABLES
private const float DIFFICULTY_DAMP = 0.01f;
[SerializeField] private GameObject finishLine;
private ScoreHandler _scoreHandler;
private LevelSpawner _levelSpawner;
private PlayersHandler _playerHandler;
private DataBearer _dataBearer;
private TileSpawner _tileSpawner;
private Vector3 playerSpawnPosition = new Vector3(-3, 0, 0);
private float maxSpeed;
private float maxDistance;
private bool raiseScoreOnDistance;
private float difficultyMultiplier;
private bool finishLineSpawned;
private short tickCounter;
#endregion
#region EVENTS
public event EventHandler<AttemptEventArgs> OnAttemptStarted;
public event EventHandler<AttemptEventArgs> OnAttemptEnding;
public event EventHandler<AttemptEventArgs> OnAttemptEnded;
public event EventHandler<EventArgs> OnGamePaused;
public event EventHandler<EventArgs> OnGameResumed;
public event EventHandler<EventArgs> OnInstanciatingPlayers;
public class AttemptEventArgs : EventArgs
{
public Attempt attempt;
public AttemptEventArgs(Attempt attempt) { this.attempt = attempt; }
}
public void HaltAttempt_OnAllPlayersFinished(object sender, EventArgs agrs)
{
HaltAttempt("Tous les joueurs sont Out");
}
public void CheckIfHumanPlayer_OnPlayerInstanciated(object sender, PlayerEventArgs args)
{
if (args.player.IsHuman)
{
HaveHumanPlayer = true;
}
}
#endregion
#region METHODS
/// <summary>
/// Return a seed which depend on selected settings and attempt number
/// </summary>
private int SetSeed()
{
int seed;
if (_dataBearer.Session.Rules.ChangeSeedEachTry & CurrentAttempt.AttemptNumber > 1) //Get a random seed each new try
{
seed = UnityEngine.Random.Range(100000000, 1000000000); //generate a random 9 digits seed
}
else //Get either default seed or custom seed
{
seed = _dataBearer.Session.Rules.UsesDefaultSeed
? PlayerPrefs.GetInt(Settings.DefaultSeedSettingValue)
: PlayerPrefs.GetInt(Settings.CustomSeedSettingValue);
}
return seed;
}
private void RaiseScore()
{
float scoreToAdd = FrameDistance;// * Time.deltaTime;
_scoreHandler.AddScoreToAllAlivePlayers(scoreToAdd);
}
private void RaiseDifficulty()
{
if(MapSpeed > maxSpeed)
{
MapSpeed = maxSpeed;
MaxDifficultyReached = true;
}
MapSpeed += MapSpeed * difficultyMultiplier * Time.deltaTime;
}
private void SpawnPlayers()
{
if (_dataBearer.Session.Players.Human)
_playerHandler.InstanciatePlayer<HumanPlayerBehaviour>(playerSpawnPosition);
if (_dataBearer.Session.Players.OmnicientBot)
_playerHandler.InstanciatePlayer<OmnicientBehaviour>(playerSpawnPosition);
if (_dataBearer.Session.Players.ObserverBot)
_playerHandler.InstanciatePlayer<ObserverBehaviour>(playerSpawnPosition);
if (_dataBearer.Session.Players.IteratorBot)
_playerHandler.InstanciatePlayer<IteratorBehaviour>(playerSpawnPosition);
}
#endregion
#region ENDPOINTS
public void PauseGame()
{
if (!IsPaused)
{
StartCoroutine(PauseGameCoroutine());
}
}
private IEnumerator PauseGameCoroutine()
{
yield return null;
Time.timeScale = 0; // Met le jeu en pause
IsPaused = true;
OnGamePaused.Invoke(this, new EventArgs());
}
public void ResumeGame()
{
if (IsPaused)
{
Time.timeScale = _dataBearer.Rules.RushMode ? 15 : 1; // Reprend le jeu
IsPaused = false;
OnGameResumed.Invoke(this, new EventArgs());
}
}
public void InitAttempt()
{
TimeAttemptDuration = 0;
TickAttemptDuration = 0;
Debug.LogWarning("Initate Attempt !");
AttemptEnding = false;
CurrentAttempt = new Attempt();
CurrentAttempt.AttemptNumber = _dataBearer.Session.Attempts.Count + 1;
Seed = SetSeed();
CurrentAttempt.Seed = Seed;
TotalDistance = 0;
MapSpeed = _dataBearer.Rules.StartSpeed;
raiseScoreOnDistance = _dataBearer.Rules.ScoreOnDistance;
maxSpeed = _dataBearer.Rules.MaxSpeed;
difficultyMultiplier = _dataBearer.Rules.DifficultyMultiplier * DIFFICULTY_DAMP;
if(_dataBearer.Rules.RushMode){
Time.timeScale = 15;
foreach (var renderer in FindObjectsOfType<Renderer>())
{
if(renderer.gameObject.GetComponent<ScorePannelScript>()==null)
{renderer.enabled = false;}
}
}
else{
Time.timeScale = 1;
}
MaxDifficultyReached = false;
TotalDistance = 0;
FrameDistance = 0;
finishLineSpawned = false;
tickCounter = 0;
SpawnPlayers();
_scoreHandler.AddScoreToAllAlivePlayers(500);
OnAttemptStarted?.Invoke(this, new AttemptEventArgs(CurrentAttempt));
}
/// <summary>
/// Halt the current attempt
/// </summary>
public void HaltAttempt(string message)
{
Console.WriteLine(message);
AttemptEnding = true;
OnAttemptEnding?.Invoke(this, new AttemptEventArgs(CurrentAttempt));
OnAttemptEnded?.Invoke(this, new AttemptEventArgs(CurrentAttempt));
}
#endregion
#region LIFECYCLE
protected override void Awake()
{
base.Awake();
_scoreHandler = ScoreHandler.Instance;
_levelSpawner = LevelSpawner.Instance;
_playerHandler = PlayersHandler.Instance;
_dataBearer = DataBearer.Instance;
_tileSpawner = TileSpawner.Instance;
_playerHandler.OnAllPlayersFinished += HaltAttempt_OnAllPlayersFinished;
_playerHandler.OnPlayerInstanciated += CheckIfHumanPlayer_OnPlayerInstanciated;
difficultyMultiplier = _dataBearer.Rules.DifficultyMultiplier;
raiseScoreOnDistance = _dataBearer.Rules.ScoreOnDistance;
maxDistance = _dataBearer.Rules.MaxDistance;
}
void Update()
{
FrameDistance = MapSpeed * Time.deltaTime;
TotalDistance += FrameDistance;
TimeAttemptDuration += Time.deltaTime;
TickAttemptDuration += 1;
tickCounter += 1;
if (tickCounter > 1000)
{
//Debug.Log("1000 TICK " + TimeAttemptDuration);
tickCounter = 0;
_dataBearer.RecordPlayersScore();
}
if(maxDistance != 0 && TotalDistance > maxDistance)
{
HaltAttempt("Ligne d'arrivée atteinte");
}
if (raiseScoreOnDistance) RaiseScore();
if (!MaxDifficultyReached) RaiseDifficulty();
if(maxDistance != 0 && !finishLineSpawned && maxDistance-TotalDistance < 20)
{
Instantiate(finishLine, new Vector3(maxDistance - TotalDistance - 3, 0, -1), finishLine.transform.rotation);
finishLineSpawned = true;
}
if (Input.GetKeyDown(KeyCode.Escape))
(IsPaused ? (Action)ResumeGame : PauseGame)();
}
private void OnDestroy()
{
Clean();
}
private void OnDisable()
{
Clean();
}
private void Clean()
{
/*StopAllCoroutines();
_playerHandler.OnAllPlayersFinished -= HaltAttempt_OnAllPlayersFinished;
_playerHandler.OnPlayerInstanciated -= CheckIfHumanPlayer_OnPlayerInstanciated;*/
}
#endregion
}

View File

@ -1,215 +0,0 @@
using Assets.Scripts;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
using static PlayersHandler;
public class PlayersHandler : SingletonMB<PlayersHandler>
{
// Start is called before the first frame update
[SerializeField] private GameObject playerPrefab;
#region PROPERTIES
public Dictionary<int, PlayerScript> Players { get; private set; } = new();
#endregion
#region VARIABLES
private ScoreHandler _scoreHandler;
private GameHandler _gameHandler;
#endregion
#region EVENTS
/// <summary>
/// Fires when a player is instanciated in the game
/// </summary>
public event EventHandler<PlayerEventArgs> OnPlayerInstanciated;
/// <summary>
/// Fires when a player have finished their attempt
/// </summary>
public event EventHandler<PlayerEventArgs> OnPlayerFinished;
/// <summary>
/// Fires when all players have finished their attempt
/// </summary>
public event EventHandler<EventArgs> OnAllPlayersFinished;
public class PlayerEventArgs : EventArgs
{
public PlayerScript player;
public PlayerEventArgs(PlayerScript player)
{
this.player = player;
}
}
#endregion
#region ENDPOINTS
/// <summary>
/// Remove a player from the game and fire a PlayerFinished event. Fire AllPlayerFinished event if needed
/// </summary>
/// <param name="playerId"></param>
public void RemovePlayer(int playerId)
{
if (Players.TryGetValue(playerId, out PlayerScript player))
{
Players.Remove(playerId);
if(!_gameHandler.AttemptEnding)
OnPlayerFinished.Invoke(this, new PlayerEventArgs(player));
player.Dispose();
}
if(Players.Count == 0 && !_gameHandler.AttemptEnding)
{
OnAllPlayersFinished.Invoke(this, new EventArgs());
}
}
public bool InstanciatePlayer<T>(Vector3 position) where T : MonoBehaviour
{
var player = SpawnPlayer(new Vector3(position.x, position.y, -1));
PlayerScript script = player.GetComponent<PlayerScript>();
player.AddComponent<T>();
Players.Add(script.Id, script);
DecoratePlayer(player);
CreateScorePanel(script);
Debug.Log($"# Player \"{script.Name}\" instanciated !");
OnPlayerInstanciated?.Invoke(this, new PlayerEventArgs(script));
return true;
}
#endregion
#region METHODS
private GameObject SpawnPlayer(Vector3 position)
{
var player = Instantiate(playerPrefab, position, Quaternion.identity);
var script = player.GetComponent<PlayerScript>();
script.SetId(Players.Count);
return player;
}
private void CreateScorePanel(PlayerScript script)
{
_scoreHandler.AddScorePanel(script.Name, script.Id, script.Color);
}
//private GameObject InstantiatePlayer(string playerName, Vector3 position)
//{
// incrementialId++;
// var player = Instantiate(playerPrefab, position, Quaternion.identity);
// var script = player.GetComponent<PlayerScript>();
// script.SetName(playerName);
// script.SetId(incrementialId);
// _scoreHandler.AddScorePanel(script.Name, script.Id, script.Color);
// Players.Add(script);
// return player;
//}
private void DecoratePlayer(GameObject player)
{
Transform child = player.transform.Find("sprite");
PlayerScript script = player.GetComponent<PlayerScript>();
var components = child.GetComponentsInChildren<SpriteRenderer>();
var jetpackFront = child.transform.Find("JetpackFront").GetComponent<SpriteRenderer>();
var jetpackBack = child.transform.Find("JetpackBack").GetComponent<SpriteRenderer>();
float alpha = _gameHandler.HaveHumanPlayer && !script.IsHuman ? 0.4f : 1.0f;
var scriptColor = script.Color!=null ? script.Color : new Color(255, 255, 255);
scriptColor.a = alpha;
foreach ( var component in components )
{
component.color = new Color(255, 255, 255, alpha);
}
jetpackFront.color = scriptColor;
jetpackBack.color = scriptColor;
}
#endregion
#region LIFECYCLE
protected override void Awake()
{
base.Awake();
_scoreHandler = ScoreHandler.Instance;
_gameHandler = GameHandler.Instance;
}
#endregion
}
//public void InstanciateOmnicient(Vector3 position)
//{
// var bot = SpawnPlayer(position);
// PlayerScript script = bot.GetComponent<PlayerScript>();
// Players.Add(script.Id, script);
// DecoratePlayer(bot);
// CreateScorePanel(script);
// OnPlayerInstanciated?.Invoke(this, new PlayerEventArgs(script));
//}
//public void InstanciateObserver(Vector3 position)
//{
// var bot = SpawnPlayer(position);
// PlayerScript script = bot.GetComponent<PlayerScript>();
// Players.Add(script.Id, script);
// DecoratePlayer(bot);
// CreateScorePanel(script);
// OnPlayerInstanciated?.Invoke(this, new PlayerEventArgs(script));
//}
//public void InstanciateIterator(Vector3 position)
//{
// var bot = SpawnPlayer(position);
// PlayerScript script = bot.GetComponent<PlayerScript>();
// Players.Add(script.Id, script);
// DecoratePlayer(bot);
// CreateScorePanel(script);
// OnPlayerInstanciated?.Invoke(this, new PlayerEventArgs(script));
//}
//public void InstanciateHumanPlayer(Vector3 position)
//{
// Debug.Log("Instanciate Human");
// var humanPlayer = SpawnPlayer(new Vector3(position.x, position.y, -1));
// PlayerScript script = humanPlayer.GetComponent<PlayerScript>();
// script.IsHuman = true;
// humanPlayer.AddComponent<HumanPlayer>();
// DecoratePlayer(humanPlayer);
// CreateScorePanel(script);
// Players.Add(script.Id, script);
// OnPlayerInstanciated?.Invoke(this, new PlayerEventArgs(script));
//}

View File

@ -1,201 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
using Assets.Scripts;
using System;
using static GameController;
using static PlayersHandler;
using static GameHandler;
using System.Linq;
public class ScoreHandler : SingletonMB<ScoreHandler>
{
#region PROPERTIES
private Dictionary<int, PlayerScore> PlayersScores { get; set; }
private Dictionary<int, PlayerScore> OuttedPlayers { get; set; }
#endregion
#region VARIABLES
[SerializeField] private GameObject ScorePanelPrefab;
[SerializeField] private GameObject UiDocument;
private GameController _gameController;
private PlayersHandler _playersHandler;
private GameHandler _gameHandler;
private DataBearer _dataBearer;
#endregion
#region EVENTS
public event EventHandler<ScoreEventArgs> OnScoreReachingZero;
public class ScoreEventArgs : EventArgs
{
public int playerId;
public ScoreEventArgs(int id)
{
playerId = id;
}
}
internal void RemovePlayer_OnPlayerFinished(object sender, PlayerEventArgs args)
{
if (PlayersScores.TryGetValue(args.player.Id, out PlayerScore looser))
{
_dataBearer.RecordPlayerDistance(looser.PlayerName);
_dataBearer.RecordPlayerScore(looser.PlayerId, looser.PlayerName);
_dataBearer.RecordPlayerAttemptDuration(looser.PlayerName);
OuttedPlayers.Add(looser.PlayerId, looser);
PlayersScores.Remove(looser.PlayerId);
}
}
private void RemoveScores_OnAttemptEnded(object sender, AttemptEventArgs args)
{
List<int> keys;
if (PlayersScores.Count > 0)
{
keys = new List<int>(PlayersScores.Keys);
foreach(var key in keys)
{
_dataBearer.RecordPlayerDistance(PlayersScores[key].PlayerName);
_dataBearer.RecordPlayerScore(key, PlayersScores[key].PlayerName);
_dataBearer.RecordPlayerAttemptDuration(PlayersScores[key].PlayerName);
OuttedPlayers.Add(key, PlayersScores[key]);
_playersHandler.RemovePlayer(key);
}
PlayersScores.Clear();
}
keys = new List<int>(OuttedPlayers.Keys);
foreach (var key in keys)
{
_dataBearer.RecordPlayerFinalScore((int)OuttedPlayers[key].Score, OuttedPlayers[key].PlayerName);
OuttedPlayers[key].ScorePanel.Dispose();
}
OuttedPlayers = new();
PlayersScores = new();
}
#endregion
#region ENDPOINTS
public void AddScorePanel(string playerName, int playerId)
{
var newScorePanel = Instantiate(ScorePanelPrefab, UiDocument.transform);
var scoreScript = newScorePanel.GetComponent<ScorePannelScript>();
scoreScript.SetPlayerName(playerName);
PlayerScore newRecord = new PlayerScore(playerName, playerId, scoreScript);
PlayersScores.Add(playerId, newRecord);
}
public void AddScorePanel(string playerName, int playerId, Color playerColor)
{
var newScorePanel = Instantiate(ScorePanelPrefab, UiDocument.transform);
var scoreScript = newScorePanel.GetComponent<ScorePannelScript>();
scoreScript.SetColor(playerColor);
scoreScript.SetPlayerName(playerName);
PlayerScore newRecord = new PlayerScore(playerName, playerId, scoreScript);
PlayersScores.Add(playerId, newRecord);
}
public void SetScore(float score, int playerId)
{
if (PlayersScores.TryGetValue(playerId, out PlayerScore updatedScore))
{
updatedScore.Score = score;
updatedScore.ScorePanel.SetScore(score);
PlayersScores[playerId] = updatedScore;
}
if ( score <= 0) //false &&
{
OnScoreReachingZero.Invoke(this, new ScoreEventArgs(playerId));
}
}
public void AddToScore(float score, int playerId)
{
if (PlayersScores.TryGetValue(playerId, out PlayerScore updatedScore))
{
updatedScore.Score += score;
SetScore(updatedScore.Score, playerId);
}
}
public void AddScoreToAllAlivePlayers(float scoreToAdd)
{
var keys = new List<int>(PlayersScores.Keys);
foreach (var key in keys)
{
AddToScore(scoreToAdd, key);
}
}
public float GetPlayerScore(int playerId)
{
return PlayersScores.TryGetValue(playerId, out PlayerScore pscore)
? pscore.Score
: OuttedPlayers.TryGetValue(playerId, out PlayerScore oscore)
? oscore.Score
: 0;
}
#endregion
#region METHODS
#endregion
#region LIFECYCLE
protected override void Awake()
{
base.Awake();
_gameController = GameController.Instance;
_playersHandler = PlayersHandler.Instance;
_gameHandler = GameHandler.Instance;
_dataBearer = DataBearer.Instance;
_playersHandler.OnPlayerFinished += RemovePlayer_OnPlayerFinished;
_gameHandler.OnAttemptEnding += RemoveScores_OnAttemptEnded;
PlayersScores = new();
OuttedPlayers = new();
}
#endregion
struct PlayerScore
{
internal string PlayerName { get; set; }
internal int PlayerId { get; set; }
internal float Score { get; set; }
internal ScorePannelScript ScorePanel { get; set; }
public PlayerScore(string playerName, int playerId, ScorePannelScript scorePannel)
{
PlayerName = playerName;
PlayerId = playerId;
Score = 0f;
ScorePanel = scorePannel;
}
}
}
#region PROPERTIES
#endregion
#region VARIABLES
#endregion
#region EVENTS
#endregion
#region METHODS
#endregion
#region LIFECYCLE
#endregion

View File

@ -1,21 +0,0 @@
using Assets.Scripts;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BadCoinScript : MonoBehaviour
{
[SerializeField] float SpawnChance = 1f;
private bool objectEnabled;
private void Awake()
{
objectEnabled = DataBearer.Instance.Rules.MalusCoins && Random.value < SpawnChance;
if (!objectEnabled)
{
Destroy(gameObject);
return;
}
}
}

View File

@ -1,31 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FinishLineScript : MonoBehaviour
{
private GameHandler _gameHandler;
// Start is called before the first frame update
void Awake()
{
_gameHandler = GameHandler.Instance;
_gameHandler.OnAttemptEnding += Destroy_OnAttemptEnding;
}
// Update is called once per frame
void Update()
{
gameObject.transform.position += new Vector3(-_gameHandler.FrameDistance, 0, 0);
}
public void Destroy_OnAttemptEnding(object sender, EventArgs args)
{
Destroy(gameObject);
}
private void OnDestroy()
{
GameHandler.Instance.OnAttemptEnding -= Destroy_OnAttemptEnding;
}
}

View File

@ -1,22 +0,0 @@
using Assets.Scripts;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GoodCoinScript : MonoBehaviour
{
[SerializeField] float SpawningChance = 1f;
private bool objectEnabled;
private void Awake()
{
objectEnabled = DataBearer.Instance.Rules.BonusCoins && Random.value < SpawningChance;
if (!objectEnabled)
{
Destroy(gameObject);
return;
}
}
}

View File

@ -1,47 +0,0 @@
using Assets.Scripts;
using UnityEngine;
public class CapsulePlacer : MonoBehaviour
{
public Transform objectA;
public Transform objectB;
private bool laserEnabled;
private void Awake()
{
if(objectA == null || objectB == null)
{
laserEnabled = false;
Debug.LogError("Les boules du laser ne sont pas définis.");
}
laserEnabled = DataBearer.Instance.Rules.Lasers;
if (!laserEnabled)
{
Destroy(gameObject);
return;
}
}
private void Update()
{
Vector2 positionA = objectA.position;
Vector2 positionB = objectB.position;
// Calcul de la distance et mise à jour de l'échelle
float distance = Vector2.Distance(positionA, positionB);
Vector3 localScale = transform.localScale;
localScale.y = distance / 2f;
transform.localScale = localScale;
// Mise à jour de la position
transform.position = (positionA + positionB) / 2f;
// Calcul de la direction et de l'angle
Vector2 direction = positionB - positionA;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg + 90f;
// Mise à jour de la rotation
transform.rotation = Quaternion.Euler(0f, 0f, angle);
}
}

View File

@ -1,77 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MissileAlertScript : MonoBehaviour
{
#region PROPERTIES
#endregion
#region VARIABLES
private float blinkInterval = 0.2f;
private bool isBlinking = false;
private SpriteRenderer spriteRenderer;
private Vector3 position;
#endregion
#region EVENTS
#endregion
#region METHODS
private IEnumerator Blink()
{
isBlinking = true;
while (isBlinking)
{
SetSpriteVisible(true);
yield return new WaitForSeconds(blinkInterval);
SetSpriteVisible(false);
yield return new WaitForSeconds(blinkInterval);
}
}
private void SetSpriteVisible(bool isVisible)
{
spriteRenderer.enabled = isVisible;
}
#endregion
#region LIFECYCLE
private void Awake()
{
position = transform.position;
spriteRenderer = GetComponent<SpriteRenderer>();
var mainCamera = Camera.main;
position.x = mainCamera.transform.position.x + mainCamera.orthographicSize * mainCamera.aspect - 1f;
transform.position = position;
SetSpriteVisible(false);
enabled = false;
}
private void OnEnable()
{
if (!isBlinking)
{
StartCoroutine(Blink());
}
}
private void OnDisable()
{
if (isBlinking)
{
StopCoroutine(Blink());
SetSpriteVisible(false); // Assure que le sprite est invisible quand on arrête le clignotement
isBlinking = false;
}
}
#endregion
}

View File

@ -1,82 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MissileScript : MonoBehaviour
{
#region PROPERTIES
public int TargetId { get; set; }
#endregion
#region VARIABLES
[SerializeField] private float speed = 15.0f;
[SerializeField] private GameObject explosion;
private bool renderSprite;
private GameHandler _gameHandler;
#endregion
#region METHODS
public void Explode()
{
StopParticles();
if (renderSprite)
{
var rotation = transform.rotation;
rotation.z = 180;
Instantiate(explosion, transform.position, rotation);
}
Destroy(gameObject);
return;
}
public void SetRenderSprite(bool render)
{
renderSprite = render;
}
public void SetTarget(int id)
{
TargetId = id;
}
public void StopParticles()
{
if (transform.childCount == 0) return;
var emit = transform?.GetChild(0);
emit.parent = null;
emit.GetComponent<ParticleSystem>().Stop(true, ParticleSystemStopBehavior.StopEmitting);
// This finds the particleAnimator associated with the emitter and then
// sets it to automatically delete itself when it runs out of particles
}
#endregion
#region LIFECYCLE
void Start()
{
_gameHandler = GameHandler.Instance;
}
// Update is called once per frame
void Update()
{
if(transform.position.x < -20)
{
StopParticles();
Destroy(gameObject);
return;
}
//Go up because up is the direction of the head of the missile, even if he goes left the player's POV
transform.Translate(Vector3.up * (speed+_gameHandler.MapSpeed) * Time.deltaTime);
}
#endregion
}

View File

@ -1,106 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using Unity.MLAgents;
using UnityEngine;
public class ObstaclePatternBehaviour : MonoBehaviour
{
[SerializeField] private GameHandler gameHandler;
private float gameObjectLength;
private float gameSpeed;
private GameHandler _gameHandler;
// Start is called before the first frame update
void Awake()
{
_gameHandler = GameHandler.Instance;
}
private void Start()
{
gameObjectLength = ComputeLengthRecursive(0.0f, gameObject.transform) - gameObject.transform.position.x;
}
// Update is called once per frame
void Update()
{
// move the obstacle depending on GameHandler's offset value
gameObject.transform.position += new Vector3(-_gameHandler.FrameDistance, 0, 0);
//kill the obstacle if it reach the GameHandler's dead end value
var position = gameObject.transform.position.x;
var spawnerPosition = LevelSpawner.Instance.transform.position.x;
//Debug.Log("position : " + position+"\n"+"scale : " + gameObjectLength);
if(position < -gameObjectLength - spawnerPosition)
{
//Debug.Log("DESTROY");
MonoBehaviour.Destroy(gameObject);
return;
}
}
private float ComputeLengthRecursive(float max, Transform @object)
{
foreach (Transform child in @object)
{
if(child.position.x > max)
{
max = child.position.x;
}
max = ComputeLengthRecursive(max, child);
}
return max;
}
//private float ComputeLength()
//{
// Debug.Log("ComputeLength");
// CenterAndChildCount centerAndChildCount = new();
// centerAndChildCount = ComputeCenterRecursive(centerAndChildCount, gameObject.transform);
// Vector3 center = centerAndChildCount.center;
// int childCount = centerAndChildCount.childCount;
// center /= childCount; //center is average center of children
// Bounds bounds = new Bounds(center, Vector3.zero);
// bounds = ComputeBoundsRecursive(bounds, gameObject.transform);
// return bounds.size.x;
//}
//private CenterAndChildCount ComputeCenterRecursive(CenterAndChildCount centerAndChildCount, Transform @object)
//{
// foreach (Transform child in @object)
// {
// centerAndChildCount.center += child.gameObject.GetComponent<Renderer>().bounds.center;
// centerAndChildCount.childCount++;
// centerAndChildCount = ComputeCenterRecursive(centerAndChildCount, child);
// }
// return centerAndChildCount;
//}
//private Bounds ComputeBoundsRecursive(Bounds bounds, Transform @object)
//{
// foreach (Transform child in @object)
// {
// ComputeBoundsRecursive(bounds, child);
// bounds.Encapsulate(child.gameObject.GetComponent<Renderer>().bounds);
// }
// return bounds;
//}
//private class CenterAndChildCount
//{
// public Vector3 center = Vector3.zero;
// public int childCount = 0;
//}
}

View File

@ -1,166 +0,0 @@
using System;
using UnityEngine;
using static ScoreHandler;
public class PlayerScript : MonoBehaviour
{
#region PROPERTIES
public bool IsHuman { get; set; } = false;
public string Name { get; private set; }
public int Id { get; private set; }
public Color Color { get; private set; } = new Color(255, 255, 255);
public bool IsGrounded { get; private set; } = false;
public bool IsFlying { get; private set; } = false;
#endregion
#region VARIABLES
private ScoreHandler _scoreHandler;
private PlayersHandler _playersHandler;
public float jumpForce = 8f;
private Rigidbody2D rb;
public float maxVelocity = 8f;
public float minVelocity = -8f;
#endregion
#region EVENTS
public event EventHandler<PrefabTouchedEventArgs> OnObstacleTouched;
public event EventHandler<PrefabTouchedEventArgs> OnBonusTouched;
public class PrefabTouchedEventArgs : EventArgs
{
public GameObject objectTouched;
public int playerId;
public string playerName;
public PrefabTouchedEventArgs(GameObject objectTouched, int playerId, string playerName)
{
this.objectTouched = objectTouched;
this.playerId = playerId;
this.playerName = playerName;
}
}
private void Lose_OnScoreReachingZero(object sender, ScoreEventArgs args)
{
if(args.playerId == Id)
_playersHandler.RemovePlayer(Id);
}
#endregion
#region METHODS
public void MoveVertically(sbyte direction,float custom_DeltaTime)
{
IsFlying=(direction>0);
rb.AddForce(Vector2.up * (direction*jumpForce) * custom_DeltaTime);
}
public void SetName(string name)
{
Name = name;
}
public void SetId(int id)
{
Id = id;
}
public void SetColor(Color color)
{
Color = color;
}
public void Dispose()
{
Destroy(gameObject);
}
#endregion
#region LIFECYCLE
void Awake()
{
rb = GetComponent<Rigidbody2D>();
rb.freezeRotation = true;
_scoreHandler = ScoreHandler.Instance;
_playersHandler = PlayersHandler.Instance;
_scoreHandler.OnScoreReachingZero += Lose_OnScoreReachingZero;
}
private void OnTriggerEnter2D(Collider2D other)
{
// Vérifier si la collision concerne l'objet que vous souhaitez détecter
switch (other.tag)
{
case "Floor":
IsGrounded = true;
break;
case "Laser":
OnObstacleTouched.Invoke(this, new PrefabTouchedEventArgs(other.gameObject, Id, Name));
_scoreHandler.AddToScore(-200, Id);
break;
case "GoodCoin":
if (IsHuman)
other.gameObject.GetComponent<SpriteRenderer>().enabled = false;
OnBonusTouched.Invoke(this, new PrefabTouchedEventArgs(other.gameObject, Id, Name));
_scoreHandler.AddToScore(50, Id);
break;
case "BadCoin":
if (IsHuman)
other.gameObject.GetComponent<SpriteRenderer>().enabled = false;
OnObstacleTouched.Invoke(this, new PrefabTouchedEventArgs(other.gameObject, Id, Name));
_scoreHandler.AddToScore(-50, Id);
break;
case "Missile":
MissileScript missileScript = other.transform.GetComponent<MissileScript>();
if (missileScript.TargetId == Id)
{
OnObstacleTouched.Invoke(this, new PrefabTouchedEventArgs(other.gameObject, Id, Name));
other.GetComponent<MissileScript>().Explode();
_scoreHandler.AddToScore(-500, Id);
}
break;
default:
break;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("Floor"))
{
IsGrounded = false;
}
}
void Update()
{
// Récupérer la vélocité actuelle du Rigidbody2D
Vector2 currentVelocity = rb.velocity;
// Limiter la vélocité verticale
float clampedVelocityY = Mathf.Clamp(currentVelocity.y, minVelocity, maxVelocity);
rb.velocity = new Vector2(currentVelocity.x, clampedVelocityY);
// Appliquer une force constante vers le haut ou le bas en fonction de la touche enfoncée
//float verticalForce = Input.GetButton("Jump") ? jumpForce : -jumpForce;
//rb.AddForce(Vector2.up * verticalForce * Time.deltaTime);
// Effectuer le raycast pour vérifier si le joueur est au sol
//RaycastHit2D hit = Physics2D.Raycast(rb.position, Vector2.down, 0.25f);
//IsGrounded = hit.collider != null && hit.transform.CompareTag("Floor");
}
#endregion
}

View File

@ -1,80 +0,0 @@
using System;
using UnityEngine;
using UnityEngine.UIElements;
/// <summary>
/// Instanciate a ScorePannel view on the UI. This class only serves as view and does not contain any game data and does not affect the game's work
/// </summary>
public class ScorePannelScript : MonoBehaviour
{
#region PROPERTIES
#endregion
#region VARIABLES
[SerializeField] private VisualTreeAsset scorePannelTemplate; // Référence au template UXML du ScorePannel
private VisualElement scoreContainer; // Le VisualElement parent où ajouter le ScorePannel
private VisualElement root;
private Label scoreLabel;
private Label playerNameLabel;
private VisualElement scorePanel;
#endregion
#region EVENTS
#endregion
#region ENDPOINTS
public void SetPlayerName(string playerName)
{
playerNameLabel.text = playerName;
}
public void SetScore(float score)
{
scoreLabel.text = score.ToString("0");
}
public void SetColor(Color color)
{
scorePanel.style.backgroundColor = new StyleColor(new Color(color.r, color.g, color.b, 0.43f));
}
public void Dispose()
{
scoreContainer.Remove(root);
Destroy(gameObject);
}
#endregion
#region LIFECYCLE
void OnEnable()
{
Debug.Log("SCORE Instanciatied");
var uiDocument = GetComponentInParent<UIDocument>(); // Obtenir le composant UIDocument attaché à l'objet
// Vérifiez que les références sont assignées
if (uiDocument == null || scorePannelTemplate == null)
{
Debug.LogError("UiDocument ou scorePannelTemplate n'est pas assigné.");
return;
}
var rootVisualElement = uiDocument.rootVisualElement;
// Vérifiez que ScoresContainer existe dans l'arborescence de l'UI
scoreContainer = rootVisualElement.Q<VisualElement>("ScoresContainer");
if (scoreContainer == null)
{
Debug.LogError("ScoresContainer n'a pas été trouvé dans le UXML.");
return;
}
// Charger et instancier le ScorePannel
root = scorePannelTemplate.CloneTree();
scorePanel = root.Q<VisualElement>("panel");
scoreLabel = root.Q<Label>("ScoreValue");
playerNameLabel = root.Q<Label>("PlayerName");
scoreContainer.Add(root);
root.AddToClassList("scorePanel");
}
#endregion
}

View File

@ -1,104 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
[UnityEngine.Scripting.Preserve]
public class AspectRatioPanel : VisualElement
{
[UnityEngine.Scripting.Preserve]
public new class UxmlFactory : UxmlFactory<AspectRatioPanel, UxmlTraits> { }
[UnityEngine.Scripting.Preserve]
public new class UxmlTraits : VisualElement.UxmlTraits
{
readonly UxmlIntAttributeDescription aspectRatioX = new() { name = "aspect-ratio-x", defaultValue = 16, restriction = new UxmlValueBounds { min = "1" } };
readonly UxmlIntAttributeDescription aspectRatioY = new() { name = "aspect-ratio-y", defaultValue = 9, restriction = new UxmlValueBounds { min = "1" } };
readonly UxmlIntAttributeDescription balanceX = new() { name = "balance-x", defaultValue = 50, restriction = new UxmlValueBounds { min = "0", max = "100" } };
readonly UxmlIntAttributeDescription balanceY = new() { name = "balance-y", defaultValue = 50, restriction = new UxmlValueBounds { min = "0", max = "100" } };
public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
{
get { yield break; }
}
public override void Init(VisualElement visualElement, IUxmlAttributes attributes, CreationContext creationContext)
{
base.Init(visualElement, attributes, creationContext);
var element = visualElement as AspectRatioPanel;
if (element != null)
{
element.AspectRatioX = Mathf.Max(1, aspectRatioX.GetValueFromBag(attributes, creationContext));
element.AspectRatioY = Mathf.Max(1, aspectRatioY.GetValueFromBag(attributes, creationContext));
element.BalanceX = Mathf.Clamp(balanceX.GetValueFromBag(attributes, creationContext), 0, 100);
element.BalanceY = Mathf.Clamp(balanceY.GetValueFromBag(attributes, creationContext), 0, 100);
element.FitToParent();
}
}
}
public int AspectRatioX { get; private set; } = 16;
public int AspectRatioY { get; private set; } = 9;
public int BalanceX { get; private set; } = 50;
public int BalanceY { get; private set; } = 50;
public AspectRatioPanel()
{
style.position = Position.Absolute;
style.left = 0;
style.top = 0;
style.right = StyleKeyword.Undefined;
style.bottom = StyleKeyword.Undefined;
RegisterCallback<AttachToPanelEvent>(OnAttachToPanelEvent);
}
void OnAttachToPanelEvent(AttachToPanelEvent e)
{
parent?.RegisterCallback<GeometryChangedEvent>(OnGeometryChangedEvent);
FitToParent();
}
void OnGeometryChangedEvent(GeometryChangedEvent e)
{
FitToParent();
}
void FitToParent()
{
if (parent == null) return;
var parentW = parent.resolvedStyle.width;
var parentH = parent.resolvedStyle.height;
if (float.IsNaN(parentW) || float.IsNaN(parentH)) return;
style.position = Position.Absolute;
style.left = 0;
style.top = 0;
style.right = StyleKeyword.Undefined;
style.bottom = StyleKeyword.Undefined;
if (AspectRatioX <= 0.0f || AspectRatioY <= 0.0f)
{
style.width = parentW;
style.height = parentH;
return;
}
var ratio = Mathf.Min(parentW / AspectRatioX, parentH / AspectRatioY);
var targetW = Mathf.Floor(AspectRatioX * ratio);
var targetH = Mathf.Floor(AspectRatioY * ratio);
style.width = targetW;
style.height = targetH;
var marginX = parentW - targetW;
var marginY = parentH - targetH;
style.left = Mathf.Floor(marginX * BalanceX / 100.0f);
style.top = Mathf.Floor(marginY * BalanceY / 100.0f);
}
}

View File

@ -1,84 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class LabelAutoFit : Label
{
[UnityEngine.Scripting.Preserve]
public new class UxmlFactory : UxmlFactory<LabelAutoFit, UxmlTraits> { }
[UnityEngine.Scripting.Preserve]
public new class UxmlTraits : Label.UxmlTraits
{
readonly UxmlIntAttributeDescription minFontSize = new UxmlIntAttributeDescription
{
name = "min-font-size",
defaultValue = 10,
restriction = new UxmlValueBounds { min = "1" }
};
readonly UxmlIntAttributeDescription maxFontSize = new UxmlIntAttributeDescription
{
name = "max-font-size",
defaultValue = 200,
restriction = new UxmlValueBounds { min = "1" }
};
public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription { get { yield break; } }
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
LabelAutoFit instance = ve as LabelAutoFit;
instance.minFontSize = Mathf.Max(minFontSize.GetValueFromBag(bag, cc), 1);
instance.maxFontSize = Mathf.Max(maxFontSize.GetValueFromBag(bag, cc), 1);
instance.RegisterCallback<GeometryChangedEvent>(instance.OnGeometryChanged);
instance.style.fontSize = 1; // Triggers OnGeometryChanged callback
}
}
// Setting a limit of max text font refreshes from a single OnGeometryChanged to avoid repeating cycles in some extreme cases
private const int MAX_FONT_REFRESHES = 2;
private int m_textRefreshes = 0;
public int minFontSize { get; set; }
public int maxFontSize { get; set; }
// Call this if the font size does not update by just setting the text
// Should probably wait till the end of frame to get the real font size, instead of using this method
// Works well in OnRenderObject() too
public void SetText(string text)
{
this.text = text;
UpdateFontSize();
}
private void OnGeometryChanged(GeometryChangedEvent evt)
{
UpdateFontSize();
}
private void UpdateFontSize()
{
if (m_textRefreshes < MAX_FONT_REFRESHES)
{
Vector2 textSize = MeasureTextSize(text, float.MaxValue, MeasureMode.AtMost, float.MaxValue, MeasureMode.AtMost);
float fontSize = Mathf.Max(style.fontSize.value.value, 1); // Unity can return a font size of 0 which would break the auto fit // Should probably wait till the end of frame to get the real font size
float heightDictatedFontSize = Mathf.Abs(contentRect.height);
float widthDictatedFontSize = Mathf.Abs(contentRect.width / textSize.x) * fontSize;
float newFontSize = Mathf.FloorToInt(Mathf.Min(heightDictatedFontSize, widthDictatedFontSize));
newFontSize = Mathf.Clamp(newFontSize, minFontSize, maxFontSize);
if (Mathf.Abs(newFontSize - fontSize) > 1)
{
m_textRefreshes++;
style.fontSize = new StyleLength(new Length(newFontSize));
}
}
else
{
m_textRefreshes = 0;
}
}
}

View File

@ -1,81 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class GameUI : MonoBehaviour
{
#region PROPERTIES
#endregion
#region VARIABLES
private UIDocument _document;
private Button _pauseButton;
private Button _playButton;
private Button _exitButton;
#endregion
#region EVENTS
private void OnPauseButton(ClickEvent e)
{
GameHandler.Instance.PauseGame();
}
private void OnPlayButton(ClickEvent e)
{
GameHandler.Instance.ResumeGame();
}
private void OnExitButton(ClickEvent e)
{
GameController.Instance.ExitToMenu("Retour au menu principal");
}
private void React_OnGamePaused(object sender, EventArgs e)
{
ChangeButtonsVisibility(true);
}
private void React_OnGameResumed(object sender, EventArgs e)
{
ChangeButtonsVisibility(false);
}
#endregion
#region METHODS
private void ChangeButtonsVisibility(bool isPaused)
{
if (isPaused)
{
_pauseButton.style.display = DisplayStyle.None;
_playButton.style.display = DisplayStyle.Flex;
_exitButton.style.display = DisplayStyle.Flex;
}
else
{
_playButton.style.display = DisplayStyle.None;
_exitButton.style.display = DisplayStyle.None;
_pauseButton.style.display = DisplayStyle.Flex;
}
}
#endregion
#region LIFECYCLE
private void Awake()
{
_document=GetComponent<UIDocument>();
_pauseButton = _document.rootVisualElement.Q("PauseButton") as Button;
_pauseButton.RegisterCallback<ClickEvent>(OnPauseButton);
_playButton = _document.rootVisualElement.Q("PlayButton") as Button;
_playButton.RegisterCallback<ClickEvent>(OnPlayButton);
_exitButton = _document.rootVisualElement.Q("ExitButton") as Button;
_exitButton.RegisterCallback<ClickEvent>(OnExitButton);
ChangeButtonsVisibility(false);
GameHandler.Instance.OnGamePaused += React_OnGamePaused;
GameHandler.Instance.OnGameResumed += React_OnGameResumed;
}
#endregion
}

View File

@ -1,41 +0,0 @@
using UnityEngine;
using UnityEngine.EventSystems;
using TMPro;
public class HoverDescriptionScript : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
[SerializeField] private string description; // La description à afficher
private TextMeshProUGUI descriptionText; // Référence au TextMeshPro pour afficher la description
private void Start()
{
// Trouver l'objet Infobox dans la scène et obtenir le TextMeshPro
GameObject infobox = GameObject.Find("Infobox");
if (infobox != null)
{
descriptionText = infobox.GetComponentInChildren<TextMeshProUGUI>();
}
else
{
Debug.LogError("Infobox not found in the scene.");
}
}
// Méthode appelée quand la souris entre dans le collider de l'objet
public void OnPointerEnter(PointerEventData eventData)
{
if (descriptionText != null)
{
descriptionText.text = description;
}
}
// Méthode appelée quand la souris sort du collider de l'objet
public void OnPointerExit(PointerEventData eventData)
{
if (descriptionText != null)
{
descriptionText.text = "Survolez un element pour en avoir une description !";
}
}
}

View File

@ -1,256 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
using Unity.VisualScripting;
public class MenuController : MonoBehaviour
{
[Header("Play Button")]
[SerializeField] Button playerButton;
[Header("Player Selection Settings")]
[SerializeField] Toggle omnicientBotToggler = null;
[SerializeField] Toggle observerBotToggler = null;
[SerializeField] Toggle iteratorBotToggler = null;
[SerializeField] Toggle humanPlayerToggler = null;
[Header("Level Settings")]
[SerializeField] Toggle laserToggler = null;
[SerializeField] Toggle missileToggler = null;
[SerializeField] Toggle coinToggler = null;
[SerializeField] Toggle badCoinToggler = null;
[SerializeField] Toggle surviveScoreToggler = null;
[Header("Seed Settings")]
[SerializeField] Toggle changeSeedToggler = null;
[SerializeField] Toggle useDefaultSeedToggler = null;
[SerializeField] TMP_InputField defaultSeedInputField = null;
[SerializeField] TMP_InputField customSeedInputField = null;
[Header("Session Settings")]
[SerializeField] Slider maxSpeedSlider = null;
[SerializeField] Slider startSpeedSlider = null;
[SerializeField] Slider difficultyMultiplyerSlider = null;
[SerializeField] Toggle rushModeToggler = null;
[SerializeField] TMP_InputField maxTryInputField = null;
[SerializeField] TMP_InputField maxTimeInputField = null;
[SerializeField] TMP_InputField maxDistanceInputField = null;
[Header("Menu Controller")]
public string _statsScene;
public string _playScene;
private void Start()
{
Time.timeScale = 1;
SetMaxSpeed();
SetStartSpeed();
SetDifficultyMultiplyer();
SetRushModeToggle();
SetMaxTry();
SetMaxTime();
SetMaxDistance();
SetChangeSeed();
SetUseDefaultSeed();
SetDefaultSeedValue();
SetCustomSeedValue();
SetLaserToggle();
SetMissileToggle();
SetCoinToggle();
SetBadCoinToggle();
SetSurviveScoreToggle();
SetOmnicientBotSelected();
SetObserverBotSelected();
SetIteratorBotSelected();
SetHumanPlayerSelected();
}
public void StatsButtonClicked()
{
Debug.Log("=============================================");
SceneManager.LoadScene(_statsScene);
}
public void PlayButtonClicked()
{
Debug.Log("=============================================");
SceneManager.LoadScene(_playScene);
}
void OnPlayerSelectionChanged()
{
bool omnicient = omnicientBotToggler.isOn;
bool observer = observerBotToggler.isOn;
bool iterator = iteratorBotToggler.isOn;
bool human = humanPlayerToggler.isOn;
playerButton.interactable = (omnicient || observer || iterator || human);
}
public void QuitButtonClicked()
{
Debug.Log("Game quitting");
Application.Quit();
}
// Session Rules
public void SetMaxSpeed()
{
float speed = maxSpeedSlider.value;
PlayerPrefs.SetFloat(Settings.MaxSpeedSettingValue, speed);
Debug.Log($"Game's max speed is set to {speed}");
}
public void SetStartSpeed()
{
float speed = startSpeedSlider.value;
PlayerPrefs.SetFloat(Settings.StartSpeedSettingValue, speed);
Debug.Log($"Game's starting speed is set to {speed}");
}
public void SetDifficultyMultiplyer()
{
float value = difficultyMultiplyerSlider.value;
PlayerPrefs.SetFloat(Settings.DifficultyMultiplierSettingValue, value);
Debug.Log($"Game's difficulty multiplyer is set to {value}");
}
public void SetRushModeToggle()
{
bool toggle = rushModeToggler.isOn;
PlayerPrefs.SetString(Settings.RushModeSettingToggle, toggle.ToString());
Debug.Log($"Rush Mode has been toggeled to {toggle}");
}
public void SetMaxTry()
{
int value = 0;
string text = maxTryInputField.text;
if (text != "")
{
value = int.Parse(text);
}
PlayerPrefs.SetInt(Settings.MaxTrySettingValue, value);
Debug.Log($"Game's maximum try count is set to {value}");
}
public void SetMaxTime()
{
int seconds = 0;
string text = maxTimeInputField.text;
if (text != "")
{
seconds = int.Parse(text);
}
PlayerPrefs.SetInt(Settings.MaxTimeSettingValue, seconds);
Debug.Log($"Game's session maximum time is set to {seconds} seconds");
}
public void SetMaxDistance()
{
int distance = 0;
string text = maxDistanceInputField.text;
if (text != "")
{
distance = int.Parse(text);
}
PlayerPrefs.SetFloat(Settings.MaxDistanceSettingValue, distance);
Debug.Log($"Level's max distance is set to {distance} units");
}
// Seed Rules
public void SetChangeSeed()
{
bool toggle = changeSeedToggler.isOn;
PlayerPrefs.SetString(Settings.ChangeSeedSetting, toggle.ToString());
Debug.Log($"Changing seed for each level is set to {toggle}");
}
public void SetUseDefaultSeed()
{
bool toggle = useDefaultSeedToggler.isOn;
PlayerPrefs.SetString(Settings.UseDefaultSeedSetting, toggle.ToString());
Debug.Log($"Usign default seed is set to {toggle}");
}
public void SetDefaultSeedValue()
{
int value = 0;
string text = defaultSeedInputField.text;
if(text != "")
{
value = int.Parse(text);
}
PlayerPrefs.SetInt(Settings.DefaultSeedSettingValue, value);
Debug.Log($"Default seed has been set to {value}");
}
public void SetCustomSeedValue()
{
int value = 0;
string text = customSeedInputField.text;
if (text != "")
{
value = int.Parse(text);
}
PlayerPrefs.SetInt(Settings.CustomSeedSettingValue, value);
Debug.Log($"Custom seed has been set to {value}");
}
// Level Rules
public void SetLaserToggle()
{
bool toggle = laserToggler.isOn;
PlayerPrefs.SetString(Settings.LaserSettingToggle, toggle.ToString());
Debug.Log($"Laser have been toggeled to {toggle}");
}
public void SetMissileToggle()
{
bool toggle = missileToggler.isOn;
PlayerPrefs.SetString(Settings.MissileSettingToggle, toggle.ToString());
Debug.Log($"Missiles have been toggeled to {toggle}");
}
public void SetCoinToggle()
{
bool toggle = coinToggler.isOn;
PlayerPrefs.SetString(Settings.CoinSettingToggle, toggle.ToString());
Debug.Log($"Coins have been toggeled to {toggle}");
}
public void SetBadCoinToggle()
{
bool toggle = badCoinToggler.isOn;
PlayerPrefs.SetString(Settings.BadCoinSettingToggle, toggle.ToString());
Debug.Log($"Bad Coins have been toggeled to {toggle}");
}
public void SetSurviveScoreToggle()
{
bool toggle = surviveScoreToggler.isOn;
PlayerPrefs.SetString(Settings.SurviveScoreSettingToggle, toggle.ToString());
Debug.Log($"Increasing score with surviving time has been toggeled to {toggle}");
}
// Player Selection
public void SetOmnicientBotSelected()
{
bool toggle = omnicientBotToggler.isOn;
PlayerPrefs.SetString(Settings.OmnicientBotSelected, toggle.ToString());
OnPlayerSelectionChanged();
Debug.Log($"Omnicient bot selected has been set to {toggle}");
}
public void SetObserverBotSelected()
{
bool toggle = observerBotToggler.isOn;
PlayerPrefs.SetString(Settings.ObserverBotSelected, toggle.ToString());
OnPlayerSelectionChanged();
Debug.Log($"Observer bot selected has been set to {toggle}");
}
public void SetIteratorBotSelected()
{
bool toggle = iteratorBotToggler.isOn;
PlayerPrefs.SetString(Settings.IteratorBotSelected, toggle.ToString());
OnPlayerSelectionChanged();
Debug.Log($"Iterator bot selected has been set to {toggle}");
}
public void SetHumanPlayerSelected()
{
bool toggle = humanPlayerToggler.isOn;
PlayerPrefs.SetString(Settings.HumanPlayerSelected, toggle.ToString());
OnPlayerSelectionChanged();
Debug.Log($"Human player selected has been set to {toggle}");
}
}

View File

@ -1,20 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayButtonBehaviour : MonoBehaviour
{
[Header("Player Selection Buttons")]
[SerializeField] Toggle omnicientBot;
[SerializeField] Toggle observerBot;
[SerializeField] Toggle iteratorBot;
[SerializeField] Toggle humanPlayer;
// Start is called before the first frame update
void Start()
{
}
}

Some files were not shown because too many files have changed in this diff Show More