本系列博文翻译自以下文章
http://blog.sklambert.com/html5-canvas-game-panning-a-background/
Languages: HTML5, JavaScript
Code: https://github.com/straker/galaxian-canvas-game/tree/master/part1
1.游戏背景滚动
最终的游戏演示界面如下:
控制:移动 – (←↑↓→)箭头
射击 – 空格
<!DOCTYPE html> <html> <head> <title>Space Shooter Demo</title> <style> canvas { position: absolute; top: 0px; left: 0px; background: transparent; } </style> </head> <body onload="init()"> <!-- The canvas for the panning background --> <canvas id="background" width="600" height="360"> Your browser does not support canvas. Please try again with a different browser. </canvas> <script src="space_shooter_part_one.js"></script> </body> </html>
以上代码创建了一个600宽,360高的画布。
基础
创建一个封装全部图像的js对象:
/** * Define an object to hold all our images for the game so images * are only ever created once. This type of object is known as a * singleton. */ var imageRepository = new function() { // Define images this.background = new Image(); // Set images src this.background.src = "imgs/bg.png"; }
接下来,我们创建Drawable对象,所有以后的可以运动的物体对象都继承于它,Drawable对象包含一个空的draw方法。
/** * Creates the Drawable object which will be the base class for * all drawable objects in the game. Sets up default variables * that all child objects will inherit, as well as the default * functions. */ function Drawable() { this.init = function(x, y) { // Default variables this.x = x; this.y = y; } this.speed = 0; this.canvasWidth = 0; this.canvasHeight = 0; // Define abstract function to be implemented in child objects this.draw = function() { }; }
接下来我们创建背景
Background对象,注意红色部分的代码,红色2句代码是背景移动的核心。
第一句让背景从纵坐标0开始向下移动,第二句
让背景从纵坐标(0-画布)高度开始向下移动,这样就产生了背景在不断向下移动的效果。
最后一句蓝色代码是将Background对象的原形设置为
Drawable对象,继承
Drawable中的变量和方法。
/** * Creates the Background object which will become a child of * the Drawable object. The background is drawn on the "background" * canvas and creates the illusion of moving by panning the image. */ function Background() { this.speed = 1; // Redefine speed of the background for panning // Implement abstract function this.draw = function() { // Pan background this.y += this.speed; this.context.drawImage(imageRepository.background, this.x, this.y); // Draw another image at the top edge of the first image this.context.drawImage(imageRepository.background, this.x, this.y - this.canvasHeight); // If the image scrolled off the screen, reset if (this.y >= this.canvasHeight) this.y = 0; }; } // Set Background to inherit properties from Drawable Background.prototype = new Drawable();
最后一步
创建Game对象,Game对象获得web页面中定义的画布,初始化背景对象Background,设置背景对象的
context以及画布宽,画布高属性。
/** * Creates the Game object which will hold all objects and data for * the game. */ function Game() { /* * Gets canvas information and context and sets up all game * objects. * Returns true if the canvas is supported and false if it * is not. This is to stop the animation script from constantly * running on older browsers. */ this.init = function() { // Get the canvas element this.bgCanvas = document.getElementById('background'); // Test to see if canvas is supported if (this.bgCanvas.getContext) { this.bgContext = this.bgCanvas.getContext('2d'); // Initialize objects to contain their context and canvas // information Background.prototype.context = this.bgContext; Background.prototype.canvasWidth = this.bgCanvas.width; Background.prototype.canvasHeight = this.bgCanvas.height; // Initialize the background object this.background = new Background(); this.background.init(0,0); // Set draw point to 0,0 return true; } else { return false; } }; // Start the animation loop this.start = function() { animate(); }; }
以下是动画功能的实现,其中requestAnimFrame类似一个timer,会不定期的回调 animate()函数; animate()函数中调用
game.background.draw();不断的重绘背景图片的位置,以实现背景滚动的动画效果。
/** * The animation loop. Calls the requestAnimationFrame shim to * optimize the game loop and draws all game objects. This * function must be a gobal function and cannot be within an * object. */ function animate() { requestAnimFrame( animate ); game.background.draw(); } /** * requestAnim shim layer by Paul Irish * Finds the first API that works to optimize the animation loop, * otherwise defaults to setTimeout(). */ window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(/* function */ callback, /* DOMElement */ element){ window.setTimeout(callback, 1000 / 60); }; })();
最后启动程序:
/** * Initialize the Game and starts it. */ var game = new Game(); function init() { if(game.init()) game.start(); }
最后的运行效果如下: