Sky Chasers

About the game
Sky Chasers is a Racing Mobile Game where you can challenge players coming from all across the multiverse in this chaotic wingsuit competition.
Compete in many locations from all the different universes while impersonating heroes from a colorful cast each with their unique quirks and skins.
Specs
-
Engine: Unity
-
Programming language: C#
-
Mobile
-
Year: 2023
-
Development time: 2,5 month
-
Team: 12 members (4 designers, 3 concept artist, 2 artist 3D, 3 programmers)
-
Type: Accademy project
-
Role: Lead programmer
![]() | ![]() | ![]() | ![]() |
---|---|---|---|
![]() |
Game Structure
The game is a Racing Mobile game where you impersonate a hero that glides with a wing suit. The game was made with the idea of being free-to-play online multiplayer, so in our case, we faked a lot of game structures like the shop, the matchmaking and the actual gameplay with others. In fact, here, the other players are AI.
The game loop is very simple: play some races and try to win using items that can be found on the map, like in the Mario Kart series. After each competition, you earn some points that are converted into game currency that can be spent to buy new heroes, skins or emotes to play at the end of the race (pure aesthetics except for the unique hero passive abilities). Some actions give more points than others and by completing daily, weekly and monthly missions, you can earn currency.
An important aspect to consider is the constraints: we have to use as little physics as possible in order to can achieve a better result in the case of a possible conversion of the game to real multiplayer. In our case, this was a critical issue since we decided to manage the collision between the players and the map.
What I have done
As a lead programmer I took care of all aspects of the organization of the internal tasks and code, have a close relationship with designers for the features, bugs and decisions to make.
I made the Gamemanager script, implemented as a singleton, with all the processes to start, play and reset a game: from the main menu click event, to the load async of the maps, keeping track of players and tutorial.
A lot of minor scripts:
-
the car's movements: simple transform movement from one point to another with a coroutine to wait after reaching the end to restart
-
the camera management: use the cinemachine camera to add effects of shake or boost when passing throw rings, and switch from the player camera to the AI camera to switch into spectating mode;
-
the animation management for the preview in the menu and the podium: interesting use of layers to put the hero in front or between the UI;
-
the countdown at the start of each match: a simple coroutine to show UI time;
-
the goal script: to end a match, count the order of the player's arrival, switch from player view to spectating view, and call the switch into the podium map;
-
the boost manager: simple script to detect players and apply a velocity boost with calls to the camera and effects
-
the passive abilities for each hero: a simple interface, then implemented by a script for each hero that calls the effect at the start of the gameobject;
-
part of the input system
-
part of the hand-by-hand collision system
-
part of the score system
-
the anti-stuck patch: since all the problems we experienced with collisions, it was necessary to add a script that checks every few seconds if a player is stucked. This was done by having references to safe points in the maps and checking if the position of the player after some time was under a threshold of distance from its previous position. In this case, the player will probably keep colliding with an obstacle or a wall, and it will be teleported to the nearest safe point.
Some relevant features I made are the AI, the mission feature (with corresponding UI), and the saving system.
AI


The second logic is very important, even if it's not perfect balancing for the differences between the maps. It was made by using a sphere cast from the AI in front of it and, based on what it found (a wall lateral, vertical, or obstacles), applying a fake input in the opposite direction to try to avoid the clash.

The third logic was made similarly with an overlap sphere to check the presence of items and boost during the race, if one of them is found, a fake input is applied to reach that position. Even if not accurate, this is enough since the target is equally large as the player or bigger.

Finally, in its confused state, the AI simply stops making all checks, resets its speed to the minimum, and applies random input for the duration.
The AI I created was very particular. At the beginning, I was wondering if I could use the navmesh, but since all the AI are flying/gliding and do not directly touch the ground, I discarded this idea. Then, another possibility that I considered was to use splines and make the AI follow different splines along the path. But even this idea was discarded due to the fact that the Unity version we used has not implemented a native spline feature yet, which means I have to make myself a similar feature with a limited time and all the other tasks I have to do and check for the others. So I came up with a strange idea that fits a little with the game and gives a better idea of real players gliding with you: adapting the movement script for the player and attaching it to the AI, then applying a fake input based on some conditions and a bit of randomness to give the feeling of other players that make the same movements as you.
Another script was used instead to make the AI use an item when picked: this is implemented with a simple check of the item's possession, and in this case, the AI waits a certain time and then uses the item.
The logic of the input movement was divided based on states: normal and confused (caused by items).
In the normal state, the AI makes 3 checks: one for applying a random movement if it is immobile for too long, one to try avoiding obstacles (and walls), and one to try to pick up bonuses like items and boos rings.
The first logic is a simple check with the physics overlap sphere, if you have not found anything for a certain period of time, then apply a small random rotation to give the idea that a player is pressing an input and moving.
Missions

The missions feature consists of daily, weekly and monthly tasks to achieve in order to obtain new coins.
Firstly, I made some scriptable object classes to give designers the chance to create each possible mission as an object of the project. Inside these classes, I put some enums to identify the type and current state of each mission.
I then made 3 scripts: MissionManager, MissionSpawner and MissionDisplay.
MissionDisplay simply implements the UI part of the menu, where all the missions can be monitored. It shows the data for each element of a mission.
MissionSpawner is related to the previous script, since it's responsible for spawning each mission to then be shown in the menu.
The last is the most important script, MissionManager is implemented as a singleton since it has to check for mission updates and the level of completeness in all the codebase. It contains all the list of available missions for each time period and all variables to take care of time. It is responsible for a lot of things: select the missions by picking them up randomly from the total available, update the counter of each mission of each type, give rewards in case of completion, reset and select new missions as needed. All this was supported by some logic to save and load some core aspects, like the time for each category of mission.
Save System
The Save System was implemented as a singleton, since it was to be called from all the codebase.
I made some serializable classes that only contain data to be saved, with simple format variables like int, string or bool, for each part of the game that needs to be saved: player, shop with skins and emotes, missions, settings and tutorial.
The load and save process was made with a BinaryFormatter, in this way, the save file created is in binary, and it will be hard to cheat.
Lastly, there are some methods to clear the savings.