Project Info
Role: Lead Programmer, Gameplay Programmer
Team Size: 14
Duration: 3 weeks
Engine: Unity
Escape the rapidly flooding sewer!
You play as Jim, an ordinary sewer rat that has grown up and spent their whole life in the sewers with their rat friend, Jam, when the sewer they've always called home suddenly floods!
Hurry up and escape by scaling the walls, use sewer pipes and debris to ascend from the depths, and reunite with your rat friend before the water swallows you!
Character, Camera, Controls
My primary responsibility on the project was to create a player controller. I began by implementing a state machine to encapsulate different player states, such as jumping, sprinting and falling. To have more precise control over movement, I chose to work with a kinematic controller rather than a physics based one. I used Unity's built-in character controller as a base, but soon found myself dealing with a number of issues stemming from the limitations of the built-in controller. Jim would fly up into the air instead of stepping on a ledge, or hang on a ledge in a falling state, or slighly climb slopes that should be too steep. Working on Jim & Jam has taught me much about the inherent complexity of character controllers. A good character controller is a synergy between level design, game feel and robust collision resolution. Whenever one or more of those areas aren't up to par... well, you may get something that controls more like a tank than a rat. Toward the end of the project I had to re-priorized my workload and focused on the most important tasks. One such task was implementing the ability to slide down steep slopes which was essential due to the abundance of round pipes in the game. I implemented it by casting a grid of rays down at the surface, then checking whether the surface angle for all hits is a stable slope. If it isn't stable, I add an additional downward force and project the velocity onto the slope.
Code - Sliding
private void FixedUpdate()
{
isGrounded = IsGrounded();
isSliding = IsSliding(out var slideNormal);
ApplyAcceleration(ref velocity, Time.deltaTime);
ApplyGravity(ref velocity, Time.deltaTime);
if (isSliding)
{
Slide(slideNormal, ref velocity, Time.deltaTime)
}
controller.Move(velocity * Time.deltaTime);
velocity = controller.velocity;
}
private void Slide(Vector3 slideNormal, ref Vector3 velocity, float deltaTime)
{
velocity.y -= slideForce * deltaTime;
velocity = Vector3.ProjectOnPlane(velocity, slideNormal);
}
private bool IsSliding(out Vector3 slideNormal)
{
slideNormal = Vector3.zero;
if (isGrounded) return false;
var radius = controller.radius;
var offset = radius;
var position = transform.position + new Vector3(0, radius, 0);
rayBuffer[0] = new Ray(position + new Vector3(-offset, 0, offset), Vector3.down);
rayBuffer[1] = new Ray(position + new Vector3(0, 0, offset), Vector3.down);
rayBuffer[2] = new Ray(position + new Vector3(offset, 0, offset), Vector3.down);
rayBuffer[3] = new Ray(position + new Vector3(-offset, 0, 0), Vector3.down);
rayBuffer[4] = new Ray(position + new Vector3(0, 0, 0), Vector3.down);
rayBuffer[5] = new Ray(position + new Vector3(offset, 0, 0), Vector3.down);
rayBuffer[6] = new Ray(position + new Vector3(-offset, 0, -offset), Vector3.down);
rayBuffer[7] = new Ray(position + new Vector3(0, 0, -offset), Vector3.down);
rayBuffer[8] = new Ray(position + new Vector3(offset, 0, -offset), Vector3.down);
foreach (var ray in rayBuffer)
{
int hits = Physics.RaycastNonAlloc(ray, slideCheckHits, slideRayLength, mask);
for (int i = 0; i < hits; i++)
{
float dot = Vector3.Dot(Vector3.up, slideCheckHits[i].normal);
if (dot < Mathf.Cos(slopeLimit * Mathf.Deg2Rad))
{
slideNormal = slideCheckHits[i].normal;
return true;
}
}
}
return false;
}
Lead Role
On this project I also took on a role of lead programmer for the first time. It was a good opportunity to try out a lead role because of the short duration of the project and the fact that there were only 2 other programmers beside me, so it wasn't a huge responsibility. My duties as a lead included facilitating communication between programmers and other departments on the team as well as assisting with defining and distributing tasks. I also conducted multiple code reviews. Overall it was a good learning experience and I got to reflect on what my strengths and weaknesses are when it comes to working in a lead position on a larger team.