| 
 DirectDraw: 
  Tile Engine Example 
By: Jack Hoxley 
Written: June 2000 
Download: 
  
  DD_Tiles.Zip 
  (15kb)  
 
Despite the fact that this is 
  extremely simple, I often get emails from people wanting to know how. Either 
  it isn't acually easy or there are some lazy people around (you choose). Because 
  you all seem interested in this aspect of graphics; this tutorial will show 
  you how to knock up a quick 2D tile engine. 
Tiling is often used for generating 
  landscapes in 2D - each tile will represent a different type of land; ie, Sea, 
  Grass, Rock or Sand. Although 3D has taken over with dramatic landscaping techniques, 
  it is still possible to create a dynamic and interesting landscape in 2D. The 
  main advantages being that is is extremely easy, every computer can do it (and 
  get the same output), and compared with 3D it is quite fast. 
The main consideration when using 
  a 2D tile engine is the tile size. If they are too big there will be a lack 
  of detail, if they are too small they'll slow the game down. Also, to keep things 
  simple, they should be a multiple of the screen size. ie, X many tiles will 
  fit into the width of the screen exactly Y many times. Use this simple reference 
  table: 
   
    | Screen Resolution | 
    Tile Dimensions | 
    Number of 
      Blits required to fill screen. | 
   
   
    | 640x480 | 
    5x5 | 
    128 tiles wide, 
      96 tiles high = 12,288 tiles to be drawn  | 
   
   
    |   | 
    10x10 | 
    64 tiles wide, 
      48 tiles high = 3062 tiles to be drawn  | 
   
   
    |   | 
    16x16 | 
    40 tiles wide, 
      30 tiles high = 1200 tiles to be drawn  | 
   
   
    |   | 
    32x32 | 
    20 tiles wide, 
      15 tiles high = 300 tiles to be drawn  | 
   
   
    | 800x600 | 
    5x5 | 
    160 tiles wide, 
      120 tiles high = 19,200 tiles to be drawn  | 
   
   
    |   | 
    10x10 | 
    80 tiles wide, 
      60 tiles high = 4800 tiles to be drawn  | 
   
   
    |   | 
    16x16 | 
    50 tiles wide, 
      37.5 tiles high = 1850 tiles to be drawn  | 
   
   
    |   | 
    32x32 | 
    25 tiles wide, 
      18.75 tiles high = 468 tiles to be drawn  | 
   
   
    | 1024x768 | 
    5x5 | 
    204.8 tiles 
      wide, 153.6 tiles high = 31,212 tiles to be drawn  | 
   
   
    |   | 
    10x10 | 
    102.4 tiles 
      wide, 76.8 tiles high = 7752 tiles to be drawn  | 
   
   
    |   | 
    16x16 | 
    64 tiles wide, 
      48 tile high = 3062 tiles to be drawn  | 
   
   
    |   | 
    32x32 | 
    32 tiles wide, 
      24 tiles high = 768 tiles to be drawn  | 
   
 
Note that in some resolutions 
  some tile size don't fit in exactly; this only means that you'll have a few 
  pixels of free space at the edges. I have marked on the table a red 
  dot for those sizes to avoid, and a green 
  dot for those that are good choices, yellow 
  dots are choices that should only be used if necessary. 
This table is irrelevent if you 
  aren't using any of these resolutions, or if you are using windowed mode; but 
  the formulas are the same; and as a general rule: Above 1000 tiles and below 
  5000 tiles will give you good speed to looks ratio. 
Tiling is extremely simple; and 
  there are two main ways it can be achieved. 
  - At runtime, render the map 
    onto a single surface - therefore only requiring one render. If done this 
    way, the table above is fairly irrelevent. However, it may well prove to be 
    difficult to do Z-Ordering and overlaps.
 
  - At runtime render the map 
    on each loop. This can seriously cut down the frame rate, even on the faster 
    computers. With some clever optimisation it can still be done with a reasonable 
    frame rate. This allows you to animate, alpha blend, do transparencies all 
    with great ease.
 
 
We will be using a very simple 
  loop to render the map; this can be called either at runtime or before hand: 
                                        
                                          
                                          
                                            
                                              
                                                
                                                  
                                                    
                                                      
                                                        
                                                          
                                                            
                                                              
                                                                
                                                                  
                                                                    
                                                                      
                                                                        |  
       Sub RenderMap(NumTilesX 
        as integer,NumTilesY as integer, TileWidth as integer,TileHeight as integer 
        _ 
        _ TileSourcesurf as DirectDrawSurface7, TileDestSurf as DirectDrawSurface7) 
      Dim X as integer, Y as 
        integer, r as RECT, retVal as long 
         
        For X = 0 to NumTilesX 
        For Y = 0 to NumTilesY 
        'Create our Rectangle. 
        r.Left = 'Left coordinate for Tile on source surface 
        r.Top = 'Top coordinate for Tile on Destination surface 
        r.Right = r.Left + TileWidth 
        r.Bottom = r.Top + TileHeight 
        'This is where we copy the tile from the source 
        to the destination 
        retVal = TileDestSurf.BltFast(int(X * TileWidth), int(Y * TileHeight), 
        TileSourceSurf, r, DDBLTFAST_WAIT) 
        Next Y 
        Next X 
      End Sub  
                                                                         | 
                                                                       
                                                                    
                                                                   
                                                                 | 
                                                               
                                                            
                                                           
                                                         | 
                                                       
                                                    
                                                   
                                                 | 
                                               
                                            
                                           
                                          
                                         
That will now render a tiled 
  map. There are several things that could be changed to speed this up if it were 
  to be done on every loop: 
  - Remove the Multiplications. 
    There are 6 different maths functions in the above code. This could possibly 
    be cut down. Using Multiplication and/or division is quite costly on a loop 
    basis. The easiest way that this can be done is by using a lookup table. On 
    the first loop we calculate ALL the coordinates and store them in an array, 
    then on subsequent loops we just look at this array and the calculation will 
    be done; this is known as a lookup table.
 
  - Only Draw if necessary. This 
    is the simplest way of speeding things up. The Fruitworld 
    game does this, all the tiles are represented by a number in an array, if 
    this array has changed since the last loop it will draw it, otherwise it moves 
    onto the next iteration. This can easily be combined with the lookup table 
    suggested above.
 
 
The other aspect of using tiles 
  is actually drawing them. The most important thing is that they join together 
  perfectly - and you cant see the joins between them, this is called a seamless 
  pattern, and you can use PaintShop Pro to generate one for you. The next aspect 
  is what they look like; although detail is important, there is a fine line. 
  Assuming that you are drawing generic landscape (Grass, Sand, Water) - you dont 
  want the user's eye to be focused on it; the grass, water and sand are the background, 
  not the foreground. 
You can download a working example 
  fromt the top of this page, or you can get it from the downloads 
  page. 
                                       |