利用Phaser开发微信小游戏(排行榜小结)
小游戏中的开放数据域可用来保存游戏数据,可实现排行榜等功能,以下是我在项目中的实现方式,提供参考:
{
“deviceOrientation”: “portrait”,
"openDataContext":"src/myOpenDataContext"
}
Phaser.XTexture = function(xCanvas,x,y,w,h){ return new PIXI.Texture(new PIXI.BaseTexture(xCanvas),new PIXI.Rectangle(x,y,w,h)); };
var openDataContext = wx.getOpenDataContext();
var sharedCanvas = openDataContext.canvas;
var pad = game.add.sprite(0,100, Phaser.XTexture(sharedCanvas,0,0,150,100));
openDataContext.postMessage({
action: 'get',
data: {
gameAspect: [game.width, game.height],
score: Score
},
});
wx.onMessage((data) => {
switch (data.action) {
case 'save':
wx.setUserCloudStorage({
KVDataList: [{ key: 'data', value: data.data.toString() }, { key: 'data', value: data.data.toString() }],
success: function () {
console.log('“Save OK …”');
}
});
break;
case 'get':
getUserData(data);
break;
case 'getGrp':
getGrpData(data);
break;
case 'close':
clearShareCanvas(data);
break;
}
})
需要注意的是主域虽然可以操作sharedCanvas,但是主域得不到数据,主域只能向开放域传递一些基本数据,只能画一些背景边框,数据只能在开放域中画。
下面就我觉得比较几个重要的地方说下:
这里排行榜的显示,我用了两个Canvas
主域中可以通过:
var sharedCanvas = openDataContext.canvas;得到。
var sharedCanvas = wx.getSharedCanvas();
var ctx = sharedCanvas.getContext('2d');
我把排行榜的静态数据绘制在SharedCanvas中,这里说的静态数据是指:排行榜的背景,标题,文字,用户进入后显示头像等等。
第二个canvas是我自己创建的canvas,是用来绘制用户排行信息,比如用户排名,用户分数,用户头像等等。
itemCanvas=wx.createCanvas();
itemCtx = itemCanvas.getContext('2d');
var avatarImg = wx.createImage();
avatarImg.src = res.data[i].avatarUrl;
avatarImg.onload = (function (cvs, avatarImage, i) {
return function () { cvs.drawImage(avatarImage, data.data.gameAspect[0] * 0.11, data.data.gameAspect[0] * 0.1 + (i + 1) * 50, 35, 35); }
})(itemCtx, avatarImg, i); //头像
itemCtx.fillStyle = "rgb(250, 250, 250)";
itemCtx.font = "16px Arial";
itemCtx.textAlign = "left";
itemCtx.textBaseline = "top";
itemCtx.fillText(i + 1, data.data.gameAspect[0] * 0.8, data.data.gameAspect[0] * 0.1 + 6 + (i + 1) * 50); // 名次
itemCtx.fillText(res.data[i].nickname, data.data.gameAspect[0] * 0.23, data.data.gameAspect[0] * 0.1 + 6 + (i + 1) * 50); // 昵称
itemCtx.fillText(res.data[i].KVDataList[0].value, data.data.gameAspect[0] * 0.6, data.data.gameAspect[0] * 0.1 + 6 + (i + 1) * 50); // 分数
我们把用户排行信息绘制到自建的canvas上之后,还需要通过SharedCanvas把他显示在屏幕上
var sharedCanvas = wx.getSharedCanvas();
var context = sharedCanvas.getContext('2d');
context.drawImage(itemCanvas, 0, y, 750 - 80 * 2, res.windowHeight * 0.5, 0,
res.windowHeight * 0.35, 750 - 80 * 2, res.windowHeight * 0.5);
res.data.sort(sorter); // 先排个序
// 排序函数(降序)
var sorter = function (data1, data2) {
var num1 = parseInt(data1.KVDataList[0].value);
var num2 = parseInt(data2.KVDataList[0].value);
if (num1 > num2) {
return -1;
} else if (num1 < num2) {
return 1; //返回值大于0则交换两数的位置
} else {
return 0;
}
}
排行数据的滑动采用原生监听手势滑动方法实现的,通过监听onTouchMove方法得到滑动距离,清除数据区域后,根据这个距离重新绘制数据区域,在监听结束后判断滑动的上限和下限设置下就可以了。
let startY = undefined, moveY = 0;
// 触摸移动事件
wx.onTouchMove(e => {
let touch = e.touches[0];
// 触摸移动第一次触发的位置
if (startY === undefined) {
startY = touch.clientY + moveY;
}
moveY = startY - touch.clientY;
reDrawItem(moveY);
});
wx.onTouchEnd(e => {
startY = undefined;
if (moveY < 0) { // 到顶
moveY = 0;
} else if (moveY > itemCanvas.height*0.65) { // 到底
moveY = itemCanvas.height * 0.65;
}
reDrawItem(moveY);
});
开放域index.js的完整代码如下:
var itemCanvas, itemCtx;
wx.onMessage((data) => {
switch (data.action) {
case 'save':
wx.setUserCloudStorage({
KVDataList: [{ key: 'data', value: data.data.toString() }, { key: 'data', value: data.data.toString() }],
success: function () {
console.log('“Save OK …”');
}
});
break;
case 'get':
getUserData(data);
break;
case 'getGrp':
getGrpData(data);
break;
case 'close':
clearShareCanvas(data);
break;
}
})
function loadRes(data,rankTitle) {
Init();
itemCanvas=wx.createCanvas();
itemCtx = itemCanvas.getContext('2d');
// itemCanvas.width = data.data.gameAspect[0]-2*data.data.gameAspect[0] * 0.1;
// itemCanvas.height = data.data.gameAspect[1] -2*data.data.gameAspect[0] * 0.6+100;
var sharedCanvas = wx.getSharedCanvas();
var ctx = sharedCanvas.getContext('2d');
const bg = wx.createImage();
bg.src = "assets/img_480/bg.png";
bg.onload = () => {
ctx.drawImage(bg, 0, 0, data.data.gameAspect[0], data.data.gameAspect[1]); //绘制主域的背景
const backBtn = wx.createImage();
backBtn.src = 'assets/img_480/back.png';
backBtn.onload = () => {
ctx.drawImage(backBtn, 5, 10, 40, 40); //返回按钮
}
const rankBg = wx.createImage();
rankBg.src = 'assets/img_480/pkk.png';
rankBg.onload = () => {
ctx.drawImage(rankBg, (data.data.gameAspect[0] - data.data.gameAspect[0] * 0.9) / 2, (data.data.gameAspect[1] - data.data.gameAspect[1] * 0.6) / 2, data.data.gameAspect[0] * 0.9, data.data.gameAspect[1] * 0.7); //排行榜背景
console.log('wid=' + (data.data.gameAspect[0] - data.data.gameAspect[0] * 0.9) / 2);
console.log('height=' + (data.data.gameAspect[1] - data.data.gameAspect[1] * 0.6) / 2);
ctx.fillStyle = "rgb(0, 250, 0)";
ctx.font = "22px Arial";
ctx.textAlign = "left";
ctx.fillText(rankTitle, (data.data.gameAspect[0] - data.data.gameAspect[0] * 0.8) / 2, (data.data.gameAspect[1] - data.data.gameAspect[1] * 0.46) / 2);
ctx.font = "15px Arial";
ctx.fillStyle = "rgb(250, 250, 0)";
ctx.fillText("头像", data.data.gameAspect[0] * 0.11, data.data.gameAspect[1] * 0.315);
ctx.fillText("用户名", data.data.gameAspect[0] * 0.25, data.data.gameAspect[1] * 0.315);
ctx.fillText("分数", data.data.gameAspect[0] * 0.59, data.data.gameAspect[1] * 0.315);
ctx.fillText("排名", data.data.gameAspect[0] * 0.79, data.data.gameAspect[1] * 0.315);
const qunPaiHang=wx.createImage();
qunPaiHang.src ="assets/img_480/share.png";
qunPaiHang.οnlοad=(function(ctx,img){
return function(){
ctx.drawImage(img, data.data.gameAspect[0] * 0.1, data.data.gameAspect[1] - data.data.gameAspect[1] *0.1);
}
})(ctx, qunPaiHang);
}
}
}
function getGrpData(data){
console.log("getGrp is Execute");
loadRes(data,'群排行');
var sharedCanvas=wx.getSharedCanvas();
var ctx=sharedCanvas.getContext('2d');
var avatarURl;
wx.getUserInfo({
openIdList: ['selfOpenId'],
lang: 'zh_CN',
success: function (res) {
avatarURl = res.data[0].avatarUrl;
const avatarImg = wx.createImage();
avatarImg.src = avatarURl;
avatarImg.onload = () => { //绘制头像
ctx.drawImage(avatarImg, (data.data.gameAspect[0] - data.data.gameAspect[0] * 0.2) / 2 + 15, 40, 50, 50);
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "22px Arial";
ctx.textAlign = "left";
ctx.fillText(res.data[0].nickName, (data.data.gameAspect[0] - data.data.gameAspect[0] * 0.2) / 2, 120);
//绘制用户名
};
wx.getGroupCloudStorage({
shareTicket: data.data.shareTicket,
keyList:['data'],
success:function(res){
res.data.sort(sorter); // 先排个序
for (let i = 0; i < res.data.length; i++) {
var avatarImg = wx.createImage();
avatarImg.src = res.data[i].avatarUrl;
avatarImg.onload = (function (cvs, avatarImage, i) {
return function () { cvs.drawImage(avatarImage, data.data.gameAspect[0] * 0.11, data.data.gameAspect[0] * 0.1 + (i + 1) * 50, 35, 35); }
})(itemCtx, avatarImg, i); //头像
itemCtx.fillStyle = "rgb(250, 250, 250)";
itemCtx.font = "16px Arial";
itemCtx.textAlign = "left";
itemCtx.textBaseline = "top";
itemCtx.fillText(i + 1, data.data.gameAspect[0] * 0.8, data.data.gameAspect[0] * 0.1 + 6 + (i + 1) * 50); // 名次
itemCtx.fillText(res.data[i].nickname, data.data.gameAspect[0] * 0.23, data.data.gameAspect[0] * 0.1 + 6 + (i + 1) * 50); // 昵称
itemCtx.fillText(res.data[i].KVDataList[0].value, data.data.gameAspect[0] * 0.6, data.data.gameAspect[0] * 0.1 + 6 + (i + 1) * 50); // 分数
reDrawItem(0);
}
},
fail:function(err){
if(err){
console.log(err);
}
}
});
}
})
}
function getUserData(data) {
loadRes(data,'好友排行');
var sharedCanvas = wx.getSharedCanvas();
var ctx = sharedCanvas.getContext('2d');
var avatarURl;
wx.getUserInfo({
openIdList: ['selfOpenId'],
lang: 'zh_CN',
success: function (res) {
console.log('success', res.data)
avatarURl = res.data[0].avatarUrl;
const avatarImg = wx.createImage();
avatarImg.src = avatarURl;
avatarImg.onload = () => { //绘制头像
ctx.drawImage(avatarImg, (data.data.gameAspect[0] - data.data.gameAspect[0] * 0.2) / 2 + 15, 40, 50, 50);
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "22px Arial";
ctx.textAlign = "left";
ctx.fillText(res.data[0].nickName, (data.data.gameAspect[0] - data.data.gameAspect[0] * 0.2) / 2, 120);
//绘制用户名
};
wx.getUserCloudStorage({
keyList: ['data'],
success: function (getData) {
var dataValue = getData.KVDataList[0].value;
if (data.data.score > dataValue) { //如果打破历史纪录,将新纪录保存
wx.setUserCloudStorage({
KVDataList: [{ key: 'data', value: data.data.score.toString() }],
success: function () {
console.log('“打破记录,新数据已保存”');
}
});
} else {
wx.getFriendCloudStorage({
keyList: ['data'],
success: function (res) {
// var shareCanvas = wx.getSharedCanvas();
// var ctx = shareCanvas.getContext('2d');
res.data.sort(sorter); // 先排个序
for (let i = 0; i < res.data.length; i++) {
var avatarImg = wx.createImage();
avatarImg.src = res.data[i].avatarUrl;
avatarImg.onload = (function (cvs, avatarImage, i) {
return function () { cvs.drawImage(avatarImage, data.data.gameAspect[0] * 0.11, data.data.gameAspect[0] * 0.1 + (i + 1) * 50, 35, 35); }
})(itemCtx, avatarImg, i); //头像
itemCtx.fillStyle = "rgb(250, 250, 250)";
itemCtx.font = "16px Arial";
itemCtx.textAlign = "left";
itemCtx.textBaseline = "top";
itemCtx.fillText(i + 1, data.data.gameAspect[0] * 0.8, data.data.gameAspect[0] * 0.1 +6+ (i+1) * 50); // 名次
itemCtx.fillText(res.data[i].nickname, data.data.gameAspect[0] * 0.23, data.data.gameAspect[0] * 0.1 + 6 + (i + 1) * 50); // 昵称
itemCtx.fillText(res.data[i].KVDataList[0].value, data.data.gameAspect[0] * 0.6, data.data.gameAspect[0] * 0.1 + 6 + (i + 1) * 50); // 分数
reDrawItem(0);
}
}
});
}
}
});
},
fail: (res) => {
reject(res)
}
})
}
function clearShareCanvas(data) {
var sharedCanvas = wx.getSharedCanvas();
var ctx = sharedCanvas.getContext('2d');
ctx.clearRect(0, 0, data.data.gameAspect[0], data.data.gameAspect[1]);
var itemCtx = itemCanvas.getContext('2d');
itemCtx.clearRect(0, 0, data.data.gameAspect[0], data.data.gameAspect[1]);
wx.offTouchMove();
wx.offTouchEnd();
}
// 排序函数(降序)
var sorter = function (data1, data2) {
var num1 = parseInt(data1.KVDataList[0].value);
var num2 = parseInt(data2.KVDataList[0].value);
if (num1 > num2) {
return -1;
} else if (num1 < num2) {
return 1; //返回值大于0则交换两数的位置
} else {
return 0;
}
}
function Init()
{
let startY = undefined, moveY = 0;
// 触摸移动事件
wx.onTouchMove(e => {
let touch = e.touches[0];
// 触摸移动第一次触发的位置
if (startY === undefined) {
startY = touch.clientY + moveY;
}
moveY = startY - touch.clientY;
reDrawItem(moveY);
});
wx.onTouchEnd(e => {
startY = undefined;
if (moveY < 0) { // 到顶
moveY = 0;
} else if (moveY > itemCanvas.height*0.65) { // 到底
moveY = itemCanvas.height * 0.65;
}
reDrawItem(moveY);
});
}
// 因为头像绘制异步的问题,需要重新绘制
function reDrawItem(y) {
console.log("vv"+y);
var sharedCanvas = wx.getSharedCanvas();
var context = sharedCanvas.getContext('2d');
wx.getSystemInfo({
success: function(res) {
context.clearRect(res.windowWidth * 0.1, res.windowHeight * 0.35, res.windowWidth * 0.8, res.windowHeight*0.5);
context.fillStyle = 'rgb(100,100,100)';
context.fillRect(res.windowWidth * 0.1, res.windowHeight * 0.35, res.windowWidth * 0.8, res.windowHeight*0.5);
context.drawImage(itemCanvas, 0, y, 750 - 80 * 2, res.windowHeight * 0.5, 0, res.windowHeight * 0.35, 750 - 80 * 2, res.windowHeight * 0.5);
//drawImage(画布,距离左,距离上,宽度缩放,高度缩放,渲染起始点X,渲染起始点Y,横向缩放显示,纵向缩放);
// context.drawImage(itemCanvas, 0, y);
// requestAnimationFrame(reDrawItem(0));
},
})
}
以上是我用Phaser进行的最初的测试版本,经后来的开发测试发现:开放域中的nickName,avatarUrl,openId,存储的数据等是可以传递到主域中使用的,不知道这个是不是微信的一个Bug,微信官方api 中说的是数据只能在开放域中使用,是对数据的一种保护,那么既然开放域中的这些重要数据都可以传递出来,那么微信又是在保护什么样的数据呢?这里如果有大神知道的话,可以下方评论或者邮箱留言告诉我哦!
下面贴出思路:
//开放域中
itemCanvas=wx.createCanvas();
itemCtx = itemCanvas.getContext('2d');
var userDataArry=[];
//中间 数据保存数组中省略...
itemCtx.canvas.usersData=userDataArry;
//主域中
var openDataContext=wx.getOpenDataContext();
var shareCanvas=openDataContext.canvas;
var userData=shareCanvas.usersData;
for(let i=0;i