The game
The game
Sons of Hypnos is a game made during the Concours universitaire Ubisoft 2024.
Phobetor, the god of nightmares, is trying to corrupt the dreams of every humans with his creations. His two brothers, Phantasos and Morpheus, joined forces to stop him from taking over the realm of dreams and causing unrest upon the mortals.
This game was made in Unreal Engine 5.3
MyPart
We wanted to make an ability system for our game. We knew it would take a lot of effort and time, but we also knew it was interesting to make it, both for the game and for our personal experience. It would allow us to have a system to add and handle multiple abilities for our characters with ease. This is why we made the Dream Ability System. I will be doing a quick overview of what I did here. For more in-depth information, visit the DAS plugin page.
I worked on multiple smaller parts of the system, but the ones I was mainly working of was the abilities themselves as well as the controller for those abilities.
Abilities
Since we needed abilities to be fully customizable, I made most functions to be BlueprintNativeEvents, meaning they were meant to be implemented in blueprints. Those functions also had a C++ implementation, allowing us to handle the more code-related parts of abilities in them.
Since we also needed them to be fully replicated in an online environment, I made most functions to handle the RPCs, as well as making RPCs specific versions of these functions.
Ability controller
We needed a way to handle multiple abilities at once, as well as something to make use of those abilities. This is why I made the ability controller. It holds instances of the abilities, handles the check if an ability is triggerable and triggers it if it can be. It also handles the cost an ability might have and it's effects on the owning character.
A way to make combos was also required for our system. I decided to add an aspect to the controller to allow this. This new aspect is a "fallback" system. Whenever an ability cannot be triggered, the controller will check if the ability has a fallback ability and try to trigger this one. Not only did it allow the use of combos in a simple way, it also allowed to use different abilities depending on the player state.
Multiple abilities had to be made for the characters to use. The main ones were the attack abilities and the ability to purify nightmares. The attacks were used by both the players and the enemies. The ability to purify was added to conform to a requirement of the contest.
Melee
Since some characters in our game were attacking at close range, a melee ability was needed. This is where I created a melee ability. The ability activates a hitbox on the user and triggers an animation. Whoever was hit by that hitbox would be pushed back and receive damage. The reason why this hitbox was directly within the user was to allow each users to have a different area of attack. It also avoided the instantiation of a hitbox each time this ability was used. Another reason why the hitbox was linked to the user was to allow the user to rotate as the attack was starting so they could adjust where to hit.
However, for an attack to be usable, and to avoid hard-coding variables, a way to modify the damage of an attack was required. I simply created a subclass of the ability class called AttackAbility. The only difference between those two was the addition of an attack modifier to the ability, which could be accessed and modified by the blueprint. This was also used to make the combos deal more damage according to how many hit into the combo we were.
Ranged
Once we had melee abilities figured out, we now wanted other characters to be able to attack at a range. With the new ability subclass, it was now easier than ever to add new attacks variants, so creating a ranged attack ability was quite simple. I made a class for a projectile, which held it's owner and it's own damage so the collision handling could be simplified. With that projectile class ready, I created the ability itself. All it did was using the deferred spawning of Unreal Engine to create a projectile going towards the direction the character was looking at. I used a deferred spawn because it allows us to instantiate an object without spawning it right away, making it easier to modify it's properties as it spawns.
Purify - Hold
One of the requirements of the contest was to have a synchronized interaction mechanic. We designed it as a purification mechanic to turn nightmares into dreams. One player had to hold the nightmare while the other had to purify it. I was asked to create the ability to hold a target.
In order to know who the player could target, an environmental query was used. In the returned result, I checked if the potential enemies were valid (they needed to have less than a certain amount of health and still be alive) and targeted the first one. This target was marked so the player could know who would be held.
The ability itself was simply a change of state on the enemy to paralyze him so he wouldn't try to hit us or move around. Once this state was applied, that enemy would get a tag to notify the other player this enemy was ready to be purified. Once the purification was complete, I turned the enemy into a dream by replacing him with an object. Every enemy purified would both heal the players and notify the gamemode so it could update the score.
Camera
We were using a third person camera resembling a top-down view with an angle. This type of camera was working fine for our game, but the character moving around was making it hard to know what was in front of them before the object was quite close. This is why I made a modification to the camera handling. The modification made the camera always be slightly moved towards the direction the player was facing. This was especially useful for our ranged character to give him a bit more usable range with his projectiles.
Characters rotation
During our tests, we realized that the rotation of our characters had a few problems, mostly linked to the replication. When the client was trying to rotate, we could see the character snap back to a previous rotation sometimes. This was also affecting the player's ability to attack since the attacks were aimed with the character's current rotation on the server, and the latency caused it to not always be the same as the one on the client.
To remedy this, I used a replicated "wanted rotation" variable. This variable was directly set using either the walking orientation when the player was not manually rotating the character or the rotation inputted by the player. The attacks were aimed using this rotation instead of the character's current rotation so it could be more accurate. The character's rotation was set with a lerp from their current rotation to the wanted one, making the rotation smoother.
Credit
Programmers
Alexandre Tremblay : Gameplay (DAS, abilities and character)
Alexandre Laroche : Gameplay (DAS and abilities)
Léa Bouchard : AI
Xavier Lavoie : UI, design and generalist
Mentors
Artists
Maxime Boudreau : Animation, modelling and environment
Simon Jolicoeur : Modelling
Anna-Sophie Lange : Concept artists, UI design, VFX
William Pak : Modelling