Earlgray
Earlgray

Reputation: 647

How to speed up drawing tiles on canvas?

I try to write an isometric tile game engine and have problem with speed of this code:

$(function() {

var canvas = document.getElementById('GameCanvas');
var context = document.getElementById('GameCanvas').getContext('2d');

var imgObj = new Image();
imgObj.src = 'img/sand_surface.png';

var Game = {

    tileScaleX: 64,
    tileScaleY: 32,
    FPSLimit: 50, // max allowed framerate
    realFPS: 0, // real framerate

    init: function() {


        this.cycle(); // main animation loop
    },

    cycle: function() {

        this.debug(); // print framerate

        startTime = new Date; // start fps time

        this.clear(); // celar canvas       
        this.draw(); // draw frame

        endTime = new Date; // end fps time

        setTimeout(function() {
            endTimeWithSleep = new Date; // end fps time with sleep
            this.realFPS = 1000 / (endTimeWithSleep - startTime);
            this.cycle(); // repeat animation loop
        }.bind(this), (1000 / this.FPSLimit) - (endTime - startTime));
    },

    debug: function() {

        $('.DebugScreen').html('<b>FPS:</b> ' + Math.round(this.realFPS*1)/1);
    },

    clear: function() {

        canvas.width = canvas.width; // clear canvas
    },

    draw: function() {

        Location.drawSurface(); // draw tiles
    },

}

var Location = {

    width: 60,
    height: 120,

    drawSurface: function() {

        for (y = 0; y < this.height; y++) {

            for (x = 0; x < this.width; x++) {

                if ((y % 2) == 0) {
                    rowLeftPadding = 0;
                } else {
                    rowLeftPadding = Game.tileScaleX / 2;
                }

                context.drawImage(imgObj, (x * Game.tileScaleX + rowLeftPadding), y * (Game.tileScaleY / 2), Game.tileScaleX, Game.tileScaleY);
            }
        }
    },
}

Game.init(); // start game
});

If I set Location.width and Location.height to low numbers, then it run fast (50 fps) but in this example (Location.width = 60, Location.height = 120) framerate is 10 fps and I need 50 fps, do you have any sugestions how to speed up this script?

Upvotes: 3

Views: 2057

Answers (3)

Loktar
Loktar

Reputation: 35309

After messing with your code on my side Im getting between 45-50 with the code you posted. One suggestion is to not use jQuery, and also don't modify the html of an element to display the fps. I also modified your demo to max out at 100 frames, and its getting about 70 fps.

You can Also try cacheing the resized image and use that instead, you should see an increase in performance. In the below demo I cache the resized image on a temp canvas and use it instead.

Live Demo (I didnt feel like implementing an onload for the image, so if its a white screen just hit run again)

Upvotes: 0

scottheckel
scottheckel

Reputation: 9244

In addition to @jjmontes excellent answer, you should also use multiple canvas elements in your game and only update the portions of the canvas that have changed. Right now you are clearing and redrawing everything each time.

Upvotes: 0

jjmontes
jjmontes

Reputation: 26895

1) Seems to me that you are drawing every tile, even if they are not in view. Use "clipping". You need to calculate whether the tile is in view before calling context.drawImage().

2) If your scenery is static, precalculate it (as much as possible). However, creating a huge image is not a good idea either, you would rather precalculate some big chunks (i.e. 512x512).

3) In some browsers, it is said you can get better frame rates if instead of using 'setTimeout()' you use requestAnimationFrame (I also found this article quite interesting).

4) Resizing/scaling may impact performance (especially in older browser or hardware). If your tiles are already 32x64, you can use drawImage() with only 3 parameters, avoiding resizing (not applicable if you do need to scale to achieve zoom effects or similar).

Upvotes: 5

Related Questions