Back to Main Page


This is a short overview of the quickly developed airbag proof of concept mod I created.

It's not a very complex mod, so I have taken the chance to explain more about the code involved this time, to give people a better insight into what goes on under the hood, in case they want to create their own version.

Download:
Airbags Test VS2015 Project File

I am providing this project in good faith. You can do whatever you like with this project, make any mods you like from it, take any code from it etc...

The only thing I ask for, is that you provide a simple credit with any project or mod you create, that acknowledges where the code came from. Please credit as LeeC2202

Please Note: This is a barebones project. That means that it provides basic functionality, whilst offering the opportunity to expand it to include additional features, if required.

Additional checking will need to be added to cater for the vehicle being destroyed, disappearing etc... This airbag system has been integrated into my DIS mod and that provides all the required checking, which is why I haven't added it here.

Airbags are meant to deploy in a fire, so there is scope for additional functionality there. Or you could make an airbag collection and place them around the vehicle. There are other options if you really want to expand the possibilities of this project.

Page 1

Airbags - Markers to the Rescue... again.


Core Code:

The airbag can exist in one of 5 states, Armed, Inflating, Inflated, Deflating, Deflated. Thoses are stored in an enum and they control what happens to the airbag during the Update function. This is the empty state management section.

// Handle the various airbag states
switch (AirbagState)
{
    case AirbagState.Armed:
        break;
    case AirbagState.Inflating:
        break;
    case AirbagState.Inflated:
        break;
    case AirbagState.Deflating:
        break;
    case AirbagState.Deflated:
        break;
}

The default state is Armed and that's where this next section of code exists.

The first thing I am using, is the relative speed vector of the vehicle, which I get with this native:

// Get the vehicle speed vector relative to the vehicle
Vector3 speedVector = Function.Call<Vector3>(Hash.GET_ENTITY_SPEED_VECTOR, AirbagVehicle, true);

I then check to see if the vehicle has collided with anything. If it has, I compare the speed vector from the previous frame when there was no collision, with the current one. If any of the values have changed by more than my defined impact difference properties, then I can deploy the airbag.

// If there has been a collision...
if (AirbagVehicle.HasCollidedWithAnything)
{
    // ... and the speed has changed by the impact difference amount, then deploy the airbag
    if (speedVector.X > LastSpeedVector.X + SideImpactDifference || speedVector.X < LastSpeedVector.X - SideImpactDifference
        || speedVector.Y > LastSpeedVector.Y + ImpactDifference || speedVector.Y < LastSpeedVector.Y - ImpactDifference)
    {
        DeployAirbag();
    }
}
else
{
    // If there has been no collision, store the current speed vector to compare against next time around
    LastSpeedVector = speedVector;
}

The DeployAirbag function switches the airbag state, sets a timer and spawns the smoke effect.

public void DeployAirbag()
{
    AirbagState = AirbagState.Inflating;
    ReDeployTimer = ReDeployTime;
    EmitSmoke();
}

We now switch to the Inflating state. This handles the transition from first being deployed to being fully inflated. This is a rapid change and only last 60 milliseconds. In real life cars, this happens in half that time.

case AirbagState.Inflating:
    // Increment the inflate timer based on the elapsed frame time
    InflateTimer += elapsedTime;

    // If we have passed the time limit...
    if (InflateTimer >= InflateTime)
    {
        // ... work out by how much we have gone past the time limit and then subtract that from the deflate delay timer
        int overrun = InflateTimer - InflateTime;
        DeflateDelayTimer -= overrun;

        // Switch states as the airbag is fully inflated
        AirbagState = AirbagState.Inflated;
    }

    // Set the inflated level based on the timer value
    inflatedLevel = (float)InflateTimer / InflateTime;
    break;

Once the airbag is fully inflated, it switches into Inflated state. This is a brief state, just 2 seconds long, but it keeps the bag inflated while the game plays the impact animation for the player, so it works fine.

case AirbagState.Inflated:
    // Decrement the delay timer, this keeps the airbag fully inflated for a short time
    DeflateDelayTimer -= elapsedTime;

    if (DeflateDelayTimer <= 0)
    {
        AirbagState = AirbagState.Deflating;
    }

    inflatedLevel = 1f;
    break;

Once the timer has elapsed, it moves into Deflating state. Like the Inflating state, this sets the inflated level as a percentage of the time that has elapsed. The idea behind this is that it remains consistent across different frame rates, plus if you ever change the time length, the animation adjusts itself to match.

case AirbagState.Deflating:
    // Decrement the deflate timer, check if we should switch state and set the inflated level based on the timer
    DeflateTimer -= elapsedTime;

    if (DeflateTimer <= 0)
    {
        AirbagState = AirbagState.Deflated;
    }

    inflatedLevel = (float)DeflateTimer / DeflateTime;
    break;

Again, the timer elapsing switches into the final Deflated state. This state handles a couple of things, the airbag marker fading out and the airbag reset process. The airbag will only reset when there is no collision happening.

case AirbagState.Deflated:
    // If the redeploy timer is greater than zero, we are still handling the bag fading
    if (ReDeployTimer > 0)
    {
        // Decrement the redeploy timer, this is just so it doesn't disappear into thin air
        ReDeployTimer -= elapsedTime;

        // Clamp the value to ensure it doesn't cause colour value exceptions
        ReDeployTimer = Math.Max(ReDeployTimer, 0);

        // Create a modifier based on the elapsed time and fade the colour and alpha based on that modifier
        float fadeModifier = (float)ReDeployTimer / ReDeployTime;
        AirbagColour = Color.FromArgb((int)(255 * fadeModifier), (int)(AirbagColour.R * fadeModifier), (int)(AirbagColour.G * fadeModifier), (int)(AirbagColour.B * fadeModifier));
    }
    else if (ReDeployTimer <= 0 && !AirbagVehicle.HasCollidedWithAnything)
    {
        // If the timer has elapsed and we're not hitting anything, reset the airbag
        ResetAirbag();
    }

    inflatedLevel = .001f;
    break;

Once the bag is reset, it goes back into the Armed state, ready for the next crash.

The final thing that happens in the Update function, is the inflated level is clamped (just in case it has wandered into exception territory) and the airbag is drawn.

// Clamp the inflated level, just to make sure it's within valid values
inflatedLevel = Math.Max(Math.Min(inflatedLevel, 1f), 0f);

// If we're in any state other than armed, check the convertible roof state and then draw the airbag
if (AirbagState != AirbagState.Armed)
{
    DrawAirbag(steeringWheelPos, inflatedLevel);
}

You will notice in the Deflated state, that a property called AirbagColour was getting modified. This value is obtained before all the state checking.

// Set the base airbag colour and then adjust it based on time
if (IsConvertible) CheckRoof();

AirbagBaseColour = IsRoofOpen ? Color.FromArgb(200, 200, 200) : Color.FromArgb(130, 130, 130);
TimeAdjustAirbagColour();

The first line checks the roof state if the vehicle is a convertible, it then sets a base colour on the state of the roof. Vehicles with a fixed roof, always report a value of !IsRoofOpen, so they get the darker base colour. It then calls the TimeAdjustAirbagColour function, to shade the airbag based on the time of day.

This shifts the 24 hour clock by 12 hours, which would give us -12 to 12 but I use Math.Abs to always return a positive value. It then creates a modifier value from the hour and subtracts that modifier from the base colour. So the nearer to midnight you get, the darker the colour of the airbag.

private void TimeAdjustAirbagColour()
{
    // Get the current hour and turn it into 12 - 0 - 12
    int hour = Math.Abs(Function.Call<int>(Hash.GET_CLOCK_HOURS) - 12);

    // Create a modifier value based on the hour, taking the roof state into account as well
    int modifierShade = (IsRoofOpen ? 12 : 8) * hour;

    // Create the modified airbag colour
    AirbagColour = Color.FromArgb(AirbagBaseColour.R - modifierShade, AirbagBaseColour.G - modifierShade, AirbagBaseColour.B - modifierShade);
}

I should point out that this isn't a code-complete description. There are other functions not shown here that are required for this code to fully work. I am considering whether to post a full working mod sample with this overview. If I do, it will be a skeleton project, that has the functionality shown in my videos and gives you the option to expand it however you see fit.

I will update this page in the next day or so with more information on that score. If anyone wants more information before then, I am more than happy to explain any further details about what else is happening in the code that isn't shown here. I will end the page with links to the videos on YouTube that demonstrate what this mod does.

Please note, these videos don't show the changed airbag target code, or some revised colour change values.

 

 

Airbags Test Project File. Please be sure to read the notes in the left column before choosing to use this project.