0
\$\begingroup\$

I load a large tileset image in at the start of my program.

To draw the game world, I loop over a 2D array of tile IDs and based on that ID I cut out a 16x16 portion from the tileset and draw that as an image, using the 9 parameter overload of canvasContext.drawImage(). This is nice since there is only a single Image object needed.

However, I am wondering if it is better practice to, as a preprocessing step, seperate the tileset image into a bunch of different Image objects for each tile, and then just draw those. My thinking is that this would potentially be faster by saving the work done each frame to crop out portions from the larger complete tileset image.

I know the ideal approach would probably be to preprocess the tile ID array into one large image containing every tile that makes up the world, and then to just draw that. This would work for me right now, however, I may decide to change the map at runtime and then I assume the overhead of recalculating the entire 'world image' during gameplay would be too large.

Given this potential requirement, which approach would be better practice?

\$\endgroup\$
2
  • \$\begingroup\$ I know the ideal approach would probably be to preprocess the tile ID array into one large image containing every tile that makes up the world, and then to just draw that. This would work for me right now, however, I may decide to change the map at runtime and then I assume the overhead of recalculating the entire 'world image' during gameplay would be too large. \$\endgroup\$ Commented Jul 7 at 3:54
  • \$\begingroup\$ It seems to me that "would this be faster" is something you can test empirically yourself, to get an answer accurate to your specific codebase and context, without any uncertainty or Internet hearsay. \$\endgroup\$
    – DMGregory
    Commented Jul 7 at 9:52

1 Answer 1

1
\$\begingroup\$

No.

seperate the tileset image into a bunch of different Image objects

It wouldn't be faster to create a slew of separate Images: each of these would be a copy existing on a different part of the heap (memory); you'd induce more cache misses by scattering multiple image copies across the heap. Better to use the 9-parameter drawImage() to refer to one single part of the Image's heap allocation to recall each sub-image, then modify it, then copy back.

however, I may decide to change the map at runtime

Image is based on an ImageData reference which itself is based on its data property, which is a Uint8ClampedArray. This buffer can be fast-set using set:

image.data.set(subImage, startOffset);

If you want to fast-modify, you can copy out part of this Uint8ClampedArray, modify it, and write it back again using subarray:

const subimage = image.data.subarray(startOffset, endOffset); //note: creates a new buffer

//...modify copied subimage here... 

image.data.set(subImage, startOffset); //set back into the atlas

This is the fastest method Javascript alone (without WASM) offers for image manipulation, to date. For modifying the subimage pixel by pixel, see this question for tips on fastest pixelwise writes.

better ... faster

These are two different things, nearly always, when it comes to code. You need to decide which you want. If you mean "easier for the programmer" then by all means split into multiple Images. If you mean faster, definitely do not do that. The Engineering Triangle is a salient reminder of the trade-offs we make. A good engineer is always very specific in their communication with themselves -- for clarity of thought -- and with others. Choose your words wisely.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .