Monday, June 24, 2013

HTML5 Blitting Using an Offscreen Buffer



One of the great benefits HTML5 has brought us javascript game hackers is the canvas and context. Or more important, the ability to create an off screen canvas and context. This brings us one step closer to game console animation. Video games use off screen buffers for rendering. This technique provides smoother animation to the video game. The process involves rendering all the video game updates to an off screen buffer. An off screen buffer means that all the animation is being written to a buffer that is not visible.  When the game code has completed the off screen rendering, the buffer is FLIPPED to the visible video memory buffer.  Your game console and pc has hardware that handles this in video hardware. We're going to emulate this functionality using software. The reason buffer flipping is used is because it produces smoother looking animation in your video game.
This javascript tutorial is going to draw a simple background and a sprite using off screen buffer rendering and flipping. I am going to keep this simple. In the next demo we'll make the sprite walk and use multiple frames of animation. I just wanted to "keep it simple stupid" so you could get the idea of what is going on in the code. 

The Game Console Class

Over time I have been revamping the original game console code so I can use it in a library to write games. The first objective in this endeavor is to create a Game Console class, js/GameConsoleClass.js.

Source Listing 1. Defining a Game Console class in Javascript

function GameConsole(width, height) {
    this.width = width;
    this.height = height;  
    
    // create the main canvas
    this.canvas = document.createElement( 'canvas' );  
    this.canvas.width = this.width;
    this.canvas.height = this.height;
    this.context = this.canvas.getContext( '2d' );
    
    // add the canvas to the DOM
    document.body.appendChild(this.canvas);
    
    // create the offscreen buffer (canvas)
    this.offscreenCanvas = document.createElement('canvas');
    this.offscreenCanvas.width = this.width;
    this.offscreenCanvas.height = this.height;
    this.offscreenCanvas.context = this.offscreenCanvas.getContext('2d');

} // END GameConsole class


Of course we're going to need some member functions to perform the initialization and rendering.

Source Listing 2. Game Console member functions in Javascript

GameConsole.prototype = {

    initGC: function() {
        this.context.fillStyle = '#000000'; // Black
        //this.context.fillStyle = '#f0eef9'; // Lavender
        this.context.clearRect(0, 0, this.width, this.height);
        this.context.beginPath();
        this.context.rect(0, 0, this.width, this.height);
        this.context.closePath();
        this.context.fill();
    },   // END initGC
    
    // render to the offscreen canvas
    renderOffscreenGC: function(image) {
     // render the background
        this.offscreenCanvas.context.drawImage(image, 0, 0);
    
    }, // END render 
    
    bitBlitOffscreenGC: function(sprite, srcx, srcy, srcwidth, srcheight, dstx, dsty, dstwidth, dstheight) {
        
        this.offscreenCanvas.context.drawImage(sprite, srcx, srcy, srcwidth, srcheight, dstx, dsty, dstwidth, dstheight);
        
    },
    
    // flip the offscreen canvas to the visible GC canvas
    flipGC: function () {
        this.context.drawImage(this.offscreenCanvas, 0, 0);
    }, // END flipGC
    
}; // END GameConsole.prototype

Again, I kept everything really simplistic to give you a good idea of how the code works. I too have spent time in javascript books where the author's intent isn't to educate but to demonstrate how great their coding skills are. What is going on in their code is not blatantly obvious to the casual observer.

The Main Driver Code

Ok, let's draw some sprites. The source code js/MainGame.js contains the demo code to create a Game Console object in javascript and render to it. The code is logically straight forward with no sneaky coding demos. The demo code loads a background image and a sprite. That's it. Nothing fancy.



Source Listing 3. Loading and Blitting Sprites in Javascript

// create a Game Console object
var gc = new GameConsole(592,448);
gc.initGC();

// Background image
// load the image
var backgroundReady = false;
var backgroundImage = new Image();
backgroundImage.onload = function () {
    backgroundReady = true;
};
backgroundImage.src = "img/Level_1_warehouse.png";


// load the sprite
// load Sprite Sheet
var sprite = {};
var spriteSheetReady = false;
var spriteSheetImage = new Image();
spriteSheetImage.onload = function () {
    spriteSheetReady = true;
};
spriteSheetImage.src = "img/aliensvspredator_ltlinnkurosawa_sheet_sclx2.png";
var spriteWidth = spriteSheetImage.width /4;
var spriteHeight = 118;

gc.renderOffscreenGC(backgroundImage);

sprite.x = 250;
sprite.y = 250;

gc.bitBlitOffscreenGC(spriteSheetImage, 0, 144, spriteWidth, spriteHeight, sprite.x, sprite.y, spriteWidth, spriteHeight); 
gc.flipGC();

The code can be downloaded from GitHub   https://github.com/retrogamecode/tutorials.
Unfortunately, I can't upload the images there. So I will add them at the end of this article where you can grab it and download it.

Walking through the not so obvious code....

var sprite = {}; 


Creates a simple javascript object I will use for storing values for the sprite's current x (sprite.x), and y, (sprite.y) position. 

gc.renderOffscreenGC(backgroundImage);

sprite.x = 250;
sprite.y = 250;

gc.bitBlitOffscreenGC(spriteSheetImage, 0, 144, spriteWidth, spriteHeight, sprite.x, sprite.y, spriteWidth, spriteHeight); 
gc.flipGC();

In the bitBlit operation, the sprite.x and sprite.y are the destination positions I will draw the sprite. The x=0, and y =144 values for the bitBlit are values I pulled from photoshop when examining the sprite's location. It's a quick a dirty demo.

Since this is retro game coding we are blitting!!!! A bit blit is bit block transfer. We're kicking it old school here. Even though most low end PCs now have far more computing power than the CRAY computer of the early 90s. Anyhow, video game consoles of ages since past had hardware blitters in them for this operation. We're doing it in our software.

When the blit is completed -we FLIP the offscreen buffer to the visible buffer with the class method,

gc.flipGC();


Play around with the code and the sprites.


Source Listing 4. The HTML source file index.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>RetroGameCode</title>
    </head>
    <h1>Game Console Offscreen Tutorial</h1>
        
        <script src="js/GameConsoleClass.js"></script>
        <script src="js/MainGame.js"></script>
    <body>
</html>

That's it for this demo. Have fun coding! Next venture we will move the sprites and do cell animation!



img/aliensvspredator_ltlinnkurosawa_sheet_sclx2.png

img/Level_1_warehouse.png

No comments:

Post a Comment