原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element

1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、写一个游戏框架(一)
5、写一个游戏框架(二)
6、通过Canvas实现视差滚动
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单

在Canvas元素上实现视差滚动

http://www.brighthub.com/internet/web-development/articles/40511.aspx

视差滚动是在2D应用中创造立体纵深感的一种技术。这篇文章就来看一看在我们刚刚创建的游戏框架基础上实现视差滚动有多容易。

视差滚动

有了游戏框架,就可以通过画布元素来做一些好玩的东西了。

视差滚动指的是屏幕上的几个图层发生相对位移的效果,换句话说,背景图层滚动得比它前面的那些图层要慢一些。这种创造视觉纵深感的技术在2D游戏中的应用极为普遍。

RepeatingGameObject.js


/**
    这个类可以重复显示纹理图像,支持纹理图像在x轴或y轴偏移
    @class
*/
function RepeatingGameObject()
{
    /** 最终图像占据的宽度
 	@type Number
    */
    this.width = 0;
    /** 最终图像占据的高度
    	@type Number
    */
    this.height = 0;
    /** 绘制时应用多少scrollX和scrollY
	@type Number
    */
    this.scrollFactor = 1;

    /**
        初始化对象
        @return 对初始化对象的引用
    */
    this.startupRepeatingGameObject = function(image, x, y, z, width, height, scrollFactor)
    {
        this.startupVisualGameObject(image, x, y, z);
        this.width = width;
        this.height = height;
        this.scrollFactor = scrollFactor;
        return this;
    }

    /**
        清理对象
    */
    this.shutdownstartupRepeatingGameObject = function()
    {
        this.shutdownVisualGameObject();
    }

	/**
        把当前元素绘制到后台缓冲
        @param dt 自上一帧绘制起经过的秒数
		@param context 绘制上下文
		@param xScroll x轴的全局滚动值
		@param yScroll y轴的全局滚动值

    */
    this.draw = function(dt, canvas, xScroll, yScroll)
    {
        var areaDrawn = [0, 0];

        for (var y = 0; y < this.height; y += areaDrawn[1])
        {
            for (var x = 0; x < this.width; x += areaDrawn[0])
            {
                // 绘制下一张贴片左上角的点
		var newPosition = [this.x + x, this.y + y];
		// 剩余的绘制空间
                var newFillArea = [this.width - x, this.height - y];
		// 第一次必须从图像的中央开始绘制
		// 后续贴片从上方或左侧绘制
                var newScrollPosition = [0, 0];
                if (x==0) newScrollPosition[0] = xScroll * this.scrollFactor;
                if (y==0) newScrollPosition[1] = yScroll * this.scrollFactor;
                areaDrawn = this.drawRepeat(canvas, newPosition, newFillArea, newScrollPosition);
            }
        }
    }

    this.drawRepeat = function(canvas, newPosition, newFillArea, newScrollPosition)
    {
        // 找到重复绘制纹理图像的起点(左上角)
        var xOffset = Math.abs(newScrollPosition[0]) % this.image.width;
        var yOffset = Math.abs(newScrollPosition[1]) % this.image.height;
        var left = newScrollPosition[0]<0?this.image.width-xOffset:xOffset;
        var top = newScrollPosition[1]<0?this.image.height-yOffset:yOffset;
        var width = newFillArea[0] < this.image.width-left?newFillArea[0]:this.image.width-left;
        var height = newFillArea[1] < this.image.height-top?newFillArea[1]:this.image.height-top;

        // 绘制图像
        canvas.drawImage(this.image, left, top, width, height, newPosition[0], newPosition[1], width, height);

        return [width, height];
    }
}
RepeatingGameObject.prototype = new VisualGameObject();

这个RepeatingGameObject类可以让图像在一个确定的区域内重复和滚动。此前,我们已经实现了绘制整幅图像。而RepeatingGameObject的不同之处在于,它拿到一幅图像并用它来填充一块范围既定的区域(其尺寸与绘制的图像无关)。我们通过这个类每次显示一幅大图像(如一座山的全景图)的一小部分,从而创建出一个背景。

也许你已经注意到了GameObjectManager的xScroll和yScroll属性,它们被传递给了GameObject的draw和updata函数。这两个值定义的是摄像机沿x轴或y轴移动了多远。RepeatingGameObject使用这两个值来它们显示的纹理,以创造移动的假象。

首先,定义RepeatingGameObject要绘制的区域。底层的GameObject类的x和y属性定义了左上角位置,而新的width和height属性定义的是绘制区域。


    /** 最终图像占据的宽度
 	@type Number
    */
    this.width = 0;
    /** 最终图像占据的高度
    	@type Number
    */
    this.height = 0;
    /** 绘制时应用多少scrollX和scrollY
	@type Number
    */

而scrollFactor属性用于改变RepeatingGameObject滚动的量,该变化是通过传递到draw函数的xScroll和yScroll来控制的。将scrollFactor设置为小于1的值,会导致滚动变慢,从而造就画面中的远景。


    /** 绘制时应用多少scrollX和scrollY
	@type Number
    */
    this.scrollFactor = 1;

最后两个draw和drawRepeat函数具体负责渲染贴片及偏移的纹理。


	/**
        把当前元素绘制到后台缓冲
        @param dt 自上一帧绘制起经过的秒数
		@param context 绘制上下文
		@param xScroll x轴的全局滚动值
		@param yScroll y轴的全局滚动值

    */
    this.draw = function(dt, canvas, xScroll, yScroll)
    {
        var areaDrawn = [0, 0];

        for (var y = 0; y < this.height; y += areaDrawn[1])
        {
            for (var x = 0; x < this.width; x += areaDrawn[0])
            {
                // 绘制下一张贴片左上角的点
		var newPosition = [this.x + x, this.y + y];
		// 剩余的绘制空间
                var newFillArea = [this.width - x, this.height - y];
		// 第一次必须从图像的中央开始绘制
		// 后续贴片从上方或左侧绘制
                var newScrollPosition = [0, 0];
                if (x==0) newScrollPosition[0] = xScroll * this.scrollFactor;
                if (y==0) newScrollPosition[1] = yScroll * this.scrollFactor;
                areaDrawn = this.drawRepeat(canvas, newPosition, newFillArea, newScrollPosition);
            }
        }
    }

    this.drawRepeat = function(canvas, newPosition, newFillArea, newScrollPosition)
    {
        // 找到重复绘制纹理图像的起点(左上角)
        var xOffset = Math.abs(newScrollPosition[0]) % this.image.width;
        var yOffset = Math.abs(newScrollPosition[1]) % this.image.height;
        var left = newScrollPosition[0]<0?this.image.width-xOffset:xOffset;
        var top = newScrollPosition[1]<0?this.image.height-yOffset:yOffset;
        var width = newFillArea[0] < this.image.width-left?newFillArea[0]:this.image.width-left;
        var height = newFillArea[1] < this.image.height-top?newFillArea[1]:this.image.height-top;

        // 绘制图像
        canvas.drawImage(this.image, left, top, width, height, newPosition[0], newPosition[1], width, height);

        return [width, height];
    }   

ApplicationManager.js


/**
    ApplicationManager用于管理应用
    @class
*/
function ApplicationManager()
{
    /**
        初始化对象
        @return A 对初始化对象的引用
    */
    this.startupApplicationManager = function()
    {
        this.startupGameObject();
	this.background3 = new RepeatingGameObject().startupRepeatingGameObject(g_back2, 0, 100, 3, 600, 320, 1);
        this.background2 = new RepeatingGameObject().startupRepeatingGameObject(g_back1, 0, 100, 2, 600, 320, 0.75);
        this.background = new RepeatingGameObject().startupRepeatingGameObject(g_back0, 0, 0, 1, 600, 320, 0.5);
        return this;
    }

	/**
        更新当前对象
        @param dt 自上一帧绘制起经过的秒数
        @param context 绘制上下文
        @param xScroll x轴的全局滚动值
        @param yScroll y轴的全局滚动值
    */
    this.update = function(/**Number*/ dt, /**CanvasRenderingContext2D*/ context, /**Number*/ xScroll, /**Number*/ yScroll)
    {
		g_GameObjectManager.xScroll += 50 * dt;
	}
}
ApplicationManager.prototype = new GameObject

在这里,我们通过ApplicationManager创建了三个RepeatingGameObject类的实例,每个实例分别显示为一个图层,使用z(深度)和scrollFactor值来创造RepeatingGameObject 渐远和渐慢的效果。

最终结果很不错。视差滚动为画布赋予了完美的纵深感,而整个效果只多编写了一个类就实现了。

看一看视差滚动的Demo吧。http://webdemos.sourceforge.net/jsplatformer4/jsplatformer4.html

原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element

1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、写一个游戏框架(一)
5、写一个游戏框架(二)
6、通过Canvas实现视差滚动
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单

4、写一个游戏框架(二)

在这篇文章里,我们继续介绍构成这个基本JavaScript游戏框架的其他必要的类。

前一篇文章介绍了GameObjectManager类,该类负责画布的渲染并允许GameObject类更新和删除它们自己。下面就来看一看GameObject类。

GameObject.js


/**
    游戏中出现的所有元素的基类
    @class
*/
function GameObject()
{
    /** 显示的深度次序。较小的zOrder值表示先渲染,因而会在背景中。
        @type Number
    */
    this.zOrder = 0;
    /**
        x轴的坐标
        @type Number
    */
    this.x = 0;
    /**
        y轴的坐标
        @type Number
    */
    this.y = 0;

    /**
        初始化游戏对象,并将其添加到GameObjectManager维护的对象列表中
        @param x        x轴的坐标
        @param y        y轴的坐标
        @param z        元素的z次序(背景元素的z值较小)
    */
    this.startupGameObject = function(/**Number*/ x, /**Number*/ y, /**Number*/ z)
    {
        this.zOrder = z;
        this.x = x;
        this.y = y;
        g_GameObjectManager.addGameObject(this);
        return this;
    }

    /**
        清理当前对象,将其从GameObjectManager维护的对象列表中删除
    */
    this.shutdownGameObject = function()
    {
        g_GameObjectManager.removeGameObject(this);
    }
}

这个GameObject类(是一个引擎类)的目的,是为游戏中将会出现的所有对象定义一些共有的属性,包括它们的位置(x和y)和深度(z)。需要注意的是,我们不会直接创建GameObject类的实例,而是会再创建一个类来扩展它。

这个类的x和y坐标值没有什么好说的——就是相应对象左上角位置在画布上的坐标。关键是GameObject中的z值,这个值定义的是对象的深度。理解这个值很重要,这个值较小的GameObject会先绘制到画布上。换句话说,z值较大的GameObject将被绘制到z值较小的GameObject上面。
查看全文 »

原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element

1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、写一个游戏框架(一)
5、写一个游戏框架(二)
6、通过Canvas实现视差滚动
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单

4、写一个游戏框架(一)

http://www.brighthub.com/internet/web-development/articles/40512.aspx
在知道了如何使用画布元素之后,接下来我教大家写一个框架,有了这个框架,我们就可以把它作为基础来创建游戏。在这第一部分,我们会介绍前两个文件/类。

编写代码之前,我们先来看一看随后几篇文章将致力于创建的示例Demo。表面上看起来,这个Demo跟第二篇文章里的那个没啥区别,但如果你看看后台(查看网页源代码)就会发现,为了更方便地创建这个最终效果,一个凝聚不少心血的基础框架已经写好了。

下面我们要介绍的JavaScript代码使用面向对象的方式来编写。对于没有编写过多少JavaScript代码的人来说,恐怕第一眼看到它们会觉得有点奇怪。如果你真的不太熟悉JavaScript的面向对象编程,建议通过Mozilla Developer Network的这个教程https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript来补补课。这篇教程里解释了我们稍后会用到的一些编程技术。

从设计思想上来看,这个框架可以分成两部分:与底层的2D引擎交互的类(用于操作画布、控制渲染循环、处理输入等的代码)和用来创建对象以便构成游戏的类。前者可以归为引擎类,后者可以归为应用类。由于应用类要构建于引擎类之上,所以我们需要先来创建引擎类。
查看全文 »

原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element

1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、通过Canvas实现视差滚动
5、写一个游戏框架(一)
6、写一个游戏框架(二)
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单

3、通过Canvas元素实现高级图像操作

http://www.brighthub.com/internet/web-development/articles/39509.aspx

这篇文章将带领大家学习使用JavaScript和Canvas元素操作图像了几种不同的方式,这些方式在Canvas元素出现之前是不可能的事儿。

上一篇文章演示了如何利用Canvas实现一个基本的图像动画。那个例子很简单,同样的效果通过修改IMG或DIV等标准HTML元素的一些属性,照样也可以轻易实现。下面我们就来演示一下画布元素的高级应用,展示一下它的真正威力。

首先,还是准备一个HTML页面。


  < !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
  <html lang="en">
     <head>
        <title>JavaScript Platformer 2</title>
        <script type="text/javascript" src="jsplatformer2.js"></script>
        <style type="text/css">
           body { font-family: Arial,Helvetica,sans-serif;}
        </style>
     </head>
    <body>
       <p>
          <a href="http://www.brighthub.com/internet/web-development/articles/38364.aspx">
             Game Development with Javascript and the canvas element
          </a>
       </p>
       <canvas id="canvas" width="600" height="400">
          <p>Your browser does not support the canvas element.</p>
       </canvas>
       <br />
       <button onclick="currentFunction=alpha;">Change Alpha</button>
       <button onclick="currentFunction=shear;">Shear</button>
       <button onclick="currentFunction=scale;">Scale</button>
       <button onclick="currentFunction=rotate;">Rotate</button>
    </body>
 </html>

与上个一例子的HTML页面相比,唯一的区别就是添加了一些按钮。单击这些按钮,就会设置currentFunction变量(稍后介绍)的值,用以改变在渲染循环中运行的函数。
查看全文 »

原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element

1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、通过Canvas实现视差滚动
5、写一个游戏框架(一)
6、写一个游戏框架(二)
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单

1、认识一下Canvas

http://www.brighthub.com/internet/web-development/articles/38364.aspx

Canvas元素以及JavaScript引擎中新增的一些特性,让Web开发人员不必借助第三方插件,即可设计开发出精细且具有交互性的2D网页。这篇文章就向大家介绍一下Canvas元素,以及它的一些可能的用途。

JavaScript与Canvas元素

HTML是为创建静态页面而生的。HTML所能实现的动态效果,也仅限于显示GIF动画和闪烁的文本。JavaScript改变了这一切,通过它能够动态修改网页。今天,很多Web服务都利用AJAX来创建网页,为用户提供更加流畅的体验,也超越了标准HTML页面中常见的“点击-重新加载-点击”式的交互模式。

然而,JavaScript的某些功能会受到其宿主浏览器的制约。尽管可以在网页中创建和修改任何元素,但JavaScript不能(轻易地)让浏览器显示一种新对象。通过JavaScript修改文本、插入图像或者缩放表格都很容易,因为这些对象本来就是HTML所支持的。如果你想再玩得刺激一点,比如写一个网页游戏,怎么办?那恐怕就得苦心积虑地改变标准HTML元素的用途,克服种种不测才能达到目的。要么,你就得求助于Flash或Silverlight这样的插件。

Canvas元素登场了。这个新HTML元素为JavaScript开发者提供了一种无需插件即可在网页中直接绘图的机制。Canvas元素最早是由苹果公司在其WebKit框架中引入的,Safari浏览器和Dashboard微件都在使用。Canvas元素现在也被建议加入了HTML5规范,得到了最新的Chrome、Firefox、Opera以及Konqueror等浏览器的支持。Internet Explorer(至少在IE8之前)还不支持Canvas,但ExplorerCanvas项目倒是为IE提供了与Canvas元素类似的功能。

Canvas元素对做过2D图形编程的人是小菜一碟。可以在这个元素上画线、画各种形状、画渐变,甚至可以利用与其他2D API中类似的函数来修改其中的每一个像素。得益于Chrome的V8、Firefox的SpiderMonkey以及Safari的Nitro等最新JavaScript引擎的性能,创建精细且具有交互性的Web应用已经完全没有问题。

我们这一系列文章将教会大家使用JavaScript和Canvas元素创建一个简单的平台游戏。将要涉及的内容包括动画、加载资源、分层渲染、滚动和交互。通过一步一步地展示示例代码和实际效果,你可以很快学会如何驾驭强大的Canvas元素。

2、在Canvas上绘图

http://www.brighthub.com/internet/web-development/articles/38744.aspx

下面,我们就通过一个循序渐进的示例及实时演示,来介绍如何使用JavaScript在Canvas元素上绘图及实现动画。

准备工作

知道了什么是Canvas元素之后,该学习在屏幕上绘图了。首先,需要一个HTML页面来放置和显示Canvas元素。
查看全文 »

原文地址:http://martinfowler.com/bliki/SoftwarePatent.html

在软件开发领域中,几乎所有我认识的人都对专利以及我们利用它的方式深恶痛绝。很长时间以来,我一直准备写篇文章,谈谈专利这玩艺儿,但一直迟迟没有动笔。最近,在收听了This American Life制作的一期特别棒的新闻观察节目(http://www.thisamericanlife.org/radio-archives/episode/441/when-patents-attack)之后,才决定把这篇文章写出来。我这篇文章的大致意思就是,虽然专利(包括软件专利)从原理上讲是个不错的想法,但在实践当中,各种专利已经给我们带来了无法容忍的灾难,取消这玩艺儿才是上策。

不过,首先还是来看一看为什么我说专利从原理上讲是个不错的主意,而且堪称人类历史上一个最有价值的“发明”。

关于专利在人类发展史中扮演的这个重要角色,Willian Rosen在他的书 The Most Powerful Idea in the World 中给出了很好的论述。Rosen在这本书里回顾了工业革命,而且他把工业革命刻画成了人类历史中最重要的事件,一个使人类财富得以向前迈进了一步的事件。

在17世纪的英格兰、法国,甚至中国,一名熟练的体力劳动者(比如纺织工或铁匠),每周工作的小时数大致相同,生产的布和钉子的数量也大致相同,而且跟他们奥古斯都时代(公元前63年至公元前14年。——译者注)的远祖也没有多大差别。他们能挣到相同数量的货币,能买到相同数量和种类的食物。他们的妻子,同样跟她的远祖一样,忙着准备一日三餐;她们也许会从本村面包师傅那儿买点面包,但除此之外,差不多什么都是自己亲自动手加工。她们甚至还要给全家人做衣服,天气变幻无常,衣服的样式也不尽相同。而且,这在很大程度上也让人无法把当时的家庭与10个世纪前的家庭区分开来,差不多清一色的自织土布,产麻区也会用亚麻布来做衣服。这样的劳动者和他的妻子,一般会生育八到十个后代,但能活到成年的差不多只有三个。如果这个劳动者要出门,那他只有靠步行;当然,如果他们家日子特别富裕的话,会花钱雇一辆两轮或四轮的马车。步行的话,每小时走三英里,坐马车则每小时走七英里。当然,时速跟他们的祖先也一样。而这就意味着,他的世界也就是他出生的地方加上方圆五、六英里远的范围这么大。

之后,历史上第一次,一切都发生了变化。这些劳动者的境况发生了最根本的变化。14世纪君士坦丁堡的一名熟练织工,只要工作三个小时就可以挣够买一磅面包的钱。到1800年,诺丁汉的一位织工为此则至少要工作两个小时。但到了1900年,买这块面包只要工作不到15分钟就够了。而到2000年,只要5分钟。这个已经是老生常谈的事实告诉我们,在发达的21世纪,一个中产阶级家庭所能享受到的奢华生活,在两个世纪以前,是连国王都很难承受得起的。

——William Rosen

查看全文 »