A downloadable game

Festicare

Festicare est un jeu au style « OverCooked » qui peut se jouer jusqu’à deux joueurs sur manettes. Il a été développé en C++ sur Unreal Engine 4.27.2.

C’est un jeu sérieux développé dans le cadre du cours d’atelier de production de jeux vidéo 1 à l’Université du Québec à Chicoutimi et en collaboration avec le NAD de Montréal. Le développement du jeu a duré 5 mois.

Nous étions 18 pour faire ce projet, 4 programmeurs, 9 artistes et 5 musiciens.

Le but

Notre jeu a pour but d’informer les joueurs sur les drogues, dans notre cas, l’alcool, la cocaïne et le LSD. « Si tu choisis de consommer, choisis aussi de t’informer » qui est le moto du Grip et l’inspiration de notre jeu  https://grip-prevention.ca/.  Le but étant évidemment la prévention, mais sans pour autant démoniser les drogues.

Description du jeu

Pour avoir plus d’informations sur notre jeu ou y jouer, vous pouvez consulter notre page itch.io : https://uqac.itch.io/festicare 

Mes tâches dans ce projet

Requête

Description de la mécanique

Notre jeu étant un jeu sérieux, il a pour but d’apprendre au joueur des informations sur les drogues. C’est à ça que la demande de brochure sert. Les joueurs, lorsqu’ils reçoivent ce type de demande, doivent aller voir notre tableau d’informations liées à la brochure demandée et doivent prendre l’information correspondante dans notre carrousel de la station de brochure, le hic est que le tableau ne peut afficher qu’une seule drogue à la fois. Donc, les joueurs ont avantage à se souvenir de ces informations pour répondre plus efficacement aux demandes. 

1. Station de brochure 2. Tableau d’informations 3. Demande de brochures

Lorsque le joueur reçoit une demande d’analyse de drogues, il doit prendre l’échantillon à analyser et l’apporter à notre station d’analyse. À ce moment, il doit attendre que l’analyse finisse pour ensuite apporter la brochure demandée par la station d’analyse, par après, il peut récupérer le résultat et le donner au festivalier.

1. Demande d’analyse 2.Échantillon 3.Station d’analyse


Demande de la station d’analyse

Tâche

Pendant ce projet, j’ai beaucoup travaillé sur le système de requêtes et d’objectif.

La requête lance ses états vers le Blueprint en utilisant des multicast delegate (done,start,failed etc.). Ces évènements sont écoutés par plusieurs autres acteurs pour faire des actions (affichage de UI par exemple).

Quelques exemples des résultats des multicast delegate :

Requête apparait lorsqu’elle démarre


Festivalier refuse un objet qui n’est pas le bon

La requête est créée dans un Blueprint qui s’occupe des vagues des festivaliers, elle est ensuite initialisée pour lui passer son propriétaire, on l’initialise grâce à la fonction du même nom qui est un BlueprintNativeEvent. Lorsque le joueur interagit avec le festivalier, le festivalier va démarrer la requête et va construire le widget de celle-ci. C’est le widget qui va s’occuper de créer ses objectifs grâce à la liste d’objectifs contenue dans la requête.

Lorsque le joueur apporte un objet au festivalier, celui-ci va appeler le UpdateProgress de la requête, on appelle la fonction du même nom dans tous ses objectifs dans le UpdateProgress. La requête bien sûr renvoie le résultat de la progression d’un objectif dans un évènement.

Finalement, elle contient aussi une fonction pour retourner le temps restant normaliser pour l'affichage de la barre de temps. 

L’objectif contient son icône, un owner, un bool qui indique sa complétion avec le delegate associé et plusieurs fonctions : Progress, CheckProgress et Initiate qui vont être override dans les enfants de l'objectif, ses enfants sur lesquels j’ai travaillé sont ItemObjective et DrugTestObjective. Il va aussi contenir une manière de savoir s’il a été initialisé et un TagContainer pour les GameplayTag, je ne parlerai pas de ça puisque ce n’est pas moi qui les ai faites.

ItemObjective va contenir l'objet voulu pour compléter l'objectif et va surcharger la fonction Progress et CheckProgress pour pouvoir définir comment il va se compléter.

DrugTestObjective va être un enfant de ItemObjective et va surcharger la fonction Initiate et CheckProgress. Il va aussi contenir le DataAsset de la drogue associé à son objectif et sa classe. Initiate va s’occuper de faire apparaitre l’échantillon de drogue en lui passant l’identifiant associé du festivalier pour ensuite le mettre sur la table.

Ces deux types d’objective ont une vérification particulière de leurs complétions.

Lors de l’appel du Progress, ItemObjective va appeler le Progress de son parent donc Objective. Objective va ensuite vérifier la progression avec CheckProgress de ses enfants, donc si ses un DrugObjective ses ce CheckProgress qui sera appelé pour par la suite appelé le CheckProgress du son parent donc ItemObjective pour finalement retourner true si les deux peuvent progresser ou false si ce n’est pas le cas. Objective va pouvoir s’occuper de ses évènements de complétion d’après ces résultats et de même pour ItemObjective.

Festivalier

J'ai aussi fait une partie du festivalier.

J'ai fait leur multicast delegate pour leurs états pour encore là l'affichage du UI (OnStartWaiting, OnLeave).

OnLeave est appelé lorsque la requête se termine (puisqu’il écoute sur l’évènement de la requête) et aussi lorsque le festivalier n'est pas répondu, il prend en paramètre un bool indiquant si le festivalier est content ou non. Le OnLeave est appelé dans la fonction Leave qui est elle-même appelée après un certain temps dans la fonction StartLeave et cela sert à enlever le widget de la requête, de faire en sorte qu'on ne peut plus interagir avec le festivalier, d’afficher du UI et d'avertir le côté Blueprint de démarrer son animation de départ.

Festivalier heureux lorsque la requête est réussie


Festivalier fâché lorsque la requête échoue

OnStartWaiting va être utilisé pour démarrer le minuteur d'attente du festivalier avant d'être répondu.

Minuteur d’attente

Un festivalier va aussi avoir sa requête pour être capable d'écouter sur ses évènements, un ID pour reconnaitre quel résultat de drogue est le leur, un get pour le minuteur pour avoir le temps restant normaliser pour l'affichage du UI et finalement il va avoir un bool qui indique si ses la première interaction du joueur au festivalier, si oui on démarre la requête et on relie ses évènements aux nôtres, sinon on progresse celle-ci.

Bien sûr, le festivalier va utiliser le système d’interaction et de l’AI mais ce n’est pas des systèmes sur lesquels j’ai beaucoup travaillé.

Tutoriel

Pour le tutoriel de notre jeu, je me suis plus occupé de la partie implémentation, donc je devais m’assurer que le tutoriel respecte les demandes des designers et je devais aussi expliquer comme travailler avec notre solution à ces mêmes designers. Je m’occupais aussi de faire la communication entre designer et code,  j’analysais les besoins et rapportais les modifications à mes collègues.

Un exemple d’une déclaration d’un nœud du tutoriel, celle-ci est un objectif d’interaction avec le festivalier

Un autre fait intéressant du tutoriel est qu’il utilise mon système de requête et d’objectif puisqu’ils sont assez volatils. Il prend les objectifs comme des étapes du tutoriel.

Sauvegarde

Je me suis aussi occupé de la sauvegarde de préférence du son et du HighScore.

Les deux ont une classe USaveGame pour contenir les informations à sauvegarder.

C’est le GameState qui va s’occuper de sauvegarder le HighScore du niveau à la fin de celui-ci, il va utiliser la classe USaveGame de manière Stateless pour que le UI utilise la fonction static LoadScore pour l’affichage du HighScore dans l'écran de sélection de niveaux. Nos données de sauvegardes prenant peu de place, recréer la classe à chaque fois n’était pas un problème.

Pour la sauvegarde du son, j’ai créé un Subsystem. Le Subsystem, pendent son initialisation, va charger les volumes sauvegardés et les mettre dans les différents USoundClass. Les USoundClass sont un tableau que les artistes peuvent changer à partir du Blueprint du Subsystem. Sauvegarder et charger fonctionne avec le nom de la USoundClass comme clé d’accès. Finalement, grâce à ce Subsystem, les volumes pour la musique et les VFX sont chargés automatiquement au début du jeu et le côté UI peut récupérer les valeurs directement dans le Subsytem, tout ce dont le Subsystem a besoin est d’avoir les valeurs par défaut du son et les USoundClass à sauvegarder. 

Créer un système plus complexe aurait pu être possible, mais le temps manquait et nous pensions faire qu’une seule classe de sauvegarde au début.

Sélection de niveaux

J’ai aussi fait une partie du code de la sélection de niveaux.

J’ai créé une classe qui s’occupe de :

  • Gérer l’index du niveau présentement sélectionné avec un tableau de niveaux, il lance un évènement qui indique vers quel côté le joueur va (gauche ou droite) et l’index du niveau vers lequel il se dirige. Les artistes utilisent cet évènement pour charger leurs asset. Il contient aussi un bool pour désactiver la sélection pendant l’animation.
  • Récupérer l’index du niveau joué précédemment par le joueur pour que lorsque le joueur retourne sur l’écran de sélection à la fin d’un niveau, son personnage soit sur le niveau qu’il a terminé.
  • Charger le niveau sélectionné.

Plancher

J’ai fait un système de sol simple pour générer des sons de pas différent et des mouvements différents pour le joueur.

J’ai créé une classe parente qui contient la friction du sol, un USoundCue (pour pouvoir choisir un son aléatoire de pas dans le Cue) et un TagContainer pour que l’animation du joueur puisse changer d’après le type de sol. Le joueur va simplement détecter le type de sol sur lequel il est avec un raycast et appliquer ses modifications de mouvements et son son de pas associés. Il va aussi recevoir le tag du sol par exemple floor.ice pour avoir l’animation de glisse.

Nous avons décidé de ne pas utiliser des physic material pour les types de sol puisque je connaissais moins cela et que les artistes n’avaient pas le temps pour ça.

Scénarisation de niveaux

J’ai aussi fait des fonctions pour la scénarisation des niveaux du côté Blueprint. Le défi était qu'il fallait que les fonctions soient faciles d'utilisation pour les designers de niveaux, de plus, il fallait que ses fonctions soient accessibles dans tous nos Gamemode. Donc j’ai créé une BlueprintLibray pour contenir nos fonctions d’apparition de festivaliers. Elle a été utile pour les tests, mais finalement une seule fonction a été utilisée dans le jeu.

La fonction en question, prend entre quel et quel moment faire apparaitre le festivalier (Between this and this) et vas générer un moment aléatoire entre les deux. Prends le Spawner, les brochures ou types de drogue demandée et finalement le temps limite de la requête

Défis

Dès le début du projet, nous avons compris que la communication et le lien entre le code et le visuel seraient un défi. Pour relever ce défi, nous avons fait quelques recherches et nous avons déterminé que l’utilisation de Data Asset règlerait nos problèmes. Nous pouvions lier notre code avec le visuel et tout ce que les artistes avaient à faire était de mettre leur icône, FX, valeur dans les Data Asset. Nous les avions utilisés par exemple pour que nos types de drogues soient liés à des icônes.

Étant une petite équipe, un autre défi pour moi était que je devais m’adapter pour faire un peu de tout. J’ai dû toucher à pas mal tous les systèmes du jeu en plus d’avoir fait des playtest.

Leave a comment

Log in with itch.io to leave a comment.