青瓷引擎是一套开源免费的JavaScript游戏引擎类库,其基于开源免费的Phaser游戏引擎,并提供了一套完全基于浏览器的跨平台集成式HTML5游戏编辑器。
上手容易,学习成本低
引擎、编辑器、后台均基于JavaScript数百个工程示例Demo及完整游戏教程助力用户学习掌握。
开发效率高
重新定义了HTML5游戏的开发工作流,开发、调试尽在浏览器内。不断丰富的插件库,让游戏开发更加便捷、简单。
一站式集成工具套件
开发和整合了游戏中用到的各种工具,强调了各工具之间的无缝融合,大大提升了开发效率。
传播更广
游戏无需浏览器安装额外插件,适应性更广,更利于传播。
开源免费 便于扩展
MIT开源协议,面向组件式编程,支持组件热拔插,方便扩展维护。核心库及编辑器都是纯JavaScript,便于用户自行扩展修改。
可视化编辑 所见即所得
先进的UI界面布局规则,简单几步,无需编码即可适配各种分辨率。强大的可视化编辑功能,拖拖拽拽便可以实现很多复杂的功能。
Windows下安装
首先安装Node.js环境
如果您本机已经安装了Node.js,推荐您升级到最新版本,青瓷引擎支持的Node.js版本:
Node.js合并io.js之前任何 v0.12.x 的版本
Node.js合并io.js之后任何高于 v4.1 的版本
下载青瓷引擎
通过青瓷引擎官网下载免安装包,并解压
运行青瓷引擎
双击start-win.bat运行,青瓷引擎编辑器将自动在浏览器中打开:
也可在解压目录下,通过命令行启动 node ./editorservice/StartService.js
青瓷引擎编辑器可运行在任何支持HTML5的浏览器,但建议采用Google Chrome浏览器运行性能最佳
浏览器的访问地址为:http://localhost:port/project.html 其中port端口值见控制台(默认都为5002):
在浏览器中打开的界面如图:
创建工程
指定工程文件夹名:HelloWorld。操作方法如下:
工程设置
设定编辑器布局为:竖屏。操作方法:选择菜单“Layout/Portrait”
选择菜单“Project/Settings...”,在Inspector面板中打开工程设置界面
设置值如下:
Project Name(工程名): HelloWorld
Game Name(游戏名):HelloWorld
Company(开发者姓名或公司名):qcplay
Identifier(游戏唯一标识符,需要保证唯一):com.qici.helloworld
Version(当前工程的版本号):0.9
其他字段使用默认值
创建场景
创建一个空的场景,步骤如下:
选择菜单“Project/New Scene”
场景中挂载个UIRoot对象(暂时可以理解为放界面元素的根节点就好了)
保存场景,场景名称:HelloWorld,场景的文件路径:Assets/state/HelloWorld.bin
设置为入口场景
将刚才创建的新场景HelloWorld加入到场景列表中。方法是打开Project Setting面板,将场景选中:
在场景列表中,第一个即为入口场景(系统会自动加载)
场景需要勾选后,才能被加载。否则发布时将视为无效场景
添加脚本
这里,我们使用代码创建一个文本,并显示:”Hello World!“。
在Project面板中,右击”Script“创建一个js文件:Init.js
双击打开,编辑代码如下:
var Init = qc.defineBehaviour('qc.helloworld.Init', qc.Behaviour, function() { }, { }); Init.prototype.awake = function() { // create a text var node = this.game.add.text(this.gameObject); node.text = 'Hello World!'; node.color = new qc.Color(0xffffff); };
将此代码挂载到UIRoot节点(方法是直接拖拽到节点上),这样此脚本就能被调度运行。
代码讲解
首先,我们定义一个类:qc.helloworld.Init。qc.defineBehaviour接收4个参数,这里可以先简单了解下:
第一个参数:类的名字为qc.helloworld.Init
第二个参数:所有挂载到场景对象(本示例为UIRoot节点)的脚本,都应该继承自:qc.Behaviour
第三个参数:脚本对象的构造函数
第四个参数:可被序列化的对象字段及其类型描述
var Init = qc.defineBehaviour('qc.helloworld.Init', qc.Behaviour, function() { }, { });
然后,在Init对象的awake函数中,添加逻辑代码以创建text。awake并不需要开发者自己去调度,当UIRoot这个节点被反序列化后,系统自动调用脚本的awake方法
在awake方法中:
this.game:游戏实例的引用
this.game.add:对象创建工厂(可以用来创建文本、图片、精灵等对象)
this.gameObject:本逻辑脚本挂载的目标游戏对象(本实例为UIRoot)
this.game.add.text(this.gameObject):在UIRoot节点下,创建一个Text对象
然后设置文本内容为:'Hello World!'
最后设置文本颜色值为白色
运行起来
保存当前场景
点击”运行“按钮,查看结果
让文字居中
默认情况下,文本的位置在屏幕左上角(0,0)。修改Init.js代码,在awake中,添加如下代码:
// 设置文本对象原点在中心 node.pivotX = 0.5; node.pivotY = 0.5; // 位置居中 node.x = this.gameObject.width/2; node.y = this.gameObject.height/2; // 文本水平对齐 node.alignH = qc.UIText.CENTER;
运行之,现在文本居中显示了。
换个方式:不要编码
在之前的实现方式中,如果非编码人员(如策划人员、美术人员)想要调整显示的文字、位置和样式等,他们是没有能力自行修改的。
因此我们换一种方式,直接在场景中可视化创建文字对象,并设置其内容、文字大小等信息:
将Init.js从UIRoot对象中干掉,这样此脚本将无法自动被调度了
在UIRoot下创建UIText节点,并在Inspector面板中设置其内容
运行查看效果
是不是比手写代码快很多?
这一切如何发生的呢?
从传统的编程方式来看,到这里会有一些疑问:程序执行入口在哪?编辑器”偷偷摸摸“干了些啥?让我们依次展开详细解释。
首先,将本工程发布出来
打开文件:StartGame.html,查看文件内容。这里按顺序摘取主要内容依次解释。
游戏配置
由编辑器根据Project Settings自动生成
qici.config = { projectName: 'HelloWorld', gameName: 'HelloWorld', companyName: 'qcplay', bundleIdentifier: 'com.qici.helloworld', gameInstance: 'qc_game', backgroundColor: 4671303, runInBackground: true, antialias: true, transparent: false, developerMode: false, renderer: 'Auto', loadingPrefab: '', scene: { "HelloWorld" : "Assets/state/HelloWorld.bin" }, entityScene : 'HelloWorld', loading: { loadingInterval: 200, brightingInterval: 10, blinkingCount: 5, blinkingInterval: 70, fadingInterval: 400 } };
导入引擎库文件和用户脚本文件
游戏一开始会出现吃豆子的加载动画,这过程加载如下几个代码文件:
qici.scripts = [ './Assets/meta/globalUrlMap.js', 'http://engine.zuoyouxi.com/lib/0.97.06/phaser.min.js', 'http://engine.zuoyouxi.com/lib/0.97.06/webfontloader.js', 'http://engine.zuoyouxi.com/lib/0.97.06/qc-core.js', // External scripts for plugins // User scripts './js/game-scripts-mini-0.9.js' ];
加载这些js文件和播放进度的动画表现,在qc-loading.js脚本中实现:
<body onload="qici.init();"> <div id="gameDiv" style="position:relative;"></div> <script src='http://engine.zuoyouxi.com/lib/0.97.06/qc-loading.js'></script> </body>
游戏实例初始化
在编辑器目录,打开lib/qc-loading-debug.js文件。加载js文件和进度表现的逻辑忽略不看;
当js文件加载完毕后,调用qici.loadGame方法:
qici.loadGame = function() { var game = window[qici.config.gameInstance] = new qc.Game({ width: '100%', height: '100%', parent: 'gameDiv', state: qici.splashState, editor: qici.config.editor === true, backgroundColor: new qc.Color(qici.config.backgroundColor), runInBackground: qici.config.runInBackground, antialias: qici.config.antialias, transparent: qici.config.transparent, debug: qici.config.developerMode === true, renderer: (function() { if (qici.config.renderer === 'WebGL') { return Phaser.WEBGL; } if (qici.config.renderer === 'Canvas'){ return Phaser.CANVAS; } return Phaser.AUTO; })() }); game.bundleIdentifier = qici.config.bundleIdentifier; game.log.important('**** [QICI Engine]Starting game: {0}', qici.config.gameName); };
游戏的初始化流程在这里实现了:实例化qc.Game,构造函数接收一个object进行配置。大部分配置属性暂时不去理会,这里着重看下state(值为qici.splashState)
Splash State
这个其实是个空的内置场景,此场景完成一些初始化信息(例如loading动画等)。最重要的是:通过此场景载入入口场景(本例子为HelloWorld)。主流程如下:
qici.splashState = { init: function() { window[qici.config.gameInstance].fullScreen(); }, preload: function() { var game = window[qici.config.gameInstance]; if (qici.config.loadingPrefab) { game.assets.load('__loading_prefab__', qici.config.loadingPrefab); } var text = game.add.text(); text.text = 'Initializing, please wait ...'; text.setAnchor(new qc.Point(0, 0), new qc.Point(1, 1)); text.left = 0; text.right = 0; text.top = 0; text.bottom = 0; text.alignH = qc.UIText.CENTER; text.alignV = qc.UIText.MIDDLE; text.fontSize = 24; text.color = new qc.Color(0xffffff); text.strokeThickness = 2; text.stroke = new qc.Color(0x000000); game._initText_ = text; game.updateScale(true); }, create: function() { var game = window[qici.config.gameInstance]; game.state.entity = qici.config.entityScene; game.state.list = qici.config.scene; var node; if (qici.config.loadingPrefab) { var prefab = game.assets.find('__loading_prefab__'); if (prefab) { node = game.add.clone(prefab); node.ignoreDestroy = true; node.visible = false; } } if (game._initText_) { if (node) { game._initText_.destroyImmediately(); } delete game._initText_; } game.phaser.time.events.add(1, function() { game.state.load(game.state.entity, true); }); if (qici.config.frameRate) game.time.frameRate = qici.config.frameRate; } };
进入主场景后,系统反序列化场景内容并逐一构建场景对象。构建完毕后依次初始化场景节点(通过调用逻辑脚本的awake函数)
初始化流程总结
这些初始化流程编辑器已经自动帮你完成:
实例化qc.Game
Game启动后,依次调用SplashState场景(空的内置场景)的init、preload和create,并载入入口场景
下载、反序列化入口场景,将场景重新构建后调用awake方法(挂载到对象的逻辑脚本才会调用)
场景统一使用:scene(原来部分使用了state)
修改截屏接口的包围盒获取方式,使用相对自己的坐标而非世界坐标
TileLayer增加接口:setTileIndex,支持动态修改地图块
增加2个加密/解密接口:qc.Des.encrypt和qc.Des.decrypt
Tween组件duration参数默认为1(原来为0)
合并Arcade物理插件帧调度时机
图片在canvas渲染模式下,当宽高为0时不绘制
Node节点增加事件:onDeserialized(反序化完成事件)
qc.Color增加属性:r、g、b
DOM节点增加alpha属性
Canvas绘制时,默认开启roundPixeds选项
替换编辑器首页样式
部分内置文件夹限制删除,如以下目录:Assets/raw、Assets/scene等
开启脏矩形时,当父节点被移除后,子节点所在范围没有更新
NodeMask组件在RenderTexture中绘制时,位置更新不及时
在X5浏览上,DOM插入到隐藏节点位置绘制不对
Slider中的滑块不是固定大小时,追踪点击失效
Button在开启NativeClick时,父亲节点隐藏后依然可以响应点击
图集只有一张图片时,frameNames属性错误
输入框被编辑时,父亲节点隐藏时依然可见
数组类型字段反序列化时,存在多处引用
帧率限制在发布后不起效
富文本插件
粒子插件预览版
动画编辑器预览版
Box2D物理插件预览版
固定游戏大小功能(见ProjectSetting的fixedGameSize配置)