4 XNA Input
ITP4710
2D/3D Graphics Programming
04
MonoGame Input
XNA Framework provides support for three categories of user input devices
Keyboard
USB Keyboards with more than 100 digital keys
Each key have one of two states – pressed or released
Mouse
Mouse position, 3 mouse buttons and scroll wheel values can be retrieved.
Gamepad
Maximum 4 gamepads
2
MonoGame Input
Input status from GamePad, Keyboard and Mouse are collected through polling.
During every frame, your game will ask the controller what its current state is
The information is stored in XNA structures GamePadState, KeyboardState and MouseState
The device state will be updated every frame and should be passed as parameter(s) to your methods
Polling may seem inefficient and you might think it will miss player input between GetState() calls.
The truth is that you are getting ~60 states each second and a player is very unlikely to press and release a button between any two polling requests
3
Polling Input Device State
Keyboards have 100+ keys, each treated as a digital button
KeyboardState information is retrieved using the GetState() method of Keyboard class
KeyboardState keyState = Keyboard.GetState();
To see if a particular key is being pressed/released, you can call the IsKeyDown() / IsKeyUp() method of KeyboardState structure
if (keyState.IsKeyDown(Keys.Up)) { /* do something */ }
To retrieve a complete list of the currently pressed key, you can call the GetPressedKeys() function, which returns a Keys array of pressed keys.
4
Keyboard
protected override void Update(GameTime gameTime) {
// Poll for current keyboard state
KeyboardState state = Keyboard.GetState();
// If they hit esc, exit
if (state.IsKeyDown(Keys.Escape)) Exit();
// Print to debug console currently pressed keys
System.Text.StringBuilder sb = new StringBuilder();
foreach (var key in state.GetPressedKeys())
sb.Append(“Key: “).Append(key).Append(” pressed “);
if (sb.Length > 0) System.Diagnostics.Debug.WriteLine(sb.ToString());
else System.Diagnostics.Debug.WriteLine(“No Keys pressed”);
// Move our sprite based on arrow keys being pressed:
if (state.IsKeyDown(Keys.Right)) position.X += 10;
if (state.IsKeyDown(Keys.Left)) position.X -= 10;
if (state.IsKeyDown(Keys.Up)) position.Y -= 10;
if (state.IsKeyDown(Keys.Down)) position.Y += 10;
base.Update(gameTime);
}
© VTC 2016
5
Sample Code For Keyboard Input
MouseState information is retrieved using GetState() function of Mouse class
MouseState mouseState = Mouse.GetState();
Supports LeftButton, RightButton and MiddleButton.
The state of the buttons is either ButtonState.Pressed or ButtonState.Releasted.
MouseState also support two special button XButton1 and XButton2, which are used by some modern mouse to as back and forward buttons that help navigating websites.
Mouse position retrieved from member properties X and Y
You can also explicitly set the mouse position through SetPosition() method.
ScrollWheelValue gets the cumulative mouse scroll wheel value since the game was started.
It’s common to want to display the mouse cursor, this is easily accomplished using:
IsMouseVisible = true;
6
Mouse
protected override void Update(GameTime gameTime) {
MouseState state = Mouse.GetState();
// Update our sprites pos to the current cursor location
position.X = state.X;
position.Y = state.Y;
// Check if Right Mouse Button pressed, if so, exit
if (state.RightButton == ButtonState.Pressed)
Exit();
base.Update(gameTime);
}
© VTC 2016
7
Sample Code For Keyboard Input
To handle input from a gamepad or joystick controller, GamePadState information is retrieved using GetState() function of GamePad class
Example:
GamePadState padState1 = GamePad.GetState(PlayerIndex.One);
The state of a GamePad is undefined when the GamePad is disconnected.
Check if a game pad is connected before polling the data:
// Check the device for Player One
GamePadCapabilities capabilities
= GamePad.GetCapabilities( PlayerIndex.One);
// If there a controller attached, handle it
if (capabilities.IsConnected) {
// Do something here
}
8
GamePad
9
Typical Gamepad –
Xbox 360 Controller
Game pad can have up to 14 digital buttons
A, B, X, Y, Start, Back, LeftShoulder and RightShoulder buttons
The four direction buttons of the DPad (DPadLeft, DPadRight, DPadUp and DPadDown)
Left and Right thumbsticks can be pressed and behave as digital buttons (LeftStick and RightStick)
Media button is not supported
Digital buttons have only 2 states – pressed and released.
Example:
if (padState1.Buttons.A == ButtonState.Pressed)
// Do something here
10
Digital Buttons
Unlike digital buttons, analog buttons report a range of values
Two triggers on the back side of the game pad
represented by a float from 0.0f (not pressed) to 1.0f (fully pressed)
Two directional thumbsticks
each thumbsticks has x-axis and y-axis represented by a single Vector2
each axis is represented by a float from -1.0f (left/down) to 1.0f (right/up)
11
Analog Buttons
GamePadState.Buttons.
A
B
X
Y
LeftShoulder
RightShoulder
LeftStick
RightStick
Start
Back
GamePadState.DPad.
Up
Down
Left
Right
GamePadState.ThumbSticks
Left (Vector2)
Right (Vector2)
GamePadState.Triggers
Left
Right
12
GamePad Buttons
protected override void Update(GameTime gameTime) {
// Check the device for Player One
GamePadCapabilities capabilities =
GamePad.GetCapabilities( PlayerIndex.One);
// If there a controller attached, handle it
if (capabilities.IsConnected) {
// Get the current state of Controller1
GamePadState state = GamePad.GetState(PlayerIndex.One);
// You can check if a gamepad has support for a feature
if (capabilities.HasLeftXThumbStick) {
// Check the direction in X axis of left analog stick
if (state.ThumbSticks.Left.X < -0.5f) position.X -= 10.0f;
if (state.ThumbSticks.Left.X > 0.5f) position.X += 10.0f;
}
// You can also check the controllers “type”
if (capabilities.GamePadType == GamePadType.GamePad) {
if (state.IsButtonDown(Buttons.A)) Exit();
}
base.Update(gameTime);
}
© VTC 2016
13
Sample Code For GamePad Input
You can have up to 4 different controllers attached, each accesible by passing the appropriate PlayerIndex to GetEvents().
The following device types can be returned:
AlternateGuitar, ArcadeStick, BigButtonPad, DancePad, DrumKit, FlightStick, GamePad, Guitar, Wheel or Unknown.
Each devices supports a different set of features, which can be polled invdividually using the GamePadCapabilities struct returned by Gamepad.GetCapabilities().
© VTC 2016
14
Supported Gamepads
When at rest, the thumbsticks will always be slightly off center.
Default GamePad.GetState() method automatically disregards values that are below a certain threshold, which is known as the dead zone.
The default setting of dead zone processing uses GamePadDeadZone.IndependentAxes which processed the deadzone of X, Y components independently.
You can also set the dead zone processing method to Circular (combining X-Y components in processing) or None (raw values without any processing) using overloaded GetState() method.
GamePadState state =
GamePad.GetState(PlayerIndex.One, GamePadDeadZone.Circular);
15
Dead Zone Processing
Gamepad can have two vibration motors
Left low-frequency rumble motor
Right high-frequency rumble motor
Each motor can be activated with different speed using GamePad.SetVibration() function
GamePad.SetVibration(
PlayerIndex.One, // player index
1.0f, // speed of left motor (0.0f – 1.0f)
1.0f); // speed of right motor (0.0f – 1.0f)
16
Vibration
17
Example – Shooting Bullets
In the following example, the player will control a plane flying around a sheet of graph paper.
Press up/down to move forward/backward
Press left/right to turn the plane
Press space to shoot a bullet
Press escape to quit the program
The plane will actually remain in the center of the screen. It appears to be moving because the background game world move relatively.
The game is tuned to use a fixed frame rate of 30 fps in the Initialize() method
IsFixedTimeStep = true;
TargetElapsedTime = TimeSpan.FromMilliseconds(33);
18
Example – Shooting Bullets
All bullets in the game is stored in an array, with a maximum size of BULLET_MAX (75)
m_Bullets = new Bullet[BULLET_MAX];
for(int i=0; i
// Shoot Bullet (only if bullet delay count == 0)
if (keyState.IsKeyDown(Keys.Space)) Shoot();
base.Update(gameTime);
}
21
Update() – Handling Input
Rotate Ship
Move Ship
The plane sits still in the center of the screen
When the plane moves forward/backward, the background and the bullets will move around
The background is formed by tiling a 51×51 sprite.
The movement is created by shifting the origin between (0,0) and (50,50).
To keep the background from appearing wavy, we round the origin to the nearest pixel before drawing the tiles in Draw()
To rotate the plane, we just need to update the rotation angle m_angle.
22
Moving and Rotating the Plane
private void MoveShip(float delta) {
// distance to travel per frame, split into X and Y
float dx = delta * (float)Math.Cos(m_angle);
float dy = delta * (float)Math.Sin(m_angle);
// update graph
m_GraphOrigin.X += dx;
m_GraphOrigin.Y += dy;
// make sure x,y are between -50 and 0
while (m_GraphOrigin.X < 0) m_GraphOrigin.X += 50.0f;
while (m_GraphOrigin.X > 50) m_GraphOrigin.X -= 50.0f;
while (m_GraphOrigin.Y < 0) m_GraphOrigin.Y += 50.0f;
while (m_GraphOrigin.Y > 50) m_GraphOrigin.Y -= 50.0f;
UpdateBullets(dx, dy);
}
private void TurnShip(float delta) { m_angle += delta; }
23
MoveShip() and TurnShip()
Vector2 loc = new Vector2(0, 0); // sprite position
float height = GraphicsDevice.Viewport.Height;
float width = GraphicsDevice.Viewport.Width;
GraphicsDevice.Clear(Color.CornflowerBlue);
_spriteBatch.Begin();
for (int y = 0; y <= height / background.Height + 1; y++) {
loc.Y = y * background.Height;
for (int x = 0; x <= width / background.Width + 1; x++) {
loc.X = x * background.Width;
_spriteBatch.Draw(background, loc, null, Color.White,
0, m_GraphOrigin, 1, SpriteEffects.None, 0);
}
}
_spriteBatch.Draw(plane, m_CenterScreen, null, Color.White,
m_angle, new Vector2(plane.Width / 2, plane.Height / 2),
1, SpriteEffects.None, 1);
_spriteBatch.End();
© VTC 2016
24
Draw() (partial)
All bullets in the bullet array were initially inactive.
When player shots a bullet, we will check
if enough time have been waited since last shot
If there is an inactive bullet in the array to be re-initialized
Bullet position and velocity will be initialized if the above conditions are satisfied
When a bullet moves on the screen, it combines the velocity of the plane (as it sits still on the screen) and its own velocity.
We also needs to check if the bullet is out-of-screen
25
Shooting and Updating Bullets
private void Shoot() {
if (m_nBulletCountDown == 0) { // check if waited enough time
for (int i = 0; i < m_Bullets.Length; i++) {
if (!m_Bullets[i].Active) { // check for available slot
m_Bullets[i].Location = m_CenterScreen;
// calc distance to travel per frame, split into X and Y
float dx = -3.0f * (float)Math.Cos(m_angle);
float dy = -3.0f * (float)Math.Sin(m_angle);
m_Bullets[i].Motion = new Vector2(dx, dy);
m_Bullets[i].Angle = 0.0f; // init rotation
m_Bullets[i].Active = true; // mark as active
m_nBulletCountDown = BULLET_DELAY; // reset delay counter
break; // we're done, so exit the loop
}
}
}
}
26
Shoot()
private void UpdateBullets(float dxShip, float dyShip) {
for (int i = 0; i < m_Bullets.Length; i++) {
if (m_Bullets[i].Active) { // update active bullets
// see if the bullet has left the screen
if ( m_Bullets[i].Location.X < -64 ||
m_Bullets[i].Location.X > SCREEN_WIDTH + 64 ||
m_Bullets[i].Location.Y < -64 ||
m_Bullets[i].Location.Y > SCREEN_HEIGHT + 64 ) {
m_Bullets[i].Active = false;
} else {
m_Bullets[i].Location.X +=
dxShip + m_Bullets[i].Motion.X;
m_Bullets[i].Location.Y +=
dyShip + m_Bullets[i].Motion.Y;
m_Bullets[i].Angle += 0.45f; // rotate the bullet
}
}
}
}
27
UpdateBullets()
The game sets a grid of 20×15 cells on the screen
If the mouse is clicked on a cell, the cell color will change permanently
If the mouse moves into a cell, the cell color will change and fade back to origin color after the mouse moves out
The cell status is stored in struct BlockState
bool IsSelected;
double EffectAge;
// representing the remaining time for keeping current color
A 2D array stores status of all cells
private BlockState[,] BlockStates =
new BlockState[GRID_WIDTH, GRID_HEIGHT];
28
Example – Draw with Mouse
29
Example – Draw with Mouse
We need to track the location of the mouse from frame to frame
to highlight the cells that the cursor passes over by initializing EffectAge of the cell
We also need to know when the player has clicked the mouse button
to toggle the IsSelected flag for the clicked cell
To make sure that we don’t register a click more than once, the MouseState from the previous frame needs to be stored
Whenever the previous state doesn’t match the current state, the player has pressed or released the button.
30
Processing Input
MouseState mouse1 = Mouse.GetState();
CursorPosition.X = mouse1.X;
CursorPosition.Y = mouse1.Y;
// which grid cell is this mouse over now?
int gx = (int)Math.Floor(CursorPosition.X / CELL_WIDTH);
int gy = (int)Math.Floor(CursorPosition.Y / CELL_HEIGHT);
for (int y = 0; y < GRID_HEIGHT; y++) {
for (int x = 0; x < GRID_WIDTH; x++) {
if (gx == x && gy == y) { // mouse on cell
BlockStates[x, y].EffectAge = EFFECT_DURATION;
if (mouse1.LeftButton == ButtonState.Pressed) {
// Check if the click has been registered
if (mouse1.LeftButton != LastMouseButtonState)
BlockStates[x, y].IsSelected =
!BlockStates[x, y].IsSelected;
}
} else { // no longer mouse over, update effect age
BlockStates[x, y].EffectAge -= elapsed;
if (BlockStates[x, y].EffectAge < 0)
BlockStates[x, y].EffectAge = 0;
}
}
}
// remember the last click state so we don't process it again
LastMouseButtonState = mouse1.LeftButton;
31
Update() Function (Partial)
DrawGrid() draws the blue grid lines between the cells by shifting a thin rectangle
DrawCursor() draws the arrow sign to indicate the mouse position
No mouse cursor is shown in XNA application by default
DrawBlocks() draws the cells
If cell is selected, draw the cell using active color (white)
If cell is not selected and EffectAge = 0, draw the cell using standard color (blue)
Otherwise, the color will be between white and blue, calculated by linear interpolation (MathHelper.Lerp)
32
Drawing the Screen
Rectangle pos = BlockRect;
Vector3 vColor = Vector3.One
Vector3 vColorA = ColorActiveBlock.ToVector3();
Vector3 vColorI = ColorIdleBlock.ToVector3();
for (int y = 0; y < GRID_HEIGHT; y++) { // for each row
pos.Y = y * 32 + 2; // row as pixel
for (int x = 0; x < GRID_WIDTH; x++) { // for each column
pos.X = x * 32 + 2; // column as pixel
// state of the current block
BlockState state = BlockStates[x, y];
if (state.IsSelected) {
batch.Draw(Texture, pos, BlockRect, ColorActiveBlock);
} else if (state.EffectAge > 0) {
vColor.X = MathHelper.Lerp(vColorI.X, vColorA.X,
(float)(state.EffectAge / EFFECT_DURATION));
vColor.Y = MathHelper.Lerp(vColorI.Y, vColorA.Y,
(float)(state.EffectAge / EFFECT_DURATION));
vColor.Z = MathHelper.Lerp(vColorI.Z, vColorA.Z,
(float)(state.EffectAge / EFFECT_DURATION));
batch.Draw(Texture, pos, BlockRect, new Color(vColor));
} else {
batch.Draw(Texture, pos, BlockRect, ColorIdleBlock);
}
}
}
33
DrawBlocks() Function
Reference
Joseph Hall, XNA Game Studio Express – Developing Games for Windows and the Xbox 360, Thomson Course Technology
Chapters 7-9
MonoGame Tutorial: Handling Keyboard, Mouse and GamePad Input, GameFromScratch.com (http://www.gamefromscratch.com/post/2015/06/28/MonoGame-Tutorial-Handling-Keyboard-Mouse-and-GamePad-Input.aspx)
34
© VTC 2016