top of page

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;
    }
}
    
Gameplay showcasing ship physics

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.




bottom of page