最近用Cocos2d-JS(因为本人只会Cocos2d-JS)做一个简单的微信应用(其实是微信版的婚礼请帖),这里写一篇流水账,记录一下一些技术问题,免得以后浪费时间(这个算不上是教程,只是流水账而已)。
PS: 原文是我有Markdown格式写的,所以下面有些奇怪的符号,还有一些危险的字符
1. 准备游戏引擎
现在的** Cocos2d-JS **已经支持在线定制模块的功能了,http://cocos2d-x.org/filecenter/jsbuilder/]
因为要制作的是一个一个移动端的App,那么容量大小必然是要控制在最小的,所以只选择自己想要的模块功能是必须的功能。
这里其实不用直接下载** Compressed ** 也就是压缩过的版本,原因如下:
-
缩过的版本不方便调试,这个是肯定的。
-
即使是定制的版本,里面也有很多不用的代码可以删除,移动端的东西么,体积越小越好。
-
我们最后可以把自己的代码和引擎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上的代码部署简单的很:
-
注册一个新的应用,选php的
-
git或者svn根据喜好选一个
-
把代码资源全部上传
-
到这里为止你就可以访问你的App了: ** http://yourapp.sinaapp.com **
4. 微信的准备
因为要接入微信平台,并且使用微信提供的一些功能的,所以免不了一坨准备工作。
我们这里要使用的是微信的http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html]。
-
注册一个公众号
-
在公众号后台设置中填写 ** JS接口安全域名 ** ,这里自然是 ** http://yourapp.sinaapp.com **
-
接着在 ** index.html ** 里面引入js:
`<s_c_r_i_p_t_危险字符 src="jweixin-1.0.0.js"></s_c_r_i_p_t_危险字符>`
- 初始化微信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 ** 的小程序。
-
SAE上注册一个Python的应用
-
上传一个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 + '×tamp=' + 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)
- 利用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/]。
继续按流程办事:
-
在 ** Parse ** 创建一个应用
-
创建一张表,用于保存从 SAE推过来的 ** signature ** 和 ** timestamp **
表 ** WeixinSignature ** ,两个字段 ** signature ** , ** timestamp **
-
在 ** 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");
}
});
});
- 在上面的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
-
这样每隔两个小时 ** Parse ** ** WeixinSignature ** 中就会有更新过的数据了。
-
接着在 ** 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");
}
});
});
- 最后自然是从客户端请求这些数据了,这里利用了 ** 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版的话,可以戳下面我的微博去下载
作者: SuperSuRaccoon
微博: http://weibo.com/supersuraccoon
QQ: 453951749