Tom and Foolery's Tavern
Unity 3D platformer for Alt. Ctrl GDC submission 2024
Unity 3D platformer for Alt. Ctrl GDC submission 2024
For my team's platformer-based alt.ctrl game, I created multiple moving platform tools for the level designers to add dynamic objects to the game and hasten their workflows with a focus on editor functionality through gizmos.
🔵 Roles: Gameplay Programmer, Tools Programmer, Game Designer
🔵 Collaborators: Lucas Diab, Kien Ho, Joey Albejo, Brandon Mohammad Ali, Isaac Collier
🔵 Year: 2023
🔵 Timeline: 2 months
Intuitive to Use - so the level designers can implement moving platforms and objects without assistance from the programmer.
Editor tools and Gizmos to provide the necessary feedback to designers on how the object will move when in play-mode for quick iteration.
Below I describe my programming work on the features found in our game! Click the dropdown to view its contents.
Apart from working on the character and camera movement, I also created a tool to help level designers add moving platforms in the world. The video below showcases creating a moving platform as a level designer.
The challenge with our movement was ensuring the player stays on the moving platform even when jumping up and down. The way I set it up, the moving platform saves the moment the player lands on it. From this moment forth, as long as the player is in a trigger box (that I made very high up), the player will share the moving platform's movement. I detect the player is outside of the platform if they leave the trigger box and land on another object.
I made use of Unity gizmos, visual renders in scene view to allow Level Designers to see the position of the moving platform at specific waypoints. To rotate these gizmos isn't easy, and to accomplish this I used matrix data to convert the local rotation to the world matrix, then when drawing the gizmos it will use the updated matrix to rotate it.
I also use [ExecuteAlways] on the script, so it runs every frame even during the scene view. This way the designers can see updates live instead of having to click play or save their work to view the changed content. It's noted that ExecuteAlways can reduce scene view speeds and is not recommended for heavy math calculations.
There are some objects that the player will not be jumping on yet we still want to move to make the environment more dynamic and fit the theme of "a wizard battle in a Tavern". For these scenarios, I made multiple scripts to move objects around.
Moving Objects in a Circle: I draw a circle gizmo to show the object's movement path, along with a line that points to the moving object so designers can search for it if it blends in with its surroundings.
Shake Objects in Place: By placing the desired object in an empty parent, the child object sets a random position in a 360-degree area from the parent object's center point in every frame.
Rotate Objects on a Given Axis: The designer must place the object in an empty parent and then the script will rotate the childObject's Transform components around the parentObject's center.
All of these script's effects can be seen in the scene view as I use the [ExecuteAlways] command on the entire script. I've also made inspector buttons to either stop the movement of these objects, reset their positions, or adjust movement parameters like move speed and radius to allow for quick iteration. If I were to do things differently, I would make a universal boolean value to stop and start the movements instead of the designer having to click on each object and toggle the value.
As a 3D platformer, the team knew we wanted precise and fluid character controls. One of my teammates created a base prototype of the character jumping automatically when they hit the ground, but it was locked to a static camera position and felt rigid and floaty.
I took over the movement control system and coded my own physics gravity system along with moving the character relative to the direction the camera is currently looking.
This system is similar to how gravity is applied in Mario Bros to make jumping feel good, except in a 3D space. I, or any designer, can adjust all parameters including the Jump Force, length of the Jump Buffer, gravity, and terminal velocity. Based on the state of the jump I also apply coded animations to the Slime character using LeanTween! When the character lands on the ground, I apply a squash effect, push the position on the Y down, and multiply the effect based on how high the slime jumped. This way the player can see the impact from landing a high jump.
I also draw a shadow sprite on the ground using a raycast downward, and adjust the scale based on the player's y position. Since the Slime Character is a shader, it does not interact with the lighting system and cannot create a shadow, so the solution I went with was to draw it and then clamp the shadow's scale so that it can't be smaller than a certain number. In this game where players bounce high, it's important to know where they will be landing, so keeping the shadow always visible helps players to control their jumps.
We wanted to create Tom and Foolery's Tavern with an alt. ctrl. setup we designed, and in that design there is no second joystick to control the camera, meaning we had to design a new way for the camera to follow the player.
After many group discussions, we decided that the camera would follow the player and look left and right based on the player's movement inputs, and if the player goes backwards, the camera should still face forwards.
I use a cinemachine free look virtual camera to help position the camera and set up the rotational constraints to the player. The part I had to modify however, was how to move the camera without the mouse.
I created a control system that is fluid and does what the player expects it to do, with the design goal of making it invisible to the player. I use acceleration and deceleration when moving both ways, so if the player is moving left and then goes right, the camera will decelerate and then accelerate in the other direction. These values can be modified in the inspector. The character moved relative to the camera's current forward position, which was a challenge to perform since I'd never done this before in a 3D space.
When the player character jumps, I tilt the camera upwards to follow the player, and when the character is falling I tilt the camera downward to the ground so the player can see where they are landing.
The last modification I coded zooms out the camera the higher the player jumps by a certain amount (the jump multiplier), and if the player mistimes their jump, the camera zooms back in to the default. I accomplish this by Lerping the camera's current zoom to the desired zoom and setting the m_cameraDistance in Cinemachine's variables to the lerped result.
As my team blocked out the level, we thought of using moving platforms to keep the players engaged. I was tasked to create a moving platform object, and with my interest in creating a tool for others to use, implemented custom editor functionalities for ease of use.
"I'm noticing that we have to make the prefab first, then make it the mesh we want... is there a way I can just make the mesh that's already in the level move lol without having to import a moving platform prefab first and then changing it into the mesh I want"
Response
This feedback has taught me that even though I, as the tools programmer, saw no faults when testing the tool, it will always be used in scenarios I haven't thought of and might become more of a hassle instead. I designed this tool to work in a very specific way, and not with pre-existing objects in mind. My advice is to work with the level designer throughout the process instead of just at the beginning or end. I solved this with the next iteration of the tool seen in the second tutorial.
After gaining feedback from the team on ways to improve, I iterated on the tools and uploaded a new video for the level designers to reference when implementing moving platforms or objects into the scene. It was important to stay open-minded and empathize with their feedback as these struggles could be frustrating and time-consuming to work with.
"It did take me a while just to create parent objects for everything, especially for the shake script... and the move object around in a circle only works on one axis, but overall the rotate script was the most useful imo, but it affects every single rotation value."
Response
With this second iteration I added all the changes from the first feedback, and I was happy to see that during this feedback there was no frustrations towards the moving platforms. In this addition I also created 3 new scripts that this feedback leans more towards solving which is what the level designer is providing generous insight on.