2
\$\begingroup\$

I have a simple ship sprite moving around the screen along with a 2D Camera.

I have zooming in and out working, however when I zoom out it goes past the world bounds and has the cornflower blue background showing.

How do I sort it that I can only zoom out as far as showing the entire world (which is a picture of OZ) and thats it? I dont want any of the cornflower blue showing.

Cheers!

namespace GamesCoursework_1
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;


    // player variables
    Texture2D Ship;
    Vector2 Ship_Position;
    float Ship_Rotation = 0.0f;
    Vector2 Ship_Origin;
    Vector2 Ship_Velocity;
    const float tangentialVelocity = 4f;
    float friction = 0.05f;

    static Point CameraViewport = new Point(800, 800);
    Camera2d cam = new Camera2d((int)CameraViewport.X, (int)CameraViewport.Y);
    //Size of world
    static Point worldSize = new Point(1600, 1600);
    // Screen variables
    static Point worldCenter = new Point(worldSize.X / 2, worldSize.Y / 2);

    Rectangle playerBounds = new Rectangle(CameraViewport.X / 2, CameraViewport.Y / 2, worldSize.X - CameraViewport.X, worldSize.Y - CameraViewport.Y);
    Rectangle worldBounds = new Rectangle(0, 0, worldSize.X, worldSize.Y);

    Texture2D background;


    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);

        graphics.PreferredBackBufferWidth = CameraViewport.X;
        graphics.PreferredBackBufferHeight = CameraViewport.Y;
        Content.RootDirectory = "Content";
    }

    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related content.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
    {
        // TODO: Add your initialization logic here

        base.Initialize();
    }

    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your content.
    /// </summary>
    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);

        // TODO: use this.Content to load your game content here
        Ship = Content.Load<Texture2D>("Ship");
        Ship_Origin.X = Ship.Width / 2;
        Ship_Origin.Y = Ship.Height / 2;

        background = Content.Load<Texture2D>("aus");

        Ship_Position = new Vector2(worldCenter.X, worldCenter.Y);
        cam.Pos = Ship_Position;
        cam.Zoom = 1f;


    }

    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }

    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input, and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Update(GameTime gameTime)
    {
        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();

        // TODO: Add your update logic here
        Ship_Position = Ship_Velocity + Ship_Position;
        keyPressed();

        base.Update(gameTime);
    }

    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        // TODO: Add your drawing code here
        spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null,null, cam.get_transformation(GraphicsDevice));
        spriteBatch.Draw(background, Vector2.Zero, Color.White);
        spriteBatch.Draw(Ship, Ship_Position, Ship.Bounds, Color.White, Ship_Rotation, Ship_Origin, 1.0f, SpriteEffects.None, 0f); 
        spriteBatch.End();



        base.Draw(gameTime);
    }

    private void Ship_Move(Vector2 move)
    {
        Ship_Position += move;
    }

    private void keyPressed()
    {
        KeyboardState keyState;
        // Move right
        keyState = Keyboard.GetState();

        if (keyState.IsKeyDown(Keys.Right))
        {
            Ship_Rotation = Ship_Rotation + 0.1f;
        }
        if (keyState.IsKeyDown(Keys.Left))
        {
            Ship_Rotation = Ship_Rotation - 0.1f;
        }
        if (keyState.IsKeyDown(Keys.Up))
        {

            Ship_Velocity.X = (float)Math.Cos(Ship_Rotation) * tangentialVelocity;
            Ship_Velocity.Y = (float)Math.Sin(Ship_Rotation) * tangentialVelocity;

            if ((int)Ship_Position.Y < playerBounds.Bottom && (int)Ship_Position.Y > playerBounds.Top) cam._pos.Y = Ship_Position.Y;
            if ((int)Ship_Position.X > playerBounds.Left && (int)Ship_Position.X < playerBounds.Right) cam._pos.X = Ship_Position.X;

            Ship_Position += new Vector2(tangentialVelocity, 0);
            if (!worldBounds.Contains(new Point((int)Ship_Position.X, (int)Ship_Position.Y))) Ship_Position -= new Vector2(tangentialVelocity * 2, 0.0f);
            Ship_Position += new Vector2(-tangentialVelocity, 0.0f);
            if (!worldBounds.Contains(new Point((int)Ship_Position.X, (int)Ship_Position.Y))) Ship_Position -= new Vector2(-tangentialVelocity * 2, 0.0f);
            Ship_Position += new Vector2(0.0f, -tangentialVelocity);
            if (!worldBounds.Contains(new Point((int)Ship_Position.X, (int)Ship_Position.Y))) Ship_Position -= new Vector2(0.0f, -tangentialVelocity * 2);
            Ship_Position += new Vector2(0.0f, tangentialVelocity);
            if (!worldBounds.Contains(new Point((int)Ship_Position.X, (int)Ship_Position.Y))) Ship_Position -= new Vector2(0.0f, 2 * tangentialVelocity);




        } 
        else if(Ship_Velocity != Vector2.Zero)
        {
            float i = Ship_Velocity.X;
            float j = Ship_Velocity.Y;

            Ship_Velocity.X = i -= friction * i;
            Ship_Velocity.Y = j -= friction * j;

            if ((int)Ship_Position.Y < playerBounds.Bottom && (int)Ship_Position.Y > playerBounds.Top) cam._pos.Y = Ship_Position.Y;
            if ((int)Ship_Position.X > playerBounds.Left && (int)Ship_Position.X < playerBounds.Right) cam._pos.X = Ship_Position.X;


            Ship_Position += new Vector2(tangentialVelocity, 0);
            if (!worldBounds.Contains(new Point((int)Ship_Position.X, (int)Ship_Position.Y))) Ship_Position -= new Vector2(tangentialVelocity * 2, 0.0f);
            Ship_Position += new Vector2(-tangentialVelocity, 0.0f);
            if (!worldBounds.Contains(new Point((int)Ship_Position.X, (int)Ship_Position.Y))) Ship_Position -= new Vector2(-tangentialVelocity * 2, 0.0f);
            Ship_Position += new Vector2(0.0f, -tangentialVelocity);
            if (!worldBounds.Contains(new Point((int)Ship_Position.X, (int)Ship_Position.Y))) Ship_Position -= new Vector2(0.0f, -tangentialVelocity * 2);
            Ship_Position += new Vector2(0.0f, tangentialVelocity);
            if (!worldBounds.Contains(new Point((int)Ship_Position.X, (int)Ship_Position.Y))) Ship_Position -= new Vector2(0.0f, 2 * tangentialVelocity);

        }
        if (keyState.IsKeyDown(Keys.Q))
        {
            if (cam.Zoom < 2f) cam.Zoom += 0.05f;
        }
        if (keyState.IsKeyDown(Keys.A))
        {
            if (cam.Zoom > 0.3f) cam.Zoom -= 0.05f;
        }

    }
}
}
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

I'm assuming you are generating a scaling matrix to perform zoom.

First is the easy part(you probably have this already). You want is to stop zooming when the background becomes the same size as the viewport. The point to stop zooming out is 'backgroundwidth/viewportwidth'. This will be 0.5 in your case. Don't let the zoom factor go below this value.

This will work fine if the camera is in the centre of the background, but if the camera is off centre, you will still see your cornflower blue on the edge of the screen.

In order to prevent the user from being shown outside the edge of the world, yo will need to change the size of playerBounds rectangle. You are already using this to stop camera movement when the player gets too close to the edge. You just need to make sure that this rectangle has the appropriate size. Basically you want to prevent the camera position from getting within camerawidth/2 pixels of the edge of the background. During zooming out the background is effectively getting smaller

float scaledWorldHalfWidth=worldSize.x*cam.Zoom/2.0f;
float scaledWorldHalfHeight=worldSize.y*cam.Zoom/2.0f;

playerBounds = new Rectangle(worldcentre.X-scaledWorldHalfWidth +CameraViewport.X/2,
                             worldcentre.Y-scaledWorldHalfHeight+CameraViewport.Y/2,
                             worldcentre.X+scaledWorldHalfWidth -CameraViewport.X/2,
                             worldcentre.Y+scaledWorldHalfHeight -CameraViewport.Y/2);

In this code I am decreasing the size of the rectangle as the zoom factor decreases (as the player sees more and more of the background).

EDIT

This new player bounds should be generated whenever you change the camera zoom factor. So call it when 'q' or 'a' is pressed.

Also, whenever the camera moves check if the camera is still in playerbounds, if not adjust it position so that it is;

        if (cam._Pos.X < playerBounds.Left) //too far left? Any further left, the camera will start to show the edge of the map
            cam._Pos.X=playerBounds.Left //put it on the left most edge player bounds

         // similar code needed for top, bottom and right

A couple of other things;

  1. As you zoom out, you will need to check if camera position still lies within the playerBounds rectangle, as the rectangle gets smaller. You will need to adjust/move the camera to make sure it lies in the rectangle.

  2. All this code will be implemented much simpler if you centre the world on the origin, rather than having the upper-left of the world drawn at the origin. (check out the 'origin' parameter in spritebatch.Draw(.....);

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for the response! :) I half understand whats happening but having difficulty as to how to make it happen ... The above code, do I put that into the code where Im zooming out i.e. holding down the 'A' Key? Also how do I check if the camera position is within the playerBounds? Thanks! \$\endgroup\$
    – Craig
    Commented Nov 23, 2012 at 19:08
  • \$\begingroup\$ see my edits... \$\endgroup\$
    – Ken
    Commented Nov 23, 2012 at 19:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .