D3DXSprite - Rendering tile engine larger than screen

Started by
3 comments, last by MasterWorks 19 years, 1 month ago
I'm creating a tile engine for my FF2/FF3 style RPG in C# using D3D9 and D3DXSprite and need advice on rendering maps larger than the screen. For example, I have a 50x50 map of tiles and the screen fits 20x15. I use camera offsets for smooth scrolling and I need to limit drawing the map to only the visible area. Before I started D3D, I used DirectDraw and rendered the whole map to an offscreen surface then copied screen-sized chunks based on the current screen position and rendered to the backbuffer. In D3D, I would need to render the whole map each frame, instead of just once, which would be inefficient. Instead, I'm guessing I should determine a range of the map to draw, maybe screen-sized plus a tile on each side for scrolling. I'm wondering which way is the most efficient/common.
Advertisement
The sprite interface takes a rectangular area of the texture
------------------------See my games programming site at: www.toymaker.info
Are you suggesting I render my whole map to a texture similar to what I did in DirectDraw? And then use that texture as the source for D3DXSprite? Otherwise I don't have a pre-rendered surface/texture of my map, I'm just rendering the whole thing each frame.

I'd rather not keep a giant texture/surface of my map in memory and just render what's currently on the screen. Currently, I use the tileset texture as the source and a single tile dimension (32x32) as the rectangle. Here's some source.

public class Tile{    int srcX, srcY;    int destX, destY;    public int SrcX    {        get { return srcX; }        set { srcX = value; }    }    public int SrcY    {        get { return srcY; }        set { srcY = value; }    }    public int DestX    {        get { return destX; }        set { destX = value; }    }    public int DestY    {        get { return destY; }        set { destY = value; }    }}


public class Map{    int width, height;    Tile[,] tiles;    public Map    {        width = 35;        height = 35;        // fill tile array in nested loop        // ...    }    public void Render(Sprite sprite, Camera camera)    {        for (int i = 0; i < width; ++i)        {            for (int j = 0; j < height; ++j)            {                Rectangle r = new Rectangle(tiles[i, j].SrcX, tiles[i, j].SrcY, 32, 32);                Vector3 v = new Vector3(tiles[i, j].DestX - camera.X, tiles[i, j].DestY - camera.Y, 0);                sprite.Draw(tileset, r, Vector3.Empty, v, Color.White);            }        }    }}


So you see, each frame, I'm rendering 35x35 tiles. I need to clip the extra tiles that are off screen somehow. One idea I have is to change the render nested loops to 20x15 (screen size) and somehow figure out the part of the map to render based on camera position. I want to know the best way to solve this problem.
You can have textures, for some cards, as big as 2048x2048 pixels. All you have to do is define a rectangle on the source texture. Look at the Draw() call.

Edit: ok pardon me, I misread your reply. If you want to conserve memory, just have a number of small textures, keep track of what should be onscreen with a world coordinate.

Say your world starts at 0,0. Then lets say your tile view surface is 10x10.
Just draw:

for(height = world_coord_y; height < (world_coord_y + 10); ++y)
for(width = world_coord_x; width < (world_coord_x +10); ++x)
Draw()


Let me see if I can find some source.

Here is some old DDraw code, but algorithm is still relevant.

//pixel positions for blt	int BLT_X  = 1;	int BLT_Y  = 1;	int BLT_X2 = 64;	int BLT_Y2 = 64;	TILE_INFO  TEMP;	for(int height = Y_AXIS; height < (Y_AXIS + 9); ++height)	{		for(int width = X_AXIS; width < (X_AXIS + 16); ++width)		{									//Blit World Array			TEMP = WORLD[width][height];		 			BltObject(TEMP.surface_tile,		        BLT_X,BLT_Y,BLT_X2,BLT_Y2);					        BLT_X  += 64;			BLT_X2 += 64;		} //end width for		BLT_X   = 1;		BLT_Y  += 64;		BLT_X2  = 64;		BLT_Y2 += 64;	} //end height for


You are on the right track. Certainly you should only draw the relevant parts of the map each frame (this is not hard to 'figure out': keep track of the camera position and draw a full screen size plus maybe one extra tile in each direction.) You don't want to draw the whole map and then blit only the relevant part.

The benefits of drawing only the relevent region each frame are many: you don't use anywhere near as much video memory, so your maximum map size can be 1000x1000 or higher and only require a tile index for each square. Since you're redrawing every frame, your tiles can even be animated, dynamic, etc. and you don't have to do any extra work. Assuming your hardware is fast enough (and it better be!) the 'redraw-every-frame' approach is much easier and more fun to program. 10+ years ago I did things the 'other way' and dreamed of the day when there was enough graphics power to do it the right way.

This topic is closed to new replies.

Advertisement