开源轻量级HTML5游戏引擎gin简介(转)

HTML5 canvas 让程序员自由的绘制想要的图形和动画,使得纯粹基于HTML/Javascript/CSS的游戏铺平了道路。只不过canvas最初并非为游戏而设计,而且除了绘图以外,做游戏还有不少其他事情要考虑,例如鼠标键盘事件、图层、动画等。为了方便程序员开发游戏,游戏框架/引擎陆续被开发出来,gin 就是其中之一。

Project home: https://github.com/huandu/gin

Author: Huan Du (blog , twitter )
Samples: https://github.com/huandu/gin-samples
Live demo: simple sample mouse tracer shape breaker

gin是什么

gin是一个开源轻量级的HTML5游戏引擎,只专注于搭建一个简单可靠的游戏基础设施,让游戏能在网页中高效流畅的运转起来。

gin能做什么

gin提供了游戏开发中各种必须的基础设施,例如固定帧率渲染、鼠标键盘事件捕捉、图层、用户数据等,这些工具都是以最简单直接的方式提供出来,不要求OOP,没有预编译。

gin在设计之初就定位为一个“引擎”,而不是框架。就像汽车引擎不负责提供动力以外的事情一样,gin只专注于驱动游戏运转,不提供表现层的任何工具。

gin的特点

gin的特点是:简单、高效

使用gin只需要写如下的代码,十分简单:

  1. $G( 'your-game-container-id' , {}, {  
  2.     render: function (e) {  
  3.         // draw canvas with e.context   
  4.     }  
  5. });  
$G('your-game-container-id', {}, {
    render: function(e) {
        // draw canvas with e.context
    }
});

点击这里 可以看到一个完整而简单的使用gin的例子,源码看这里

相比其他现有的js游戏/绘图框架的设计思路,gin摈弃了传统的事件驱动模型,只提供固定帧率的回调接口,所有的鼠标键盘事件都由gin负责接收和缓存。gin的使用者可以在beforerender或render回调中集中处理所有缓存的事件,这样能最大化游戏性能,并提高整体游戏响应速度。

根据google chrome 8.0的profiling结果,在canvas绘图函数中数清除画布数据的clearRect()消耗CPU时间最多,画布越大性能消耗越明显,约是 stroke()一个相同大小的圆或长方形耗时的100倍甚至更多。如果采用传统的事件驱动模型,游戏会立即处理接收到的事件,执行绘图、逻辑判断等等,这样会不断的清除画布,浪费大量的CPU,而实际上只要达到30帧/s就能有流畅体验,在真正需要绘图的时候再绘图才更合理。

而且由于现在所有javascript引擎都是单线程的,脚本执行时无法响应任何DOM事件,浏览器也不会缓存这些事件,如果脚本较长时间占用CPU,还会造成事件丢失,最终影响用户体验。

由gin来缓存事件还有一个好处,这可以让键盘鼠标状态检测变得更简单。gin在beforerender和render回调中传入的事件对象带有 keyStates和buttonStates数组,分别对应键盘和鼠标的按键状态,可以支持多个键盘/鼠标按键同时按下的状态检测。

  1. $G( 'your-game-container-id' , {}, {  
  2.     render: function (e) {  
  3.         // check if 'blank' key pressed   
  4.         if  (e.keyStates[0x20]) {  
  5.             // do something   
  6.         }  
  7.   
  8.         // check if mouse L button pressed   
  9.         if  (e.buttonStates[0]) {  
  10.             // do something   
  11.         }  
  12.     }  
  13. });  
$G('your-game-container-id', {}, {
    render: function(e) {
        // check if 'blank' key pressed
        if (e.keyStates[0x20]) {
            // do something
        }

        // check if mouse L button pressed
        if (e.buttonStates[0]) {
            // do something
        }
    }
});

beforerender和render

gin将帧的回调函数分为两个,beforerender和render。这两个回调的唯一区别是beforerender参数e里面没有 canvas context,不能用于画图。这样做的好处是鼓励使用者将与绘图无关的逻辑放入beforerender,让每次脚本运行的时间更短,降低丢失消息的可能性。

遍历鼠标事件

gin缓存的鼠标事件并不能直接暴露出来,这是因为gin支持图层,在不同图层里面,鼠标事件的clientX和clientY都会因为图层的偏移量不同而不一样。如果为每一个图层事先计算好正确的clientX和clientY并且缓存起来,那会造成很大的性能损耗。gin的做法是提供遍历鼠标事件的接口,并在遍历中计算正确的坐标。

  1. $G( 'your-game-container-id' , {}, {  
  2.     render: function (e) {  
  3.         if  (e.buttonStates[0]) {  
  4.             // draw mouse move path   
  5.             var  ctx = e.context;  
  6.             ctx.strokeStyle = 'rgb(0,0,0)' ;  
  7.             ctx.beginPath();  
  8.   
  9.             e.traverseHistory(function (cur, prev) {  
  10.                 // the first point   
  11.                 if  (!prev) {  
  12.                     ctx.moveTo(cur.clientX, cur.clientY);  
  13.                 } else  {  
  14.                     ctx.lineTo(cur.clientX, cur.clientY);  
  15.                 }  
  16.             });  
  17.   
  18.             ctx.stroke();  
  19.         }  
  20.   
  21.         // cached mouse history must be cleared explicitly   
  22.         // history will not be really cleared until next beforerender/render is ready to call   
  23.         // so it's safe to clear history many times in one function or in other layer   
  24.         e.clearHistory();  
  25.     }  
  26. });  
$G('your-game-container-id', {}, {
    render: function(e) {
        if (e.buttonStates[0]) {
            // draw mouse move path
            var ctx = e.context;
            ctx.strokeStyle = 'rgb(0,0,0)';
            ctx.beginPath();

            e.traverseHistory(function(cur, prev) {
                // the first point
                if (!prev) {
                    ctx.moveTo(cur.clientX, cur.clientY);
                } else {
                    ctx.lineTo(cur.clientX, cur.clientY);
                }
            });

            ctx.stroke();
        }

        // cached mouse history must be cleared explicitly
        // history will not be really cleared until next beforerender/render is ready to call
        // so it's safe to clear history many times in one function or in other layer
        e.clearHistory();
    }
});

图层

gin虽然并没有把图层的内部类(GinLayer)暴露出来,但它无处不在,例如,所有的回调函数的this都是当前layer的实例。

gin默认会创建一个图层作为所有图层的基础,使用者可以在图层上创建无数的子图层,子图层可以继续嵌套更多子图层。每个子图层有自己的beforerender和render,可以独立的渲染。

图层可以用stop()方法来停止渲染,stop的图层和它的子图层的beforerender/render都不会被调用,直到调用play()让它们继续渲染。

  1. $G( 'your-game-container-id' , {}, {  
  2.     start: function () {  
  3.         this .layers( 'sample' , {  
  4.             left: 0,  
  5.             top: 20,  
  6.             width: 100,  
  7.             height: 200  
  8.         }, {  
  9.             render: function (e) {  
  10.                 // your code   
  11.             }  
  12.         })  
  13.         .layers('sample_again' , {}, {  
  14.             play: function () {  
  15.                 // this function will be called once the layer starts to play   
  16.                 this .layers( 'sub_sample' , {});  
  17.             }  
  18.         });  
  19.     },  
  20.     render: function (e) {  
  21.         // it's how to find layer, or even sub-layer   
  22.         var  sample =  this .layers('sample '),  
  23.             sub = this.layers([' sample_again ', ' sub_sample']);  
  24.   
  25.         // do something   
  26.     }  
  27. });  
$G('your-game-container-id', {}, {
    start: function() {
        this.layers('sample', {
            left: 0,
            top: 20,
            width: 100,
            height: 200
        }, {
            render: function(e) {
                // your code
            }
        })
        .layers('sample_again', {}, {
            play: function() {
                // this function will be called once the layer starts to play
                this.layers('sub_sample', {});
            }
        });
    },
    render: function(e) {
        // it's how to find layer, or even sub-layer
        var sample = this.layers('sample'),
            sub = this.layers(['sample_again', 'sub_sample']);

        // do something
    }
});

gin的未来

gin刚刚发布了1.0.0版 ,是一个just work的版本,并且发布了三个例子,放在gin-samples 项目中。

gin未来将首先考虑支持更多的浏览器和平台,特别是移动设备上的支持。对于简单高效的引擎而言,最适合应用的环境就是性能相对较差的移动设备。

其次需要考虑的是实现更强大的图层(layers),现在的图层已经实现了类似于flash MovieClip的各种基本功能,还差key frame没有实现,未来会增加支持。

FAQ

Q: gin的api文档呢?
A: 暂时还没有,有问题请直接联系我(githubtwitter

Q: gin支持哪些浏览器?
A: 测试过的是Firefox 3.6、Chrome 8.0/10.0,理论上Opera、Safari也会支持。IE就别想了,等IE9支持了canvas再说吧。

Q: gin-samples里面的例子不能正常运行是什么原因?
A: 如果是下载源码运行,需要手动将gin.js拷贝到sample目录里面才能运行。此外,需要保证电脑可以访问网络,因为有些例子使用了jQuery,用的是google dns。如果还不行,请检查一下浏览器是否是gin所支持的浏览器,gin自己没有做任何浏览器检测。

附录

我在开发过程中了解过的游戏/绘图框架包括The Render EngineGameJScakejscocos2d ,它们都是非常好的框架,gin从某种程度上说是针对它们的不足而设计的。 :P

gin最开始是我心血来潮写的一个小框架,还未完成就在“给力HTML5 —— 2011 Google HTML5训练营” 第一期活动 中使用,非常意外且幸运的用它实现了第一个可玩的HTML5游戏Raiden 5 (Chrome only) ,在这里 可以围观最原始的gin。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值