08-在运行时创建动态的图片表
9.2 在运行时创建动态的图片表
在第4章中,本书简要介绍了两条原则,可以不用在图片表中预先创建物体的旋转。创建这种类型的图片表是一项繁重的工作,可以将宝贵的时间用于项目的其他地方。
此处的想法是,使用游戏对象的一张图片(例如中型陨石的第一张图片),在运行时动态生成“动态的图片表”并将它存储在数组中。这样就不用预先绘制旋转的图片了。
为了实现这个想法,需要使用第二个画布,并使用Canvas的getImageData()和putImage Data()方法。请读者回忆第4章的内容:如果HTML页面没有在网页服务器上调用getImageData(),函数将抛出一个安全异常错误。
目前,如果从本地文件系统打开文件,只有Safari浏览器不会抛出这个错误。因为这个原因,需要在Geo Blaster扩展版游戏中将这个功能分离出来。下面简单演示一下如何使用这种预渲染的方法替代在游戏中使用全部图片表的方法。
首先在HTML页面中创建两个
<body>
<div>
<canvas id="canvas" width="256" height="256" style="position: absolute; top:
50px; left: 50px;">
Your browser does not support HTML5 Canvas.
</canvas>
<canvas id="canvas2" width="32" height="32" style="position: absolute; top:
256px; left: 50px;">
Your browser does not support HTML5 Canvas.
</canvas>
</div>
</body>
第一个
第二个
需要使用JavaScript为每个
var theCanvas = document.getElementById("canvas");
var context = theCanvas.getContext("2d");
var theCanvas2 = document.getElementById("canvas2");
var context2= theCanvas2.getContext("2d");
需要使用Geo Blaster扩展版游戏中的mediumrocks.png文件(见图9-9)作为动态图片表的源。读者无须感到困惑,此处不会用到图片表上的全部5张图片,仅使用第一张图片。
在Geo Blaster扩展版中,使用全部5张图片创建了模拟旋转的动画。在这里仅使用第一张图片。将第一张图片旋转10°绘制在Canvas2上,然后使用画布的imageData属性将当前的像素复制到一个由imageData实例组成的数组中,数组名是rotationImageArray。
然后,在一个循环中重复这个步骤,每次将theCanvas2多旋转10°,直到得到代表中型陨石旋转动画的36个独立的imageData帧,保存在这些对象数组中。
var rotationImageArray = [];
var animationFrame = 0;
var tileSheet = new Image();
tileSheet.addEventListener('load', eventSheetLoaded , false);
tileSheet.src = "mediumrocks.png";
rotationImageArray用于保存生成的imageData对象实例。在theCanvas2上通过使用旋转形状变换生成这些imageData。
当在第一个画布theCanvas上演示动画时,需要重新将rotationImageArray中的旋转动画帧显示在上面,此时需要用到animationFrame。
当加载tileSheet后,调用eventSheetLoaded()函数,并在其中调用startup()函数。在startup()函数中,通过一个循环创建36个动画帧。
function startUp(){
for (var ctr=0;ctr<360;ctr+=10){
context2.fillStyle = "#ffffff";
context2.fillRect(0,0,32,32);
context2.save();
context2.setTransform(1,0,0,1,0,0)
var angleInRadians = ctr * Math.PI / 180;
context2.translate(16, 16);
context2.rotate(angleInRadians);
context2.drawImage(tileSheet, 0, 0,32,32,-16,-16,32,32);
context2.restore();
var imagedata = context2.getImageData(0, 0, 32, 32)
rotationImageArray.push(imagedata);
}
setInterval(drawScreen, 100 );
}
这个循环首先使用白色清空theCanvas2,并将它保存在栈中。然后,将画布平移到对象的中心,使用当前ctr值旋转画布(ctr值每次增加10)。接下来,绘制mediumrocks.png中的第一张图片,然后调用getImageData()函数将结果保存在一个名为imageData的新的局部变量中。
提示
如果图片的域名和HTML文件的域名不一样,那么此处会抛出安全错误。在本地计算机上(从文件系统运行页面,而不是从本地网页服务器上),除Safari之外的所有浏览器都会抛出错误。
最后,将新的imageData放入rotationImageArray。当循环结束后,将每隔100ms调用一次drawScreen()函数。
为了在第一个画布上显示动画,在计时器循环的间断中调用putImageData()函数顺序绘制每一帧,以此来模拟动画效果。使用图片表时,并没有使用全部36帧动画,而仅使用了5帧。显然,使用更多的帧可以使动画更加平滑。但是,本例的处理方式展示了在运行时,创建简单的变形动画比预先准备图片文件要容易得多。
function drawScreen(){
//context.fillStyle = "#ffffff";
//context.fillRect(50,50,32,32);
context.putImageData(rotationImageArray[animationFrame],50,50);
animationFrame++;
if (animationFrame ==rotationImageArray.length){
animationFrame=0;
}
}
例9-1中给出了全部代码。
例9-1 动态图片表的示例
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH9EX2: Canvas Copy</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded(){
canvasApp();
}
function canvasSupport (){
return Modernizr.canvas;
}
function canvasApp(){
if (!canvasSupport()){
return; }else{
var theCanvas = document.getElementById("canvas");
var context = theCanvas.getContext("2d");
var theCanvas2 = document.getElementById("canvas2");
var context2= theCanvas2.getContext("2d");
}
var rotationImageArray = [];
var tileSheet = new Image();
var animationFrame = 0;
tileSheet.addEventListener('load', eventSheetLoaded , false);
tileSheet.src = "mediumrocks.png";
function eventSheetLoaded(){
startUp();
}
function startUp(){
//context.drawImage(tileSheet, 0, 0);
//context2.drawImage(theCanvas, 0, 0,32,32,0,0,32,32);
for (var ctr=0;ctr<360;ctr+=10){
context2.fillStyle="#ffffff";
context2.fillRect(0,0,32,32);
context2.save();
context2.setTransform(1,0,0,1,0,0)
var angleInRadians = ctr * Math.PI / 180;
context2.translate(16, 16);
context2.rotate(angleInRadians);
context2.drawImage(tileSheet, 0, 0,32,32,-16,-16,32,32);
context2.restore();
var imagedata = context2.getImageData(0, 0, 32, 32);
rotationImageArray.push(imagedata);
}
setInterval(drawScreen, 100 );
}
function drawScreen(){
//context.fillStyle="#ffffff";
//context.fillRect(50,50,32,32);
context.putImageData(rotationImageArray[animationFrame],50,50);
animationFrame++;
if (animationFrame ==rotationImageArray.length){
animationFrame = 0;
}
}
}
</script>
</head>
<body>
<div>
<canvas id="canvas" width="256" height="256" style="position: absolute; top:
50px; left: 50px;">
Your browser does not support the HTML 5 Canvas.
</canvas>
<canvas id="canvas2" width="32" height="32" style="position: absolute; top:
256px; left: 50px;">
Your browser does not support HTML5 Canvas.
</canvas>
</div>
</body>
</html>
在本章的其余部分中,本书将使用一些第4章讨论过的技术创建一个简单的基于区块的游戏。