top of page

After School

Solo and cooperative game - Unreal Engine 5.1 C++

After School was produced as part of the Ubisoft university contest 2023. It was made in 10 weeks with the Arcade theme. The game also needed to have an element of gravity, of construction and of destruction. In after school, I took care of gravity by making a rocket that affects the gravity of characters in the area around where it was built.

Trailer

Gameplay video

image équipe ubi.jpg

Sommaire

The rocket

Rocket initialization and general behavior

When the player has enough cardboard boxes, he can then make a rocket appear. This rocket creates a gravity zone in the form of a sphere that changes gravity for both the player and the enemies. So I had to vary the gravity when a character is inside the sphere and put it back to normal gravity when it comes out or when the rocket is destroyed. 

The rocket

Floating enemies 

To make the enemies float I change their gravity and give them a reduced speed to make them float in the air quietly to give the floating effect as if they were in space. I also made sure they didn't go out of the way by putting a maximum height limit. 

Challenges encountered

The main challenge with this mechanic was figuring out how to make enemies float without making them fly too high or too fast. I also had to make sure to make the enemies float inside the sphere when it is created and to make them fall back to the ground when the enemies are still inside the area and the rocket is destroyed. To fix this, I had to use the "get overlapping actors" method and manage these behaviors with detected enemies.

rocket high res screenshot for itch.png

The rocket

Enemy AI navigation while floating

In the gravity zone, enemies must also follow the player's movement while they are floating. Otherwise the enemies would just float straight ahead of them until they get out of the area and that's not much fun. 

Challenges encountered

This mechanics was probably one of the most complex aspects of the rocket. Indeed, the problem was that as these are in the air, they are not on the nav mesh. So any classic path finding behavior of AI is impossible. That's why I had to find a way to move all the AI floating in the area optimally and without using the nav mesh. To solve this problem, I decided to create a blackboard key which determines when an enemy enters the gravity zone and thus activates an AI task in the enemy behavior tree to then make them move in the air. After several attempts with add force and add impulse, I finally got the desired result by adding "AddMovementInput" inputs to each enemy in the area. And the input in question is a direction vector normalized in the direction of the player. This way, I manage to move the AIs in the gravity zone by adding several inputs to them towards the player's position without using the nav mesh!

The rocket

The player's floating movement

As you can see in the video above I also made sure that the player can control his movements in the gravity zone well while giving the impression of floating. This is because the player can go up in the gravity zone by holding the A button and go down by holding the B button. I also made sure that even if the player is holding the B button and they are already level with the ground, the character does not set foot on the ground and continues to float in the air as long as the rocket is active. Thus, it does not break the immersion of gravity. I also gave a slight resulting velocity when the player stops moving up or down to give a floating feel and demonstrate the lack of friction and maintaining the velocity in the same direction even if the player is currently stationary. However, I still kept some friction from the lateral movements when the player stops moving to keep the movement pleasant and easily controllable. This therefore avoids any frustration from the player and makes the gravity zone more pleasant. 

Challenges encountered

The main challenge with this mechanic was to make player movement fun, controllable, and relatively realistic. It therefore required a lot of fine-tuning, but since I was already familiar with the enhanced input of unreal engine, it still went well and I'm very satisfied with the result.  

The rocket

Managing the overlapping of multiple rockets

Unexpectedly, the mechanic of the rocket that caused me the most problems is the management of the overlapping of several rockets. Indeed, in the game, it is possible to create as many rockets as desired in the same place, as long as the player has enough cardboard. So I had to manage the overlap of several rockets without it breaking the gravitational movement of the characters. 

Challenges encountered

Indeed, I activate gravity when a character enters an area or when the area is created and the character is inside it. Then I deactivate it (return gravity to default) when a character leaves the area or the rocket is destroyed (after the 25 second timer I assigned to it). However, if the characters are in several rockets one above the other,   the characters must not fall to the ground when they go out of the first rocket or continue to float when they go be out of all gravity zones. To solve this problem, I had to be ingenious and I found that the best way was to create an overlap counter to detect the current overlap count for each character and when the character is exit exited the area. 

 

Each character (enemy or player) therefore has its own gravity zone counter. So when a character enters a new  gravity zone and was already floating, I add 1 to the counter. And each time the character leaves a gravity zone, I remove 1 from the counter. If this counter goes back to 0, it means that the character is out of all areas, so I can put him back to normal gravity. I also added 1 to the counter when the character is initially in the rocket when it is built, otherwise the overlap would not be called correctly. Similarly, removes 1 from the counter if a rocket is destroyed while the character is already floating in a gravity zone at the same location. Otherwise, the character would not be up to date with the counter or the character would fall back to the ground. In the video below I demonstrate the final result obtained thanks to this methodology.  

The rocket

The rocket bubble shader and last mechanic that was canceled for game design reasons

I also created the shader of the rocket (the multicolored bubble that delimits the gravity zone) and made it a material instance. I also made an additional mechanic with the rocket, but it was not retained in the final version of the game for a game design reason. This mechanic is the addition of a jump increase, a bit like an astronaut jumping on the moon. Indeed, as long as there is an active rocket in the game, the player can jump higher and further, as if he was on the moon. He also resumes his normal jump when there is no more active rocket in the game. In the video below you can see once again my shader and this jump mechanic, which I called: moon jump.

Challenges encountered

The first challenge encountered was to route how to make the bubble shader visible both inside and outside. Since it's a bubble and it's a translucent type shader to see through, making it "two-sided" didn't give an interesting result. So I solved this problem by giving a small opacity to the shader, so that the "two-sided" property of the shader can make the inside of the bubble more visible. For the moon jump, the main challenge was to detect if there was an active rocket in the map. But I easily solved this challenge by using a counter on the player much like the counter I  used for overlapping multiple rockets. 

DayNightActor between the waves

Displacement of the sun by programming in a configurable way between the enemie waves

Between the waves of enemies, I made an actor that moves the sun to the desired location in a progressive way. It is also possible to decide how long the sun takes to move from position A to position B. The longer the time, the slower the movement of the sun will be. This travel time is particularly interesting, because it allows you to clearly see the movement of the sun and the shadows of the various objects in the map at the same time. I achieved this by creating an enum array that represents waves 1 through 5. Each element of the enum is associated with a certain angle from the sun, these angles are then looped in a linear fashion between each wave of enemies. Thus, from wave 0 to wave 4, the sun continues to descend towards the horizon to show the player that time is progressing and to vary the light in the map. It also warns that the nightmare wave is approaching. Once the nightmare wave is launched at wave 5, the sun returns to its original position and the cycle continues for an infinite number of enemy waves.  

Challenges encountered

The main challenge of this mechanic was to make everything as optimal as possible, without costing too much in terms of performance. Indeed, the change of position of sun and shadow of many objects at the same time can be very demanding in computation time. But by removing some shadows and setting a travel time for the sun that wasn't too high, I was able to get a more satisfactory result. I was also careful not to put a gradual move to the nightmare wave, because due to the thick red fog the player doesn't see the sun moving or even the shadows on the ground anyway.

Movement behavior of the collectables

Moving collectables on spawn and when detecting the player

When an enemy dies, it spawns various collectables that move in a random radius around its initial spawn position within a certain radius. So I took care of this movement as well as  the detection of the environment to allow the collectables to always spawn and move to a place visible and accessible to the player. I also made sure that the collectables are attracted like a magnet to the player when he is nearby. This makes collecting items easier and more satisfying. For the sake of optimization, I use "timelines" for the various movements of the collectables, but the detection and verification of the final destinations of the collectables are all found by programming. 

Challenges encountered

The main challenges of this mechanic revolved around managing and detecting the random final position of the various collectables that were spawned. Indeed, I had to make sure that the collectables always spawn on the ground, regardless of the level of the ground (either at a normal height or lower in the skatepark). Indeed, enemies who die while floating high in a rocket must thus spawn their collectables at ground level, so that the player can reach them. I also had to prevent collectables from spawning in the fountain, outside the map or in an object on the map. To solve these problems, I used several "sphere traces" and "line traces" at the place of the random final destination of the collectables which is precalculated, to make sure that it is valid. Thus, I detect if the end destination is in a fountain, if so I change the end destination to a random position around the fountain in question. For ground detection, I lower a small sphere from the initial potion down to contact with the ground and I retrieve the position of  the impact to then determine the position in Z of the end destination. To prevent the collectable from spawning outside the map (playable area) I check if the end destination is on the nav mesh and I reduce the distance from the random end destination accordingly. And I proceed in the same way to avoid spawning the collectables inside a static mesh in the map. You can see the behavior of my collectables in the video below  

Enemies loot drop system

Spawn loot when an enemie dies

I also took care of spawning the collectables on the death of the enemy according to a percentage chance of dropping the collectable in question for each enemy. With the movement behaviors described in the previous section.

Pinata loot drop system

Spawn loot when the pinata is destroyed

I also made the system of spawning collectables when destroying the pinata (the pinata was made by one of my team members). However, for the pinata collectables spawn, there is no random value and you can decide the precise number of items you want to spawn each time. The random movement radius when spawning pinata collectables is also larger and does not require any additional detection, as the spawn and initial position is always in the same place, unlike the enemy loot drop system.

Spawner

Spawn zone of any actor at a random position within the limits of the zone according to a configurable time

I also made a spawner at the very beginning of the project which allows you to spawn any actor. This spawner was later used by one of my colleagues to spawn spider eggs at random positions in the spawner area with a desired time interval.

Challenges encountered

The main challenge of the spawner was to find how to make sure to correctly calculate the dimensions of the spawn area and to find the random position inside it. As well as allowing to be able to choose among all the actors which one wishes to spawn in the area. It was my first spawner with unreal in c++, so it was very informative. 

Visual indicator during a co-op game (blueprint)

Visual indicator that shows the position of the other player in coop

The visual indicator that shows the position of the second player is by far the most complex UI I've made so far. Indeed, it is the arrow that informs the player of the position in the world of the second player in splitscreen. For that I had to make a detection when the second player is onscreen or offscreen. I also have to define the position of the arrow so that it is always above the head of the second player and that the position that point the arrow does not hide important elements of the rest of the HUD. However, this is not the part that caused me the most problems. Indeed, the element that caused me the most problem was not submitted in the final build of the game for a game design reason.

Version in the final build
Challenges encountered

​The challenge with this mechanic is that with different screen sizes, the indicator does not correctly track the screen borders. To fix this, I had to play with the percentage of the screen size on which the player is playing to make sure that the rotation behavior of the arrow is always well represented. I also had to experiment with the offsets to make sure that the arrow did not protrude from the edge of the screen and kept a smooth movement in the corresponding splitscreen section.

Project Design Challenges and Conclusion

Finally, After School has been a project full of really interesting challenges that I never thought I would encounter one day. But each pitfall allowed me to improve and discover new things to solve my problems (learning to use line traces, delegates, manipulating random values and positions, manipulating gravity, spawning actors, etc). Each of these mechanics allowed me to show my ingenuity and to gain experience as a gameplay programmer. Even if two of these mechanics didn't make it to the final build, I'm still very happy with the work I did and to be able to share them in my portfolio. Even though it wasn't always easy and it took me many hours of hard work I am proud of the game that me and my team created and the incredible experience that was the Ubisoft 2023 Contest.

bottom of page