Draw Methods
The final game screen that you see is made up of many independent components. I've broken the task of rendering all of these components into separate, specialized drawing methods.
- DrawButton renders a graphic based on a digital button's state (pressed or released).
- DrawHBar renders a slider bar and its arrow to reflect the state of one of the thumbstick's x-axis values.
- DrawVBar renders a slider bar and its arrow to reflect the state of one of triggers or one of the thumbstick's y-axis values. Since triggers have a range of values from zero to one, and thumbsticks report values between negative one and positive one, this method assumes that the vertical bar represents a trigger. By passing it a value of -- 1 for the optional min parameter, the bar can represent the full range of a thumbstick.
- DrawGraph fills the screen with the graph paper tile, rendered relative to the ship's current location. The results of this render step will be overlayed with the background image and the graph paper will show through the viewport. You could save some processing by only rendering the part of the paper that will be visible through the viewport, but filling the entire screen means that you can move your viewport to another part of the screen in your graphics editor without editing the rendering code.
- DrawCursor draws the ship, at its current rotation. For details on the implementation of these methods, keep reading. They're described in greater detail in the section entitled, "Drawing the Game."
Update()
The Update method of this game includes logic to poll the controllers and update the ship's location based on the player's input. The ship itself doesn't actually move on the screen. The paper texture below the ship moves to show the relative direction and speed of the ship.
The Update method is where the code that tracks the state of each of the controller buttons is housed. And this is also where the vibration motors on the controller are set to rumble as long as the A button is pressed on the controller. While the state of each of the four controllers is polled, only the controller in port one can affect the state of the button images on the screen. The other controller states are only used to detect when those controllers are added or removed, updating the port connection indicator images.
/// run logic such as updating the world protected override void Update(GameTime gameTime) { // capture pad state once per frame m_pad1 = GamePad.GetState(PlayerIndex.One); m_pad2 = GamePad.GetState(PlayerIndex.Two); m_pad3 = GamePad.GetState(PlayerIndex.Three); m_pad4 = GamePad.GetState(PlayerIndex.Four); // only process input from player one, and only if // the controller is connected if (m_pad1.IsConnected) { // combine states to rotate left, true if any are pressed bool bLeft = m_pad1.DPad.Left == ButtonState.Pressed; bLeft |= m_pad1.ThumbSticks.Left.X < 0; bLeft |= m_pad1.ThumbSticks.Right.X < 0; if (bLeft) { m_angle -= 5.0f; } // combine states to rotate right, true if any are pressed bool bRight = m_pad1.DPad.Right == ButtonState.Pressed; bRight |= m_pad1.ThumbSticks.Left.X > 0; bRight |= m_pad1.ThumbSticks.Right.X > 0; if (bRight) { m_angle += 5.0f; } // distance to travel per frame, split into X and Y float dx = (float)Math.Cos(m_angle * ToRadians); float dy = (float)Math.Sin(m_angle * ToRadians); // check button states to determine thrust float fMove = 0.0f; // assume no movement // is the player moving the ship? if (m_pad1.ThumbSticks.Left.Y != 0.0f) { fMove = m_pad1.ThumbSticks.Left.Y; } else if (m_pad1.ThumbSticks.Right.Y != 0.0f) { fMove = m_pad1.ThumbSticks.Right.Y; } else if (m_pad1.Triggers.Right != 0.0f) { fMove = m_pad1.Triggers.Right; } else if (m_pad1.Triggers.Left != 0.0f) { fMove = -m_pad1.Triggers.Left; } else if (m_pad1.DPad.Up == ButtonState.Pressed) { // treat as max thumbstick Y fMove = 1.0f; } else if (m_pad1.DPad.Down == ButtonState.Pressed) { // treat as min thumbstick Y fMove = -1.0f; } // ship's thrust is relative to analog button states m_GraphOrigin.X -= dx * fMove; m_GraphOrigin.Y -= dy * fMove; // make sure that 0 <= graph origin x <= 50 while (m_GraphOrigin.X < 0.0f) { m_GraphOrigin.X += 50.0f; } while (m_GraphOrigin.X > 50.0f) { m_GraphOrigin.X -= 50.0f; } // make sure that 0 <= graph origin y <= 50 while (m_GraphOrigin.Y < 0.0f) { m_GraphOrigin.Y += 50.0f; } while (m_GraphOrigin.Y > 50.0f) { m_GraphOrigin.Y -= 50.0f; } // shake the controller while the A button is pressed if (m_pad1.Buttons.A == ButtonState.Pressed) { GamePad.SetVibration(PlayerIndex.One, 1.0f, 1.0f); } else { GamePad.SetVibration(PlayerIndex.One, 0f, 0f); } } base.Update(gameTime); }
Drawing the Game
The standard Draw method of this game includes logic to build the screen, piece by piece. First, the graph paper is drawn via a call to DrawGraph. Then, the background image is rendered over the graph tiles so that they can show through the viewport. Then, all of the digital and analog buttons are drawn, along with the four controller connection states, via a call to DrawButtons (note the plural version). And finally, the ship is drawn via a call to DrawCursor.