I decided to use a nested class in my input class that contains the inputState and previousInputState fields. The problem I was having was that, being structs, the inputStates were not able to be changed directly at the beginning and end of each update loop without using return values - and for the sake of simplicity in my main class I wanted the input states to remain solely in the Input class.
The problem with structs is that they are simply data types as opposed to reference types so they couldn't be updated persistently in methods. By referencing a field of a struct (eg. keyboardInputState which is in the Input class) in one of the methods in that class (eg. CameraTarget, which might be used for rotating where the camera is "looking") and updating the field (eg. with a GetState() call which gathers the current state of the keyboard), that value is essentially copied as opposed to referencing the original value and updating it. This is bad for two reasons:
1) A previousInputState variable (for both keyboard and gamepad) needs to be updated at the end of each update loop for detecting things like single button presses (compares the inputState with previousInputState from the last update loop). This doesn't work unless the inputState is persistent so that it can be referenced at the end of the update loop.
2) The GetState() will be called 60 times per second for both the keyboard and gamepad so it seems like a waste of resources to create a new updated copy of the inputState for both of those on every call that'll go to the stack. It's only a single 32-byte data type for each of them so I suppose it's not that much for a PC or 360 - assuming the garbage collector does a good job of getting rid of that. Seems like a bad idea anyway though since garbage collection takes some resources and it's a bad habit that could cause a memory leak if not caught in an unmanaged code environment.
Here's the code for the Input class so far:
Code: Select all
class Input
{
protected class InputStates
{
public GamePadState gamePadState;
public KeyboardState keyboardState;
public GamePadState previousGamePadState;
public KeyboardState previousKeyboardState;
}
protected InputStates inputStates;
// Constructor
public Input()
{
inputStates = new InputStates();
}
/// <summary>
/// Run at the beginning of input calls
/// in the Update() loop.
/// </summary>
public void GetState()
{
inputStates.gamePadState = GamePad.GetState(PlayerIndex.One);
inputStates.keyboardState = Keyboard.GetState();
}
/// <summary>
/// Run at the end of input calls
/// in the Update() loop.
/// </summary>
public void PreviousInputState()
{
inputStates.previousGamePadState = inputStates.gamePadState;
inputStates.previousKeyboardState = inputStates.keyboardState;
}
public bool SinglePress()
{
#region Keyboard
if (inputStates.keyboardState.IsKeyDown(Keys.Space) &&
inputStates.previousKeyboardState.IsKeyUp(Keys.Space))
{
return true;
}
else
{
return false;
}
#endregion
}
public Vector3 ObjectRotation()
{
Vector3 objectRotation = new Vector3();
#region GamePad
objectRotation.Y -=
inputStates.gamePadState.ThumbSticks.Left.X * 0.1f;
objectRotation.X -=
inputStates.gamePadState.ThumbSticks.Left.Y * 0.1f;
#endregion
#region Keyboard
if (inputStates.keyboardState.IsKeyDown(Keys.Left))
{
objectRotation.Y += 0.05f;
}
if (inputStates.keyboardState.IsKeyDown(Keys.Right))
{
objectRotation.Y -= 0.05f;
}
if (inputStates.keyboardState.IsKeyDown(Keys.Up))
{
objectRotation.X += 0.05f;
}
if (inputStates.keyboardState.IsKeyDown(Keys.Down))
{
objectRotation.X -= 0.05f;
}
#endregion
return objectRotation;
}
public Vector3 CameraPosition()
{
Vector3 cameraPosition = new Vector3();
#region Keyboard
if (inputStates.keyboardState.IsKeyDown(Keys.PageDown))
{
cameraPosition -= new Vector3(0.0f, 5.0f, 0.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.PageUp))
{
cameraPosition += new Vector3(0.0f, 5.0f, 0.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.Left))
{
cameraPosition -= new Vector3(20.0f, 0.0f, 0.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.Right))
{
cameraPosition += new Vector3(20.0f, 0.0f, 0.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.Up))
{
cameraPosition -= new Vector3(0.0f, 0.0f, 20.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.Down))
{
cameraPosition += new Vector3(0.0f, 0.0f, 20.0f);
}
#endregion
return cameraPosition;
}
public Vector3 CameraTarget()
{
Vector3 cameraTarget = new Vector3();
#region Keyboard
if (inputStates.keyboardState.IsKeyDown(Keys.PageDown))
{
cameraTarget -= new Vector3(0.0f, 5.0f, 0.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.PageUp))
{
cameraTarget += new Vector3(0.0f, 5.0f, 0.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.Left))
{
cameraTarget -= new Vector3(20.0f, 0.0f, 0.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.Right))
{
cameraTarget += new Vector3(20.0f, 0.0f, 0.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.Up))
{
cameraTarget -= new Vector3(0.0f, 0.0f, 20.0f);
}
if (inputStates.keyboardState.IsKeyDown(Keys.Down))
{
cameraTarget += new Vector3(0.0f, 0.0f, 20.0f);
}
#endregion
return cameraTarget;
}
}
I still need to update the playerindex for gamepads so that it grabs whatever controller the player may be using and assigns it to PlayerIndex.One. Also, I realized that what I said about a new copy of a data type being "a bad habit that could cause a memory leak" is rather ironic, given I do this a large number of times with Vector3s (as a matter of fact it's the primary use of this class so far aside from detection of the keyboard/gamepad states).