imposeMovementLimits()
helper method to the PlayerManager class:private void imposeMovementLimits() { Vector2 location = playerSprite.Location; if (location.X < playerAreaLimit.X) location.X = playerAreaLimit.X; if (location.X > (playerAreaLimit.Right - playerSprite.Source.Width)) location.X = (playerAreaLimit.Right - playerSprite.Source.Width); if (location.Y < playerAreaLimit.Y) location.Y = playerAreaLimit.Y; if (location.Y > (playerAreaLimit.Bottom - playerSprite.Source.Height)) location.Y = (playerAreaLimit.Bottom - playerSprite.Source.Height); playerSprite.Location = location; }
Update()
method to the PlayerManager class:public void Update(GameTime gameTime) { PlayerShotManager.Update(gameTime); if (!Destroyed) { playerSprite.Velocity = Vector2.Zero; shotTimer += (float)gameTime.ElapsedGameTime.TotalSeconds; HandleKeyboardInput(Keyboard.GetState()); HandleGamepadInput(GamePad.GetState(PlayerIndex.One)); playerSprite.Velocity.Normalize(); playerSprite.Velocity *= playerSpeed; playerSprite.Update(gameTime); imposeMovementLimits(); } }
Draw()
method to the PlayerManager class:public void Draw(SpriteBatch spriteBatch) { PlayerShotManager.Draw(spriteBatch); if (!Destroyed) { playerSprite.Draw(spriteBatch); } }
PlayerManager playerManager;
LoadContent()
method of the Game1 class, set up the PlayerManager after the AsteroidManager is initialized:playerManager = new PlayerManager( spriteSheet, new Rectangle(0, 150, 50, 50), 3, new Rectangle( 0, 0, this.Window.ClientBounds.Width, this.Window.ClientBounds.Height));
Update()
method of the Game1 class, add an update line for the PlayerManager right after the AsteroidManager is updated:playerManager.Update(gameTime);
Draw()
method of the Game1 class, add a draw line for the PlayerManager right after the AsteroidManager is drawn:playerManager.Draw(spriteBatch);
The imposeMovementLimits()
method begins by making a copy of the playerSprite's Location
property. Since Location
is a property and not a public member, we cannot modify the components of the vector (X and Y) individually. Creating a temporary copy allows us to independently modify these values and then save the whole vector back to the property.
The X and Y values of the vector are checked against the edges of the playerAreaLimit
rectangle. On the right and bottom edges, the width and height of the player sprite is subtracted from the bounding rectangle edges to ensure that the player sprite stays entirely on the screen. If any of the edges are out of alignment, the components of the location vector are adjusted to keep the ship within the play area.
The location is then saved back into the playerSprite.Location
property.
Updating the player manager begins by updating its related ShotManager. Then, if the Destroyed
variable is false, the remainder of the Update()
method is allowed to proceed. This check prevents the player from continuing to fire shots after being killed. The update begins by setting the player's velocity to zero and incrementing the timer for firing shots. The HandleKeyboardInput()
and HandleGamepadInput()
methods are then called, passing each the current state of the appropriate input device.
We need to resolve two potential issues with player movement at this point. If the player were simply moving to the right, the player's velocity vector would be equal to (1, 0), with a length of 1 unit. If, however, the player is holding down both the right and up keys, the resulting vector would be (1, 1). As we saw when discussing normalized vectors while bouncing asteroids off of each other, this vector has a length of 1.414 units, meaning that the player can move faster by moving diagonally than they can by moving in straight lines.
A related side issue is that if the player has an Xbox gamepad connected to their PC, they could hold down both the thumbstick and the arrow keys to move at twice the normal speed in their chosen direction.
To compensate for both of these potential issues, the Update()
method normalizes the player's velocity vector and then multiplies it by the playerSpeed
variable. This results in a vector that is always the same length (unless, of course, the player is not moving, in which case the vector value of (0, 0) has no length at all).
After all of the input and velocity changes have been accounted for, the Update()
method of the playerSprite
object is called to allow the velocity to be added to the player's location and to advance the animation frame. Finally, the imposeMovementLimits()
method is called to make sure the ship stays within the play area.
When the PlayerManager is drawn, the same series of events takes place as the Update()
method. First, the associated ShotManager is drawn, followed by a check to see if the player has been destroyed. If they are still alive, the player ship gets drawn to the screen. It is important to note that the ShotManager gets drawn before the player, because any shot the player fires will begin somewhat overlapping the player's sprite. By drawing the shots before the player ship, the shot will appear to come from inside the ship, instead of appearing on top of the ship when fired.
3.16.147.124