我使用的是cocos2d-js开发u8远程接入客户端出错游戏,应该怎样接入统计

是一个开源的可用于web、native环境的游戏引擎,它是HTML5版本的 。 cocos2d-js 包含两部分,一部分是用于web game的,另一部分则是用于native game的 cocos2d-x javaScript binding (JSB)。cocos2d-js 提供了 cocos2d-html5 和 cocos2d-x jsb 兼容的 API,使得用cocos2d-js编写的game可以无缝、不需要任何修改的就可以跑在基于cocos2d-x jsb的native环境里。
基于javaScript技术栈,在开发效率上有不少的优势。众所周知,web开发是相当高效的,开发过程无需编译、打包,直接刷新下浏览器即可,而且现在的浏览器都支持强大的开发工具,比如,包含了比如单步调试、网络数据访问、动态修改、内存/CPU性能分析、交互式控制台。
cocos2d-js 正是基于这一点,通过上层提供统一兼容API,快速的迭代,无缝的跨平台部署,进行游戏产出。
不过,在追求开发效率的道路上还不能仅仅这样就可以满足了,由于是基于javaScript栈开发,就需要考虑比如:
怎么进行依赖管理?
如何使用第三方库?
怎么做单元测试?
代码如何进行复用?
接下来会针对这些问题,提出探讨,并给出相应的解决方案,最后会提供一个 cocos2d-js 的 example 以供参考
cocos2d-js 开发存在的问题探讨
javaScript 依赖管理是一个老生长谈的话题,因为javaScript语言本身到目前为止还没推出统一的模块、依赖管理的机制(es6 module还需要好久),cocos2d-js 在这方面使用的是:
依赖管理挂载在 global 对象下面
脚本加载用 project.json 里的 jsList进行手动配置
基于全局的依赖管理作为引擎这样子使用问题还不大,因为我们最终使用cocos2d-js的地方都从cc对象获取即可,但是如果是你自己的应用层代码呢?
很明显,自己在做应用(游戏)开发时,要尽量避免使用全局变量,使用全局变量则必须小心,要考虑所有使用到的地方,否则一步留神就改出了问题,同时使用全局变量,对于一个文件依赖全局变量很难一眼看出这个文件原来依赖了某个全局变量,这样子尤其是在项目的后期带来了不少的麻烦,更不利于多人协作的项目
脚本加载需要手动配置,也是存在问题的。如果文件之间相互有加载顺序的依赖关系呢?(比如a.js依赖于b.js那么b.js就需要先加载完才行)这样一来就必须小心的写好jsList里面的顺序,否则就加载不成功了
因此,一个完善的依赖管理机制还是需要的。解决这个问题的关键是要把脚本的加载过程与脚本之间的依赖处理过程分开。
目前也有不少解决方案,AMD、CommonJS、Dependency Injection,关于这三种方式的比较与讨论可以参考另外一篇博文
可以看到基于 Dependency Injection 模式进行管理,可以极大程度的松散耦合,前后端代码进行复用,并且可以直接使用基于CommonJS的模块(npm module)。目前,bearcat 已经对 cocos2d-x jsb 环境进行了支持, 意味着我们可以编写同一套代码逻辑,然后跑在浏览器和jsb环境中,当然也可以在node.js中进行复用。而 AMD(只适用于浏览器)、CommonJS(依赖于文件I/O,浏览器环境受到限制) 都不能很好的适用于所有的环境,也包括cocos2d-js的jsb环境
第三方库的使用
第三方库其实也是一种依赖,目前最大的javaScript module生态圈是npm,我们可以使用
直接使用 npm 里面现有的 module,而非拷贝代码,然后手动添加,如果要升级第三方库呢?再拷贝,再添加。明显不太优雅。使用 browserify 的时候,配置个 package.json,然后添加依赖,build一下即可,由于依赖并不是频繁更改的,因此这个browerify的build的过程也是可以接受的
使用全局global变量的情况下是很难做单元测试的,要做单元测试首先引用的全局变量要是一个拷贝,这样子可以防止由于在这里的单元测试修改了全局变量,而造成在另外的单元测试里面失败。
var fs = require('fs');
module.exports = function(path, cb) {
fs.readFile(path, 'utf-8', function(err, content) {
cb(null, JSON.parse(content));
这里有几个依赖?怎么做单元测试?
(依赖了 fs 和全局的 JSON)
Dependency Injection则没有这个问题,因为依赖是传入的,并不是自己获取的,传入的这个依赖可以很方便的进行mock进行单元测试。
换成依赖注入则一目了然
module.exports = function(fs, JSON) {
return function(path, cb) {
fs.readFile(path, 'utf-8', function(err, content) {
cb(null, JSON.parse(content));
对于javaScript,我们可以使用
作为测试驱动框架,使用
作为断言库
由于我们基于javaScript技术栈,代码共享肯定能带来不少好处,游戏开发中不少的逻辑是可以直接复用的,比如model定义、数据的校验、寻路逻辑等等,这里的代码复用不是指的代码拷贝,真正的复用是同一个文件直接在客户端和服务端引用
cocos2d-js + bearcat
接下来会以改造 cocos2d-js 官方 helloworld 例子为例,说明如何使用 cocos2d-js + bearcat 来进行开发
创建 cocos2d-js 项目
直接使用 cocos 命令
cocos new -l js bearcat_cocos2d_js_example
然后在web上跑起来
cd bearcat_cocos2d_js_example/
cocos run -p web
添加 package.json
package.json 中,我们可以填写需要依赖的第三方库,然后利用npm进行库的维护与管理
这里我们依赖库需要 bearcat,开发库需要 grunt 以及相关 grunt task
package.json
"name": "bearcat-cocos2d-js-example",
"version": "0.0.1",
"dependencies": {
"bearcat": "0.4.x"
"devDependencies": {
"grunt": "~0.4.2",
"grunt-bearcat-browser": "0.x",
"grunt-browserify": "3.2.x"
添加完依赖,我们执行 npm 命令,安装依赖
npm install
添加 gruntfile.js
我们可以建立起自动化的开发构建发布流程
比如,我们可以配置 browserify build task,bearcat 生成 bearcat-bootstrap.js task
使用grunt前,需要全局安装grunt
npm install -g grunt
gruntfile.js
'use strict';
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-bearcat-browser');
var src = [];
// Project configuration.
grunt.initConfig({
// Metadata.
pkg: grunt.file.readJSON('package.json'),
bearcat_browser: {
default: {
dest: "bearcat-bootstrap.js",
context: "client-context.json"
// browserify everything
browserify: {
standalone: {
src: ['client.js'],
dest: 'main.js',
options: {
// Default task.
grunt.registerTask('default', ['bearcat_browser', 'browserify']);
这里加入了 browserify 和 bearcat 的 task,然后我们执行grunt命令即可运行这些tasks
browserify 里配置 source 文件 client.js,目标文件为 main.j。client.js 其实就是之前的 main.js 文件,只不过使用 browserify 提供的 require 加载了第三方库,main.js 则是已经打包好第三库的主文件,供cocos2d-js作为项目入口文件加载
bearcat 里配置根据 client-context.json 来生成 bearcat-bootstrap.js 以便支持script的加载
如果还想加入单元测试、代码覆盖率的工作流,加入相关mocha、coverage task即可
创建源码文件夹
由于是javaScript技术栈,考虑前后端代码共享,那么我们可以把源码文件夹分成三个,比如
创建 client 源码文件夹
mkdir app-client
创建 server 源码文件夹
mkdir app-server
创建共享源码文件夹
mkdir app-shared
修改 project.json 文件
这里采用bearcat进行script脚本加载与依赖管理,因此修改 project.json 文件,把 jsList 的配置置为空数组
project.json
"project_type": "javascript",
"debugMode": 1,
"showFPS": true,
"frameRate": 60,
"id": "gameCanvas",
"renderMode": 0,
"engineDir": "frameworks/cocos2d-html5",
"modules": ["cocos2d"],
"jsList": []
添加 client-context.json
这个是 bearcat 在 client 端的配置,配置着需要扫描的源文件路径,这里我们扫描 app-client 与 app-shared 这两个文件夹,相应的,对于node.js开发,有一个context.json,里面配置的扫描文件夹则是 app-server 与 app-shared
client-context.json
"name": "bearcat-cocos2d-js-example",
"description": "client context.json",
"scan": ["app-client", "app-shared"],
"beans": []
添加 client.js
client.js 就是之前的main.js,只不过使用了browserify进行第三方库依赖、使用bearcat进行业务层代码的管理
require('./bearcat-bootstrap.js');
var bearcat = require('bearcat'); // 依赖bearcat库
window.bearcat = // using browserify to resolve npm modules
cc.game.onStart = function() {
cc.view.adjustViewPort(true);
cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL);
cc.view.resizeWithBrowserSize(true);
var self =
//load resources
bearcat.createApp();
bearcat.use(['helloWorldScene']);
bearcat.start(function() {
var resourceUtil = bearcat.getBean('resourceUtil');
var g_resources = resourceUtil.getResources();
cc.LoaderScene.preload(g_resources, function() {
var helloWorldScene = bearcat.getBean('helloWorldScene');
cc.director.runScene(helloWorldScene.get());
cc.game.run();
至此客户端项目环境已经配置完毕,我们可以愉快的coding
使用bearcat编程
添加 HelloWorldLayer
使用cocos2d-js创建一个layer相当容易
var HelloWorldLayer = cc.Layer.extend({
ctor: function() {
我们继承cc.Layer,然后在子类里实现我们具体的逻辑
但是这个 HelloWorldLayer 放在哪儿呢?全局吗?当然 say no!
在bearcat里,你可以编写一个管理HelloWorldLayer的factory bean也即工厂类,由这个factory bean维护着这个HelloWorldLayer,需要时,依赖这个factory bean,然后通过factory bean的工厂方法获取HelloWorldLayer的实例,同时,如果HelloWorldLayer需要依赖其他的script文件,也在factory bean通过bearcat提供的依赖注入来进行即可
// HelloWorldLayer 工厂bean
var HelloWorldLayer = function() {
this.$id = "helloWorldLayer";
this.$init = "init";
this.ctor =
// 如果需要依赖,直接在这里用bearcat依赖注入
// this.$xxxUtil =
HelloWorldLayer.prototype.init = function() {
var self =
// 初始化HelloWorldLayer
this.ctor = cc.Layer.extend({
sprite: null,
helloLabel: null,
ctor: function() {
// 工厂方法
HelloWorldLayer.prototype.get = function() {
return new this.ctor();
bearcat.module(HelloWorldLayer, typeof module !== 'undefined' ? module : {});
在HelloWorldLayer中我们需要依赖一个resourceUtil来处理资源的管理,那么我们可以简单的这样做就行
var HelloWorldLayer = function() {
this.$id = "helloWorldLayer";
this.$init = "init";
this.$resourceUtil =
this.ctor =
使用时,我们直接用 this.$resourceUtil 即可拿到依赖,调用里面的方法
在HelloWorldLayer中,我们可能有这样的逻辑
添加一个close按钮
添加helloWorld标签
添加splash背景
添加action动画
HelloWorldLayer.prototype.init = function() {
var self =
this.ctor = cc.Layer.extend({
sprite: null,
helloLabel: null,
ctor: function() {
// 1. super init first
this._super();
self.addCloseItem(this);
self.addHelloWorldLabel(this);
self.addSplashScreen(this);
self.runAction(this);
然后我们可以这么进行抽象与封装,把每个逻辑代理到factory bean的无状态的方法上去,并把this context作为参数传入
这样子实现好处也是明显的:
ctor函数里的逻辑进行了函数封装,便于后期维护
每个封装的函数都是无状态的,可以轻松实现component,来进行代码复用
在这里就是ui组件的复用(当然,对于ui,我们已经有了cocosstudio来进行编辑生成,而不用自己一个个去编写代码,但是代码实现的思路是一致的)
时刻不忘代码的可维护性、复用性
实现具体逻辑
实现就大胆使用cocos2d-js里提供的各种方法即可,这里的self参数则是ctor里的this context
HelloWorldLayer.prototype.addCloseItem = function(self) {
// 获取 resourceUtil 依赖
var res = this.$resourceUtil.getRes();
// 2. add a menu item with "X" image, which is clicked to quit the program
// ask the window size
var size = cc.winS
// add a "close" icon to exit the progress. it's an autorelease object
var closeItem = new cc.MenuItemImage(
res.CloseNormal_png,
res.CloseSelected_png,
function() {
cc.log("Menu is clicked!");
closeItem.attr({
x: size.width - 20,
anchorX: 0.5,
anchorY: 0.5
var menu = new cc.Menu(closeItem);
menu.x = 0;
menu.y = 0;
self.addChild(menu, 1);
其它代码也类似,不一一说明,完整例子在
部署与运行
写完代码,执行 grunt 进行构建
对于web平台,直接执行cocos命令
cocos run -p web
对于android平台,则需要修改 cocos2d-js 库下的 frameworks/runtime-src/proj.android/build-cfg.json 文件
把我们自定义的源代码文件添加进打包apk配置中
build-cfg.json
"ndk_module_path": [
"../../js-bindings",
"../../js-bindings/cocos2d-x",
"../../js-bindings/cocos2d-x/cocos",
"../../js-bindings/cocos2d-x/external"
"copy_resources": [{
"from": "../../../app-client",
"to": "app-client"
"from": "../../../app-shared",
"to": "app-shared"
"from": "../../../res",
"to": "res"
"from": "../../../main.js",
"from": "../../../project.json",
"from": "../../js-bindings/bindings/script",
"to": "script"
这里我们添加了 app-client 与 app-shared 文件夹
然后,我们就可以直接用cocos命令编译部署到android设备(模拟器)上
cocos run -p android
ios 平台,由于没有mac,暂时无法测试,还望读到这的同学实际测试下,并给予反馈
cocos2d-js非常不错,基于javaScript我们可以用一份代码就可以发布到web、pc、android、ios等平台上,大大减少了开发成本。当然cocos2d-js开发并不是非常完美,这其中也有javaScript这门语言本身的不足。本文就对这些问题进行了探讨,并给出了使用bearcat来编写cocos2d-js项目的例子,抛砖望引玉,enjoy coding with bearcat
你可能感兴趣的文章
235 收藏,22.6k
3 收藏,965
分享到微博?
你好!看起来你挺喜欢这个内容,但是你还没有注册帐号。 当你创建了帐号,我们能准确地追踪你关注的问题,在有新答案或内容的时候收到网页和邮件通知。还能直接向作者咨询更多细节。如果上面的内容有帮助,记得点赞 (????)? 表示感谢。
明天提醒我
我要该,理由是:
扫扫下载 App
SegmentFault
一起探索更多未知如何使用Cocos2d-JS引擎快速开发一个微信游戏?
微信互动游戏开发
今天我们来介绍一下如何使用Cocos2d-JS引擎快速开发一个微信游戏《来自喵星的你》。源码下载:1.Cocos2d-JS引擎Cocos2d-JS引擎是Cocos2d-x的JS版本,它极大简化与整合的API设计使得游戏开发变得前所未有地轻松。依托Web平台使Cocos2d-JS享有高效的开发测试环境,命令行工具提供三步就可以搞定Web与Native全平台部署的便捷功能。官方下载地址:官方指南:2.Cocos Code IDECocos Code IDE是基于 Eclipse的跨平台 IDE,可帮助开发者便捷地创建游戏工程、编写调试代码、实时查看修改效果、并最终直接发布成可上架的安装包。官方下载地址:官方JS指南:3.游戏《来自喵星的你》主角是一只喵星小喵咪。他的使命是在危险的地球上铲除汪星人(为什么这两个星球的主战场要设置在地球上,我也没想通,不要在意这些细节嘛。。。),配角就是一群不断产生的汪星人,玩家需要控制喵星人的移动和攻击来躲避或者打败汪星人,打败的越多分数越高,如果喵星人被打败了,游戏结束,这个时候就可以把你的成绩分享给你的好友,比一比谁更牛。游戏无外乎就是各种游戏内的对象根据你所设定的逻辑或者用户的交互运行一系列动画而已。明白了这一点,我们就开始动手来开发一个已经在微信上刷屏刷的一塌糊涂的《来自喵星的你》,想想是不是有点小激动。我们先简单介绍一下这个游戏的结构,A.不要主菜单不要一堆乱七八糟的东西,一进游戏就开始玩,越简洁越能抓住用户。B.游戏的主界面,玩家操作界面和分数标签。C.喵星人只有一只,汪星人随着时间一直产生。D.游戏结束显示游戏结束的界面,可以重玩也可以分享。所以这是个单场景游戏。1)window.onload函数是Cocos2d-JS游戏的入口。在这里,我们加载了资源后进入到游戏场景。cc.LoaderScene.preload(["catnorrisd.png",&"pg.png",&"arrow.png",&"end.png"],&function&()&{cc.director.runScene(new&MyScene());MyScene就是游戏的主场景了,MyScene从cc.Scene继承而来。进入游戏后,引擎会调用onEnter函数,所以我们可以在onEnter函数里初始化整个游戏场景UI和处理触摸相关的逻辑。2)创建分数标签比如,创建一个分数标签,很简单,只需要初始化标签的属性,并添加到场景中即可。& &this.scoreLabel&=&&UI.scoreLabel&=&new&cc.LabelTTF("0",&"黑体",&24,&cc.size(150,&30),&cc.TEXT_ALIGNMENT_LEFT);&&&&this.addChild(this.scoreLabel);&&&&this.scoreLabel.attr({&&&&&&&&&x:30,&&&&&&&&y:cc.director.getVisibleSize().height&-&25,&&&&&&&&strokeStyle:&cc.color(0,0,0),&&&&&&&&lineWidth:&2,&&&&&&&&color:&cc.color(255,150,100),&&&&&&&&anchorX:0.1&&&&});3)处理触摸逻辑如何捕获用户的触摸?Cocos2d-JS提供了一个很方便的接口,我们只要在游戏的场景中调用cc.eventManager.addListener重写下面三个函数就可以了。onTouchBegan:function(touch,&event);onTouchMoved:function(touch,&event);onTouchEnded:function(touch,&event);从命名就可以看出来这三个函数是处理触摸的三个时间点。这里要注意的是,如果我们根据触摸的时间来判断用户是长按还是点击,如果是长按,喵星人就会移动,如果是点击,喵星人就会进行攻击。目标点坐标怎么获取呢?touch参数暴露咯。。。4)主角登场喵星人终于要出来了,为了方便操作喵星人,我们把它封装为一个类Cat,从cc.Sprite继承而来。ctor构造函数初始化了喵星人的状态。ctor:function(){&&&&this._super("catnorrisd.png",&cc.rect(144,148,70,32));&&&&this.attr({&&&&&&&&anchorX:0.5,&&&&&&&&anchorY:0&&&&});&&&&this.scheduleUpdate();&&&&this.idle();},5)状态机从上面的代码我们可以看到有2个奇怪的函数调用scheduleUpdate()和idle(),又是做什么用的呢?这里我们把喵星人设置为状态机模式。给定了喵星人可能的几个状态,idle,walking,attacking,dieing和dead。同样的调用walk(),attack()等等也会切换喵星人的状态。那这些只是状态的切换,如何表现这些状态呢?假如此时是walk的状态,喵星人会有一个左右摇晃的动作。this.runAction(cc.sequence(cc.rotateTo(0.12,&-3),&cc.rotateTo(0.12,3)).repeatForever());cc.rotateto是一个旋转动作。Sequence是一个连续动作,它把2个rotateTo连接在一起,按顺序执行。而repeatForever表示这个动作循环进行。同理,2种汪星人Doge和Husky也被封装为状态机的模式,然后后Manager对象统一进行管理,Manager对象主要做的事就是定时产生汪星人。6)碰撞检测碰撞检测同样在每帧进行,主角喵星人是一个精灵,在游戏看来它只是一块矩形纹理,汪星人同样也是,即便有的地方是透明的。所以这里采用的方式是检测喵星人的位置和汪星人的位置,一旦距离低于我们设置的值就判定为碰撞。if(cc.pDistance(this.target.getPosition(),this.getPosition())&&&COLSIZE)&&&&...如果喵星人被打败后,显示游戏界面,即UI对象。在这里可以重新游戏,也可以进行微信分享。7)微信分享这么碉堡的游戏怎么能少得了微信分享功能呢?分享功能的代码可以在index.html中看到。Api.shareToFriend(wxFriend,&wxCallbacks);Api.shareToTimeline(wxData,&wxCallbacks);Api.shareToWeibo(wxData,&wxCallbacks);其中wxFriend.desc和wxData.desc表示内容描述,可以在游戏结束的时候会进行修改。其他字段同样也可以在这里进行修改,比如链接,图标等等。document.title&=&window.wxData.desc&=&"喵星刷屏!喵获得"+UI.score+"分,在众喵中排名"+(0|(percent*100))+"%,尼能超过喵吗!";document.title&=&window.wxFriend.desc&=&"我拿了"+UI.score+"分,战胜了"+&UI.pg&+"个汪,超越了"+(0|(percent*100))+"%的好友!你能超过我吗";4.总结可以看到,《来自喵星的你》只用了一个js文件就搞定了,使用Cocos2d-JS引擎可以高效快速方便地开发一个JS游戏,只要有了Cocos2d-JS和Cocos Code IDE,你也能轻松、快速地打造出一款趣味的优秀游戏。目前微信游戏平台已成为国内最高人气的平台之一,并且诞生出了许多成功作品。借助Cocos2d-JS和Cocos Code IDE,从此开发者可以告别繁琐的工序,让开发来得更有效率。也许下款微信大热之作就属于你哦!5.源码下载地址和效果图游戏地址:源码下载:游戏效果图:
「GetParty」
关注微信号,推送好文章
微信中长按图片即可关注> 用Cocos2d-JS制作一个类神经猫游戏《你是我的小羊驼》教程
用Cocos2d-JS制作一个类神经猫游戏《你是我的小羊驼》教程
一夜之间,微信上一款叫《围住神经猫》的小游戏火了。它的玩法很简单,用最少的步数把一只神经兮兮的猫围死。 7月22号上线以来,3天、500万用户和1亿访问,想必各位程序猿都按耐不住了,想实现自己的神经猫游戏。
在这篇教程里,我会教大家如何用Cocos2d-JS来实现一个神经猫这样的游戏。 让我们先看下游戏最后完成了的效果图:
你可能注意到了,神经猫换成了可爱的小羊驼:)
在线游戏地址:
源码地址:
三个界面基本上就是整个游戏的全部内容:
1.左边的是主界面,展示游戏名称以及主角,让玩家对游戏的整体画风有个大概的印象。
2.中间的是游戏界面,点击空格防止橙色六边形砖块来围堵小羊驼。
3.右边的是游戏成功或失败的界面。
整个游戏的主逻辑都在游戏界面中完成。
玩法是这样:
1.游戏初始化开始,小羊驼始终是站在地图中间,在地图的其他区域随机生产一些位置随机的砖块。
2.玩家点击一个空白区域,放置一个砖块来围堵羊驼。
3.羊驼AI寻路移动一步。
4.循环2和3,直到羊驼被围堵在一个圈里面(游戏成功),或羊驼到达地图边界(游戏失败)
整个游戏的思路理清楚了,接下来我们开始进入编码阶段。
开发环境与新建项目
本教程开发基于当前最新的. &(其他版本下载地址:)
下载引擎并解压到磁盘的某个目录。
打开控制台,输入下面的命令来新建项目。
$cd&cocos2d-js-v3.0-rc1/tools/cocos2d-console/bin&
$./cocos&new&-l&js&--no-native&
$cd&MyJSGame/&
$../cocos&run&-p&web&
环境搭建并不是这篇文章的重点,更详细的信息可以参考:《》
主界面实现
游戏的入口代码在main.js中,用编辑器打开并修改为下面的代码。
cc.game.onStart&=&function(){&
&&&&cc.view.adjustViewPort(true);&
&&&&if&(cc.sys.isMobile)&
&&&&&&&&cc.view.setDesignResolutionSize(320,500,cc.ResolutionPolicy.FIXED_WIDTH);&
&&&&else&cc.view.setDesignResolutionSize(320,480,cc.ResolutionPolicy.SHOW_ALL);&
&&&&cc.view.resizeWithBrowserSize(true);&
&&&&cc.LoaderScene.preload(resources,&function&()&{&
&&&&&&&&gameScene&=&new&GameScene();&
&&&&&&&&cc.director.runScene(gameScene);&
&&&&},&this);&
cc.game.run();&
关键点解析如下:
1.设置浏览器meta来适配屏幕,引擎内部会根据屏幕大小来设置meta的viewport值,会达到更好的屏幕适配效果。
2.针对手机浏览器和PC浏览器启用不同的分辨率适配策略。
3.预加载图片声音等资源。 cc.LoaderScene.preload会生成一个&加载中 x%&的界面,等待资源加载结束后,调用第二个参数传入的匿名函数。 对于基于html的游戏,页面是放在服务器端供浏览器下载的,为了获得流畅的用户体验,cc.LoaderScene.preload让浏览器先把远程服务器的资源缓存到本地。需要预加载的资源定义在src/Resources.js文件中。
4.启动游戏的第一个场景。
主界面的由两个层实现:
1.GameLayer层,游戏主逻辑层,在未初始化地图矩阵时,它只显示背景地图。
2.StartUI层,显示logo图片和开始游戏按钮。
GameScene的初始化代码如下:
var&GameScene&=&cc.Scene.extend({&
&&&&onEnter&:&function&()&{&
&&&&&&&&this._super();&
&&&&&&&&var&bg&=&new&cc.Sprite(res.bg);&
&&&&&&&&bg.attr({&
&&&&&&&&&&&&anchorX&:&0.5,&
&&&&&&&&&&&&anchorY&:&0.5,&
&&&&&&&&&&&&x&:&cc.winSize.width/2,&
&&&&&&&&&&&&y&:&cc.winSize.height/2&
&&&&&&&&});&
&&&&&&&&this.addChild(bg);&
&&&&&&&&layers.game&=&new&GameLayer();&
&&&&&&&&this.addChild(layers.game);&
&&&&&&&&layers.startUI&=&new&StartUI();&
&&&&&&&&this.addChild(layers.startUI);&
&&&&&&&&layers.winUI&=&new&ResultUI(true);&
&&&&&&&&layers.loseUI&=&new&ResultUI(false);&
&&&&&&&&layers.shareUI&=&new&ShareUI();&
由引擎提供的cc.Scene.extend方法,让js能实现高级面向对象语言的继承特性。 onEnter方法是场景初始化完成即将展示的消息回调,在onEnter中必须调用this._super();来确保Scene被正确的初始化。
整个游戏的设计只有一个scene,界面之间的切换由layer来实现,这可能不是一个最优的设计,但也提供另一种思路。 为了用layer来实现切换,全局变量layers存储了各层的一个实例。
GameLayer我们在下一章节中详细讲解。
StartUI的实现如下:
var&StartUI&=&cc.Layer.extend({&
&&&&ctor&:&function&()&{&
&&&&&&&&this._super();&
&&&&&&&&var&start&=&new&cc.Sprite(res.start);&
&&&&&&&&start.x&=&cc.winSize.width/2;&
&&&&&&&&start.y&=&cc.winSize.height/2&+&20;&
&&&&&&&&this.addChild(start);&
&&&&onEnter&:&function&()&{&
&&&&&&&&this._super();&
&&&&&&&&cc.eventManager.addListener({&
&&&&&&&&&&&&event:&cc.EventListener.TOUCH_ALL_AT_ONCE,&
&&&&&&&&&&&&onTouchesEnded:&function&(touches,&event)&{&
&&&&&&&&&&&&&&&&var&touch&=&touches[0];&
&&&&&&&&&&&&&&&&var&pos&=&touch.getLocation();&
&&&&&&&&&&&&&&&&if&(pos.y&&&cc.winSize.height/3)&{&
&&&&&&&&&&&&&&&&&&&&layers.game.initGame();&
&&&&&&&&&&&&&&&&&&&&layers.startUI.removeFromParent();&
&&&&&&&&&&&&&&&&}&
&&&&&&&&&&&&}&
&&&&&&&&},&this);&
cc.Layer.extend作用同cc.Scene.extend一样,只不过是一个扩展Scene,一个扩展Layer。ctor是Cocos2d-JS中的构造函数,在ctor中必须调用this._super();以确保正确的初始化。
在onEnter中,我们为StartUI层绑定事件监听,判断触摸点的位置坐标来触发scene切换。
细心的读者可能要问,为什么不用Menu控件? 当前的Cocos2d-JS版本已实现模块化,可以选择只加载游戏中用到的模块,已减少最终打包size。 为了不加入Menu模块,这里使用了最简单的触摸点坐标判断来实现通用的事情。
游戏界面的实现
橙色块的初始化
游戏地图区域是由9*9的六边形方块组成的,首先用InActive的图片初始化一边矩阵。相关代码如下:
var&ox&=&x&=&y&=&0,&odd&=&false,&block,&tex&=&this.batch.&
for&(var&r&=&0;&r&&&ROW;&r++)&{&
&&&&y&=&BLOCK_YREGION&*&r;&
&&&&ox&=&odd&*&OFFSET_ODD;&
&&&&for&(var&c&=&0;&c&&&COL;&c++)&{&
&&&&&&&&x&=&ox&+&BLOCK_XREGION&*&c;&
&&&&&&&&block&=&new&cc.Sprite(tex,&BLOCK2_RECT);&
&&&&&&&&block.attr({&
&&&&&&&&&&&&anchorX&:&0,&
&&&&&&&&&&&&anchorY&:&0,&
&&&&&&&&&&&&x&:&x,&
&&&&&&&&&&&&y&:&y,&
&&&&&&&&&&&&width&:&BLOCK_W,&
&&&&&&&&&&&&height&:&BLOCK_H&
&&&&&&&&});&
&&&&&&&&this.batch.addChild(block);&
&&&&odd&=&!&
每次循环odd改变,已实现上下错位的排布。 attr是Node基类的新方法,可以方便的一次性设置多个属性。
橙色方块的初始化是由initGame函数来完成。 先来看initGame的实现:
initGame&:&function()&{&
&&&&if&(this.inited)&return;&
&&&&this.player_c&=&this.player_r&=&4;&
&&&&this.step&=&0;&
&&&&for&(var&i&=&0,&l&=&this.active_nodes.&i&&&l;&i++)&{&
&&&&&&&&this.active_nodes[i].removeFromParent();&
&&&&this.active_nodes&=&[];&
&&&&for&(var&r&=&0;&r&&&ROW;&r++)&{&
&&&&&&&&for&(var&c&=&0;&c&&&COL;&c++)&{&
&&&&&&&&&&&&this.active_blocks[r][c]&=&false;&
&&&&&&&&}&
&&&&this.randomBlocks();&
&&&&this.player.attr({&
&&&&&&&&anchorX&:&0.5,&
&&&&&&&&anchorY&:&0,&
&&&&&&&&x&:&OFFSET_X&+&BLOCK_XREGION&*&this.player_c&+&BLOCK_W/2,&
&&&&&&&&y&:&OFFSET_Y&+&BLOCK_YREGION&*&this.player_r&-&5&
&&&&this.player.stopAllActions();&
&&&&this.player.runAction(this.moving_action);&
&&&&this.inited&=&true;&
要点解析如下:
1.为了方便逻辑处理,这里用了active_nodes和active_blocks来记录被激活的方块。在初始化矩阵前,需要清理上一次游戏已生成的橙色方块。active_nodes存储精灵实例,active_blocks记录精灵的矩阵坐标。
2.randomBlocks函数生成随机橙色砖块。 首先产生一个7-20的随机数,也就是确定橙色块的数量。然后循环确定每一个块的位置坐标,当然位置坐标也是随机确定的。
3.复位小羊驼的位置以及动画。
响应触摸事件
按照我们之前的分析,游戏界面初始化完成后,需要等待用户指令才能进行下一步的游戏。
相关代码如下:
cc.eventManager.addListener({&
&&&&event:&cc.EventListener.TOUCH_ALL_AT_ONCE,&
&&&&onTouchesBegan:&function&(touches,&event)&{&
&&&&&&&&var&touch&=&touches[0];&
&&&&&&&&var&pos&=&touch.getLocation();&
&&&&&&&&var&target&=&event.getCurrentTarget();&
&&&&&&&&if&(!target.inited)&return;&
&&&&&&&&pos.y&-=&OFFSET_Y;&
&&&&&&&&var&r&=&Math.floor(pos.y&/&BLOCK_YREGION);&
&&&&&&&&pos.x&-=&OFFSET_X&+&(r%2==1)&*&OFFSET_ODD;&
&&&&&&&&var&c&=&Math.floor(pos.x&/&BLOCK_XREGION);&
&&&&&&&&if&(c&&=&0&&&&r&&=&0&&&&c&&&COL&&&&r&&&ROW)&{&
&&&&&&&&&&&&if&(target.activateBlock(r,&c))&{&
&&&&&&&&&&&&&&&&target.step&++;&
&&&&&&&&&&&&&&&&target.movePlayer();&
&&&&&&&&&&&&}&
&&&&&&&&}&
},&this);&
1. cc.eventManager.addListener加入新的事件监听。
2. 设置事件监听模式为TOUCH_ALL_AT_ONCE。
3. 重写onTouchesBegan方法,判断触摸点的坐标,确定是哪个块被点击,并做响应的处理。 activateBlock方法在对应的矩阵位置加入橙色块,并更新状态数组。然后调用movePlayer移动小羊驼。
羊驼的移动
整个逻辑的关键是AI.js中的getDistance函数,
getDistance有6个参数:
1.羊驼所在行号
2.羊驼所在列号
3.前进方向,l_choices、r_choices、t_choices或b_choices
4.激活块的记录数组
5.辅助记录表,记录在寻路算法中某个节点是不是已经被访问过。
6.最短路径
返回值有三种情况:
1.羊驼到达地图边界,返回羊驼坐标和最短路径0
2.羊驼还在地图中,返回羊驼的下一个坐标值和最短路径cost
3. -1表示羊驼被圈住了,但可能可以移动。
getDistance的代码实现如下:
var&getDistance&=&function&(r,&c,&dir_choices,&activate_blocs,&passed,&cost)&{&
&&&&passed[r][c]&=&true;&
&&&&if&(r&&=&0&||&r&&=&ROW_MINUS_1&||&c&&=&0&||&c&&=&COL_MINUS_1)&{&
&&&&&&&&return&[r,&c,&cost];&
&&&&var&odd&=&(r&%&2&==&1)&?&1&:&0;&
&&&&var&choices&=&dir_choices[odd];&
&&&&var&nextr,&nextc,&&
&&&&for&(var&i&=&0,&l&=&choices.&i&&&l;&i++)&{&
&&&&&&&&nextr&=&r&+&choices[i][0];&
&&&&&&&&nextc&=&c&+&choices[i][4];&
&&&&&&&&if&(!activate_blocs[nextr][nextc]&&&&!passed[nextr][nextc])&{&
&&&&&&&&&&&&cost&++;&
&&&&&&&&&&&&result&=&getDistance(nextr,&nextc,&dir_choices,&activate_blocs,&passed,&cost);&
&&&&&&&&&&&&if&(result&!=&-1)&{&
&&&&&&&&&&&&&&&&result[0]&=&&
&&&&&&&&&&&&&&&&result[1]&=&&
&&&&&&&&&&&&&&&&return&&
&&&&&&&&&&&&}&
&&&&&&&&}&
&&&&return&-1;&
在羊驼移动函数movePlayer中,首先通过getDistance来判断上下左右4个方向,来寻找最佳移动方向。根据getDistance的返回结果做相应的逻辑处理。
游戏结束界面
游戏结束的两种情况,玩家胜利或失败。
在ResultUI的构造函数中,加入参数win,用来标识是否胜利。而胜利和失败仅仅是显示文字的区别,下方的两个按钮均一样。
在ctor中,根据是否胜利加载不同的图片来显示:
ctor&:&function&(win)&{&
&&&&&this._super();&
&&&&&this.win&=&&
&&&&&if&(win)&{&
&&&&&&&&&this.winPanel&=&new&cc.Sprite(res.succeed);&
&&&&&&&&&this.winPanel.x&=&cc.winSize.width/2;&
&&&&&&&&&this.winPanel.anchorY&=&0.2;&
&&&&&&&&&this.winPanel.y&=&cc.winSize.height/2;&
&&&&&&&&&this.addChild(this.winPanel);&
&&&&&else&{&
&&&&&&&&&this.losePanel&=&new&cc.Sprite(res.failed);&
&&&&&&&&&this.losePanel.x&=&cc.winSize.width/2;&
&&&&&&&&&this.losePanel.anchorY&=&0.2;&
&&&&&&&&&this.losePanel.y&=&cc.winSize.height/2;&
&&&&&&&&&this.addChild(this.losePanel);&
在onEnter中,根据是否胜利加载不同的文字描述:
if&(this.win)&{&
&&&&this.winPanel.removeAllChildren();&
&&&&var&w&=&this.winPanel.width,&h&=&this.winPanel.&
&&&&var&label&=&new&cc.LabelTTF(&继续刷屏!\n&+step+&步推倒我的小羊驼\n打败&+percent+&%朋友圈的人!\n你能超过我吗?&,&&宋体&,&20);&
&&&&label.x&=&w/2;&
&&&&label.y&=&h/4;&
&&&&label.textAlign&=&cc.LabelTTF.TEXT_ALIGNMENT_CENTER;&
&&&&label.width&=&w;&
&&&&label.color&=&cc.color(0,&0,&0);&
&&&&this.winPanel.addChild(label);&
&&&&this.losePanel.removeAllChildren();&
&&&&var&w&=&this.losePanel.width,&h&=&this.losePanel.&
&&&&label&=&new&cc.LabelTTF(&我滴小羊驼呀它又跑掉了\nT_T&快帮我抓回来!&,&&宋体&,&20);&
&&&&label.x&=&w/2;&
&&&&label.y&=&h/4+5;&
&&&&label.textAlign&=&cc.LabelTTF.TEXT_ALIGNMENT_CENTER;&
&&&&label.width&=&w;&
&&&&label.color&=&cc.color(0,&0,&0);&
&&&&this.losePanel.addChild(label,&10);&
&通知好友&按钮加载shareUI层,这个层其实是一个帮助指导界面,指示用户点击微信右上角的分享按钮进行分享。
gameScene.addChild(layers.shareUI,&100);&
target.win&?&share(1,&step,&percent)&:&share(2);&
&再来一次&实现很简单,调用initGame重新初始化矩阵,并移除ResultUI层。
layers.game.initGame();&
target.win&?&layers.winUI.removeFromParent()&:&layers.loseUI.removeFromParent();&
分享指导界面
在游戏结束界面我们加入了分享按钮。现在我们就来实现分享界面。
分享界面由分享图标和分享说明组成。这和前面的layer创建一样。很简单,唯一的区别是,分享界面是cc.LayerColor(cc.LayerColor支持设置层的颜色)的子类。下面是实现代码:
ctor:&function&()&{&
&&&&this._super(cc.color(0,&0,&0,&188),&cc.winSize.width,&cc.winSize.height);&
&&&&var&arrow&=&new&cc.Sprite(res.arrow);&
&&&&arrow.anchorX&=&1;&
&&&&arrow.anchorY&=&1;&
&&&&arrow.x&=&cc.winSize.width&-&15;&
&&&&arrow.y&=&cc.winSize.height&-&5;&
&&&&this.addChild(arrow);&
&&&&var&label&=&new&cc.LabelTTF(&请点击右上角的菜单按钮\n再点\&分享到朋友圈\&\n让好友们挑战你的分数!&,&&宋体&,&20,&cc.size(cc.winSize.width*0.7,&250),&cc.TEXT_ALIGNMENT_CENTER);&
&&&&label.x&=&cc.winSize.width/2;&
&&&&label.y&=&cc.winSize.height&-&100;&
&&&&label.anchorY&=&1;&
&&&&this.addChild(label);&
加入touch事件用于移除分享界面:
onEnter:&function&()&{&
&&&&this._super();&
&&&&cc.eventManager.addListener({&
&&&&&&&&event:&cc.EventListener.TOUCH_ONE_BY_ONE,&
&&&&&&&&onTouchBegan:&function&(touch,&event)&{&
&&&&&&&&&&&&layers.shareUI.removeFromParent();&
&&&&&&&&}&
&&&&},&this);&
我们需要的功能:
1.分享到微信朋友圈
2.分享给微信好友
3.分享到腾讯微博
4.关注指定用户
本功能已经有大神提供了完整的库,地址是: ,以下我们做一个简单的使用分析。
注:除特殊说明外,本小节实现均在文件 WeixinApi.js中。
现在我们实现的分享有,发给指定朋友,分享到朋友圈,分享到腾讯微博。对于不同的分享方式,实现方式大同小异,我们主要以分享到朋友圈为例。
我们分享的时候需要的数据有:appid,图片,链接,标题,文字内容,例如:
对应在代码中就需要以下数据:
&appid&:theData.appId&?&theData.appId&:&'',&
&img_url&:theData.imgUrl,&
&link&:theData.link,&
&desc&:theData.desc,&
&title&:theData.title,&&
为了得到数据,我们需要在GameScene.js中实现ResultUI的时候,将以上数据生成出来。 比如胜利时,我们需要显示:
var&label&=&new&cc.LabelTTF(&继续刷屏!\n&+step+&步推倒我的小羊驼\n打败&+percent+&%朋友圈的人!\n你能超过我吗?&,&&宋体&,&20);&
完成数据后,我们需要判断胜利或失败,并传回ui中显示:
target.win&?&share(1,&step,&percent)&:&share(2);&
为了监测分享的状态,无论分享成功与否我们回调都会上报状态,以便程序处理,我们需要的状态有:
1.用户取消分享
2.分享失败
3.分享成功 所以我们需要以下实现:
WeixinJSBridge.on('menu:share:timeline',&function&(argv)&{&
&&&&if&(callbacks.async&&&&callbacks.ready)&{&
&&&&&&&&window[&_wx_loadedCb_&]&=&callbacks.dataLoaded&||&new&Function();&
&&&&&&&&if(window[&_wx_loadedCb_&].toString().indexOf(&_wx_loadedCb_&)&&&0)&{&
&&&&&&&&&&&&window[&_wx_loadedCb_&]&=&new&Function();&
&&&&&&&&}&
&&&&&&&&callbacks.dataLoaded&=&function&(newData)&{&
&&&&&&&&&&&&window[&_wx_loadedCb_&](newData);&
&&&&&&&&&&&&shareTimeline(newData);&
&&&&&&&&};&
&&&&&&&&callbacks.ready&&&&callbacks.ready(argv);&
&&&&}&else&{&
&&&&&&&&callbacks.ready&&&&callbacks.ready(argv);&
&&&&&&&&shareTimeline(data);&
WeixinJSBridge
在微信上,通过公众平台推送给用户的文章,是在微信内部直接打开的,用的无外乎就是一个UIWebView控件(IOS上,Android上也差不多)。但特殊的是,微信官方在这里面加了一个默认的Js API--WeixinJSBridge,通过它,能直接在该页面上做分享操作。 以下代码,拿去玩吧:
WeixinJSBridge.on('menu:share:timeline',&function&(argv)&{&
&&&&if&(callbacks.async&&&&callbacks.ready)&{&
&&&&&&&&window[&_wx_loadedCb_&]&=&callbacks.dataLoaded&||&new&Function();&
&&&&&&&&if(window[&_wx_loadedCb_&].toString().indexOf(&_wx_loadedCb_&)&&&0)&{&
&&&&&&&&&&&&window[&_wx_loadedCb_&]&=&new&Function();&
&&&&&&&&}&
&&&&&&&&callbacks.dataLoaded&=&function&(newData)&{&
&&&&&&&&&&&&window[&_wx_loadedCb_&](newData);&
&&&&&&&&&&&&shareTimeline(newData);&
&&&&&&&&};&
&&&&&&&&callbacks.ready&&&&callbacks.ready(argv);&
&&&&}&else&{&
&&&&&&&&callbacks.ready&&&&callbacks.ready(argv);&
&&&&&&&&shareTimeline(data);&
最后,赶紧写点诱惑的东东,让用户分享出去吧,这是微信病毒传播的乐趣!
你可以在这里获取本教程的全部 & 。&

我要回帖

更多关于 u8远程接入客户端出错 的文章

 

随机推荐