Frog The Door Games


  • Home
  • Vintage Hero
  • Alawishus Pixel
  • Space Racer
  • Cryptogram
  • Other Stuff
  • Contact

Adding a Rumble Component to Your Game

5/11/2011

4 Comments

 
Let's face it. Rumble and XNA haven't had the best history. I still hear people refer to "massage games" and such when people talk about Xbox Live Indies, but there are many out there who can now look past the sea of undesirables and see the potential and successful. It's come a long way since it's innocent beginnings. Yet many developers still shy away from utilizing force feedback and some that do, use it in a brute force kind of way that can annoy the player. Even some AAA titles seem to just set the motors at full blast for three seconds when you crash off the road. Ugh. When it comes to rumble, subtle is the way to go.

Before I go on, I'd like to say that the following code is free to use in your game and is licensed under the Microsoft Permissive License with one caveat. You can not use this component in a game where the sole purpose is to vibrate the controller. You cannot use this for a "massage game" or any other where the purpose of the application is rumble based. You can only use it to enhance existing gameplay where otherwise, not having rumble will not change the objective of the game. Hate that I have to say it, but it needs to be said.

Here I've implemented a time dependent, dynamic based rumble component. I've played many indie games that accidentally leave the motors running until I quit to the dashboard. You can get around this by using time based triggers. When an event happens that requires force feedback (say a collision) just add a new rumble for a set amount of time and when the times up, the rumble stops.

To make things subtle I use the idea of dynamics. Since sound is a vibration, we can use it as an example in which to compare. In music, dynamics are control over the volume of the sound, or intensity of the vibration. By varying the dynamics over time, I could apply a "shape" to the controller's rumble. I've added the ability to change to different rumble shapes by calling them by name, such as binary (on/off), linear (start at minimum and increase to maximum over time) or parabolic (fade in to maximum by time/2 and fade out to minimum by time).

I'll show the core mechanics here and you can download the code at the bottom of this post. It's well commented so you shouldn't haven't any problems following along. The component uses a reusable pool of RumbleInstance objects to draw from when a request to shake the controller is made. Here is the RumbleInstance.
struct RumbleInstance
{
//Control over the motors and for how long.
public float LeftMotorAmount;
public float RightMotorAmount;
public float TimeLeft;

public PlayerIndex playerIndex;

//This instance's state members. These define if it is alive,
//for how long, and the shape of the rumble.
public bool IsAlive { get; private set; }
public Rumble.Shape Shape { get; private set; }
public float Time { get; private set; }

//This initializes this instance with these attributes.
public void Rumble(
PlayerIndex playerIndex,
float leftMotorAmount,
float rightMotorAmount,
float rumbleTime,
Rumble.Shape Shape)
{
if (rumbleTime <= 0)
throw new Exception("rumbleTime must be greater than zero");

IsAlive = true;
LeftMotorAmount = MathHelper.Clamp(leftMotorAmount, 0 ,1);
RightMotorAmount = MathHelper.Clamp(rightMotorAmount, 0, 1);
TimeLeft = rumbleTime;
Time = rumbleTime;

this.playerIndex = playerIndex;
this.Shape = Shape;
}

//Set this instance to null values.
public void Kill()
{
IsAlive = false;
LeftMotorAmount = 0;
RightMotorAmount = 0;
TimeLeft = 0;
Time = 0;
}
}
In the Rumble class, inherit from GameComonent. I make a Shape enum with the names of the shapes the rumble can take, like linear, SmoothStep, etc... and a RumbleInstance array that will be used for the pool. After the constructor, add this for the update.

public override void Update(GameTime gameTime)
{
for (int i = 0; i < rumblePool.Length; i++)
{
if (rumblePool[i].IsAlive)
{
//Reduce the time left to vibrate the motors,
//but don't go less than zero.
rumblePool[i].TimeLeft =
MathHelper.Clamp(
rumblePool[i].TimeLeft -
(float)gameTime.ElapsedGameTime.TotalSeconds,
0,
rumblePool[i].Time);

//Reduce or shape the "curve" of the vibration.
Vector2 motorSpeeds = ShapeRumble(rumblePool[i]);

if (rumblePool[i].TimeLeft <= 0)
{
rumblePool[i].Kill(); //Reset the rumble properties.
motorSpeeds = Vector2.Zero; //Stop the active motors.
}

GamePad.SetVibration(
rumblePool[i].playerIndex, motorSpeeds.X, motorSpeeds.Y);
}
}

base.Update(gameTime);
}
This counts down the time left in each active instance, sets the motor speed based on the shape, and kills the instance when time is up. The "ShapeRumble" method is what defines the strength of the motor over time. All it does is call a switch statement on the Shape enum which in turn calls the appropriate method to shape the vibration. Here are some examples of these methods.

private Vector2 Linear(RumbleInstance rumble)
{
//Start rumble at zero and linearly increase force feedback
//over the length of time until motorAmount is reached.

Vector2 motorSpeeds = Vector2.Zero;

motorSpeeds.X = MathHelper.Lerp(0, rumble.LeftMotorAmount, 1 - (rumble.TimeLeft / rumble.Time));
motorSpeeds.Y = MathHelper.Lerp(0, rumble.RightMotorAmount, 1 - (rumble.TimeLeft / rumble.Time));

return motorSpeeds;
}

private Vector2 Exponential(RumbleInstance rumble)
{
//Start rumble at zero and exponentially increase force feedback
//over the length of time until motorAmount is reached.

Vector2 motorSpeeds = Vector2.Zero;

float amount = (float)Math.Pow(1 - (rumble.TimeLeft / rumble.Time), 2);

motorSpeeds.X = MathHelper.Lerp(0, rumble.LeftMotorAmount, amount);
motorSpeeds.Y = MathHelper.Lerp(0, rumble.RightMotorAmount, amount);

return motorSpeeds;
}

private Vector2 Parabolic(RumbleInstance rumble)
{
//Start rumble at zero and gradually increase force feedback.
//When rumble.Time / 2.0 is reached motors will be at motorAmount.
//Then, gradually reduce force feedback back to zero.

Vector2 motorSpeeds = Vector2.Zero;

//Use the parabolic equation: 4x(1-x)
//This gives us a curve that starts at zero when x = 0,
//reaches its apex at x = 0.5, and ends at zero when x = 1.
float amount = 1 - (rumble.TimeLeft / rumble.Time);
amount = 4 * amount * (1 - amount);

motorSpeeds.X = MathHelper.Lerp(0, rumble.LeftMotorAmount, amount);
motorSpeeds.Y = MathHelper.Lerp(0, rumble.RightMotorAmount, amount);

return motorSpeeds;
}
The only thing left now is to add a public method that allows us to add a rumble. Here it is.

/// <summary>
/// Add a rumble with a predefined shape.
/// </summary>
/// <param name="playerIndex">The player controller to rumble.</param>
/// <param name="leftMotor">The maximum value of the
/// left motor, between 0.0f and 1.0f.</param>
/// <param name="rightMotor">The maximum value of the right motor, between 0.0f and 1.0f.</param>
/// <param name="time">The amount of time to rumble the motor in seconds.</param>
/// <param name="shape">The shape of the rumble.</param>
public void AddRumble(PlayerIndex playerIndex, float leftMotor, float rightMotor, float time, Shape shape)
{
//Find the first unused rumble instance and use it.
//If one is not available, rumble will not be set.
for (int i = 0; i < rumblePool.Length; i++)
{
if (!rumblePool[i].IsAlive)
{
//Use this instance of a rumble and break.
rumblePool[i].Rumble(playerIndex, leftMotor, rightMotor, time, shape);
break;
}
}
}
Now in your main game you can add a rumble class to the list of components. When the time comes to add a rumble sting, simply call the "AddRumble" method. The component class will clean everything up for you. Just be sure to call the method during an event specific time and not in a loop as it will continue to add new instances every time it's called. The full implementation is below, complete with a simple demonstration to show you how it works. The license is included in the zip. Let me know what you think or if you found this useful in the comments below.
rumblecomponentexample.zip
File Size: 43 kb
File Type: zip
Download File

4 Comments
James link
7/27/2011 11:04:34 am

Very nice code man. Thanks so much for saving us the time, plus the extra options!

Reply
Matt link
7/27/2011 01:16:23 pm

My pleasure.:)

Reply
psp parts link
5/29/2012 06:54:03 pm

I am very happy to saw that your code because i am waiting for this code long time, thanks for sharing.

Reply
SupportOmniTech link
6/26/2013 07:53:26 pm

This is certainly an informative article for all those who are fond of computer games. This gives us an exact knowledge about the addition of rumble component to our game. This helps us in increasing the time limit allotted to finish a particular task and this really help us in completing each task.

Reply

Your comment will be posted after it is approved.


Leave a Reply.

    RSS Youtube Twitter Facebook

    Games

    Vintage Hero
    Alawishus Pixel
    Space Racer
    Crytpogram


    Tweets by @frogthedoor



    Archive

    August 2017
    July 2017
    February 2017
    October 2014
    September 2014
    July 2013
    June 2012
    May 2012
    March 2012
    January 2012
    December 2011
    October 2011
    September 2011
    August 2011
    July 2011
    May 2011
    March 2011
    February 2011
    January 2011
    November 2010
    September 2010
    August 2010
    July 2010
    June 2010
    May 2010
    April 2010


    Categories

    All
    Alawishus Pixel
    Cryptogram
    Development
    Press
    Programming
    Screen Shots
    Setlist
    Space Racer
    Tutorial
    Video
    Vintage Hero
    Windows Phone
    Windows Phone 7
    Xna

    RSS Feed



Powered by Create your own unique website with customizable templates.