
Shift


Un jeu de platformer / énigme en 3D en vue FPS, où l'on incarne un robot capitaine de vaisseau-cargo spatial à la dérive. Transportant le restant de l'humanité, il est déterminé à le remettre d'aplomb pour la survie de l'espèce humaine, mais quelque chose cloche...
Projet de fin d'études, réalisé avec Loeiz Legrand, Carla Herry, Marie Duarte et Nicolas Cotté.
Genre
Platformer / Énigme
Support
PC / Unreal Engine 4
Date
Septembre 2022 - Juin 2023
10 mois
Rôles
Game Designer,
Level Designer,
Script Blueprint,
Intégration,
VFX Artist,
Game Tester
Contexte
Shift est mon projet de fin d'études, avec sujet libre.
Au lieu de poursuivre le jeu de l'année précédente comme initialement prévu, mon groupe et moi avons décidé d'utiliser l'expérience gagnée avec Synos pour reprendre de zéro le développement d'un jeu et de confirmer nos connaissances en production.
Shift est sorti en Juillet 2023 sur itch.io et a été élu meilleur jeu étudiant de l'année 2023 par un jury de professionnels de l'industrie (Ubisoft, Don't Nod, Amplitude, Kylotonn).
Téléchargement
Vous trouverez ici un lien de téléchargement pour jouer au jeu !
Trailer
Vous trouverez ci-dessous le trailer de Shift !
Game Design
Le projet Shift a connu de nombreux tournants lors de sa réalisation, et cela concerne également le concept du jeu.
Origine du concept
L'intention de base était de créer un jeu que l'équipe entière avait envie de faire et de jouer par la suite, tout en prenant en compte les compétences de chacun.
En nous prenant ainsi comme cible, il fut collectivement décidé que nous ferions un jeu en 3D en vue FPS axé sur une trame narrative impliquant 2 visions de la réalité qui alterneraient, d'où le nom du jeu. Le joueur parcourt un vaisseau spatial à la dérive au contrôle d'un robot. Notre recherche de références similaires nous a mené à Portal, qui nous a ensuite servi d'inspiration (parmi d'autres) pour trouver le genre de notre jeu et sa mécanique principale qui sera aussi son USP: un platformer puzzle solo où l'on peut faire apparaitre différentes formes utilisant le moteur physique d'Unreal Engine afin de progresser dans les niveaux du jeu.

Mockup du concept par Loeiz Legrand
Features
Le développement des différentes mécaniques a réellement commencé une fois le genre du jeu fixé. Chacune d'elle répondait à un besoin pour le jeu afin de ne pas submerger le joueur de superflu. J'ai ensuite itéré de nombreuses fois pour rendre l'utilisation de ces mécaniques pertinente et la plus agréable possible.
-
Déplacement
-
Basique
Tout platformer doit avoir des contrôles de déplacement de base afin de déplacer le personnage contrôlé au sol. Dans le cas de Shift, j'ai décidé de garder des contrôles au sol simples avec un déplacement omnidirectionnel, un bouton pour sauter plus ou moins haut en fonction du temps d'appui et un bouton à maintenir pour courir. Cela avait pour but d'occuper le moins de boutons possibles et de créer de la familiarité pour les joueurs de platformer, qui allaient être confrontés à la mécanique principale et inhabituelle du jeu.
Basiques mais centrales pour le gamefeel, la vitesse de déplacement, la hauteur du saut et la vitesse de course ont été minutieusement paramétrées, testées sur un niveau de calibrage sur-mesure et validées par l'équipe entière afin de permettre la construction des niveaux définitifs en fonction de celles-ci.

Screenshot de la partie déplacement de la showroom gameplay
-
Jump pad
Afin de ne pas causer trop de répétitivité et de varier les challenges proposés, j'ai aussi conçu et intégré des moyens de déplacement spéciaux utilisant des supports spécifiques. Cela a ensuite permis d'imaginer et construire des parties de niveau totalement axées sur le déplacement qui alternent avec les parties d'énigmes utilisant la capacité spéciale du jeu, dans le but d'obtenir un rythme qui maintient la découverte du joueur le plus longtemps possible.
Le Jump Pad est une plateforme ronde reconnaissable à sa couleur verte et permet de réaliser un saut vertical dont la force est réglable par le Level Designer afin de l'adapter pour toute occasion.

-
Murs magnétiques

En recherchant des moyens de déplacement originaux cohérents avec l'environnement d'un vaisseau spatial et réalisables par un robot, il m'est venu l'idée de créer des murs magnétiques pour se déplacer latéralement sur les murs plutôt que sur le sol. Ceux-ci utilisent la compétence d'attrapage du personnage pour que celui-ci reste accroché, ainsi le joueur doit garder ce bouton enfoncé pour ne pas tomber. Il a également la possibilité d'inverser la polarité de ses mains servant de points d'accroche pour effectuer un saut en longueur dans la direction où il regarde, simplement en appuyant sur le bouton de saut. Les murs magnétiques ont aussi été pensés pour être placés rapidement dans les niveaux à l'aide d'un outil de placement sur X et Y.
-
zéro gravité
Toujours dans l'optique de proposer une expérience de gameplay variée, cohérente avec notre univers et compatible avec notre capacité spéciale, nous avons collectivement décidé de réaliser un passage en zéro gravité inspiré de Dead Space. Comme son nom le suggère, dans cet environnement les contrôles de déplacement sont complètement différents de ceux du déplacement au sol pour simuler au mieux ce changement radical, tout en utilisant les mêmes boutons. En zéro gravité, le joueur se propulse dans la direction de son regard en maintenant le bouton de saut et se stabilise avec le bouton de course. Il est aussi possible de se déplacer latéralement par rapport à la direction du regard avec les contrôles de déplacement omnidirectionnels.

Screenshot de la partie zéro gravité de la showroom gameplay
-
Capacité spéciale
Le cœur de notre jeu se trouve dans la capacité spéciale du personnage de pouvoir faire apparaitre plusieurs "formes" soumises au moteur physique afin de permettre sa progression dans les entrailles du vaisseau. De cette façon, le joueur pourrait se satisfaire à trouver des solutions qui lui sont propres aux énigmes proposées en combinant les formes.
Cette capacité s'active quand deux boutons dédiés sont maintenus en même temps, simulant la concentration nécessaire du personnage afin de faire apparaitre ces formes. Par soucis de lisibilité, la caméra passe en vue 3ème personne par dessus l'épaule du personnage, permettant de placer les formes avec une vision plus globale.

Obstacle infranchissable

Placement d'une planche

Obstacle franchissable
Lorsque plusieurs formes sont débloquées, elles cyclent automatiquement lors de la concentration afin de transmettre une sensation de contrôle limité, centrale à l'histoire. Il est quand même possible de mettre ce cycle en pause pour faciliter la pose des formes, notamment pour les faire pivoter.
En prévision que le joueur se bloque tout seul, j'ai également intégré un bouton de suppression de forme permettant de faire disparaitre une forme posée soi-même plus tôt à distance, simplement en la regardant. Si le joueur ne regarde aucune forme, la dernière posée sera supprimée automatiquement par ergonomie.
Par anticipation d'éventuels abus, j'ai limité l'usage de cette capacité à des zones restreintes, le nombre de formes présentes en simultané par zone et le temps de concentration, simulant une fatigue du personnage à force de se concentrer. Si le personnage dépasse cette dernière limite, la vision du joueur passera en mode "chaos", où le vaisseau apparaitra plus délabré et le placement de forme deviendra chaotique. Il devra attendre un peu avant de pouvoir réutiliser la concentration.
-
La Planche
Les quatre formes disponibles dans le jeu ont été pensées pour avoir une utilité immédiate d'elles-mêmes, mais aussi pour réaliser des combinaisons avec les autres afin de laisser le champ libre au joueur de trouver ses propres solutions. La planche est une forme en longueur qui permet de combler des trous infranchissables sans courir, bloquer les lasers et de prendre de la hauteur quand placé sur le bord d'un cube. Dans le monde du jeu, il s'agit en réalité d'une unité de stockage de données.

-
La sphère

La sphère est une forme dont l'utilité première est de pouvoir se déplacer indépendamment du joueur en roulant sur des pentes afin d'activer des mécanismes du vaisseau. Elle est assez petite pour passer dans le cylindre afin de lui faire traverser des trous. Elle a été également conçue pour être un noyau d'énergie dans le monde du jeu et peut activer des systèmes en se logeant dans des inserts prévus à cet effet.
-
Le cube
Le cube sert essentiellement de levier à la planche pour accéder à des endroits trop élevés pour être atteints par un saut ou la planche seule. Il s'agit d'une caisse de cargaison du vaisseau dans le monde du jeu, et peut également activer des plaques de pression au sein de celui-ci.

-
Le cylindre

Le cylindre est la forme la plus complexe de notre jeu. Il a été pensé pour agir comme une roue de hamster, où le joueur peut entrer et avancer en le faisant rouler pour passer des gouffres ou des lasers en étant à l'abri. La sphère peut également y rentrer dans le même but. Le cylindre est en fait une cuve de cryogénisation dont le couvercle et la base ont été retirés.
-
Manipulation d'objets simulés physiquement
De nombreux tests avec la capacité spéciale ont révélé un besoin de contrôle sur les formes que le joueur fait apparaitre, afin de les replacer sans devoir les supprimer puis les faire réapparaitre. J'ai ainsi eu l'idée de proposer au joueur de pouvoir attraper et déplacer les objets soumis à la physique du jeu, à la manière du Gravity Gun de Half-Life. Cette mécanique utilise le même bouton que celui d'accroche aux murs magnétiques pour simuler un pouvoir magnétique sur les objets manipulés. Cela a ouvert de nouvelles possibilités en particulier pour les zones en zéro gravité où de nombreux objets soumis à la physique étaient devenus manipulables. Une mécanique de jet a été ajoutée en supplément pour permettre de jeter ou repousser ces mêmes objets et simplifier la navigation en zéro gravité.

Screenshot de la manipulation physique d'une sphère dans une salle en zéro gravité
-
Obstacles
De multiples obstacles ont été mis en place dans le jeu pour challenger les compétences du joueur lors des phases de déplacement et forcer la réflexion sur la manière d'avancer sur les phases d'énigme.
-
Plateformes instables
Les plateformes instables réagissent à la présence du joueur ou d'un objet sur leur surface et se rabattent au bout de quelques secondes. Il s'agit d'un mécanisme créé pour forcer le joueur à jouer sur son élan et l'inciter à ne pas prendre de pause lors de son trajet. Ces plateformes ont principalement servi pour les phases de parkour du jeu, et ont permis d'appuyer sur la sensation d'urgence de la séquence de fin du jeu.

-
Arcs électriques

Les arcs électriques sont des obstacles infligeant des dégâts au personnage sur la durée s'il les touche. Ils ont été conçus principalement pour tester l'aptitude du joueur à se déplacer en zéro gravité sans trop dériver, c'est pourquoi on en retrouve majoritairement dans les zones en zéro gravité. Outre cette utilisation, ils servent également à rendre des surfaces de sol inutilisables par le joueur afin de le forcer à utiliser la capacité spéciale pour les franchir.
-
Lasers
Les lasers sont les obstacles les plus dangereux du jeu car ils détruisent le personnage immédiatement au contact, mais ils ne sont pas pour autant infranchissables: il est possible de les bloquer à l'aide de n'importe quel objet pour les traverser en sécurité. C'est pourquoi nous les avons placé à la fois sur des parcours d'obstacles où le joueur ne peut pas les bloquer pour le forcer à respecter le trajet prévu, mais aussi comme élément de salle d'énigme où les franchir devient possible grâce aux formes que le joueur peut faire apparaitre.

Level Design
Le Level Design de Shift a été guidé par l'univers du jeu et sa narration, il s'agit ainsi d'un travail collaboratif avec Carla Herry, Narrative Designer sur le projet.
Concept du Vaisseau
Le concept initial du vaisseau par nos Environment Artists à partir du narratif du jeu a fortement influencé le Level Design de celui-ci.
La narration du jeu avait pour thème l'avortement, et nous voulions que le vaisseau paraisse être une entité propre et vivant quand le joueur parcourt ses entrailles, malgré son état général délabré. Nos Environment Artists ont ainsi créé des assets utilisant des codes organiques inspirés par des créatures sous-marines pour créer l'intérieur du vaisseau, cassant avec le style brutaliste souvent vu dans la science-fiction. Nos Sound Designers quand à eux ont placé des sons d'ambiance pouvant rappeler des hurlements de bête.


Images de référence pour le vaisseau


Screenshots de l'intérieur du vaisseau, avec des couloirs aux formes de côtes et câbles mouvants
La volonté était aussi de donner la sensation de labyrinthe au joueur à l'intérieur du vaisseau, avec de nombreux détours dû aux dégâts subis par celui-ci, afin qu'il puisse se sentir désorienté même sur un parcours linéaire. C'est pourquoi nous avons décidé de ne pas réaliser d'aspect extérieur du vaisseau, et de laisser libre court à notre imagination pour le chemin critique du jeu.
Le Level Design a donc été réalisé comme une structure interne organique pour le vaisseau, non contraint par un logique de construction de vaisseau spatial.
Structure du jeu
Avant de commencer à toucher au moteur de jeu, Carla et moi avions prévu de construire le jeu de façon à éviter la répétition et conserver la sensation de surprise du joueur le plus longtemps possible pour l'encourager à aller jusqu'au bout du jeu.
L'un des piliers narratifs du jeu est l'impuissance, et nous voulions que le joueur se sépare graduellement de l'illusion que le vaisseau puisse être sauvé en progressant. Le début du jeu laissait entrevoir un espoir et la fin devait imposer l'acceptation que le vaisseau et ses habitants étaient déjà perdus. De ce fait, nous avons décidé de rejoindre le début et la fin au même endroit pour un contraste maximal au cockpit du vaisseau, créant une boucle.

Flowchart du jeu
​Outre la salle de cryogénisation très narrative où nous voulions une séquence de fin de jeu best-of intense, le nombre de niveaux représentés ici par des salles a été guidé par notre volonté de créer des challenges variés utilisant toutes nos mécaniques de jeu, et par celle d'avoir des salles de vaisseau spatial crédible. Nous avons d'abord réalisé ces niveaux séparément, avant de nous pencher sur l'ordre à adopter pour rendre le jeu cohérent.​​​
Dans un premier temps, nous avons cherché un moyen d'empêcher la monotonie du gameplay du jeu. Nous avons alors décidé de ne pas donner tous les outils au joueur immédiatement, mais plutôt de répartir ces mécaniques sur toute la longueur du jeu afin qu'il puisse expérimenter avec quelque chose de nouveau régulièrement. L'ordre de déblocage a permis de réaliser un premier jet de l'ordre des niveaux, dépendants de l'obtention de ces capacités. Les tests ont par la suite montré que cette approche a fonctionné, car aucun testeur ne s'est jamais plaint d'ennui en jouant.
Ensuite, nous nous sommes penchés sur la répétitivité des contenus des niveaux. Certains étaient orientés sur les capacités de déplacement, tandis que d'autres étaient orientés sur l'utilisation de la capacité spéciale pour résoudre des énigmes. Cela nous a donné l'idée de faire un rythme alterné de paires de niveaux selon ce critère, même si cela a requis d'adapter certains d'entre eux et de réordonner les niveaux, voire même d'en supprimer dû à un manque de temps pour les adapter correctement, comme une section entière dédiée au cylindre.
Pour finir, une fois l'ordre des niveaux établi, nous avons fait en sorte que ceux-ci aient l'air graduellement plus délabrés même sans le filtre du mode chaos afin de convier la sensation de perdition du vaisseau, avec le climax narratif placé comme initialement prévu dans la salle de cryogénisation où la séquence de fuite du vaisseau a lieu.
Scripting
J'ai été responsable de tout le scripting de Shift, excepté la partie son réalisée par Rémi Darmedru. Cette expérience m'a permis de beaucoup développer mes capacités existantes en Blueprint, mais aussi d'en apprendre de nouvelles. Il m'est impossible de montrer l'intégralité des Blueprints créés ni leurs connections avec d'autres Blueprints, mais vous retrouverez ci-dessous une sélection de mes meilleurs travaux isolés.
Personnage
J'ai utilisé comme base la Blueprint de personnage à la première personne d'Unreal Engine afin de récupérer son Character Movement Component. Je l'ai ensuite modifié à ma guise, en centralisant tous les scripts concernant les contrôles du jeu et la caméra, ce qui en a fait la Blueprint le plus importante du projet.

Aperçu de l'event graph du personnage
Pour conserver une lisibilité correcte, j'ai compartimenté chaque mécanique dans un cadre de commentaire nommé d'après la mécanique concernée, et fais communiquer toutes les mécaniques internes au personnage entre elles à l'aide de Custom Events afin de ne pas croiser les fluxs d'éxécution.
Pour les mécaniques communiquant avec des Blueprints externes au personnage comme les éléments d'interaction​​ ou l'interface du jeu, j'ai utilisé plusieurs Blueprint Interfaces elles-mêmes nommées selon leur fonction.
Pour les communications ponctuelles de variables d'un Blueprint à un autre, ne demandant pas forcément une BPI, j'ai aussi utilisé plusieurs nodes Cast To intégrées dans des fonctions venant d'une Function Library afin qu'elles soient accessibles depuis n'importe quelle Blueprint.
Puisqu'il s'agit du centre névralgique du jeu, la Blueprint du personnage comporte 31 fonctions, 4 macros et 96 variables.
Voici maintenant en détail la mécanique d'accélération du personnage en zéro gravité :

Script de l'accélération en zéro gravité
Le principe général de cette action est d'appliquer en continu une force allant dans le sens du regard sur le corps du personnage ne subissant pas de gravité.
J'ai donc commencé par utiliser une node InputAxis lié à une gachette de manette ou une touche de clavier afin de pouvoir lancer ce script à chaque tick et permettre un contrôle de l'accélération par pression continue.
Vient ensuite une vérification de variables avec une node Branch afin de s'assurer que ce script s'active uniquement aux bons moments et soit inactif le reste du temps. Dans le cas présent, je demande au jeu de vérifier que le personnage est bien en mode zéro gravité avec la variable "0g?" et qu'il n'est pas en train de se stabiliser en même temps avec la variable "Braking 0G?". Si ces deux conditions sont vraies, alors le script actionnera la node Add Force.
La node Add Force, quand activée, utilise le vecteur avant de la caméra première personne du personnage pour créer une direction par rapport à la position du personnage dans le monde, qui est ensuite convertie en force que la node peut appliquer sur le personnage, ici représenté par le "Capsule Component". Cela permet de propulser le personnage dans la direction de son regard, en conservant également l'inertie de l'environnement en zéro gravité.
Enfin, pour éviter que l'accumulation de force de déplacement ne devienne trop importante pour être contrôlable, j'ai créé une macro pour réguler la vélocité du personnage, toujours activée après l'application de force supplémentaire:

Macro de régulation de vélocité en zéro gravité
Cette macro commence par vérifier à l'aide d'une node Branch que la vélocité ne dépasse pas des valeurs minimales et maximales définies par deux variables "Min 0g Velocity" et "Max 0g Velocity" sur tous les axes.
A n'importe quel moment où ces valeurs sont dépassées sur l'un des 3 axes, une node "Set All Physics Linear Velocity" vient réajuster le vecteur de vélocité du personnage avec les valeurs définies par les variables de limite afin que le joueur puisse continuer à avancer avec une vitesse maximale peu importe sa direction.
MenuS
Les menus de Shift utilisent des Widget Blueprint dont j'ai complètement conçu le fonctionnement afin de contourner certaines restrictions d'Unreal Engine.


Event Graph du menu principal
Le fonctionnement des menus est le même pour tous: plusieurs boutons sont disposés en liste et le bouton sélectionné correspond à une variable Integer qui change avec l'action haut ou bas du joueur. Ensuite, cette même variable est utilisée pour lancer le script du bouton correspondant quand le joueur confirme son choix comme montré ci-dessus. J'ai préféré ce système fait main à celui du Keyboard Focus d'Unreal car ayant le contrôle sur une variable Integer, j'ai pu faire en sorte que dès qu'un extrême de la liste est atteint, le joueur puisse accéder à l'autre extrême immédiatement en continuant dans la même direction.
Le menu le plus complexe du jeu est celui des Options, qui contient de très nombreux éléments et sous-menus:


Event Graph du menu des options
Tous les éléments du menu des options ont été triés par catégorie et rangé dans des Widget Panels au sein du Widget Blueprint de ce menu afin de simplifier sa réalisation. Il existe donc 3 sous-menus: Jeu, Contrôles et Son contenant chacun plusieurs paramètres ajustables par le joueur selon sa volonté. Afin que ces paramètres soient conservés tout le long de la partie, chaque changement de paramètre est enregistré dans une Game Instance et ainsi restitué quand le menu est ouvert à nouveau.
Level Streaming
Shift utilise le Level Streaming afin de réguler la charge sur le processeur du support du jeu et éviter les lags.

Rangement des niveaux dans le Content Browser
J'ai découpé l'intégralité du jeu en niveaux numérotés de 00 à 11, tous composés de 6 sous-niveaux contenant respectivement l'architecture, les Blueprints, les effets spéciaux, les lumières, les éléments de décor et les sons. Des niveaux de transitions entre chacun d'entre eux ont également été créés afin d'avoir un maximum de contrôle sur le chargement du jeu. Cette architecture de niveaux nous a permis de travailler efficacement: en utilisant le Source Control, chaque membre de l'équipe pouvait travailler sur un aspect différent d'un même niveau simultanément sans risquer d'écraser le travail d'un autre membre.
Voici maintenant le Blueprint que j'ai créé pour gérer le chargement de ces niveaux:

Event Graph du Level Streaming Master
Afin d'éviter des pertes de performances durant le jeu, j'ai décidé de charger l'intégralité de celui-ci avant de commencer à jouer.
Le Blueprint Level Streaming Master se lance automatiquement dès que le joueur accède au niveau permanent du jeu. Il commence par afficher l'écran de chargement du jeu afin de cacher le véritable chargement du monde, puis récupère et stocke tous les niveaux à charger dans une variable en Array.
Ensuite, une boucle s'activant toutes les 0,01 secondes utilise cet Array et une variable Integer "Buffer Load Level" pour vérifier qu'un niveau est valide et si oui tente de le charger puis passe au suivant jusqu'à ce que tous les niveaux soient chargés. Quand un niveau est chargé, je fais également avancer la barre de chargement grâce à la variable buffer pour que celle-ci retranscrive précisément le chargement du jeu.
Une fois tous les niveaux chargés, j'affiche ceux nécessaires lors de la séquence de début de jeu et je la lance quand un personnage est présent. Pour simplifier le débug, j'ai aussi fais en sorte que le jeu se lance en God Mode si le personnage est absent, ce qui n'arrive pas une fois le jeu exporté mais permet aux développeurs de facilement tester le jeu.
Outils de Level Building
J'ai aussi eu l'occasion de réaliser des outils de Level Building afin de simplifier la construction des niveaux du jeu.
Mon objectif avec ces outils était de simplifier au maximum le travail des Level Builders pour créer les niveaux du jeu, tout en restant simple d'utilisation. J'ai donc créé plusieurs outils pour de multiples usages de Level Building.
Voici comme exemple un générateur de murs:


Construction Script du générateur de murs
Cet outil est une Blueprint utilisant deux variables Integer en X et Y exposées pour afficher ce que serait un mur de ces dimensions en utilisant un exemple de chaque mesh de mur disponible. Dans Shift, nos murs comportent des plinthes et peuvent être de 2 tailles différentes, nommées ici "2x2" et "2x4". Tous ces éléments sont automatiquement disposés ensemble dans le Construction Script de la Blueprint pour former un mur déplaçable dans l'éditeur. Les Level Builders peuvent ainsi créer des salles rapidement sans devoir placer eux-mêmes tous les meshs.

Détail de l'Event Graph du générateur de murs
Quand le mur parait satisfaisant, l'outil permet ensuite de le concrétiser dans le monde du jeu avec un Event en Call In Editor. En activant cet Event depuis l'éditeur, une boucle se lance pour chaque type de mesh en prévisualisation, qui est remplacé par un mesh similaire placé dans le monde du jeu exactement au même endroit, construisant ainsi le mur entier en un clic.
Pour ajouter un peu de variance aux murs et ainsi éviter la monotonie, nous avions préparé plusieurs meshs différents pour chaque type de mesh de mur. Grâce à cela, j'ai pu réaliser un choix aléatoire automatique lors du placement des meshs dans le monde du jeu, créant l'effet voulu de non-répétition lors de la génération du mur à l'aide de cet outil.