choosing which image to draw (tilemap)

BlitzMax Forums/BlitzMax Programming/choosing which image to draw (tilemap)

slenkar(Posted 2010) [#1]
when you draw a tilemap you are choosing which tile to draw for each cell of the map.

How do people store their images Map,Array?
Ive noticed my game slows down a lot when the map is on the screen and im getting paranoid that cycling through a map or array to find the right image to draw for every cell of the map might be slowing it down.

I was blaming it on the fillrate of the card but now im having doubts.


Brucey(Posted 2010) [#2]
Tile selection shouldn't be bottleneck... unless your code happens to be really inefficient.

How many tiles are you drawing per frame?


ImaginaryHuman(Posted 2010) [#3]
You definitely want to think about putting lots of tiles on a single `tilesheet` like a spritesheet, and then drawing rectangles from that image to draw your tiles. Otherwise all the texture swaps between images is a major slowdown.

Depending on your complexity most people just define a 2D array to store tile references.


slenkar(Posted 2010) [#4]
its an iso game so first i draw the floor then the back walls, then objects then the front walls. yes i could do a spritesheet i spose.

so in a busy scene im drawing about 50 tiles max

each image is represented as a string (in the mapdata) which is searched for in a Tmap to actually draw to screen.

I could create a temp variable for the last image selected and draw that unless the next image to draw is different i spose.


Brucey(Posted 2010) [#5]
each image is represented as a string (in the mapdata) which is searched for in a Tmap to actually draw to screen.

That sounds like a lot of work to find an image...

50 is nothing at all. Look at Tachyon's Book II... that seems to be drawing a lot more tiles.


slenkar(Posted 2010) [#6]
HMMM I just did that thing where i dont search the map if the image was drawn last and my FPS went from 60 to 80 - oops!


Czar Flavius(Posted 2010) [#7]
TMaps are not good for high-demand random access.

Usually I have a TTemplate type, which has the image of a cell type and its properties (such as passable) as a field. Then I have a TTile (or TCell) type, which has a template:TTemplate field, and other things such as what objects are currently in that cell.

Store your templates in an array where each has an ID number so you can find it quickly (grass=0, water=1, ..) (or you could even store in a TMap if you find that easier - it's only used once when the game loads, so won't slow it down during play)

Store your game map as a 2D array TTile[x,y]. When you create each tile give it the ID number or name as string of the type of tile it is, eg

code written from head and not tested

Const map_x_size = 64, map_y_size = 64
Const grass_id = 0, water_id = 1, tree_id = 2
Const tile_type_num = 3
Global map:TTile[,] = New TTile[map_x_size, map_y_size]

Load() 'see load function below to load templates

'make map of grass

For Local x:Int = 0 Until map_x_size
	For Local y:Int = 0 Until map_y_size
		map[x, y] = TTile.Create(grass_id)
	Next
Next

Type TTile
	Field template:TTemplate
	'...
	
	Method Draw(x:Int, y:Int)
		'look up my image from the template
		DrawImage template.image, x, y
	End Method

	Function Create:TTile(id:Int)
		Local tile:TTile = New TTile
		tile.template = TTileTemplate.Get(id)
		Return Tile
	End Function
End Type

Type TTileTemplate
	'stores all possible templates, so each image is in one location
	Global all:TTileTemplate[] = New TTileTemplate[tile_type_num]

	'the appearance of this type of cell
	Field image:TImage

	'whether characters can enter this cell
	Field passable:Int
	
	Function Get:TTileTemplate(id:Int)
		'returns the tile for this id number
		'gives a debug error if tile not found
		Assert id > 0 and id < tile_type_num
		Assert all[id] <> Null
		Return all[id]
	End Function

	Function Create:TTileTemplate(id:Int, image:TImage, passable:Int)
		'creates a new tile template for a given image
		'and stores it in the all array automatically
		Assert image <> Null
		Local template:TTileTemplate = New TTileTemplate
		template.image = image
		template.passable = passable
		all[id] = template
		Return template
	End Function
End Type

Function Load()
	'here it is hardcoded, or you could load data from text files
	TTileTemplate.Create(grass_id, LoadImage("grass.png"), True)
	TTileTemplate.Create(water_id, LoadImage("water.png"), False)
	TTileTemplate.Create(tree_id, LoadImage("tree.png"), False)
End Function



slenkar(Posted 2010) [#8]
yeah im going to do that next, the map contains strings but i can create ints when the game starts. Then the images go into an array.


Leon Drake(Posted 2010) [#9]
usually i use a layers and 2d arrays to draw tiles. Drawing off screen tiles is moot and best to avoid that.


Blueapples(Posted 2010) [#10]
I have something like this...

The idea is that image lookup for each tile is based strictly on an integer index into an array that stores each images a single time. This is very fast.

I have considered using an AnimImage to get all tiles in a single PNG file, but so far this have proved to not be needed. My rule is: Just get it done, optimize later. Separate files works fine.

Note that this code isn't tested. I can post the real thing I'm using later if this proves to not be helpful.

const tilew:int = 32
const tileh:int = 32
Type TScreen
  map:Int[24,18]

  Method Load()
    ' Usually this loads from a text file specifying image paths
    images[1] = LoadImage("data/tile1.png")
    images[2] = LoadImage("data/tile2.png")
  End Method

  Method Draw()
    Local x:int, y:int
    For x = 0 To 23
      For y = 0 To 17
        If images[screen.map[x,y]] <> Null Then DrawImage(images[screen.map[x,y]], x * tilew, y * tileh)
      Next
    Next
  End Method
EndType

Global images:TImage[255]
Global screen:TScreen = new TScreen 

screen.Load()

While Not KeyHit(KEY_ESC)
  Cls
  screen.Draw()
  Flip(1)
Wend



_Skully(Posted 2010) [#11]
The way I did this in TileMax is that each tile layer references the tileset (which can be a single image, composit image etc), the X,Y coordinates of the upper left corner and width/height of the tile. I then use drawsubimagerect to draw the image or subimage to the screen at render time.