专栏首页游戏开发之旅利用Phaser开发微信小游戏(排行榜小结)

利用Phaser开发微信小游戏(排行榜小结)

版权声明:本文为博主原创文章,遵循 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));
    },
  })

}

五.更新说明

以上是我用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<userData.length;i++){
    console.log(userData[i]);   //这里可以打印出开放域中传递出来的数据
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 创建发送异步通讯对象Ajax请求、数据回调及属性状态说明

    AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术。

    德顺
  • HTML滚动标签marquee的属性及效果实现

    slide从右往左滚动-停止 scroll从右往左循环 alternate从右往左再往右循环

    德顺
  • React build项目部署后IE浏览器报错:对象不支持"assign"属性或方法的解决

    用React build项目,部署后 IE 浏览器打不开(我用的是 IE11),控制台报错:SCRIPT438:对象不支持"assign"属性或方法。

    德顺
  • CSS3伪类和伪元素的特性及两者的区别

    前端工作者肯定或多或少地接触过CSS伪类和伪元素,比如最常见的:focus,:hover以及<a>标签的:link、visited等,伪元素较常见的比如:bef...

    德顺
  • Sweet Alert弹窗点击确定后执行页面跳转等操作

    首先参考上文,引入 Sweet Alert 所需的文件,我这里写了一个修改密码的确认框。

    德顺
  • NLP硬核入门-PointerNet和CopyNet

    PointerNet和CopyNet是同一类网络模型,只是在不同的论文里叫法不同,后文统一用PtrNet来表示。

    zenRRan
  • Javascript操作将session资料存入window.name里

    查了一些资料,大家一致认为除了 Node.js 和服务端,在 JavaScript 里没有 session 这种东西(或者说很不常见),所有的变数,函式等等的资...

    德顺
  • JavaScript设置定时器、取消定时器及执行机制解析

    今天整理了一下 JavaScript 定时器,顺便了解了一下 JavaScript 的运行机制,现在记录一下。

    德顺
  • jQuery动态添加/删除元素及内容

    添加新的 HTML 内容,四种方法:可根据上面的图片来区分四种方法插入元素的位置。

    德顺
  • 聊聊rocketmq的AccessChannel

    rocketmq-client-4.5.2-sources.jar!/org/apache/rocketmq/client/AccessChannel.java

    codecraft

扫码关注云+社区

领取腾讯云代金券