版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/CJB_King/article/details/81502141
利用Phaser开发微信小游戏(排行榜小结)
小游戏中的开放数据域可用来保存游戏数据,可实现排行榜等功能,以下是我在项目中的实现方式,提供参考:
一.开发前的配置
1、参考官方文档,在game.json中增加openDataContext的配置项,并创建相应的open目录和index.js文件:
{
“deviceOrientation”: “portrait”,
"openDataContext":"src/myOpenDataContext"
}
2、在小游戏版的phaser引擎文件的合适位置增加:
代码语言:javascript复制Phaser.XTexture = function(xCanvas,x,y,w,h){ return new PIXI.Texture(new PIXI.BaseTexture(xCanvas),new PIXI.Rectangle(x,y,w,h)); };
3、在游戏场景中创建一个以sharedCanvas作为Texture的精灵:
代码语言:javascript复制var openDataContext = wx.getOpenDataContext();
var sharedCanvas = openDataContext.canvas;
var pad = game.add.sprite(0,100, Phaser.XTexture(sharedCanvas,0,0,150,100));
4、用openDataContext.postMessage()发送通信数据:
代码语言:javascript复制 openDataContext.postMessage({
action: 'get',
data: {
gameAspect: [game.width, game.height],
score: Score
},
});
5、在index.js中设计通信数据接收和处理逻辑(这个index.js就像服务端):
代码语言:javascript复制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
1.SharedCanvas这是开放域的并且能够在主域中拿到的canvas,
主域中可以通过:
var sharedCanvas = openDataContext.canvas;得到。
代码语言:javascript复制 var sharedCanvas = wx.getSharedCanvas();
var ctx = sharedCanvas.getContext('2d');
我把排行榜的静态数据绘制在SharedCanvas中,这里说的静态数据是指:排行榜的背景,标题,文字,用户进入后显示头像等等。
2.自建Canvas
第二个canvas是我自己创建的canvas,是用来绘制用户排行信息,比如用户排名,用户分数,用户头像等等。
代码语言:javascript复制 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把他显示在屏幕上
代码语言:javascript复制 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);
3.排行榜的排序:
代码语言:javascript复制 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;
}
}
4.数据的滑动
排行数据的滑动采用原生监听手势滑动方法实现的,通过监听onTouchMove方法得到滑动距离,清除数据区域后,根据这个距离重新绘制数据区域,在监听结束后判断滑动的上限和下限设置下就可以了。
代码语言:javascript复制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的完整代码如下:
代码语言:javascript复制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 中说的是数据只能在开放域中使用,是对数据的一种保护,那么既然开放域中的这些重要数据都可以传递出来,那么微信又是在保护什么样的数据呢?这里如果有大神知道的话,可以下方评论或者邮箱留言告诉我哦!
下面贴出思路:
代码语言:javascript复制//开放域中
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<userData.length;i ){
console.log(userData[i]); //这里可以打印出开放域中传递出来的数据
}