Quantum’s Pursuit is a 2D action platformer that uses flex controls to activate different powers that the player unlocks throughout the game, with each power interfacing with and building upon the flex control scheme in different ways. When I first began work on this project, I helped with smaller coding tasks, such as implementing animations from our artists and fixing small bugs, but over time I grew into a more involved position that has seen me help rebuild our player controller, create a variety of new level mechanics, improve our touch controls, UI, and flex calibration systems, and more. I have been on the project for over two years at this point and will help see it through our initial full release in the coming weeks, having acted in a producer-style role on the project for the past two semesters.
One of the first major tasks I was assigned on this project after I had become acclimated with Limbitless was helping to rebuilding and optimize our player controller script. The shell of Quantum’s Pursuit was built off of a prior project from the Limbitless games team, Limbitless Adventure, which was a similar 2D platformer with a much smaller scope and younger target audience. This title was developed on a much smaller window with a much tighter scope and team, and thus some of the old codebase was not designed to work efficiently and effectively with a larger project. To address this, myself and another coworker completely overhauled our old player controller, which was thousands of lines of code long and handled anything from health to power activation to input handling. We moved the old code into a finite state machine for the player to help improve workflow, increase modularity and organization, and allow much easier development down the road for new mechanics or gameplay ideas.
[clip showing player jumping between different states and state updating]
The player object holds the PlayerStateController script, which contains local instances of each state inheriting from PlayerBaseState. The state controller calls the update, fixed update, and collision methods from the current state and handles switching between states when necessary. Each state the player contains inherits from the PlayerBaseState class, which is an abstract class containing abstract methods for the the state is entered and exited, as well as local update, fixed update, and on-collision functions, if necessary.
This clean organization of the state machine pattern allows player code to be extremely organized and readable, greatly increasing efficiency and workflow down the road of the project. Bugs can easily be traced back to the specific state they occur in, behavior in different states is much more easily read, added to, and understood due to its separation from other states, and new states can be created very easily when needed, even inheriting from premade states when necessary for even quicker creation and iteration. For instance, I recently created a PlayerLevelEnterState which the player only enters when a level is first loaded, and is responsible for playing the proper animations and visual effects when the player drops in to a new scene. This scene inherits from a previously-made PlayerFreezeState, which already handled logic for blocking player input, allowing me to implement this state entirely in a much shorter time than if the state architecture was not present.
Once we had the general groundwork laid for player movement and abilities and enough assets to work with, one of my coworkers and I were tasked with building out levels for the game based on some concept art of what the different areas would look like. A few weeks into this process, we started running into an issue - there just wasn’t that much for players to do! We needed more things for players to interact with, and more importantly, we needed reasons for players to use their powers by flexing with our EMG flex controller. I quickly began drafting and building a plethora of level mechanics for players to engage with, with many of them centering around the core mechanic of using the character’s flex-activated abilities.
One of my favorite interactables I created are these generators that begin appearing after the player acquires the game’s final power, electricity. This power works on a flex-threshold system that matches the real system that the EMG sensors on our company’s bionic arms use. Flex between different percentages of your max flex potential, and trigger different variations of electric shocks, which on the real prosthetic would enable different gestures (for instance, a flex between 0% and 30% of your maximum might trigger a small zap, while 70%-100% fires a screen-clearing ring of lightning). The ultimate goal of the game’s training is to help our bionic kids with limb difference be able to easily and accurately hit these different thresholds on command. In order to help train this in-game, I designed generators that require different voltages, or thresholds of flexion, to be used in order to be activated and turn on other parts of the level.
I designed this system to be incredibly easy for our designers to adjust and use in different ways. The amount of voltage required can easily be adjusted in Unity’s inspector and automatically adjusts the meter visual using a custom editor script that I wrote. Almost every aspect of the generator is easily customizable from the editor window, such as the required voltage threshold, if the generator should permanently hold its charge or otherwise how long it should take to drain, and actions to be called when the generator is actuated, drained, or if too high or low of a voltage is passed in. The generators will animate and fill up in varying degrees depending on the passed-in voltage from the power threshold used. This system uses tweening to properly fill up the voltage meter and drain it over a set amount of time if desired. The main goal was to create a system that was as straightforward to implement as it would be fun to interact with in-game by putting a focus on our unique flex power mechanic.
Some other level mechanics I designed for the game include jump pads that boost the player in a desired direction, portals that warp powers for unique puzzles, and vines that be climbed up or swung from. All of these mechanics were designed to make the world of Quantum’s Pursuit more interesting and engaging to interact with and provide as many new and fun ways to flex as possible.
[clip(s) of other mechanics)
As Quantum’s Pursuit has grown closer to completion, one area that needed a lot of attention was the game’s front-end menus. We had art drawn for a world map of the game that would be interactable, with players being able to zoom in on each world and view markers for unlocked levels, as well as supporting animations for new levels being unlocked as players progress. Building out this system allowed me to take a great deep dive into Unity’s UGUI system and learn the ins and outs of working with RectTransforms to manipulate the positions, scales, and pivots of UI elements in code during runtime.
[clip of menu navigation]
The map is broken into the game’s four worlds, each based on a design “empowerment class” for Limbitless’s prosthetic arms. Each world can be zoomed in on pressed, causing any unlocked levels in the world to animate in to be selected and loaded. I created the map on a Camera Space Canvas that will scale and stretch with the resolution of the screen, as Quantum’s Pursuit is developed for a variety of Android tablets of differing resolutions and aspect ratios to be given to our bionic kids. In order to create the zoom effect, when a world is selected, its location on the canvas is converted to a normalized vector position on the map art itself. The pivot of the map image is then set to this position, causing it to “zoom in” on the desired portion of the map when its scale is increased.
[picture of pivot function script]
When changing the pivot of the map, I have to also calculate and add the position offset of the moved pivot, as otherwise the map will attempt to move to keep its pivot in the center of the screen when changed. This involves finding the number of pixels the pivot was moved on the map image and adding this offset to the map’s position before adjusting the pivot, then tweening the scale of the map up to fit the desired zoom effect. I also designed this system in a way that it would smoothly adjust and zoom the map without any stuttering or jittering on any aspect ratio and screen resolution, thus being able to work smoothly on any tablet or device we built the game to.