Time for action - creating the Camera class

  1. Add a new class called "Camera" to the Robot Rampage project.
  2. Add the following using directive to the class:
    using Microsoft.Xna.Framework;
    
  3. Modify the declaration of the Camera class to make the class public and static:
    public static class Camera
    
  4. Add declarations to the Camera class:
    #region Declarations
    private static Vector2 position = Vector2.Zero;
    private static Vector2 viewPortSize = Vector2.Zero;
    private static Rectangle worldRectangle = new Rectangle(0, 0, 0, 0);
    #endregion
    
  5. Add properties to the Camera class to access and modify the underlying members:
    #region Properties
    public static Vector2 Position
    {
    get { return position; }
    set
    {
    position = new Vector2(
    MathHelper.Clamp(value.X,
    worldRectangle.X,
    worldRectangle.Width - ViewPortWidth),
    MathHelper.Clamp(value.Y,
    worldRectangle.Y,
    worldRectangle.Height - ViewPortHeight));
    }
    }
    public static Rectangle WorldRectangle
    {
    get { return worldRectangle; }
    set { worldRectangle = value; }
    }
    public static int ViewPortWidth
    {
    get { return (int)viewPortSize.X; }
    set { viewPortSize.X = value; }
    }
    public static int ViewPortHeight
    {
    get { return (int)viewPortSize.Y; }
    set { viewPortSize.Y = value; }
    }
    public static Rectangle ViewPort
    {
    get
    {
    return new Rectangle(
    (int)Position.X, (int)Position.Y,
    ViewPortWidth, ViewPortHeight);
    }
    camera classcreating}
    #endregion
    
  6. Add methods to the Camera class:
    #region Public Methods
    public static void Move(Vector2 offset)
    {
    Position += offset;
    }
    public static bool ObjectIsVisible(Rectangle bounds)
    {
    return (ViewPort.Intersects(bounds));
    }
    public static Vector2 Transform(Vector2 point)
    {
    return point - position;
    }
    public static Rectangle Transform(Rectangle rectangle)
    {
    return new Rectangle(
    rectangle.Left - (int)position.X,
    rectangle.Top - (int)position.Y,
    rectangle.Width,
    rectangle.Height);
    }
    #endregion
    
  7. In the LoadContent() method of the Game1 class, initialize the Camera class after the textures and sprite font have been loaded:
    Camera.WorldRectangle = new Rectangle(0, 0, 1600, 1600);
    Camera.ViewPortWidth = 800;
    Camera.ViewPortHeight = 600;
    
    

What just happened?

The first thing to notice about this code is that we have included directives to define code "regions". These directives (#region and #endregion) instruct the C# development environment to treat these code areas as blocks that are collapsible as a related unit. You can click on the little minus sign on the left side of the screen at the beginning of a region to collapse it, hiding the code and leaving behind just the region title. We will include region declarations in all of the classes in Robot Rampage as an example of their usage.

Tip

Regions

Grouping your code into region blocks, known as "Code Folding", can be a big help for readability purposes. If you give your regions descriptive names, you can keep them all collapsed until you need to work on a particular method or other code element. Finding the element you need is then as simple as expanding the region it is located in.

Our Camera object only needs three pieces of information to operate. The first is its position within the game world. This vector points to the upper left corner of the viewing area represented by the camera. That is, if you think of the game world as a huge grid of pixels, the pixel pointed to by the position vector is the pixel that will be drawn in the upper left corner of the display area when the camera is used to draw a scene.

The viewPortSize vector represents the number of pixels to the right and down from the position that are covered by the viewing area. While this size defaults to zero during our game's initialization, we will set the size to match the size of the game's client window. Together, the position and viewPortSize vectors can be thought of as defining a rectangle that represents the portion of the game world that is currently visible on the screen.

The last piece of information the Camera needs is the size of the game world itself. This is the space in which all the objects in the game world will exist and it is measured in pixels. Again, this value defaults to a zero by zero pixel game world, but will be set appropriately in the game's initialization.

Because the position vector actually represents the upper left corner of a rectangle (the visible screen) instead of a single point, we want to make sure that position remains not only within the game world, but also does not get any closer to the right or bottom edges of the game world than the width and height of the viewing area. In other words, if the game world is 1000 by 1000 pixels, and the display screen is 800 by 600 pixels, the largest values we ever want for the components of the position vector are 200 for the X position and 400 for the Y position. If the camera were allowed to get closer to the edge of the game world, we would not have anything to display to the right or bottom of the game world's edges.

To enforce this limitation, we use MathHelper.Clamp() in the set portion of the Position property. This ensures that a full display of the game world will always be visible on the screen.

While external code could directly set the position of the camera via the Position property, we will generally prefer to use the Move() method to relocate the camera relative to its current position. Even though the Move() method is a member of the Camera class and could access the position member directly, it uses the Position property just as external code would, allowing us to maintain the limitations that Position imposes without having to rewrite them in the Move() method.

Tip

Accessing private members

This method of using the public properties to access your class' variables even from within the class' member methods is a good way to keep your code organized and make hunting down and dealing with bugs easier. As long as your properties are robust in their validation of data, using them in your methods prevents unchecked values from slipping in and disrupting other code.

When a game object is going to be drawn, we can check its display rectangle against the ObjectIsVisible() method of the Camera to determine if any pixels in the object would be visible on the screen based on the camera's current position. If ObjectIsVisible() returns false, there is no need to draw the object, as all of its pixels are off screen. We will build this check into the updated Sprite object we construct, so that each sprite will check its own visibility and skip drawing itself if it does not appear anywhere on the display.

Lastly, we have a pair of methods named Transform(). Given either a pixel location (as a Vector2) or a Rectangle, the Transform() methods subtract the camera's current position from them and return the result. To visualize the transformation of world coordinates into screen coordinates, let's return to the world and camera diagram we saw previously, with the addition of an object in the game world:

What just happened?

Here, we can see that the Sprite object's world coordinates represent its absolute position within the context of the game world. On the right side of the previous diagram, we can see that if the Camera Position Vector is subtracted from the World Coordinate Vector, the resulting vector has the same direction and length as the Screen Coordinates vector. When this vector is placed relative to the upper left corner of the screen, the position of the object in screen coordinates is given.

By storing the location of all of our game objects in world-based coordinates, the Camera's Transform() methods will provide screen-coordinate locations for the SpriteBatch.Draw() method to display them in the appropriate locations.

World-aware sprites

This world coordinate focus means that the Sprite class we built for Asteroid Belt Assault will not work directly in the world of Robot Rampage. While all of the concepts are still valid, we need to build in ways to account for the world's camera.

The general concepts behind the Sprite class from Asteroid Belt Assault are all still valid in Robot Rampage, but we must account for the fact that we are no longer limiting the entire play area to the dimensions of the screen. It is possible for a sprite object to be completely on screen, partially on screen, or completely off screen.

In addition, while the sprite's world location may remain constant, the game's camera may move, requiring the onscreen location of the sprite to be adjusted to compensate for the camera's new position.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.105.124