利用Phaser开发微信小游戏

发布时间:2025-12-09 11:47:04 浏览次数:1

版权声明:本文为博主原创文章,遵循 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引擎文件的合适位置增加:

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的精灵:

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()发送通信数据:

    openDataContext.postMessage({      action: 'get',      data: {        gameAspect: [game.width, game.height],        score: Score      },    });

5、在index.js中设计通信数据接收和处理逻辑(这个index.js就像服务端):

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;得到。

 var sharedCanvas = wx.getSharedCanvas();  var ctx = sharedCanvas.getContext('2d');

我把排行榜的静态数据绘制在SharedCanvas中,这里说的静态数据是指:排行榜的背景,标题,文字,用户进入后显示头像等等。

2.自建Canvas

第二个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);

3.排行榜的排序:

  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方法得到滑动距离,清除数据区域后,根据这个距离重新绘制数据区域,在监听结束后判断滑动的上限和下限设置下就可以了。

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));    },  })}

五.更新说明

下面贴出思路:

//开放域中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]);   //这里可以打印出开放域中传递出来的数据}
xcanvas
需要做网站?需要网络推广?欢迎咨询客户经理 13272073477