Rishabh Anand

Astro Kinetic
Itch Link: https://rish0303.itch.io/astrokinetic
Prototype Link: https://drive.google.com/file/d/1BiJx0q-nwROYm-zk3Nj4NqCMLIO5vCiE/view?usp=sharing
Role: Sole Developer / Independent Technical Designer and Gameplay Programmer
Date: February 2025 - June 2025
Type: Personal
Description:
Astro Kinetic is a top down space ship time trial game. Take control of 4 uniquely handling ships to try and set the fastest times possible. Race against your ghost on 3 distinct tracks which test your ability to control your ship.
I used Godot 4 with C# to develop Astro Kinetic. The game started as a technical design gunship prototype which you can download with the link above. After realizing the potential of my physics model I decided to create a new gamemode based around driving ships as fast as possible around courses instead of shooting. I was the sole developer on the project meaning I did all the design, programming and art on my own.
Key Points:
Prototyped space shooter game from scratch in Godot utilizing C# to script satisfying and customizable handling and weapon mechanics
Used composition to to streamline interaction between objects when attacking and damaging, used most effectively with damagable asteroids and boss in prototype
Utilized Resources to create varied ships which each appear and feel distinct in handling and performance
Created custom AI behavior to mimic players and collect telemetry during gameplay to compare against playtests and adjust game balance in prototype build
Designed and implemented modular time trial mode to showcase ship performance and flight physics, encouraging replayability and skill-based expression
Developed 3 distinct tracks which each encourage experimenting with ship selection and driving styles to achieve faster times.
Created particle systems and custom shaders
Boost Ship Ghosts, Sparks, Track Glow Effect, etc.
Created User Interfaces
Main Menu
Track and Ship Selection Menus
Pause Menu
Implemented all Music and SFX both in menus and during gamplay
Modeled all assets including ships and tracks
Physics Model:
In order to create a satisfying physics model, I used jerk for both linear acceleration and rotational acceleration to create smoother input for turning and using thrust. Furthermore, in order to handle deceleration, I used a dampening value which is multiplied by the current linear velocity every physics tick, reducing the floatiness of ships. This also serves as a way to give ships traction when turning, allowing ships to handle completely distinctly through the change of one value. For rotation, I also used a dampener for when the player stops steering, which I felt was better than linear deceleration. Below is a code snippet from my ship script which handles most of the ship maneuvering behavior.
public virtual void InputActions(double delta)
{
float thrustInput = 0.0f;
float rotationInput = 0.0f;
if (!Global.Auto)
{
if (Input.IsActionPressed("Forward"))
thrustInput += 1.0f;
else if (Input.IsActionPressed("Backward"))
thrustInput -= 1.0f;
if (Input.IsActionPressed("Left")) // A key
rotationInput += 1.0f;
if (Input.IsActionPressed("Right")) // D key
rotationInput -= 1.0f;
}
PushShip(delta, thrustInput);
RotateShip(delta, rotationInput);
}
// Apply force to ship in forward or backward direction
public void PushShip(double delta, float input)
{
LinearVelocity *= ShipResource.ThrusterDamping; // Damping for rotating while accelerating, more grip, makes rotation less float
currentLinearJerk = ShipResource.ThrusterJerk * input;
// Get input for forward and backward thrust
if (input > 0.0f && ThrustEnabled) // W Key
{
// Apply force in the direction the ship is facing
Vector3 thrustDirection = -Transform.Basis.X;
currentLinearThrust = Mathf.Min(currentLinearThrust + ShipResource.ThrusterJerk * (float)delta, ShipResource.ThrusterAccelerationMax);
ApplyCentralForce(thrustDirection * currentLinearThrust); // mass is 1 so acceleration can be directly applied
if (Mathf.Abs(LinearVelocity.Length()) > ShipResource.ForwardLinearVelocityMax)
{
//LinearVelocity = LinearVelocity.Normalized() * ShipResource.ForwardLinearVelocityMax;
LinearVelocity =
LinearVelocity.Lerp(LinearVelocity.Normalized() * ShipResource.ForwardLinearVelocityMax,
_thrustMaxSpeedLerp * (float)delta);
}
}
else if (input < 0.0f && ThrustEnabled) // S Key
{
// Apply force in the direction the ship is facing
Vector3 thrustDirection = Transform.Basis.X;
currentLinearThrust = Mathf.Min(currentLinearThrust + ShipResource.ThrusterJerk * (float)delta, ShipResource.ThrusterAccelerationMax);
ApplyCentralForce(thrustDirection * currentLinearThrust); // mass is 1 so acceleration can be directly applied
//ApplyCentralForce(thrustDirection * 1.0f); // mass is 1 so acceleration can be directly applied
//LinearVelocity *= ShipResource.ThrusterDamping; // Damping for release
float speed = LinearVelocity.Dot(-Transform.Basis.X);
if (speed < -ShipResource.BackwardLinearVelocityMax)
{
LinearVelocity = LinearVelocity.Normalized() * ShipResource.BackwardLinearVelocityMax;
}
}
else
{
currentLinearThrust = Mathf.Max(currentLinearThrust - ShipResource.ThrusterJerk * (float)delta, 0); // return thrust back to 0
//LinearVelocity *= ShipResource.ThrusterDamping; // Damping for release
}
}
// Rotate Ship On Y Axis
public void RotateShip(double delta, float rotationInput)
{
currentRotationalJerk = ShipResource.RotationalJerk * rotationInput;
// Apply rotation torque around the Y axis
if (rotationInput != 0.0f)
{
currentRotationalThrust += rotationInput * ShipResource.RotationalJerk * (float)delta; // value that will be used in ApplyTorque
if (rotationInput > 0)
{
// if rotation direction is switched start currentRotationalThrust from 0 for snappier rotation
currentRotationalThrust = Mathf.Clamp(currentRotationalThrust, 0, ShipResource.RotationalAccelerationMax);
}
else if (rotationInput < 0)
{
// if rotation direction is switched start currentRotationalThrust from 0 for snappier rotation
currentRotationalThrust = Mathf.Clamp(currentRotationalThrust, -ShipResource.RotationalAccelerationMax, 0);
}
ApplyTorque(new Vector3(0, currentRotationalThrust, 0));
}
else
{
if (currentRotationalThrust > 0)
{
currentRotationalThrust = Mathf.Max(currentRotationalThrust - ShipResource.RotationalJerk * (float)delta, 0);
}
else
{
currentRotationalThrust = Mathf.Min(currentRotationalThrust + ShipResource.RotationalJerk * (float)delta, 0);
}
AngularVelocity = new Vector3(AngularVelocity.X, Mathf.Lerp(AngularVelocity.Y, 0, ShipResource.RotationalDamping), AngularVelocity.Z);
}
VisualRotationValue = Mathf.Lerp(VisualRotationValue, rotationInput, ShipResource.ShipVisualRotationWeight * (float)delta);
ShipHull.Rotation = new Vector3(VisualRotationValue * ShipResource.ShipVisualRotationAngle, ShipHull.Rotation.Y, ShipHull.Rotation.Z);
// Clamp Angular Velocity
Vector3 currentAngularVelocity = AngularVelocity;
if (Mathf.Abs(currentAngularVelocity.Y) > ShipResource.RotationalVelocityMax)
{
currentAngularVelocity.Y = Mathf.Sign(currentAngularVelocity.Y) * ShipResource.RotationalVelocityMax;
AngularVelocity = currentAngularVelocity;
}
}
Prototype:
Before I turned this into a personal project, the game started as a gunship prototype for my Technical Design Class. As such there are some key differences between the two versions for the game I have listed. For one I had to create weapons for the gunship game.










