Cocos2d-JS 制作微信应用分享 (有源码非教程)

最近用Cocos2d-JS(因为本人只会Cocos2d-JS)做一个简单的微信应用(其实是微信版的婚礼请帖),这里写一篇流水账,记录一下一些技术问题,免得以后浪费时间(这个算不上是教程,只是流水账而已)。

PS: 原文是我有Markdown格式写的,所以下面有些奇怪的符号,还有一些危险的字符:12:

1. 准备游戏引擎

现在的** Cocos2d-JS **已经支持在线定制模块的功能了,http://cocos2d-x.org/filecenter/jsbuilder/]

因为要制作的是一个一个移动端的App,那么容量大小必然是要控制在最小的,所以只选择自己想要的模块功能是必须的功能。

这里其实不用直接下载** Compressed ** 也就是压缩过的版本,原因如下:

  1. 缩过的版本不方便调试,这个是肯定的。

  2. 即使是定制的版本,里面也有很多不用的代码可以删除,移动端的东西么,体积越小越好。

  3. 我们最后可以把自己的代码和引擎js打包在一起,到时再一起压缩混淆就行了。

引擎准备好了,就可以开始敲代码了,这个没什么特别的,和平时些游戏一样,该怎么写就怎么写。

2. 代码的打包

代码敲完,本地测试基本功能后,就要准备部署了,部署之前,先把代码压缩一下。

这里Cocos已经提供了全面的工具,这里本人习惯将所有代码打包在一起,这里使用ant加一个简单的python的脚本(因为sublime里面可以直接 ** CMD+B ** 运行,很方便)。

ant用的 ** build.xml ** 文件:

<?xml version="1.0"?>
    <project name="Javascript compress project" basedir="path_to_your_app" default="compile">
        <taskdef name="jscomp" classname="com.google.javascript.jscomp.ant.CompileTask" classpath="compiler-1.7.jar"/>

        <target name="compile">
            <jscomp compilationLevel="simple" warning="quiet" debug="false" output="your_app.min.js" languagein="ECMASCRIPT5">
                <sources dir="${basedir}">
                    <file name="cocos2d-js-v3.7.js"/>
                    <file name="your_srouce_code_a"/>
                    <file name="your_srouce_code_b"/>
                    <file name="your_srouce_code_c"/>
                </sources>
            </jscomp>
        </target>
    </project>

部署编译用的python:

# coding=utf-8
    import subprocess
    import os

    if __name__ == "__main__":
        try:
            os.remove('your_app.min.js')
        except Exception, e:
            pass
        subprocess.call('ant -f build.xml', shell=True)

运行完以后,就会有一个 ** your_app.min.js **。

这样,** index.html ** 里面就只要:

<s_c_r_i_p_t_危险字符 cocos src="your_app.min.js"></s_c_r_i_p_t_危险字符>

如果不想要 ** project.json ** , 可以在 ** index.html ** 里面加入:


    <s_c_r_i_p_t_危险字符>
        document.ccConfig = {
            "debugMode"     : 0,
            "frameRate"     : 60,
            "id"            : "gameCanvas",
            "renderMode"    : 1,
            "jsList"        : ]
        };
    </s_c_r_i_p_t_危险字符>

本地跑一下试试,没问题的话就可以正式部署了。

3. 代码的部署

Hosting的地方很多,但是这里有一个要注意的:

因为要接入微信,使用微信的JSDSDK,所以** 国外的一些Hosting服务,可能会因为没有备案的原因,在后面微信公众号绑定JS接口安全域名的时候被拒 **。

这里推荐使用新浪的 ** http://www.sinacloud.com/] **,这个没有任何问题。

SAE上的代码部署简单的很:

  1. 注册一个新的应用,选php的

  2. git或者svn根据喜好选一个

  3. 把代码资源全部上传

  4. 到这里为止你就可以访问你的App了: ** http://yourapp.sinaapp.com **

4. 微信的准备

因为要接入微信平台,并且使用微信提供的一些功能的,所以免不了一坨准备工作。

我们这里要使用的是微信的http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html]。

  1. 注册一个公众号

  2. 在公众号后台设置中填写 ** JS接口安全域名 ** ,这里自然是 ** http://yourapp.sinaapp.com **

  3. 接着在 ** index.html ** 里面引入js:

`<s_c_r_i_p_t_危险字符 src="jweixin-1.0.0.js"></s_c_r_i_p_t_危险字符>`
  1. 初始化微信SDK, 这个是最麻烦的,不过为了使用一些微信的SDK,必须正确的配置所有参数:
wx.config({
            debug: false,
            appId: 'your_app_id',
            timestamp: timestamp,
            nonceStr: nonceStr,
            signature: signature,
            jsApiList: 
                'previewImage',
                'openLocation'
            ]
        });
 * appId - 公众号后台就有
  • jsApiList - 需要使用的微信功能列表,这个按需填写

  • timestamp / nonceStr / signature 这些是麻烦的东西,是为了做微信权限验证的。

5. 微信权限验证

为了用微信的一些功能,必须通过权限验证,首先可以用快速的方式测试,就是使用测试平台,生成需要的 ** timestamp / nonceStr / signature **。

这个Cocoachina有 http://www.cocoachina.com/bbs/read.php?tid=281137]。

将从测试平台获得的 ** timestamp / nonceStr / signature ** 填入上面的函数中,就应该可以顺利通过验证了。

但是这个是有缺点的,这里的生成 ** signature ** 的 ** ticket ** 每隔7200秒就会失效,就是说2个小时以后,** signature ** 就需要重新生成一次。

所以说微信官方的建议,是有一个自己的服务器,每隔两个小时重新生成一下 ** signature **,然后客户端用这个 ** signature ** 肯定是没问题的。

这里就再次用SAE做一个定时刷新微信 ** signature ** 的小程序。

  1. SAE上注册一个Python的应用

  2. 上传一个Python脚本(index.wsgi)用于请求新的 ** signature ** 代码很简单,按照流程办事:

import urllib2
        import hashlib
        import json
        import time
        import sae
        import web

        appid = "??????"
        secret = "??????"

        urls = ('/','Index','/weixin','RenewSignature')
        class Index:
            def GET(self):
                pass

        class RenewSignature:
            def GET(self):
                # 搞定token
                url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + appid + '&secret=' + secret
                response = urllib2.urlopen(url)
                html = response.read()
                tokeninfo = json.loads(html)
                token = tokeninfo'access_token']

                # 搞定ticket
                url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + token + '&type=jsapi'
                response = urllib2.urlopen(url)
                html = response.read()
                ticketinfo = json.loads(html)
                ticket = ticketinfo'ticket']

                # 搞定jsapi_ticket
                noncestr = '1234567890'
                timestamp = int(time.time())
                print 'timestamp: ' + str(timestamp)
                jsapi_ticket = 'jsapi_ticket=' + ticket + '&noncestr=' + noncestr + '&timestamp=' + str(timestamp) + '&url=http://yourapp.sinaapp.com/'

                # 搞定signature
                signature = hashlib.sha1(jsapi_ticket).hexdigest()

        app = web.application(urls, globals()).wsgifunc() 
        application = sae.create_wsgi_app(app)
  1. 利用SAE的Corn服务,定义一个2小时运行一次的定时任务, 创建一个config.yaml
cron:
        - description: weixin
          url: /weixin
          timezone: Beijing
          schedule: "2 * * * *"

这样每隔2个小时,我们就能有一个新的,有效的 ** signature ** 了。

6. Parse BAAS

前面获取到的** signature ** 以及刷新的 ** timestamp ** 都是需要服务器保存下来稍后发送给客户端的。

这里你可以在上面的脚本里加点儿代码搞定,很简单的事情。

这里因为我后面会用到Parse的一些功能,所以我的选择是,把 ** signature ** 和 ** timestamp ** 保存到 ** Parse **,稍后客户端直接向 ** Parse ** 请求最新的数据就行。

不清楚 ** Parse ** 的可以 http://parse.com/]。

继续按流程办事:

  1. 在 ** Parse ** 创建一个应用

  2. 创建一张表,用于保存从 SAE推过来的 ** signature ** 和 ** timestamp **

    表 ** WeixinSignature ** ,两个字段 ** signature ** , ** timestamp **

  3. 在 ** Parse Cloud ** 上新建一个函数,用于把接收到的数据保存到表里

Parse.Cloud.define("updateWeixinSignature", function(request, response) {
            var WeixinSignature = Parse.Object.extend("WeixinSignature");
            var query = new Parse.Query(WeixinSignature);
            query.get("DATA_ID", {
                success: function(weixinSignature) {
                    weixinSignature.s_e_t_危险字符("timestamp", request.params.timestamp);
                    weixinSignature.s_e_t_危险字符("signature", request.params.signature);
                    weixinSignature.save();
                    response.success();
                },
                error: function(object, error) {
                    console.error(error);
                    response.error("ERR_REQUEST_FAILED");
                }
            });
        });

  1. 在上面的Python脚本的最后加几行,利用 ** Parse REST API ** 把数据传送过去
connection = httplib.HTTPSConnection('api.parse.com', 443)
        connection.connect()
        connection.request(
            'POST', 
            '/1/functions/updateWeixinSignature', 
            json.dumps(
            {
               "timestamp": timestamp,
               "signature": signature
            }), 
            {
               "X-Parse-Application-Id": APPLICATION_ID,
               "X-Parse-REST-API-Key": REST_API_KEY,
               "Content-Type": "application/json"
            }
        )
        results = json.loads(connection.getresponse().read())
        print results
  1. 这样每隔两个小时 ** Parse ** ** WeixinSignature ** 中就会有更新过的数据了。

  2. 接着在 ** Parse Cloud ** 里加一个函数,返回最细你的 ** WeixinSignature ** 数据,给客户端用:

Parse.Cloud.define("queryWeixinSignature", function(request, response) {
            var WeixinSignature = Parse.Object.extend("WeixinSignature");
            var query = new Parse.Query(WeixinSignature);
            query.get("DATA_ID", {
                success: function(weixinSignature) {
                    response.success(JSON.stringify({
                        "timestamp": weixinSignature.get("timestamp"), 
                        "signature": weixinSignature.get("signature")
                        }
                    ));
                },
                error: function(object, error) {
                    response.error("ERR_REQUEST_FAILED");
                }
            });
        });
  1. 最后自然是从客户端请求这些数据了,这里利用了 ** Parse JSSDK ** :
<s_c_r_i_p_t_危险字符 src="src/parse-1.5.0.min.js"></s_c_r_i_p_t_危险字符>
        Parse.initialize("APPLICATION_ID", "JS_KEY");
        Parse.Cloud.run('queryWeixinSignature', {}, {
            success: function(response) {
                var data = JSON.parse(response);
                wx.config({
                    debug: false,
                    appId: 'your_app_id',
                    timestamp: data.timestamp,
                    nonceStr: '1234567890',
                    signature: data.signature,
                    jsApiList: 
                        'previewImage',
                        'openLocation'
                    ]
                });
                wx.ready(function() {
                });
                wx.error(function(res) {
                });
            },
            error: function(error) {
            }
        });

到这里,微信权限验证的麻烦事情就搞定了。

7. 其他一些杂事

  • App应用全屏显示

    这个可以在初始化的时候,把canvas大小设置为屏幕大小

var winWidth = 0;
        if (window.innerWidth)
            winWidth = window.innerWidth;
        else if ((document.body) && (document.body.clientWidth))
            winWidth = document.body.clientWidth;
        var winHeight = 0;
        if (window.innerHeight)
            winHeight = window.innerHeight;
        else if ((document.body) && (document.body.clientHeight))
            winHeight = document.body.clientHeight;
        if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth) {
            winWidth = document.documentElement.clientWidth;
            winHeight = document.documentElement.clientHeight;
        }
        document.getElementById("container").style.width = winWidth + "px";
        document.getElementById("container").style.height = winHeight + "px";
还不能忘了下面的(这里还禁止了用户缩放):
<meta name="viewport" content="width=device-width, user-scalable=no"/> 
  • 播放音乐

    这个网上搜索了一下,貌似cc.audio播放是有各种问题,自己也试了播放mp3,ogg等格式的,都无法在iOS和Android上同时完美播放。所以用了网上搜到的方法:

<div class="hide">
            <audio id="myAudio" loop="true"></audio>
        </div>
        
        var audio = document.getElementById("myAudio");
        audio.src = "res/???.mp3";
        audio.play();
这样在iOS和Android上都能正常播放。
  • 混用一些html元素

    ** Cocos2d-JS ** 中没有完美的 ** TextArea ** 控件,有些控件有,但是为了使用需要引入不少源码,比如ccui.Widget,cc.Control之类的。

    因为自己的应用不需要对应Native,只是Web端的,所以这里直接用html里面的控件更加省力。

    其实 ** Cocos2d-JS ** 里面提供了将html控件元素转换成cc.Node的方法,比如下面的方式可以创建一个TextArea控件:

   var DOMUtil = function() {}
     DOMUtil.createTextArea = function(......) {
     
         var textArea = cc.newElement("textarea");
         textArea.style.fontSize = fontSize + "px";
         textArea.style.color = fontColor;
         textArea.style.background = background;
         textArea.style.width = "100%";
         textArea.style.height = "100%";
         textArea.style.resize = "none";

         var textAreaDOM = new cc.Sprite();
         textAreaDOM.draw = function () {};
         textArea._textAreaDOM = textArea;
         parent.addChild(textAreaDOM, 0, "textAreaDOM");
         cc.DOM.convert(textAreaDOM);
         textAreaDOM.dom.appendChild(textArea);
         textAreaDOM.dom.style.width =  width + "px";
         textAreaDOM.dom.style.height = height + "px";
         textAreaDOM.canvas.remove();
     textAreaDOM.s_e_tPosition_危险字符(posx, posy);

         return textArea;
     };

使用的时候只需要:

this._textArea =  DOMUtil.createTextArea(this, ......);
 // 可以用cocos的方式添加监听,比如实现CMD+ENTER提交内容的功能
 cc._addEventListener(
 this._textArea,
            "keypress", 
 function (e) {
             if (e.keyCode === cc.KEY.enter && e.ctrlKey == true) {
             // 提交内容
             e.preventDefault();
             }
        );    
  • 再优化一下
    微信内置的浏览器还是比较cuo的,所以我们的App能优化还是多优化一下

  • 引擎源码能删多少就删多少,在App差不多写完后,可以把引擎里没用到的源码都删掉,再和自己App代码一起打包,越小越好

  • 图片优化,该压缩解压缩,可以放到熊猫网上去压缩压缩 http://tinypng.com]

  • 有些好性能的酷炫效果,能不用就不用

最后总结下,** Cocos2d-JS ** 做做接入微信的小游戏,小应用之类的还是很不错的,只是要注意性能方面有没有问题。配合SAE,Parse,Heroku之类一些好用的服务,如果你会javascript,python,php的话,就可以写一些简单的服务器逻辑。

最后打个小广告,本人的独立游戏 http://5minsmystery.parseapp.com] v3.0发布了,有兴趣的可以玩玩。

下次有机会,也准备写一篇流水账总结一下,分享一些好的服务,应用,自动化脚本,Parse的深入应用,node-webkit 移植桌面版本,Cocos2d-JS 简单的新手引导的实现,等等。

PS: 格式比较难看,如果需要PDF版的话,可以戳下面我的微博去下载:14:


作者: SuperSuRaccoon

微博: http://weibo.com/supersuraccoon

QQ: 453951749

博客: http://supersuraccoon-cocos2d.com]