Reputation: 3783
I am attempting to create a 2d scrolling XNA game as a learning exercise, but have run into some issues with the scrolling background. I am loading a level from a text file, parsing through to create the appropriate tiles and store them in a matrix (tiles[,]).
I then have an update method which alters the position of the tile so when it is redrawn it will move.
Currently, I loop through all tiles to draw them all before moving. This is clearly not very efficient. Ideally, I only want to draw the tiles on the screen. I can do this by taking the viewport and using the height/width of a tile to determine how many tiles will fit on the screen and only loop through those tiles, as follows:
private void DrawTiles(SpriteBatch spriteBatch)
{
float tileWidth = 40;
float tileHeight = 32;
for (int y = 0; y < (int)Math.Ceiling(mViewport.Height / tileHeight); ++y)
{
for (int x = 0; x < (int)Math.Ceiling(mViewport.Width / tileWidth); ++x)
{
tiles[x, y].Draw(spriteBatch);
}
}
}
However, this only draws the iles in the original viewport. Tiles outside will never be drawn even though their position does come into view.
I think this can be resolved by using a counter to start and end the loop, incrementing it each time the draw method is called. However, I do not think this is a great solution, but alas I cannot think of a better way to ensure only tiles in the viewport are drawn.
Upvotes: 3
Views: 1614
Reputation: 701
What I implemented in my design was an inherited interface that contained the properties IsInView and a collision property or in your case a position could be substituted. I then created a seperate thread that would loop through and determine if the object is in view. you can then in each object have the InView and OutView add and remove it from a draw list.
Run from seperate thread with a loop. -- This could be adapted to determine if the tile is visible
public void CalculateObjsInView()
{
foreach (Obj o in new List<Obj>(ObjInstanceList))
{
if (o == null)
continue;
if (Camera.CollisionMask.Intersects(o.CollisionMask))
o.IsInView = true;
else
o.IsInView = false;
}
}
In Obj class
private bool _isInView = false;
public bool IsInView
{
get { return _isInView; }
set
{
if (_isInView != value)
{
if (value)
InView();
else
OutView();
}
_isInView = value;
}
}
private void InView()
{
Game.ObjectDrawEventHandler.Add(Draw);
}
private void OutView()
{
Game.ObjectDrawEventHandler.Remove(Draw);
}
Upvotes: 0
Reputation: 32258
You need to keep track of the starting X
and Y
of the ViewPort, as you're always starting at 0
in your example. e.g.
var startX = 10; // Should increment as viewport scrolls
var startY = 10; // Should increment as viewport scrolls
...
for (int y = startY; y < (int)Math.Ceiling(mViewport.Height / tileHeight); ++y)
{
for (int x = startX; x < (int)Math.Ceiling(mViewport.Width / tileWidth); ++x)
{
tiles[x, y].Draw(spriteBatch);
}
}
On a side note, your ViewPort probably has a Top and Left or X and Y to keep track of this as well. In that case, replace startX
and startY
with the appropriate property from your ViewPort.
Upvotes: 0