In this post I will add a class for the camera and have the map scroll smoothly in any direction. What I mean by smoothly in any direction is that if you were to hold down the Down and Right keys the map would scroll at the same speed as if it was scrolling in just one direction.
I am going to add six integer variables to the game, four of them will be static variables. For the static variables I will add get only properties so they can be accessed in the Camera class. The first two are not static tileMapWidth and tileMapHeight are for the width and height of the map in tiles so I do not have to keep recalculating them. The next two screenWidth and screenHeight are for the width and height of the screen. These are static and will be needed in the class for the camera to lock the camera. The next two mapWidthInPixels and mapHeightInPixels are for width and height of the map in pixels. They are also static because they will be needed in the class for the camera to lock the camera. The get only properties are ScreenWidth, ScreenHeight, MapWidthInPixels and MapHeightInPixels. The first two return screenWidth and screenHeight respectively. The last two return tileMapWidthInPixels and tileMapHeightInPixels respectively. These are the variables and properties.
I will assign these variables in the LoadContent method. This is the code for the new LoadContent method.
tileMapWidth is set to the X value of the map array using the GetLength method passing in 1. tileMapHeight is set to the Y value of the map array again using the GetLength method passing in 0.
mapWidthInPixels is calculated by taking tileMapWidth and multiplying it by tileWidth which if you remember is the width of the tiles on the screen. I did the same for the mapHeightInPixels variable. I took the tileMapHeight variable and multiplied it be the tileHeight variables.
To get the screenWidth variable I use GraphicsDevice.Viewport.Width. Finally to get the screenHeight variable I use GraphicsDevice.Viewport.Height.
I have all of the variables I will need in the camera class. Right click the project name and add a new class called Camera. This is the code for the Camera class.
I will be using Vector2 to store the position of the camera and MathHelper.Clamp to lock the camera so I need a using statement for the XNA framework. There are two private variables in this class: position which is a Vector2 and speed which is a float. In the constructor I set the position to Vector2.Zero and the speed to 4.0f.
I don’t like making variables public unless I absolutely have to. The reason is if they need to be set outside of the class I want to do some sort of validation on them. So I have two get and set properties for this class Speed which is a float and is for the speed vairable. Position is for the position variable and is a Vector2. The get part of the properties just return the associated variable.
In the set part of the Speed property I use the MathHelper.Clamp method to set the minimum and maximum scroll values. I chose the minimum value to be 0.5f which is really slow and partly because you don’t want the camera speed to be anything negative.
In the set part of the Position property I use the MathHelper.Clamp method for the X and Y properties of the Vector2. For the X value I clamp the camera so that it can never be negative and it can never be set so that the background color will never show through if the right edge of the map is reached, just like in the LockCamera method I used in the last post. I did something similar for the Y value. I made sure it was never negative and made sure that the background will never show through if the bottom edge of the map is reacher.
Now it is time to implement the Camera class in the game. First thing you can do is find the cameraPositionX, cameraPositionY and cameraSpeed variables and replace them with these two variables.
The first one is of course for a Camera object. The second has probably got you confused. I will get to it in just a moment.
Now you will want to find the Scroll methods. I changed them to use the Camera class and so that they would scroll the map at a constant speed. These are the new methods.
Each of the methods sets either the X or Y values of the motion Vectore2 just as if the map was scrolling before. Positive values of X scroll the map to the right and negative values for X scroll the map left. Positive values of Y scroll the map down and negative values scroll the map up.
Now it is time to change the Update method. I will give you the code and then explain it. This the new Update method.
You will notice that I set motion to Vector2.Zero. I do this because if I was holding down a key and let go the map would continue scrolling on in that direction. I removed the else part of the if statements. This way if you are pressing down the Right and Down key the map will scroll diagonally in that direction. Pressing both the Up and Down key at the same time will not allow the map to scroll either up or down. The same is true for the Right and Left key. If you are holding both of them down the map will not scroll left or right.
There is an if statement next that checks to see if motion is not Vector2.Zero. The reason is I will be normalizing the vector. Normalizing is the process of making a vector have a length of 1 unit. This will not affect the cardinal directions Up, Down, Left or Right but it will affect any of the diagonals and keep the map from scrolling faster on the diagonals. Why would the map scroll faster on the diagonals? If you have the point (1,1) to find the distance from the origin you would use the formula:
d^2 = 1^2 + 1^2. So d^2 = 2. That translates to root 2 which is approximately 1.41 and that is greater than 1. That is why the map would scroll faster on the diagonals. After normalizing the vector I add the motion vector times the camera’s speed to the camera’s position.
I just have to make one more change before you can test this. I have to update the Draw method to use the new class. I replace cameraPositionX with (int)camera.Position.X and cameraPositionY with (int)camera.Position.Y. This is the new Draw method.
Well, that is it for this post. In the next post I will create a game library for the tile engine so if you ever need one you will just be able to add the library to your game and add in the references you need.
~Six
I am going to add six integer variables to the game, four of them will be static variables. For the static variables I will add get only properties so they can be accessed in the Camera class. The first two are not static tileMapWidth and tileMapHeight are for the width and height of the map in tiles so I do not have to keep recalculating them. The next two screenWidth and screenHeight are for the width and height of the screen. These are static and will be needed in the class for the camera to lock the camera. The next two mapWidthInPixels and mapHeightInPixels are for width and height of the map in pixels. They are also static because they will be needed in the class for the camera to lock the camera. The get only properties are ScreenWidth, ScreenHeight, MapWidthInPixels and MapHeightInPixels. The first two return screenWidth and screenHeight respectively. The last two return tileMapWidthInPixels and tileMapHeightInPixels respectively. These are the variables and properties.
int tileMapWidth;
int tileMapHeight;
static int screenWidth;
static int screenHeight;
static int mapWidthInPixels;
static int mapHeightInPixels;
public static int ScreenWidth
{
get { return screenWidth; }
}
public static int ScreenHeight
{
get { return screenHeight; }
}
public static int MapWidthInPixels
{
get { return mapWidthInPixels; }
}
public static int MapHeightInPixels
{
get { return mapHeightInPixels; }
}
I will assign these variables in the LoadContent method. This is the code for the new LoadContent method.
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
tiles.Add(Content.Load<Texture2D>("grasstile"));
tiles.Add(Content.Load<Texture2D>("dirttile"));
tiles.Add(Content.Load<Texture2D>("snowtile"));
tiles.Add(Content.Load<Texture2D>("watertile"));
tileMapWidth = map.GetLength(1);
tileMapHeight = map.GetLength(0);
mapWidthInPixels = tileMapWidth * tileWidth;
mapHeightInPixels = tileMapHeight * tileHeight;
screenWidth = GraphicsDevice.Viewport.Width;
screenHeight = GraphicsDevice.Viewport.Height;
}
tileMapWidth is set to the X value of the map array using the GetLength method passing in 1. tileMapHeight is set to the Y value of the map array again using the GetLength method passing in 0.
mapWidthInPixels is calculated by taking tileMapWidth and multiplying it by tileWidth which if you remember is the width of the tiles on the screen. I did the same for the mapHeightInPixels variable. I took the tileMapHeight variable and multiplied it be the tileHeight variables.
To get the screenWidth variable I use GraphicsDevice.Viewport.Width. Finally to get the screenHeight variable I use GraphicsDevice.Viewport.Height.
I have all of the variables I will need in the camera class. Right click the project name and add a new class called Camera. This is the code for the Camera class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
namespace SimpleTileEngine
{
class Camera
{
Vector2 position;
float speed;
public Camera()
{
this.position = new Vector2();
this.speed = 4.0f;
}
public float Speed
{
get { return speed; }
set
{
speed = MathHelper.Clamp(value, 0.5f, 50f);
}
}
public Vector2 Position
{
get { return position; }
set
{
position.X = MathHelper.Clamp(value.X,
0,
Game1.MapWidthInPixels - Game1.ScreenWidth);
position.Y = MathHelper.Clamp(value.Y,
0,
Game1.MapHeightInPixels - Game1.ScreenHeight);
}
}
public void LockCamera()
{
position.X = MathHelper.Clamp(
position.X,
0,
Game1.MapWidthInPixels - Game1.ScreenWidth);
position.Y = MathHelper.Clamp(
position.Y,
0,
Game1.MapHeightInPixels - Game1.ScreenHeight);
}
}
}
I will be using Vector2 to store the position of the camera and MathHelper.Clamp to lock the camera so I need a using statement for the XNA framework. There are two private variables in this class: position which is a Vector2 and speed which is a float. In the constructor I set the position to Vector2.Zero and the speed to 4.0f.
I don’t like making variables public unless I absolutely have to. The reason is if they need to be set outside of the class I want to do some sort of validation on them. So I have two get and set properties for this class Speed which is a float and is for the speed vairable. Position is for the position variable and is a Vector2. The get part of the properties just return the associated variable.
In the set part of the Speed property I use the MathHelper.Clamp method to set the minimum and maximum scroll values. I chose the minimum value to be 0.5f which is really slow and partly because you don’t want the camera speed to be anything negative.
In the set part of the Position property I use the MathHelper.Clamp method for the X and Y properties of the Vector2. For the X value I clamp the camera so that it can never be negative and it can never be set so that the background color will never show through if the right edge of the map is reached, just like in the LockCamera method I used in the last post. I did something similar for the Y value. I made sure it was never negative and made sure that the background will never show through if the bottom edge of the map is reacher.
Now it is time to implement the Camera class in the game. First thing you can do is find the cameraPositionX, cameraPositionY and cameraSpeed variables and replace them with these two variables.
Camera camera = new Camera();
Vector2 motion;
The first one is of course for a Camera object. The second has probably got you confused. I will get to it in just a moment.
Now you will want to find the Scroll methods. I changed them to use the Camera class and so that they would scroll the map at a constant speed. These are the new methods.
private void ScrollRight()
{
motion.X += 1;
}
private void ScrollDown()
{
motion.Y += 1;
}
private void ScrollLeft()
{
motion.X -= 1;
}
private void ScrollUp()
{
motion.Y -= 1;
}
Each of the methods sets either the X or Y values of the motion Vectore2 just as if the map was scrolling before. Positive values of X scroll the map to the right and negative values for X scroll the map left. Positive values of Y scroll the map down and negative values scroll the map up.
Now it is time to change the Update method. I will give you the code and then explain it. This the new Update method.
protected override void Update(GameTime gameTime)
{
currentState = Keyboard.GetState();
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
motion = Vector2.Zero;
if (currentState.IsKeyDown(Keys.Up))
ScrollUp();
if (currentState.IsKeyDown(Keys.Left))
ScrollLeft();
if (currentState.IsKeyDown(Keys.Down))
ScrollDown();
if (currentState.IsKeyDown(Keys.Right))
ScrollRight();
if (motion != Vector2.Zero)
{
motion.Normalize();
camera.Position += motion * camera.Speed;
}
base.Update(gameTime);
}
You will notice that I set motion to Vector2.Zero. I do this because if I was holding down a key and let go the map would continue scrolling on in that direction. I removed the else part of the if statements. This way if you are pressing down the Right and Down key the map will scroll diagonally in that direction. Pressing both the Up and Down key at the same time will not allow the map to scroll either up or down. The same is true for the Right and Left key. If you are holding both of them down the map will not scroll left or right.
There is an if statement next that checks to see if motion is not Vector2.Zero. The reason is I will be normalizing the vector. Normalizing is the process of making a vector have a length of 1 unit. This will not affect the cardinal directions Up, Down, Left or Right but it will affect any of the diagonals and keep the map from scrolling faster on the diagonals. Why would the map scroll faster on the diagonals? If you have the point (1,1) to find the distance from the origin you would use the formula:
d^2 = 1^2 + 1^2. So d^2 = 2. That translates to root 2 which is approximately 1.41 and that is greater than 1. That is why the map would scroll faster on the diagonals. After normalizing the vector I add the motion vector times the camera’s speed to the camera’s position.
I just have to make one more change before you can test this. I have to update the Draw method to use the new class. I replace cameraPositionX with (int)camera.Position.X and cameraPositionY with (int)camera.Position.Y. This is the new Draw method.
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
for (int y = 0; y < map.GetLength(0); y++)
{
for (int x = 0; x < map.GetLength(1); x++)
{
spriteBatch.Draw(tiles[map[y, x]],
new Rectangle(
x * tileWidth - (int)camera.Position.X,
y * tileHeight - (int)camera.Position.Y,
tileWidth,
tileHeight),
Color.White);
}
}
base.Draw(gameTime);
spriteBatch.End();
}
}
Well, that is it for this post. In the next post I will create a game library for the tile engine so if you ever need one you will just be able to add the library to your game and add in the references you need.
~Six
1 Comments On This Entry
Page 1 of 1
OOklatheMok
12 March 2010 - 11:51 PM
Really well done. Thanks for going the extra bit and explaining some of the underlying concepts!! Looking forward to part 4.
Page 1 of 1
← February 2022 →
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 |
My Blog Links
Recent Entries
Recent Comments
Search My Blog
1 user(s) viewing
1 Guests
0 member(s)
0 anonymous member(s)
0 member(s)
0 anonymous member(s)



1 Comments









|