rapport de projet - ollopa.fr · ollopa.fr 1 rapport de projet 27 mai 2020 vernay rémi roussille...
TRANSCRIPT
ollopa.fr 1
Rapport de projet 27 mai 2020
VERNAY Rémi
ROUSSILLE Philippe
PINGARD Adrien
VEYRE Thimot
LITOUX Pierre
DEPLAGNE Hugo
LLOMBART Lucas
ollopa.fr 2
Table des matières :
1 – Introduction………………………………………………….……………………………………….……....3
2 – Cahier des charges………………………………………………………………………………………….4
3 – Réalisation…………………………..………………………………………..…………………………..….12
2.1 – Graphisme 2D……………………………………………..…………………………………....12
2.1.1 – Dessins………………………………………………………………………………12
2.1.2 – Fonctionnement animation…………………………………………………………13
2.2 – Le personnage…………………………………………………………………………….……..14
2.2.1 – les déplacements (y compris dans les liquides)…………………………………14
2.2.2 – différents modes…………………………………………………………………….15
2.2.3 – Autres…………………………………………………………………………………17
2.2.4 – placements des blocs……………………………………………………………….18
2.3 – Les bâtiments…………………………………………………………………………………….19
2.3.1 – Les différents bâtiments………………………………………………….19
2.3.1.1 – Panneau solaire………………………………………………19
2.3.1.2 – Générateur d’oxygène……………………………………….19
2.3.1.3 – Stockage………………………………………………………19
2.3.1.4 – Broyeur…………………………………………………………20
2.3.1.5 – Imprimante 3D…………………………………………………20
2.3.1.7 – Infirmerie……………………………………………………….20
2.3.1.8 – Puits de pétrole………………………………………………..20
2.3.1.9 – Raffinerie……………………………………………………….21
2.3.1.10 – Foreuse……………………………………………………….21
2.3.1.11 – Générateur thermique……………………………………….21
2.3.2 – Fonctionnement général……………………………………………………………..22
2.3.3 – Réseau énergétique………………………………………………………………….23
2.3.4 – Interface des bâtiments………………………………………………………………24
2.3.5 – Placement des bâtiments…………………………………………………………….25
2.3.6 – Fusée et fin du jeu……………………………………………………………………26
2.4 – Les ressources et les inventaires………………………………………………….………….…27
2.4.1 – Les ressources………………………………………………………………………..27
2.4.2 – Obtention des ressources……………………………………………………………28
2.4.3 – Inventaire du joueur………..…………………………………………………………30
2.5 – Environnement………………………………………………………………………………….…31
2.5.1 – Système jour/nuit……………………………………………………………………..31
2.5.2 – Paysages (Parallaxe) ………………………………………………………………..31
2.5.3 – Liquides………………………………………...……………...………………………32
2.5.4 – L’herbe…………………………………………………………………………………34
2.5.5 – Monde infini……………………………………………………………………………34
2.6 – La génération de l’environnement…………………………………….………………………....35
2.6.1 – génération du monde…………………………………………………………………35
2.6.2 – génération des minerais ……………………………………………………………..36
2.6.3 – génération des arbres………………………………………………………………...38
2.6.4 – génération des grottes………………………………………………………………..39
2.6.5 – génération des lacs……………………………………………………………………41
2.7 – Le pistolet laser……………………………………………………………………………...……..42
2.8 – Les ennemis………………………………………………………………………………………..44
2.9 – Interfaces utilisateurs……………………………………………………………………………...47
2.9.1 – Ajustement d’écrans……………………………………………..……………………47
2.9.2 – Menus….……………………………………………………………………………….47
2.10 – système de sauvegardes……………………………………………………………..…………48
2.11 – son……………………………………………………………………………………..…………..50
4 – Chronologie……………………………………………………………….……………………………..……...52
5 – Conclusion……………………………..…………………………………………………………...………..….53
ollopa.fr 3
1 – Introduction :
Dans ce rapport nous allons vous présenter notre projet de jeu développé lors de cette
première année à l’EPITA Toulouse. Durant la dernière période de développement de notre jeu nous
avons pu rajouter beaucoup de nouveautés qui, on l’espère, plaira aux joueurs. Nous avons pensé à
beaucoup de détails pour démarquer notre projet des autres, malheureusement nous ne pouvions pas
tous réalisés et espérés être dans les temps, nous nous sommes donc focalisés sur les tâches les plus
importantes. Cependant, beaucoup de choses ont été rajoutés depuis la dernière soutenance pour finir
le projet et maintenant nous sommes fiers et heureux de vous présenter notre projet nommé Ollopa.
En effet, vous allez vous rendre compte que beaucoup de choses ont changé et avancé depuis
la dernière soutenance, tout ce que nous voulions faire après la deuxième soutenance a été fait et
même plus encore. Pour prendre des exemples, le laser a été complètement changé, beaucoup de
bâtiments ont été rajoutés et fonctionnent parfaitement, le contexte de notre astronaute devant réparer
sa fusés a été créé et les animations du joueur, des ennemis, de la fusée et de l’herbe a aussi été
rajouté. Le jeu est entièrement fonctionnel et offre un vrai amusement quand on y joue. On peut voir
dans cet avancement l’apprentissage de Godot par notre équipe entre le début et la fin du projet.
Dans ce qui suit nous vous présenterons en détail le jeu, ce qui a été fait, pour quelle raison et
comment on y joue. On y explique ce qui a ou n’a pas fonctionné et ce qu’on a dû faire pour optimiser
nos lignes de code pour que la plupart des joueurs puissent faire tourner le jeu.
Nous allons donc décrire les éléments composant notre jeu puis nous donnerons les tâches de
chacun de nos membres et enfin nous finirons par une conclusion sur ce projet.
ollopa.fr 4
2 – Cahier des charges :
1 - Introduction
1.1 Présentation du groupe
13 est un groupe de quatre personnes de la promotion 2024 de l’EPITA Toulouse. Nous nous
sommes tous les quatre rencontrés à notre arrivée à l’EPITA en début d’année et sommes devenus amis. Il nous est donc paru naturel de former ce groupe pour le projet.
Notre projet est un jeux vidéo de type gestion survival en 2D, il se nomme Ollopa.
1.2 Nom du groupe
Le nom du groupe est 13, il fait référence à la mission Apollo 13, qui a rencontré de graves
avaries au cours de sa mission. De même, dans notre jeu, un astronaute se crashe sur une planète après avoir rencontré des problèmes importants sur son vaisseau. Le nom du jeu, Ollopa, le verlan d’Apollo, et le nom du groupe sont une référence à la mission éponyme.
1.3 Présentation des membres
• Adrien Pingard (chef de projet)
Adrien a été à l’unanimité choisi en tant que chef de projet car il possède déjà
des connaissances en programmation et a déjà eu l’occasion d’utiliser Unity. Il a également suivi la section S-SI spécialité ISN au lycée, ce qui lui confère des aptitudes supplémentaires pour la gestion de groupe.
• Lucas Llombart
Il est le moins expérimenté du groupe, mais sa motivation et sa volonté d’apprendre compenseront ce léger retard. En outre, Lucas est une personne consciencieuse, il ne s’arrêtera pas tant que le travail n’est pas correctement réalisé.
• Pierre Litoux
En première année dans le supérieur comme les autres, Pierre a lui aussi étudié la spécialité ISN au lycée, de plus il est volontaire mais assez peu organisé.
• Thimot Veyre
Comme Adrien, il a suivi la section S-SI spécialité ISN, associé aux nombreuses heures passées à apprendre sur l’informatique, cela lui confère une base de connaissance générale en informatique. Il pourra également apporter sa rigueur et son perfectionnisme au groupe.
• Hugo Deplagne Avant d’étudier à l’EPITA, Hugo était en classe préparatoire scientifique
(PCSI) ce qui lui a apporté beaucoup de rigueur dans son travail mais aussi une facilité dans les matières scientifiques. Ces qualités seront donc d’un grand avantage pour le projet.
ollopa.fr 5
2 - Présentation du projet 2.1 Origines
Nous nous sommes rapidement mis d’accord que notre projet serait un jeu, mais il a été difficile
de se décider à quel genre appartiendrait celui-ci. Trouver une idée qui convenait à tous les membres du groupe a demandé de nombreuses propositions. L’idée de départ était un jeu en 2D, mais nous avons eu plusieurs concepts comme la création d’un Tower Defense ou d’un jeu de plateforme. Finalement, nous avons opté pour un jeu avec une génération procédurale, et nous nous sommes mis d’accord sur un style de jeu de gestion survival dans l’espace.
Le premier jeu de type space survival est Robinson's Requiem, un jeu vidéo de survie doté
d'éléments de jeu de rôle sorti en 1994 sur PC, 3DO, Amiga, Atari ST, Atari Falcon, Mac et en 2011 sur Atari Jaguar. Il a été développé et édité par Silmarils. Il est l'un des premiers space survival sorti. Il s'agit d'une simulation de survie dans laquelle le joueur est perdu sur une planète inexplorée et hostile et doit trouver un moyen de la quitter à l'aide des ressources présentes dans son environnement.
2.2 Inspiration
• Astroneer : Ce jeu a pour but d’explorer et de remodeler des mondes lointains. Il se déroule
durant une époque intergalactique. Les joueurs doivent évoluer dans des environnements hostiles pour faire des découvertes sur l’univers. Ce jeu est disponible sur Xbox et Playstation ainsi que sur PC.
• Terraria : C’est un jeu en 2D de type action-aventure dont le système de jeu est basé sur l’exploration, la construction et l’action. IL est présent sur les plateformes de jeu Xbox, et Playstation et PC. Pour notre jeu, nous avons décidé d’imiter le système de construction et d’exploration pour créer des bâtiments et évoluer dans une planète inconnue.
• Starbound : Starbound se déroule dans un univers en deux dimensions. Le joueur doit explorer l’univers afin de trouver de nouvelles armes et armures ainsi que de trouver des objets divers pouvant être utile au joueur. Nous récupérons pour notre jeu l’univers spatial ainsi le système en 2D.
2.3 Histoire
Notre héros vit à une époque où le voyage spatial est démocratisé, il est aussi courant de
posséder un vaisseau qu’un avion de nos jours. Il est explorateur et de ce fait il voyage en terre inexploré. Lorsqu’il se crashe c’est donc sur une planète inconnue à la suite d’avaries sur son vaisseau. En outre, tous ses moyens de communications longues portées sont endommagés. Il ne peut donc compter que sur lui-même pour repartir.
Le personnage pourrait à des moments clés du jeu se remémorer des souvenirs mais cela reste très secondaire (voir optionnel) et ne sera implanté qu’en fin de développement si nous y trouvons un intérêt.
2.4 But et intérêt
Le but du jeu est de réparer son vaisseau pour retourner dans l’espace et finir son voyage. Il faut pour cela, récupérer différentes ressources (matières premières, électricité, pétrole...). Le joueur devra gérer les rendements de ses unités de productions pour ne pas dépenser plus d’énergie qu’il n’en produit.
Une partie doit durer au maximum une demi-heure. En effet pour une partie plus longue, il faut intégrer plus de contenu et nous ne pensons pas avoir assez de temps.
ollopa.fr 6
2.5 Fonctionnalités
Toutes les fonctionnalités du jeu ne sont pas encore établies, certaines pourraient venir à être
modifiées ou supprimées ou bien même de nouvelles fonctionnalités pourraient venir au cours du développement du jeu.
Nous pouvons néanmoins faire une liste d’éléments qui seront implémentés dans le jeu :
• Le personnage est vu de côté, il peut se déplacer de gauche à droite ainsi que sauter et tomber. Il peut également interagir avec le terrain en cassant des blocs et en récupérant des ressources dans son inventaire. Le personnage doit gérer plusieurs variables (santé, oxygène, énergie).
• La carte est un terrain construit à l’aide de blocs, la génération de la carte est aléatoire.
• Des bâtiments (e.g raffinerie, panneau solaire) peuvent être posés sur la carte, chaque bâtiment a une utilité spéciale.
• Une arme/outil (e.g laser) permet de casser les blocs du monde et de se défendre contre d’éventuel ennemi. L’utilisation de cet outil fait perdre de l'énergie électrique au joueur.
• Une génération aléatoire de minerais dans la carte.
• Des matières premières (e.g bois, métaux) et des liquides (e.g eau, pétrole, fuel) pour permettre l’avancement dans le jeu.
• Un système d'énergie électrique qui permettra le fonctionnement de certains bâtiments mais servant aussi à la survie du joueur.
• Cycle jour/nuit permettant l’interaction avec certains bâtiments (e.g panneau solaire) et sur la vision du personnage.
• Système de mort partielle et permanente. La mort partielle est une mort du personnage après laquelle il peut revivre avec des malus. La mort permanente met fin à la partie.
• Un ennemi qui attaquera le joueur et lui fera perdre de la vie.
ollopa.fr 7
3 - Réalisation projet 3.1 Outils utilisés et Moyens mis en œuvre
Moyens matériels et logiciels :
• Pour réaliser ce projet nous travaillerons avec Visual studio et plus précisément avec le langage de programmation C# complété par le framework GODOT. Visual studio et C# seront utilisés pour les scripts du jeu et GODOT pour la modélisation de celui-ci.
• Nous avons à notre disposition quatre ordinateurs (dont trois portables) ainsi que les ordinateurs de l’EPITA mis à notre disposition.
Moyens intellectuels : • En plus de nos connaissances actuelles en C# nous pouvons consulter sur internet des
tutoriels, des manuels numériques et des forums pour nous aider à avancer et à mieux comprendre le fonctionnement du framework GODOT. Nous avons aussi la possibilité de nous faire aider par les professeurs de l’EPITA et les étudiants des années supérieures.
• La communication étant importante dans un projet comme celui-ci, nous avons mis en place un serveur de chat vocal et écrit via l’application “Messenger” pour faciliter nos échanges durant toute la création de notre jeu. De plus, nous avons un groupe “Discord” permettant, tout comme “Messenger”, de communiquer avec le groupe entier via des messages ou encore des appels vocaux. Mais le plus gros avantage de “Discord” c’est que nous pouvons faire des partages d’écran vidéo, ce qui facilite la résolution des problèmes, comme un problème de code pour ne citer qu’un type de problème.
• Un git ainsi que des stockages cloud sont mis en place pour le partage des fichiers du projet.
• Nous avons également mis en place un système de gestion de projet, sur freedcamp, qui permet d’assigner des tâches à chacun en fonction de l’avancement du projet.
ollopa.fr 8
3.2 Aspects économiques
Pour notre jeu, nous utilisons quatre ordinateurs dont trois portables. Chacun coûte aux
alentours de mille euros. De plus, nous louons, cinquante euros par an, un serveur afin de pouvoir héberger le site internet de notre jeu (ollopa.fr). Il servira à présenter le jeu et ainsi que l’équipe Ollopa. Nous pourrons implémenter, dans le futur, diverses fonctionnalités que nous ne souhaitons pas prendre en compte pour l’instant.
Il est possible que durant le projet nous ayons à réaliser des dépenses supplémentaires et nous avons donc prévu une enveloppe maximale de deux cents euros.
Nous souhaitons lancer notre jeu gratuitement sur différentes plateformes de jeu en ligne. En effet, étant notre premier jeu vidéo, nous serions déjà ravis que des gens y jouent.
3.3 Description des tâches
• Joueurs : Dans cette catégorie nous mettrons toutes les fonctionnalités que le joueur peut faire durant une partie dans le jeu, comme par exemple accéder à son inventaire pendant le jeu, ou encore placer différents bâtiments utilise au déroulement du jeu.
• Menu : Création d’un menu facile à utiliser mais comportant différentes fonctionnalités permettant au joueur une meilleure qualité de jeu.
• Génération de niveau : Génération d’une carte de jeu aléatoire pour chaque partie afin que le joueur n’ait pas l’impression de jouer tout le temps à la même partie. Création de forêts, d’eau et de montagne seront également crées de façon aléatoire.
• Environnement : Mise en place d’un environnement d’un planète spatiale inconnue contenant différentes ressources comme du bois spatial, ou de la roche spatiale. Création d’un environnement plus hostile grâce notamment aux ennemies se trouvant sur la planète et thèmes sonores liés aux ennemies.
• Site internet : Plateforme sur laquelle le jeu sera disponible au téléchargement. Mais aussi diverses informations sur le jeu comme des renseignements sur de futures mises à jour.
• Sons et musiques : Musique permettant au joueur de jouer dans une ambiance tranquille et détendue. Mais des thèmes sonores changeant selon la présence d’ennemie autour du joueur afin que le joueur puisse être dans l’ambiance du jeu.
ollopa.fr 9
3.4 Répartition des tâches
Adrien Lucas Pierre Thimot Hugo
Développement
XX
X
X
X
X
Graphismes
X
XX
Musiques et
bruitages
XX
X
Site internet
XX
X
Gestion
XX
X
X
Tableau de répartition des taches
Adrien Lucas Pierre Thimot Hugo
Joueur
X
XX
X
Génération
XX
X
Bâtiments
XX
X
Interfaces
XX
X
Menus
X
X
XX
Equilibrage
X
XX
Ennemis
XX
X
Autre
X
X
X
X
X
Tableau des taches de la partie développement
Légende :
− Responsable : XX
− Suppléant : X
ollopa.fr 10
3.5 Planning de développement
Pour l’organisation du planning de développement, nous utilisons Freedcamp.
ollopa.fr 11
4 - Conclusion
En conclusion, Ollopa est pour tous notre plus gros projet à réaliser à ce jour. Nous avons
imaginé un jeu simple et rapide pour éviter car nous ne connaissons pas nos capacités. Ce choix nous permet également de rajouter du contenu si nous estimons être en avance. Il nous permettra d’acquérir des connaissances en développement et de l’expérience dans la gestion d'un projet.
ollopa.fr 12
3 – Réalisation :
2.1 - Graphisme 2D
2.1.1 – Les dessins :
Les graphismes 2D ont été réalisés sur le logiciel “piskel” pour ce qui concerne la création des bâtiments
et les animations du joueur.
Broyeur Raffinerie Bâtiment de soin
Comme nous voulions que le jeu ait une allure plus « extraterrestre » nous avons aussi dessiné un ciel
qui rappelle l’espace et des montagnes violet clair en arrière-plan il en va de même pour l’allure des
arbres plutôt onirique.
Ciel en arrière-plan image feuillage et tronc d’arbre
Montagne en arrière-plan
ollopa.fr 13
2.1.2 – Fonctionnement des animations
Plusieurs bâtiments du jeu sont animés à l’aide d’un nœud animationPlayer. Nous allons
présenter ici l’exemple du puits de pétrole. Celui-ci est divisé en deux partie le corps et la
tête.
Tête
Corps
Il n’y a que la tête du puits qui se met à osciller lorsque le bâtiment s’active et puise du
pétrole, le corps ne bouge pas. De la même manière nous avons animé la foreuse pour que
la tête se déploie et vibre lorsque le système s’active.
De la même manière, les animations du joueur et de l’ennemi utilisent des nœuds
AnimationPlayer enregistrant différentes animations et étant jouées grâce à du code
implémenté aux scènes respectives.
Ci-dessous un exemple de l’animation de la mort du joueur :
ollopa.fr 14
2.2 - Le personnage
Le personnage du joueur est au centre de tout le jeu car c’est lui qui va pouvoir casser des
blocs, se déplacer dans le monde, construire des bâtiments. Ce sera donc le représentant du joueur
dans le jeu c’est pourquoi la bonne réalisation de ce personnage est primordiale.
Il possédera 3 variables l’affectant directement qui est la vie, l’oxygène et l’énergie. Si sa vie tombe à
0 il meurt, si son oxygène vient à manquer il mourra lentement et si son énergie est nulle il ne pourra
plus utiliser son pistolet laser et sera donc vulnérable face aux ennemis. Il est à noter que la mort du
joueur entrainera une perte de ces ressources qu’il ne pourra pas récupérer.
Le joueur doit pouvoir utiliser plusieurs objets, poser des blocs, poser des bâtiments, se déplacer et
peut aussi mourir. Pour cela nous avons besoin de donner au personnage plusieurs états pour
différencier par exemple le moment où le personnage utilise le laser ou le moment ou il pose des
bâtiments.
Ici nous pouvons voir l’organisation des différents nœuds de la scène du personnage.
2.2.1 - Mouvements du personnage :
Dans la scène Player, le script attaché correspond aux mouvements basiques du joueur (le
saut et le déplacement de gauche à droite) accorder aux animations du joueur, mais il comprend aussi
la téléportation du joueur au début de la carte si celui-ci est à la fin.
Ce script s'occupe aussi de savoir si le joueur se trouve dans de l’eau et si oui, son mouvement
se doit d’être altéré. Nous avons donc créé la fonction booléenne “IsInWater()” pour savoir si une partie
du joueur est en contact avec de l’eau. Celle-ci passe en revue les positions de sa taille (y) et de sa
largeur (x) et regarde si une de ces positions (x,y) est dans de l’eau. Si le joueur arrive dans l’eau avec
une grande vélocité l’eau le ralentira soudainement puis ses mouvements seront ralenties.
Grâce aux nœuds “AnimationPlayer” et “Image” nous avons créé plusieurs animations du
joueur qui sont la mort, la course, le saut, et l’attente du joueur. Celles-ci sont jouées dans le script du
“Player”.
ollopa.fr 15
2.2.2 - Différents modes pour le personnage :
Pour le Nœud 2D nommé « Inputs » nous donnons ce que peut faire le joueur en fonction des
différents états qui peuvent différer entre :
- Normal : Mode de base dans lequel le joueur peut
utiliser les outils et les blocs.
- Build : Mode dans lequel le joueur peut poser un
bâtiment.
- Inventory et BuildingInterface : Mode qui s’active
respectivement lors de la présence de l’inventaire et lors
de la présence de l’interface d’un bâtiment.
- Dead : Mode qui intervient à la mort du joueur.
- Link : Mode qui s’active lors d’une phase de liaison entre bâtiments, effectué par le joueur.
Nous nous occupons de savoir si nous pouvons poser des blocs, des bâtiments et faire des liens
entre les bâtiments. Par exemple :
public enum State
{
Normal,
Build,
Inventory,
Dead,
BuildingInterface,
Link
}
ollopa.fr 16
Ici, la fonction ClickNormalState regarde si la position où nous voulons poser un bloc est valide (pas
trop loin du joueur), si la catégorie du bloc peut être placée, s’il nous reste ce type de bloc dans
l’inventaire et si nous avons pu placer le bloc. Avec toutes ces conditions remplies, on peut enlever un
bloc (du même type) de l’inventaire.
Enfin, on s’occupe aussi de savoir si le joueur a appuyé sur la touche Tab pour ouvrir l’inventaire ou
sur la touche Echap pour fermer n’importe quelle fenêtre ouverte dans le jeu, ou encore s’il a appuyé
sur le clic droit de la souris pour poser un objet ce qui peux activer la fonction précédente si le joueur
est à l’état Normal.
private void ClickNormalState()
{
Usable.Type type = Player.UsableSelected;
Usable.Category cat = Usable.category[(int)type];
if (MouseInRange(9,false))
{
if(cat==Usable.Category.Block)
{
int amount = Player.inventoryUsables.GetItemCount(type);
if (amount>0)
{
bool succeed =
PlaceBlock.Place((int)mousePos.x,(int)mousePos.y,
Usable.blocks[type]);
if (succeed)
{
Player.inventoryUsables.Remove(type, 1);
if (ToolBar.GetInstance() != null)
{
ToolBar.SendRefresh();
}
}
}
}
}
}
ollopa.fr 17
2.2.3 - Autres :
Le nœud 2D « PlayerBuildingActions » sert à faire un lien entre le bâtiment stockant l’énergie
et le joueur afin de lui donner de l’énergie et aussi pour créer une animation entre les deux entités.
Le nœud « light » applique juste une lumière autour du joueur qui permet de mieux voir l’entourage du
personnage lors du cycle nuit.
Le nœud Area2D possède un CollisionShape2D qui est le même que celui du joueur seulement celui-
ci sert à savoir si les ressources représentées par des billes scintillantes jaunes sont arrivées au joueur.
Si oui, les ressources sont stockées dans l’inventaire ou détruites si on ne peut pas les ajouter à
l’inventaire, voir la fonction ci-dessous :
On remarque que même si on ne peut pas ajouter la ressource à notre inventaire, celle-ci disparaît à
notre rencontre.
Le nœud « Stream » sert à produire les bruits de pas du joueur et l’entré dans l’eau.
Enfin, le nœud Raygun possède toutes les fonctionnalités du pistolet laser pour que le joueur puisse
détruire des blocs. Lorsque le pistolet laser est utilisé il sera dans les mains du personnage (voir partie
2.7).
public void _on_Area2D_area_shape_entered(int id, Area2D area, int areaShape, int
selfShape)
{
/* REcuperation des loots*/
if (area.GetGroups().Contains("loot"))
{
/*On a un loot*/
Loot loot = area.GetNode<Loot>("..");
bool canAdd =
Player.inventoryItems.CanAdd(loot.GetLootType(),loot.GetLootAmount());
Player.inventoryItems.Add(loot.GetLootType(),
Player.inventoryItems.GetAmountCanAdd(loot.GetLootType(),
loot.GetLootAmount()));
area.RemoveFromGroup("loot");
if (canAdd)
{
loot.QueueFree();
}
else
{
loot.Explosion();
loot.dead = true;
loot.GetNode<Sprite>("img").Visible = false;
Delay.StartDelay(loot, 0.3f, () => loot.QueueFree());
}
}
}
ollopa.fr 18
2.2.4 - Poser des blocs :
Pour poser un bloc dans le monde, le joueur doit le sélectionner, il passe alors dans un mode lui
permettant de poser le bloc sélectionné. Attention, le joueur ne peut poser des blocs que dans un
certain rayon, dans les limites des mondes, en dehors de sa personne, en dehors de la surface d’un
bâtiment et s’il lui reste encore des blocs.
Une zone carré rouge ou verte qui suit la souris, lui indique si le joueur peut poser ou non un bloc. S’il
peut poser un bloc la zone devient verte, sinon elle reste rouge.
Une fonction CanPlace () vérifie si un bloc peut être posé ou non.
public static bool CanPlace(int x, int y, out bool res)
{
res = y >= 0 && y <= Chunk.height ;
if (res)
{
Block b = World.GetBlock(x,y);
res = res && b.GetType==Block.Type.Air;
res = res && (b.isAutoGenerated || HasNeighbors(b));
res = res && !HasBuilding(x, y);
}
return res;
}
ollopa.fr 19
2.3 - Les bâtiments :
2.3.1 - Les différents types de bâtiments :
2.3.1.1 Panneau solaire
Le premier bâtiment produisant de l’énergie, il la puise de
l’énergie de l’étoile, il ne fonctionne donc que la journée. Nous avons
intégré une fonction qui nous permet d’augmenter la puissance de
l’étoile en fonction de l’heure de la journée. La puissance atteint son
point culminant à midi.
Sur le graphique ci-contre, nous pouvons clairement
constater une variation de la production énergétique au cours de
la journée.
2.3.1.2 Générateur d’oxygène
Le joueur consommant en permanence de l’oxygène, il lui faut un
moyen de reconstituer sa réserve. C’est là qu’entre en jeu le générateur
d’oxygène qui produit de l’oxygène en utilisant de l’énergie et l’atmosphère
présente sur place.
2.3.1.3 Stockage
Le stockage est une batterie qui permet de stocker l’énergie. La
batterie peut recevoir et envoyer de l’énergie. Elle permet donc de faire un
tampon et de centraliser les connexions entre les générateurs et les
consommateurs. Sa fonction secondaire est de recharger l’énergie du joueur.
Le rechargement du joueur est prioritaire sur tout autre transfert d’énergie et
s’effectue dès que le joueur est assez proche du stockage.
Nuit Jour
ollopa.fr 20
2.3.1.4 Broyeur
Ce bâtiment utilise différentes ressources pour les transformer en
composite, matériau obligatoire pour réparer la fusée.
2.3.1.5 Imprimante 3D
L’imprimante 3D est un bâtiment essentiel, il permet au joueur
d’imprimer les autres bâtiments présents dans le jeu. Chaque bâtiment
est imprimable et demande des ressources, un temps de production et
une quantité d’énergie particuliers.
Une fois toutes les conditions réunis le joueur peut lancer
l’impression du nouveau bâtiment, une barre de progression ainsi qu’une
animation lui indique l’avancée de l’impression. Il peut aussi décider de mettre l’impression en
pause pour arrêter de consommer de l’énergie. Il peut également annuler l’impression, il
récupère alors toutes les ressources mais pas l’énergie dépensée.
2.3.1.6 Compacteur
Ce bâtiment permet la création des différents blocs utilisable par
le joueur. Chaque bloc possède une composition précise d’item (par
exemple, il faut l’item pierre pour confectionner un bloc de pierre).
2.3.1.7 Infirmerie
Le joueur peut utiliser ce bâtiment pour régénérer sa vie, le
bâtiment consomme de l’énergie et des items.
2.3.1.8 Puits de pétrole
Comme son nom l’indique, le puits de pétrole puise du pétrole
du sol de la planète.
ollopa.fr 21
2.3.1.9 Raffinerie
C’est un bâtiment complémentaire du puit de pétrole. Il permet au
joueur de raffiner le pétrole en carburant pour la fusée.
2.3.1.10 Foreuse
La foreuse est un bâtiment permettant la récupération des minéraux
enfuis dans le sous-sol de la planète. Elle évite au joueur d’aller chercher des
ressources lui-même.
2.3.1.11 Générateur thermique
Ce générateur permet de créer de l’énergie en utilisant des
matériaux combustibles comme le bois ou le pétrole. Ce bâtiment fonctionne
jour et nuit contrairement au panneau solaire qui ne fonctionne que le jour.
ollopa.fr 22
2.3.2 – Fonctionnement général :
La plupart des bâtiments fonctionnent autour du système d’énergie c’est-à-dire qu’il y a des bâtiments
qui vont produire l’énergie comme le panneau solaire ou le générateur thermique. Des bâtiments qui
stockent comme le stockage et enfin ceux qui utilisent l’énergie comme l’imprimante 3D, le compacteur
ou le broyeur.
Pour fonctionner, tous les bâtiments ont donc une réserve d’énergie qu’ils peuvent alimenter en
produisant/recevant de l’énergie et qu’ils peuvent soit consommer directement soit partager à d’autre
bâtiments.
L’écran de droite permet d’avoir toutes les informations utiles sur l’état du bâtiment :
La réserve d’énergie permet de connaitre la quantité d’énergie actuelle et maximum du bâtiment.
Les deux flèches permettent de savoir si le bâtiment donne ou consomme (flèche rouge) ou reçoit
(flèche verte) de l’énergie en ce moment.
Enfin les trois graphiques qui permettent de savoir dans l’ordre :
- jaune : connaitre la quantité d’énergie par rapport au temps
- vert : connaitre la quantité d’énergie par seconde reçus dans le temps
ollopa.fr 23
- rouge : connaitre la quantité d’énergie par seconde consommée dans le temps
2.3.3 – Réseau énergétique :
Le système de passage et transfert d’énergie se fait grâce au « link ». Cette fonction permet de lier les
bâtiments de production et de stockage à d’autre bâtiments du même types ou des bâtiments qui
utilisent l’énergie.
Ces bâtiments disposent donc d’un menu « link » permettant de choisir à quel bâtiment ils vont être
liés.
Un fois en mode « link » on peut sélectionner les différents bâtiments qui ont désormais un code couleur
qui les enveloppe.
- Le bâtiment violet est celui que l’on veut lier avec d’autre bâtiments.
- Un bâtiment rouge signifie qu’il ne peut pas recevoir d’énergie
- Un bâtiment jaune est un bâtiment déjà lié au bâtiment d’origine
- Un bâtiment vert est un bâtiment que l’on peut lier
Il suffit de cliquer/recliquer pour lier/délier un bâtiment et d’appuyer sur la touche échap pour quitter le
mode « link ».
ollopa.fr 24
2.3.4 – Interface des bâtiments :
Chaque bâtiment possède une interface personnelle. Celle-ci est divisé en deux partie, une première
partie sur son énergie qui est identique sur tous les bâtiments, puis une deuxième qui lui est propre.
Chaque interface est une scène entière qui est chargée lorsque l’on clique sur le bâtiment et qui est
déchargé lors de la fermeture de l’interface.
Celles-ci sont conçues pour toujours rester au milieu de l’écran peu importe la taille ou le ratio de de
ce dernier.
Ces interfaces sont principalement constituées de bouton, de label et de shader (pour l’affiche de
l’énergie, de l’oxygène, du pétrole et du carburant).
Ci-dessous vous pouvez voir l’interface la plus compliquée du jeu, celle de l’imprimante 3D.
On retrouve la partie énergique à droite, identique sur tous les bâtiments. La partie de gauche est
constitué de plusieurs boutons qui sont activés selon certaines circonstances. Nous avons ensuite
une description, au milieu, du bâtiment sélectionné, à gauche, celle-ci doit être modifié pour chaque
bâtiment car chaque information affichée est spécifique à un seul bâtiment (temps de construction,
ressources nécessaires, etc.).
ollopa.fr 25
2.3.5 – Placement des bâtiments :
Dans le jeu le placement des bâtiments suit plusieurs règles pour être cohérent avec l’environnement.
En effet le placement des bâtiments suit certaines règles :
-Il est de dimension 4 par 4 blocs
-il doit être placé sur quatre blocs pleins
-il ne peut pas être placé par-dessus un bloc ou un autre bâtiment
-il ne peut pas être placé en dehors des limites de jeux
Afin que le placement soit facile d’utilisation une fonction isplacable est constamment vérifié et un
carré de 4*4 de couleur verte ou rouge en fonction de si on peut placer le bâtiment est affiché à
l’emplacement de la souris.
ollopa.fr 26
2.3.6 – Le vaisseau spatial
Le vaisseau spatial est un bâtiment spécial, il est posé dès le début de la partie et ne peut
pas être imprimé avec l’imprimante 3D. Il ne peut pas non plus être raccordé au réseau énergétique.
Pour placer le vaisseau spatial, nous construisons une plateforme faite de blocs de fer et de
warningBloc. Pour se créer, le script de la plateforme prend en entrée une position “X” et une
longueur. De ces de valeurs, le script va déterminer la hauteur maximale sur toute la longueur
donnée à partir de la position “X”. Il va ensuite poser une rangée de bloc de fer deux bloc plus haut
par rapport à la hauteur maximale. Le système pose ensuite des warningBloc en guise de poteau à
intervalle régulier entre la plateforme et le sol.
Ci-dessous deux exemples de génération
A la suite de ça, deux lumières sont placées à chaque bout de la plateforme de manière à éclairer le
vaisseau. Enfin le vaisseau spatial est placé sur la plateforme terminée.
Le vaisseau de nuit
En ouvrant l’interface, nous pouvons trouver les trois objectifs à remplir pour terminer le jeu.
Nous devons réparer le vaisseau avec du composite, remplir son énergie ainsi que son réservoir de
carburant. Une fois tout ceci fait, le bouton Launch devient accessible, en cliquant dessus le joueur
ollopa.fr 27
fini la partie et le jeu affiche le temps total de jeu. Un objectif secondaire est fini le jeu le plus vite
possible.
Pour transférer des ressources au vaisseau, le joueur clique sur un bouton en dessous de la jauge de
la ressource de son choix. Le jeu transverse en continue tant que le joueur ne l’arrête pas, que
l’interface n’est pas fermée, que le joueur à de la ressource ou que la quantité n’est pas suffisante.
L’interface du vaisseau spatial
2.4 - Les ressources et les inventaires :
2.4.1 – Les ressources :
Dans notre jeu, nous pouvons classifier les ressources dans 3 catégories différentes : les
matière premières (« Items »), les utilisables (« Usables ») et les liquides.
Les matières premières :
- Le composite est la principale matière première. Cette matière est essentielle pour le
déroulement de la partie, elle est à la base de la construction de quasiment tous les
bâtiments du jeu. Le composite est aussi nécessaire pour gagner la partie en réparant le
vaisseau spatial du joueur.
- Les autres matières premières servent également à la construction de certains bâtiments.
Par exemple il faut du bois pour la construction du générateur d’oxygène.
Des catégories au sein même des matières premières sont créée tel que les combustibles et les
cristaux .
Les combustibles (bois) permettront de faire fonctionner certain bâtiment comme le générateur
thermique.
Les cristaux et le bois permettront de soigner le joueur si ce dernier les utilise dans le bâtiment de
soin.
Les utilisables :
Deux catégories se trouve dans les utilisables, les outils et les blocs.
Les outils ne sont pas accumulables contrairement aux blocs. Notre jeu ne possède qu’un outil qui est
le laser qui permet de casser et d’infliger des dégâts.
ollopa.fr 28
2.4.2 – Obtention des ressources :
Tous d’abord, le système d’obtention des ressources se fait grâce à un système de « loot » qui
nous permet de récupérer la matière première directement lorsque l’on casse des blocs.
Celui-ci possède un type (bois, pierre, terre, etc) et une quantité représentant le nombre d’objet du type
« type » que nous avons dans ce « loot » (par exemple 3 pierres), enfin le booléen « hasLoot » vérifie
si la variable « amount » passe en dessous de 0. Si c’est le cas, cette variable passera à « false ».
Les « loot » ont plusieurs caractéristiques pour simplifier la vie du joueur et diminuer le lag dû à
l’affichage :
- Ils sont attirés par le joueur si celui-ci se trouve à proximité.
Ces deux fonctions présentent dans le _PhysicProcess de « loot » permettent cette attraction, le corps
du « Player » est tout d’abord recherché et s’il se trouve dans une zone définie on actualise la position
du « loot » pour le rapprocher du « Player »
public void setLoot(Item.Type type, int amount) {
this.type = type; this.amount = amount; this.hasLoot = true;
}
var bodies = area.GetOverlappingBodies();
Node2D Player = null; foreach (var body in bodies) {
if (((Node2D)body).GetGroups().Contains("Player")) { Player = (Node2D) body; } } if (Player != null) { Vector2 vec = Player.Position - Position;
Position = Position + (vec.Normalized() * speed);
}
ollopa.fr 29
- Les « loot » du même type peuvent se compacter c’est-à-dire que casser dix blocs de
pierre ne fera apparaitre qu’un seul « loot » au lieu d’avoir dix « loot ».
Cette fonction permet à un premier « loot » de donner le plus de quantité possible a un deuxième
« loot ». Ainsi, si les quantités sont petites et inférieures à STACKSIZE, exécuter de nombreuse fois
cette fonction permet de compacter plein de petits « loot » en un seul.
- Ils ont une durée de vie limitée pour libérer la mémoire.
Cette simple fonction dans le _PhysicProcess de l’objet « loot » s’exécute à chaque tic de jeux et gère
la disparition du « loot » en temps voulu.
Une fois les matières premières obtenue il faudra la transformer soit :
- En blocs avec le compacteur
- En composite avec le broyeur
- Créer des bâtiments grâce à l’imprimante 3D
public void GiveLoot(Loot l)
{
int r = STACKSIZE - l.GetLootAmount(); int amount = 0; if (GetLootAmount() <= r) { amount = GetLootAmount(); } else { amount = r; } this.amount -= amount; l.AddLoot(amount); l.ResetTime(); }
public int GetLootAmount() => amount;
if (dead)
return; time += delta; if (time >= LIFETIME)
{
Explosion(); dead = true; Delay.StartDelay(this, 0.3f, () => QueueFree()); }
ollopa.fr 30
2.4.3 – L’inventaire :
Pour que notre joueur puisse interagir avec les objets et ressources qu’il transporte, nous avons créé
un inventaire. Celui-ci peut s’afficher en appuyant sur la touche tabulation. Ce dernier est divisé en
cinq. Il y a une partie pour les items, les blocs, les bâtiments, les liquides ainsi que les statistiques du
joueur (santé, oxygène, énergie).
Pour réaliser l’inventaire nous utilisons un nœud « Control » qui contient trois listes correspondant aux
zones items, blocs et bâtiment. A droite, nous trouvons une section affichant la quantité de liquide
(pétrole et carburant) et en dessous les statistiques du joueur (énergie, oxygène et vie).
De base, ce nœud n’est pas visible, il le devient lorsque nous appuyons sur la tabulation. Une mise à
jour des éléments est faite dans chaque liste pour que l’inventaire puisse être toujours à jour. Et l’état
du joueur devient « Inventory ».
Lorsque que le joueur appuie à nouveau sur tabulation ou sur le bouton en haut de l’inventaire cela
rend le nœud à nouveau invisible et change l’état du joueur sur celui présent avant d’ouvrir l’inventaire.
Ci-dessous une capture d’écran de l’inventaire, nous pouvons également sélectionner un item dans
l’inventaire, cela ferme alors automatiquement l’inventaire et met l’item dans la « main » du joueur.
Un compteur affiche également le nombre d’éléments pour chaque item.
ollopa.fr 31
2.5 – Environnement :
2.5.1 – Système jour/nuit :
Pour ce système jour/nuit nous utilisons toujours le paramètre “delta” de la fonction _Process
en guise d’horloge du jeu et un « CanvasModulate » (outil pour assombrir ou éclaircir les objets) associé
à un « Tween » (outil qui sert à animer une propriété au fil du temps) pour faire effet jour/nuit. Nous
avons changé le laps de temps entre le jour et la nuit et nous avons rajouter du brouillard par moment.
Il existe actuellement deux fonctions contrôlant le cycle, une pour le jour et une pour la nuit.
Voyons celle pour le jour :
Dans cette fonction la première condition donne les propriétés du CanvasModulate, la deuxième donne
les propriétés du brouillard et la troisième active la lampe du joueur en fonction du jour et de la nuit.
Le brouillard et la lumière du joueur sont actifs tout le temps mais sont visibles surtout la nuit.
2.5.2 – Paysages (parallaxe) :
Pour rendre notre jeu plus beau, nous avons décidé d’ajouter un décor de fond. Pour le rendre
plus réaliste nous l’avons animé en suivant les règles de la parallaxe.
Le paysage du jeu fonctionne avec un système de parallaxe sur trois plans pour donner un effet de
distance avec les montagnes et le ciel en arrière-plan.
Le principe est simple lorsque le joueur se déplace sur le premier plan a une certaine vitesse les deux
plans en arrière-plan (la montagne et le ciel) se déplacent à une vitesse différente de celle du joueur.
En effet pour donner l’impression que les montagnes sont lointaines celles-ci se déplacent moins vite
que le joueur, de même, pour ne pas donner l’impression que le ciel et les montagnes sont sur le même
plan ces deux plans n’ont pas la même vitesse. Cet effet est appliqué à la fois sur les mouvements
horizontaux et verticaux.
ollopa.fr 32
2.5.3 – Liquides :
Notre jeu étant un monde en 2D et fait de blocs, nous avions le choix d’intégrer les liquides
sous forme réaliste (infinités de position possible) ou sous forme de subdivision de bloc. Nous avons
choisi la seconde option qui nous semblait être la plus simple à mettre en place et la moins gourmande
en performance.
Pour la réaliser, nous avons dû trouver un équilibre entre nombre de subdivision et performances. En
effet plus le bloc est subdivisé plus les mouvements des liquides sont fluides mais cela amène un
réalisme qui ne correspond pas forcement au style graphique du jeu. Nous sommes donc parties sur 8
subdivisions par bloc, ceux-ci faisant 16 pixels. Ils nous seraient possible de monter à 16 subdivisions,
car cela nous permet de visuellement bien voir comment réagit le fluide.
Pour concevoir les liquides nous avons créé une
scène contenant un compte à rebours et une
grille contenant des images appelée TileMap.
Pour chaque type de liquide pouvant être sur la
carte (actuellement l’eau et le pétrole) mais le
processus d’ajout d’un liquide est assez simple.
Prenons un exemple avec l’eau, tout autre liquide se comporte exactement pareil, lors du lancement
de jeux un objet LiquidMove de type eau va être créé et ajouté à un dictionnaire permettant de l’appeler
par son type. L’objet que nous venons de créer contient une matrice de la taille de la carte et une liste
contenant la position des blocs contenant de l’eau. Lorsque le TimerWater, atteint 0, il appelle la
fonction Move de notre objet eau. Cette fonction a pour but comme son nom l’indique de déplacer l’eau.
Lors d’un mouvement, nous vérifions d’abord que l’objet
LiquidMove eau a fini de s’initialiser, nous avons dû utiliser
un thread donc nous vérifions que le thread est mort. Nous
calculons dans un premier temps le déplacement vertical de
chaque bloc, puis son mouvement horizontal. Enfin nous
redessinons notre liste de blocs sur la TileMap
correspondant à celle de l’eau.
Pour réaliser ces mouvements, nous avons dû réaliser trois versions différentes avant d’avoir quelque
chose d’utilisable.
Dans la première version nous avons utilisé uniquement la matrice du monde, pour chaque fonction
sur l’image ci-dessus nous parcourions toutes la matrice pour mettre à jour chaque case de cette
dernière seulement si elle contenait de l’eau. Nous faisions un quatrième parcours au début également
public void Move()
{
if (!init.IsAlive)
{
VerticalWater();
HorizontalWater();
DrawWater();
}
}
ollopa.fr 33
pour mettre à jour les blocs ne pouvant contenir de l’eau (si le joueur en supprime ou ajoute un bloc de
terre par exemple).
Cette version bien que fonctionnelle consommait beaucoup trop de performance et ne nous permettais
pas de dépasser les vingt images par seconde. De plus les performances dépendaient en train grande
partie de la taille du monde, la présence de bloc d’eau avait une influence négligeable.
Nous nous sommes donc attelés à créer une deuxième version n’utilisant cette fois ci plus qu’une liste
contenant de la position des blocs d’eau. Bien que plus performante, nous ne sommes jamais arrivés
à un résultat totalement fonctionnel. Nous avons donc décidé de créer une troisième version, utilisant
une matrice et une liste de blocs contenant de l’eau.
Celle-ci ne fait plus aucun parcours de matrice, en effet toutes les fonctions ne s’exécutent plus que
sur la liste contenant de l’eau. Cette version a d’excellentes performances mais peut néanmoins
consommer beaucoup de mémoire, jusqu’à 250Mo de RAM.
Il reste des améliorations possibles, dans un premier temps sur les règles pour les mouvements des
liquides qui pourrait être perfectionnées. Dans un second temps, il faudrait faire un travail de
simplification du code pour qu’il soit plus clair et plus court. Pour la complexité, nous pensons avoir
atteints le maximum que nous sachions faire, donc nous ne n’allons pas essayer de réduire encore la
complexité.
Tests de performances :
Version 1 : uniquement une matrice, les performances dépendent de la taille du monde
Version 2 : pas de test car jamais fonctionnelle
Version 3 : matrice et liste contenant les positions du liquide, les performances dépendent du nombre
de bloc de liquide
La taille du monde est de 100 blocs de hauteur et fera environ 500 blocs de largeur (peut-être sujet à
des modifications). Ce qui donne un monde d’environ 50 000 blocs. Nous souhaitons que les
performances ne diminuent pas avant d’avoir 500 blocs de liquides présents sur le monde. Notre ban
de test a été le suivant : Intel i7, 16 Go RAM, Nvidia 1050 Ti.
Version 1 Hauteur (blocs) Largeur (blocs) Total (blocs) Images par seconde
Test 1 100 48 4800 ̴ 50
Test 2 100 96 9600 ̴ 30
Test 3 100 160 16 000 ̴ 10
Test 4 100 500 50 000 < 5
Version 3 Nombres de blocs de liquide Images par seconde
Test 1 < 500 ̴ 110
Test 2 1000 ̴ 90
Test 3 2500 ̴ 80
Test 4 4000 ̴ 70
Test 5 5000 ̴ 50
Test 6 6000 ̴ 100
Avec la version 3, lorsque nous somme en 6000 et 7000 blocs image par seconde remontent comme
au départ, nous n’avons pas trouvé pourquoi mais nous pensons atteindre une limite de taille sur la
liste de position car le nombre d’élément se bloque entre 6000 et 7500.
ollopa.fr 34
2.5.4 – L’herbe :
Pour rajouter du contenu et de l’interaction avec l’environnement du jeu, nous avons décidé
de créer de l’herbe à la surface des blocs d’herbe.
Pour ce faire, nous avons désigné deux images, une pour l’herbe à l’état normale et une autre
lorsque cette dernière est écrasée par le joueur.
De plus pour rajouter du dynamisme à l’herbe, nous avons utilisé un « shader » (programme qui
paramètre le processus de rendu) qui simule l’ondulation de l’herbe face au vent.
Pour l’apparition de l’herbe sur le monde, nous avons créé une fonction qui pose l’herbe à la surface
du relief et qui vérifie que le bloc en dessous soit bien un bloc d’herbe :
Chaque image (« Sprite ») de l’herbe sur le monde possède son propre script qui vérifie plusieurs
choses. Premièrement, le script vérifie les changements de blocs :
- Le bloc en dessous de l’herbe
- Le bloc au niveau de l’herbe
Si les types de blocs ne correspondent pas respectivement à un bloc d’herbe et un bloc d’air, l’herbe
est supprimée du monde.
Dernièrement, le script détecte si le joueur se trouve sur l’herbe et modifie en fonction son image
entre les deux ci-dessus.
L’herbe n’est pas entièrement terminée car il reste à intégrer la gestion de fin de monde, car pour
l’instant l’herbe n’apparait pas aux bornes du monde.
2.5.5 - Monde Infini
Nous avons décidé que le joueur devait pouvoir faire le tour du monde et revenir à son point de
départ. Pour ce faire nous téléportons le joueur en position 0 lorsqu’il atteint la fin du monde ou en fin
de monde lorsqu’il atteint la position 0.
public static void GrassGenerate();
public static void GrassGenerate()
{
for (int x = 0; x < World.size * Chunk.size; x++)
{
Chunk c = GetChunk(x);
int yground = c.GetGroundY(Chunk.GetLocaleX(x));
if (World.GetBlock(x, yground - 1).GetType == Block.Type.Grass)
{
Grass.Spawn(x,yground);
}
}
}
ollopa.fr 35
Néanmoins l’affichage des éléments « au-delà » de la fin du monde (donc ceux du début) ne
s’affichait pas avant la téléportation. Nous avons donc mis en place un système d’affichage qui
affiche tous les éléments « au-delà » du monde.
2.6 - La génération de l’environnement :
Pour la génération de l’environnement du jeu, nous avons décidé de créer un algorithme qui
génère le terrain du jeu de façon aléatoire mais contrôlé (alias procédurale).
Nous avons également mis en place un système de graine (« Seed ») de la carte du jeu pour que les
utilisateurs puissent jouer sur la même génération de carte. Cela pourra permettre aux joueurs de jouer
plusieurs fois sur une certaine génération ou bien même d’imaginer un système de compétition en
limitant l’aspect aléatoire du jeu.
2.6.1– génération du monde :
La génération du monde correspond à celle du relief du monde.
La génération du relief est procédurale (aléatoire contrôlé).
Pour créer un relief qui correspond à celui d’une planète tel que la nôtre, nous avons décidé d’utiliser
un « Simplex noise » qui est une texture de bruit qui permet d’avoir des nuances de gris qui peuvent
être contrôlé en fonction de plusieurs variables tel que : les octaves, la période, la persistance et la
lacunarité.
Après de nombreux essais nous avons déterminé les valeurs associer aux variables ci-dessus :
- Octaves = 3
- Période = 20
- Persistance = 0.1
- Lacunarité = 3.5
Apres l’obtention de nos valeurs de nuances de gris, nous avons déterminé les variables de
délimitation de notre monde :
« chunkMax » : l’ordonné maximale de notre monde
« chunkMin » : l’ordonné minimale de notre monde
« height » : La hauteur de notre monde
« seaLevel » : correspond au niveau de la mer théorique
« minYGeneration » : l’ordonné minimale de la génération de la surface du monde
« maxYGeneration » : l’ordonné maximale de la génération de la surface du monde
Ainsi avec les valeurs « minYGeneration », « maxYGeneration » et les valeurs du bruit simplex, nous
avons pu générer le relief de notre monde.
public const int chunkMax = 100;
public const int chunkMin = 0;
public const int height = (chunkMax-chunkMin)+1;
public const int seaLevel = chunkMin + 30;
public const int minYGeneration = seaLevel - 12;
public const int maxYGeneration = seaLevel + 20;
ollopa.fr 36
2.6.2 –
génération
des minerais :
Pour la génération des minerais nous avons créé une nouvelle classe statique « Ore ». Cette classe
possède :
- Une liste « ores » qui contient les types des blocs correspondant à un minerai
- Un Dictionnaire « probabilities » qui associe une probabilité d’apparition a un minerai
- Un Dictionnaire « heights » qui associe une hauteur à un minerai, la hauteur représente
une hauteur en dessous de laquelle le minerai peut apparaitre
La classe « Ore » possède également une liste de couples composés d’un flottant et d’une chaine de
caractères « veins » qui permet à partir d’un nombre flottant compris entre 0.0 et 1.0 d’obtenir la
forme d’un filon.
Pour obtenir la forme du filon à partir d’une chaine de caractère de la forme « ___,___,___ » avec ‘_’
égale ‘0’ ou ‘X’, il faut voir la chaine de charactère comme un tableau de 3 colonnes et de 3 lignes qui
public static List<Block.Type> ores = new List<Block.Type>
{
Block.Type.SonarOre,
Block.Type.OspiritOre
};
public static Dictionary<Block.Type, float> probabilities = new
Dictionary<Block.Type, float>
{
{Block.Type.SonarOre, 0.15f},
{Block.Type.OspiritOre, 0.02f}
};
public static Dictionary<Block.Type, int> heights = new Dictionary<Block.Type, int>
{
{Block.Type.SonarOre, 15},
{Block.Type.OspiritOre, 10}
};
ollopa.fr 37
représente la position spatiale des minerais dans le filon. Ainsi, il faut parcourir de gauche à droite la
chaine caractère sachant qu’un ‘X’ correspond à un minerai du filon.
Exemple : pour « 0XX,XXX,XX0 » on a :
Ainsi, grâce à ces données on peut générer les minerais en fonction de leur rareté et de leurs couches
d’apparition et ils peuvent apparaitre sous différente forme de filon.
La génération des minerais se déroule juste après la génération du relief du terrain à travers la
fonction :
Cette fonction parcourt le terrain de gauche à droite, blocs par blocs. A chaque valeur de x du terrain
elle calcule un flottant pour chaque minerai, et si celui-ci correspond à la probabilité d’apparition du
minerai en question alors un filon aléatoire sera créé à une hauteur aléatoire en dessous de la hauteur
d’apparition du minerai.
0 X X
X X X
X X 0
public static List<(float, string)> veins = new List<(float, string)>
{
(0.1f, "000,0XX,000"),
(0.2f, "0X0,0X0,000"),
(0.3f, "0X0,0X0,X00"),
(0.4f, "0XX,0XX,000"),
(0.5f, "0X0,0XX,000"),
(0.6f, "0XX,0XX,XX0"),
(0.7f, "0XX,XXX,XX0"),
(0.8f, "0XX,X00,XXX"),
(1.0f, "XX0,0X0,000")
};
private static void OreGenerate();
ollopa.fr 38
2.6.3 – Génération des arbres :
Nous avons ajouté les arbres pour créer les prémices d’une biodiversité avec laquelle le joueur
pourra interagir. Les arbres sont constitués de deux éléments, le tronc et le feuillage, actuellement il
existe deux types d’arbres, les « verts » et les « roses ».
Un monde possède un paramètre de densité moyenne d’arbre par chunk (subdivisons du monde) qui
permet de régler la densité d’arbre présente sur le monde. Tous les arbres générés sont ensuite stockés
dans une liste
Chaque arbre, généré aléatoirement, peut avoir une infinité de tailles. Il possède également plusieurs
paramètres comme de la vie, ses ressources et sa position.
La vie d’un arbre dépend de sa taille, plus il est grand plus il aura de vie. Il en est de même avec les
ressources (ce qu’il donne au joueur lorsqu’il est détruit), les ressources étant toujours du bois.
Le joueur peut détruire un arbre de deux manières. Soit en le détruisant avec le pistolet laser, une
animation est présente sur l’arbre pour montrer qu’il perd de la vie. Lorsque l’arbre est détruit il tombe
sur le sol et donne ses ressources. Soit en détruisant le bloc sur lequel repose l’arbre, à ce moment-là
l’arbre se détruit instantanément mais ne donne aucune ressource au joueur.
La génération de l’arbre elle se fait donc par chunk car une fois un chunk généré il va ensuite générer
ses arbres.
private static void OreGenerate()
{
for (int x = 0; x < World.size * Chunk.size; x++)
{
foreach (var ore in Ore.ores)
{
float p = (float)random.NextDouble();
if (p <= Ore.probabilities[ore])
{
int height = random.Next(1, Ore.heights[ore] + 1);
if (World.GetBlock(x, height).GetType == Block.Type.Stone)
{
Ore.CreateVein(ore, x, height);
}
}
}
}
}
ollopa.fr 39
A chaque coordonnée “x” du chunk on va juste rendre aléatoire le fait que nous allons oui ou non placer
un arbre en fonction de la TREE_FREQUENCY , les arbres seront alors simplement placés à la
coordonnée “y” du sol.
2.6.4 – Génération des grottes :
Le jeu comporte aussi un système aléatoire de génération des grottes.
Pour cela on utilise un automate cellulaire simple semblable à celui du “jeu de la vie” de John Horton
Conway.
La génération s’effectue en deux étapes tout d’abord sur une matrice de booléens représentant le
monde :
1) En utilisant les nombres aléatoires de la graine on initialise aléatoirement chaque case du
monde en « vrai » (bloc plein) ou en « faux » (bloc d’air)
2) Ensuite on effectue plusieurs générations, ici 7, de l’automate cellulaire pour former des
grottes.
private void TreesGeneration()
{
for (int x = 0; x < size; x++)
{
float r = (float)World.random.NextDouble();
if (r <= TREE_FREQUENCY)
{
int y = GetGroundY(x);
Tree.SpawnTree(new Vector2(x+id*size,y));
}
}
}
ollopa.fr 40
A chaque génération on applique les règles de génération sur chacune des cases en fonction de leur
nombre de voisins « vrai », qui sont les suivantes :
- Si un bloc est « vrai » alors s’il a moins de 4 voisins il devient « faux » sinon il reste vrai.
- Si un bloc est « faux » alors s’il a moins de 4 voisins il devient « vrai » sinon il reste faux.
Chacune des générations est effectuée pas en place et demande la création d’une nouvelle matrice.
Une fois les générations effectuées on applique la dernière matrice au monde et on remplace par de
l’air tous les blocs de roches de la carte qui sont à une coordonnée « faux » de la matrice.
ollopa.fr 41
2.6.5 – Génération des lacs :
La génération des lacs se base sur un niveau d’océan qui est la hauteur maximum d’apparition de
l’eau. Tous les blocs d’air en dessous de ce niveau et au-dessus du sol sont remplacés par de l’eau.
private static void CaveGenerate()
{
bool[,] cellmap = Cave.InitCave();
for (int i = 0; i < World.size * Chunk.size; i++)
{
for (int j = 0; j < Chunk.maxYGeneration; j++)
{
if (!(cellmap[i, j]))
{
int yWorld = j;
Chunk c = GetChunk(i);
int xChunk = i % Chunk.size;
Block.Type t = World.GetBlock(i, j).GetType;
if (t == Block.Type.Stone)
c.AddBlock(xChunk, yWorld,
Block.Type.Air);
}
}
}
}
ollopa.fr 42
Les blocs de terre avec de l’herbe sont remplacé par un bloc de terre si de l’eau est dessus.
La fonction qui génère les lacs
Un exemple de génération
2.7 – Le pistolet laser :
Le pistolet laser, nommé « Raygun » dans la plupart du projet, sert à se défendre face à des
ennemis mais aussi à récupérer des ressources en cassant des blocs par exemple. Il sera l’outil le plus
private static void SeasGenerate()
{
for (int x = 0; x < World.size * Chunk.size ; x++)
{
Chunk c = GetChunk(x);
int yground = c.GetGroundY(Chunk.GetLocaleX(x));
if (yground <= Chunk.seaLevel)
{
GetChunk(x).AddBlock(Chunk.GetLocaleX(x), yground-1,
Block.Type.Dirt);
}
for (int y = Chunk.seaLevel; y >= yground; y--)
{
Liquid.PlaceLiquid(x, y, Liquid.Type.Water);
GetChunk(x).GetBlock(Chunk.GetLocaleX(x), y).isAutoGenerated =
true;
}
}
}
ollopa.fr 43
utilisé par notre personnage et sera vital à sa survie, il est donc impératif qu’il soit fonctionnel et qu’il
donne au joueur une bonne sensation quant à son utilisation.
Il sera en tout temps dans les mains du joueur et se focalisera sur l’emplacement de la souris mais
n’aura qu’une certaine rangé d’action.
La scène du laser utilise un nœud RayCast2D pour pouvoir savoir ce qui rentre en collision
avec son rayon.
La fonction shoot(float delta) nous montre bien que si le RayCast2D rentre en collision avec un
bloc, un arbre ou un ennemi cela appelle respectivement les fonctions BlockCollision, TreeCollision et
EnemyCollision qui vont faire des dégâts à l’objet concerné jusqu’à ce qu’il n’ait plus de vie et
disparaisse. Pour que cela fonctionne le laser possède un taux de dégât et les objets ont une vie limitée.
L’animation du rayon laser se fait grâce à deux images begin et beam chacune dans un sprite
différent. L’image begin sert à faire un cercle à la sortie du canon du pistolet laser, puis beam fait la
continuité du laser jusqu’à la fin. Ceci est possible en agrandissant l’image beam avec la RegionRect
d’un sprite qui contrôle la taille d’une image. En donnant pour celle-ci la longueur entre le canon et la
collision ou s’il n’y a pas de collision entre le canon et la fin du Raycast2D. Pour que cette image
suive le RayCast2D elle va prendre la rotation du celui-ci. Enfin, si le rayon collisionne avec un objet
cela crée une Particule2D à l’endroit de la collision. Nous pouvons donc remarquer ces configurations
dans la fonction shoot du dessus présent au début après la première condition et à la fin lors de la
condition contraire.
ollopa.fr 44
public void shoot(float delta)
{
begin.Visible = true;
beam.Visible = true;
if (raycast.IsColliding())
{
end.GlobalPosition = raycast.GetCollisionPoint();
float collision = (raycast.GetCollisionPoint()-
raycast.GlobalPosition).Length()*500/120;
beam.Rotation = raycast.CastTo.Angle();
var beamRegionRect = beam.RegionRect;
var vector2 = beamRegionRect.End;
vector2.x = collision;
beamRegionRect.End = vector2;
beam.RegionRect = beamRegionRect;
var hit_collider = raycast.GetCollider();
// Collide with a block
if (hit_collider is TileMap)
{
BlockCollision(delta);
}
else if (hit_collider is StaticBody2D)
{
// Collide with a tree
StaticBody2D s = (StaticBody2D) hit_collider;
if (s.GetGroups().Contains("tree"))
{
TreeCollision(delta);
}
}
else if (hit_collider is Ennemy_Fly)
{
EnemyCollision(delta);
}
}
else
{
end.GlobalPosition = raycast.CastTo;
beam.Rotation = raycast.CastTo.Angle();
var beamRegionRect = beam.RegionRect;
var vector2 = beamRegionRect.End;
vector2.x = raycast.CastTo.Length();
beamRegionRect.End = vector2;
beam.RegionRect = beamRegionRect;
}
particule.Emitting = true;
}
}
ollopa.fr 45
2.8 – Les ennemis :
Les ennemis rajoutent de la difficulté au jeu et est un bon moyen de faire apprécier le jeu à la
personne qui y joue. Quoi de mieux que de tuer des ennemis avec un laser ?
Nous avons donc créé des ennemis volants ressemblant à ceci :
Ils vont un peu moins vite que le joueur et sont attiré par lui
lorsque celui-ci est proche. A la collision du joueur il lui inflige des
dégâts et recule pour réattaquer par la suite.
Les mouvements de ces ennemis sont basés sur le même
principe que celui du joueur à la différence près qu’ils ont des mouvements aléatoires lorsque le
joueur n’est pas proche et vont vers le joueur lorsqu’il est d’eux. Tous cela grâce à la fonction
« MoveAndSlide() » de Godot.
La fonction booléenne PlayerNear() indique si le joueur est près de l’ennemi.
private bool Player_Near()
{
var bodies = proximity.GetOverlappingBodies();
bool verif = false;
int i = 0;
int n = bodies.Count;
while (!verif && i < n)
{
if (((Node2D)bodies[i]).GetGroups().Contains("Player"))
{
verif = true;
}
i++;
}
return verif;
}
ollopa.fr 46
La fonction Random_moves() donne sur l’axe x une vitesse et une direction aléatoire à
l’ennemi et le garde en l’air.
private void Random_moves()
{
if (EnemyDirection == 1)
{
Enemy_fly.FlipH = true;
}
else
{
Enemy_fly.FlipH = false;
}
if (IsOnWall() || IsOnFloor())
{
vel.y += JUMP_power;
vel.x = 0;
}
else
{
vel.y = 0;
}
if (!IsOnWall())
{
vel.x = EnemyDirection * rand.Next((int) (Speed/5),(int)(Speed));
}
MoveAndSlide(vel, up);
}
ollopa.fr 47
La fonction Get_To_Player() fait bouger l’ennemi vers le joueur en le faisant glisser sur les
parois pour ne pas qu’il se bloque.
Lorsque la nuit tombe et toutes les 45 secondes un ennemi apparaît près du joueur en
« tombant du ciel » puis se relève et entreprend ces mouvements habituels.
Cet ennemi apparait car il a été instancié à la scène principale du jeu. Il ne peut pas y avoir plus de 5
ennemis à la fois et l’ennemi disparaît au bout d’un certain temps.
Pour rendre l’ennemi plus vivant on lui à ajouter une animation lorsqu’il vole (battement
d’ailes) et lorsqu’il meurt (désintégration).
Le joueur peut tuer un ennemi en maintenant son rayon laser dessus et il disparaitra au bout
d’un certain temps en tombant par terre et se désintégrant.
private void Get_To_Player()
{
Position_Player = new Vector2(PlayerMouvements.GetX(),
PlayerMouvements.GetY());
Position_Player = Convertion.World2Location(Position_Player);
Vector2 direction = (-this.GlobalPosition
+Position_Player).Normalized();
vel = direction * Speed;
if (vel.x >= 0)
{
Enemy_fly.FlipH = true;
}
else
{
Enemy_fly.FlipH = false;
}
MoveAndSlide(vel, up);
}
ollopa.fr 48
2.9 – Interfaces utilisateur :
2.9.1 – Ajustement d’écrans :
Dès le début du jeu, nous avons fait face à un problème majeur, les différentes définitions et
de tailles de nos écrans. Nous avons donc ajouté un nœud au jeu chargé de ce problème.
Nous voulions que la fenêtre s’adapte aux écrans plus grands mais surtout plus petits. Pour cela nous
maximisons la fenêtre à la taille de l’écran au lancement du jeu et nous redéfinissons notre maximum
comme étant la taille de la fenêtre actuelle à ce moment-là. De cette manière le fenêtre du jeu ne peux
pas être plus grande que l’écran sauf si le joueur l’agrandit manuellement.
Si nous voulons utiliser le jeu dans une fenêtre plus petite, celle-ci passe automatiquement à quatre-
vingt-dix pourcents de la taille maximale lorsque nous cliquons sur le bouton pour passer en mode
fenêtre.
Nous avons décidé que notre jeu serait généré en 1920*1080 car c’est la définition d’écran la plus
courante selon “Steam”. Nous n’allons cependant pas nous adapter aux autres définitions car trop
compliqué à mettre en place et à tester. De plus, nous ne pensons pas avoir le temps de le faire. Le
jeu sera néanmoins jouable dans toute sorte de définition, si la définition est plus élevée, le viewport
(fenêtre générée par le jeu) affichera plus de bloc car il sera plus grand. Pour les résolutions inférieures,
cela sera l’inverse et nous verrons moins de bloc à l’écran.
2.9.2 – Menus :
Les menus prennent une place importante dans le jeu puisqu’ils permettent au joueur de gérer
ses sauvegardes.
Le menu d’accueil nous permet de lancer une partie avec une « seed » prédéfinie ou avec une « seed »
aléatoire. Le “seed” spécifique permet de générer une carte précise.
Un menu de gestion des sauvegardes est implémenté pour permettre de charger des parties, de les
sauvegarder et de les supprimer pour permettre au joueur de garder son avancée et d’étendre sa
session de jeux sur une vaste période de temps.
ollopa.fr 49
2.10 – Système de sauvegardes :
La sauvegarde d’une partie est un point essentiel de notre jeu, notamment dû à longueur
d’une partie qui peut s’étendre sur plusieurs heures. Nous avons rencontré plusieurs difficultés
puisque nous avons commencé le jeu sans vouloir faire de système de sauvegarde, et donc celui-ci
n’était pas conçu pour.
Les sauvegardes de notre jeu se trouve dans les fichiers locaux de l’utilisateur
(« home/$USER/.Godot/app_userdata/ThirteenProject» sur linux et
« %appdata%/Godot/app_userdata/ThirteenProject » sur windows).
Un dossier de sauvegarde est composé de plusieurs fichiers, qui contienne toutes les informations
d’une partie.
Chaque fichier .data du dossier de sauvegarde est un fichier qui contient des informations au format
JSON.
Pour la sauvegarde :
Pour chaque fichier .data, on sérialise une classe au format JSON est on écrit le texte JSON
dans le fichier correspondant.
Pour le chargement :
Pour chaque fichier .data, on désérialise le texte du fichier pour obtenir la classe
correspondante avec les variables contenues dans la sauvegarde.
Exemple de la classe de sauvegarde du fichier « Environnement.data » :
Architecture d’une sauvegarde
ollopa.fr 50
La classe « EnvironementDataModel » possède trois variables, ce sont les variables qui vont être
sauvegardées dans le fichier « Environnement.data ». La fonction « GetValues() » a pour but d’aller
récupérer les informations dans le jeu pour les stocker dans les variables de la classe avant que la
classe soit sérialiser pour obtenir un fichier JSON.
A l’inverse, la fonction « SetValues() » a pour but de mettre à jour les valeurs du jeu en fonction des
variables de la classe de sauvegarde.
public class EnvironementDataModel
{
public float time { get; set; }
public List<float> sunPowerhistory { get; set; }
public float timePlayed { get; set; };
public void GetValues()
{
time = Environement.time;
sunPowerhistory = Environement.sunPowerhistory;
timePlayed = Game.timePlayed;
}
public void SetValues()
{
Environement.time = time;
Environement.sunPowerhistory = sunPowerhistory;
Game.timePlayed = timePlayed;
}
}
ollopa.fr 51
2.11 – Son :
Tous les sons du jeu attachés au joueur sont joués grâce à la fonction « PlayStream » présente dans
le script « PlayerMouvements.cs ».
Celle-ci permet de diffuser un grand nombre de son différents simultanéments.
Pour chaque son, un nouveau AudioStreamPlayer2D est créé, le son est joué puis le nœud est
détaché.
Les sons sont regroupés dans un dictionnaire dans le script « Sound.cs » pour rendre leur utilisation
plus pratique, leur nom, leur chemin d’accès et le niveau de décibel à lequel on veut les jouer est
ainsi stocké.
public async void PlayStream(Sounds.Type sound)
{
AudioStreamPlayer2D Sound = new AudioStreamPlayer2D();
Sound.Stream = Sounds.sounds[sound];
Sound.VolumeDb = Sounds.soundAjust[sound];
AddChild(Sound);
Sound.Play();
await ToSignal(Sound, "finished");
Sound.QueueFree();
}
ollopa.fr 52
public enum Type
{
PlayerDeath,
PlayerGetloot,
PlayerLaser,
BlockBreak,
PlayerHurt,
PlayerStep,
PlayerPlouf,
PlayerLanding
}
public static Dictionary<Type, AudioStream> sounds = new Dictionary<Type,
AudioStream>
{
{Type.PlayerDeath,
GD.Load<AudioStream>("res://Assets/Ressources/Sounds/Player/player_death.wav")},
{Type.PlayerGetloot,
GD.Load<AudioStream>("res://Assets/Ressources/Sounds/Player/player_getloot.wav")},
{Type.PlayerLaser,
GD.Load<AudioStream>("res://Assets/Ressources/Sounds/Player/player_laser.wav")},
{Type.BlockBreak,
GD.Load<AudioStream>("res://Assets/Ressources/Sounds/Blocks/BlockBreak.wav")},
{Type.PlayerHurt,
GD.Load<AudioStream>("res://Assets/Ressources/Sounds/Player/player_hurt.wav")},
{Type.PlayerStep,
GD.Load<AudioStream>("res://Assets/Ressources/Sounds/Blocks/BlockBreak.wav")},
{Type.PlayerPlouf,
GD.Load<AudioStream>("res://Assets/Ressources/Sounds/Player/player_plouf.wav")},
{Type.PlayerLanding,
GD.Load<AudioStream>("res://Assets/Ressources/Sounds/Blocks/BlockBreak.wav")},
};
public static Dictionary<Type, float> soundAjust = new Dictionary<Type, float>
{
{Type.PlayerDeath, -15},
{Type.PlayerGetloot, -25},
{Type.PlayerLaser, -15},
{Type.BlockBreak, -20},
{Type.PlayerHurt, -15},
{Type.PlayerStep, -37},
{Type.PlayerPlouf, -20},
{Type.PlayerLanding, -28}
};
ollopa.fr 53
3 - Chronologie :
Dans cette partie, nous détaillons la liste des choses faites depuis le début du projet, pour
chaque section le nombre d’heure indique le temps approximatif passé dessus.
Adrien Pierre Hugo Lucas Thimot
Documentation 30h 20h 20h 45h 25h
Dessins 2h 30h 30h 3h
Le joueur 18h 20h
Bâtiments 35h 12h 10h 50h
Ressources 25h
Environnement 25h 80h
Génération du monde 30h 5h
Ennemi 1h 2h 15h
Interface utilisateur 20h 5h
Son 1h 5h
Sauvegardes 20h
Equilibrage 5h 4h 2h 15h
Site 4h 30h 8h
Corrections de bug 20h 5h 2h 4h
Détails mineurs 10h 5h 3h 5h
Total 246h 83h 94h 91h 192h
ollopa.fr 54
4 - Conclusion : En conclusion de ce rapport, nous sommes fiers de notre jeu même s’il n’est pas parfait. Nous ne
pensions pas avoir quelque chose d’aussi amusant au départ, sachant que c’était notre premier
véritable projet en équipe. Lors des phases d’équilibrages nous avons eu d’excellent retour de la part
d’amis qui le trouvaient amusant, ce à quoi nous ne nous attendions pas vraiment.
De plus, ce projet nous a permis d’apprendre énormément en informatique mais également en gestion
de planning, préparation de soutenances et gestion de groupe.
Les points que nous n’avons pas réaliser ou modifier par rapport au cahier des charges sont des
adaptations pour rendre le jeu meilleur. Après la dernière soutenance, nous souhaitons continuer le jeu
et peut-être le mettre gratuitement sur une plateforme de jeu.
En définitive, ce jeu nous a beaucoup apportés et nous espérons qu’il vous amusera.