在Canvas元素上实现视差滚动
http://www.brighthub.com/internet/web-development/articles/40511.aspx
视差滚动是在2D应用中创造立体纵深感的一种技术。这篇文章就来看一看在我们刚刚创建的游戏框架基础上实现视差滚动有多容易。
视差滚动
有了游戏框架,就可以通过画布元素来做一些好玩的东西了。
视差滚动指的是屏幕上的几个图层发生相对位移的效果,换句话说,背景图层滚动得比它前面的那些图层要慢一些。这种创造视觉纵深感的技术在2D游戏中的应用极为普遍。
RepeatingGameObject.js
05 | function RepeatingGameObject() |
18 | this .scrollFactor = 1; |
24 | this .startupRepeatingGameObject = function (image, x, y, z, width, height, scrollFactor) |
26 | this .startupVisualGameObject(image, x, y, z); |
29 | this .scrollFactor = scrollFactor; |
36 | this .shutdownstartupRepeatingGameObject = function () |
38 | this .shutdownVisualGameObject(); |
49 | this .draw = function (dt, canvas, xScroll, yScroll) |
51 | var areaDrawn = [0, 0]; |
53 | for ( var y = 0; y < this .height; y += areaDrawn[1]) |
55 | for ( var x = 0; x < this .width; x += areaDrawn[0]) |
58 | var newPosition = [ this .x + x, this .y + y]; |
60 | var newFillArea = [ this .width - x, this .height - y]; |
63 | var newScrollPosition = [0, 0]; |
64 | if (x==0) newScrollPosition[0] = xScroll * this .scrollFactor; |
65 | if (y==0) newScrollPosition[1] = yScroll * this .scrollFactor; |
66 | areaDrawn = this .drawRepeat(canvas, newPosition, newFillArea, newScrollPosition); |
71 | this .drawRepeat = function (canvas, newPosition, newFillArea, newScrollPosition) |
74 | var xOffset = Math.abs(newScrollPosition[0]) % this .image.width; |
75 | var yOffset = Math.abs(newScrollPosition[1]) % this .image.height; |
76 | var left = newScrollPosition[0]<0? this .image.width-xOffset:xOffset; |
77 | var top = newScrollPosition[1]<0? this .image.height-yOffset:yOffset; |
78 | var width = newFillArea[0] < this .image.width-left?newFillArea[0]: this .image.width-left; |
79 | var height = newFillArea[1] < this .image.height-top?newFillArea[1]: this .image.height-top; |
82 | canvas.drawImage( this .image, left, top, width, height, newPosition[0], newPosition[1], width, height); |
84 | return [width, height]; |
87 | RepeatingGameObject.prototype = new VisualGameObject(); |
这个RepeatingGameObject类可以让图像在一个确定的区域内重复和滚动。此前,我们已经实现了绘制整幅图像。而RepeatingGameObject的不同之处在于,它拿到一幅图像并用它来填充一块范围既定的区域(其尺寸与绘制的图像无关)。我们通过这个类每次显示一幅大图像(如一座山的全景图)的一小部分,从而创建出一个背景。
也许你已经注意到了GameObjectManager的xScroll和yScroll属性,它们被传递给了GameObject的draw和updata函数。这两个值定义的是摄像机沿x轴或y轴移动了多远。RepeatingGameObject使用这两个值来它们显示的纹理,以创造移动的假象。
首先,定义RepeatingGameObject要绘制的区域。底层的GameObject类的x和y属性定义了左上角位置,而新的width和height属性定义的是绘制区域。
而scrollFactor属性用于改变RepeatingGameObject滚动的量,该变化是通过传递到draw函数的xScroll和yScroll来控制的。将scrollFactor设置为小于1的值,会导致滚动变慢,从而造就画面中的远景。
最后两个draw和drawRepeat函数具体负责渲染贴片及偏移的纹理。
09 | this .draw = function (dt, canvas, xScroll, yScroll) |
11 | var areaDrawn = [0, 0]; |
13 | for ( var y = 0; y < this .height; y += areaDrawn[1]) |
15 | for ( var x = 0; x < this .width; x += areaDrawn[0]) |
18 | var newPosition = [ this .x + x, this .y + y]; |
20 | var newFillArea = [ this .width - x, this .height - y]; |
23 | var newScrollPosition = [0, 0]; |
24 | if (x==0) newScrollPosition[0] = xScroll * this .scrollFactor; |
25 | if (y==0) newScrollPosition[1] = yScroll * this .scrollFactor; |
26 | areaDrawn = this .drawRepeat(canvas, newPosition, newFillArea, newScrollPosition); |
31 | this .drawRepeat = function (canvas, newPosition, newFillArea, newScrollPosition) |
34 | var xOffset = Math.abs(newScrollPosition[0]) % this .image.width; |
35 | var yOffset = Math.abs(newScrollPosition[1]) % this .image.height; |
36 | var left = newScrollPosition[0]<0? this .image.width-xOffset:xOffset; |
37 | var top = newScrollPosition[1]<0? this .image.height-yOffset:yOffset; |
38 | var width = newFillArea[0] < this .image.width-left?newFillArea[0]: this .image.width-left; |
39 | var height = newFillArea[1] < this .image.height-top?newFillArea[1]: this .image.height-top; |
42 | canvas.drawImage( this .image, left, top, width, height, newPosition[0], newPosition[1], width, height); |
44 | return [width, height]; |
ApplicationManager.js
05 | function ApplicationManager() |
11 | this .startupApplicationManager = function () |
13 | this .startupGameObject(); |
14 | this .background3 = new RepeatingGameObject().startupRepeatingGameObject(g_back2, 0, 100, 3, 600, 320, 1); |
15 | this .background2 = new RepeatingGameObject().startupRepeatingGameObject(g_back1, 0, 100, 2, 600, 320, 0.75); |
16 | this .background = new RepeatingGameObject().startupRepeatingGameObject(g_back0, 0, 0, 1, 600, 320, 0.5); |
27 | this .update = function ( dt, context, xScroll, yScroll) |
29 | g_GameObjectManager.xScroll += 50 * dt; |
32 | ApplicationManager.prototype = new GameObject |
在这里,我们通过ApplicationManager创建了三个RepeatingGameObject类的实例,每个实例分别显示为一个图层,使用z(深度)和scrollFactor值来创造RepeatingGameObject 渐远和渐慢的效果。
最终结果很不错。视差滚动为画布赋予了完美的纵深感,而整个效果只多编写了一个类就实现了。
看一看视差滚动的Demo吧。http://webdemos.sourceforge.net/jsplatformer4/jsplatformer4.html