美文网首页
egret微信小游戏相关

egret微信小游戏相关

作者: Coder_Cat | 来源:发表于2019-12-12 15:53 被阅读0次

    官方文档

    微信小游戏快速上手
    egret微信小游戏开发指南
    菜鸟|Egret微信小游戏好友排行榜教程
    小程序与小游戏获取用户信息接口调整,请开发者注意升级

    • 整体的效果如下


      微信小游戏整体效果

    开放数据域

    • 开放数据域的绘制文件中已经拥有一个通过Canvas API绘制的排行榜 ,SharedCanvas 是主域和开放数据域都可以访问的一个离屏画布,原理如下所示。


      开放数据域
    • egret中的ts类文件是在主域,编译生成微信小游戏文件中会有一个openDataContext文件夹里面有一个index.js文件,这个文件中可以绑定上传积分等信息到微信后台,从微信后台获取用户信息和积分数据,清除微信后台积分数据,以及微信后台获取微信好友排行榜数据;(这个文件中可以获取微信相关数据)
    • index.js文件还有一个SharedCanvas,是主域和开放数据域都可以访问的一个离屏画布;主域和开放数据域的分工不同,在主域中创建离屏画布并添加到显示容器中,主域通知共享域去获取微信好友列表信息,然后根据这些信息去绘制排行榜;(主域中创建添加移除销毁离屏画板,并传递分数时间等信息给共享域;共享域接受到信息后经过一系列的数据判断处理后去绘制排行榜);
    • 总而言之就微信不会将一些信息给你随便用(主要是好友列表信息),所以只能在该共享域中获取微信好友列表信息;共享域是可以收到主域的信息的,但主域是不可以是收到共享域的信息;所以这个好友排行榜会的绘制只能放在共享域了.

    微信登录授权登录,获取微信用户信息,分享小游戏

    • egret中有一个Platform.ts类,对应编译后微信小游戏中的platform.js文件;主要用于获取平台数据接口的,包括微信授权登录获取用户信息,分享小游戏给好友,分享小游戏到群;

    • Platform.ts中全部代码

          //正式版中只有方法声明
          declare interface Platform {
              //获取用户信息
              getUserInfo(): Promise<any>;
              //微信授权登录
              login(): Promise<any>;
              //分享转发,主要用于被动转发
              showSharemMenu():Promise<any>
              //主动转发
              shareAppMessage():Promise<any>
          
              openDataContext:any
          
          }
          //测试版
          class DebugPlatform implements Platform {
              async getUserInfo() {
                  return { nickName: "username" }
              }
              async login() {
          
              }
          
              async showSharemMenu() {
          
              }
          
              async shareAppMessage() {
                   
              }
          
              openDataContext
          }
          
          //设置为全局
          if (!window.platform) {
              window.platform = new DebugPlatform();
          }
          
          declare let platform: Platform;
          declare interface Window {
              platform: Platform
          }
      
    • platform.js中全部代码

      class WxgamePlatform {
          name = 'wxgame'
          //登录
          login() {
            return new Promise((resolve, reject) => {
              wx.login({
                success: (res) => {
                  resolve(res)
                }
              })
            })
          }
          //获取用户信息
          getUserInfo() {
            let windowWidth = wx.getSystemInfoSync().windowWidth;
            let  windowHeight = wx.getSystemInfoSync().windowHeight;
            return new Promise((resolve, reject) => {
              let button = wx.createUserInfoButton({
                type: 'text',
                text: '获取用户信息',
                style: {
                  left: windowWidth/2 - 100,
                  top: windowHeight/2 - 20,
                  width: 200,
                  height: 40,
                  lineHeight: 40,
                  backgroundColor: '#008888',
                  color: '#ffffff',
                  textAlign: 'center',
                  fontSize: 16,
                  borderRadius: 4
                }
              });
              //允许按钮
              button.onTap((res) => {
                // button.hide();
                var userInfo = res.userInfo;
                resolve(userInfo);    
                console.log(res);
                button.destroy();
              });
              //关闭按钮
              button.offTap((res) => {
                resolve(res);
                button.destroy();
              });
            });
          }
        //被动分享
        showSharemMenu(){
              console.log("微信分享");
            return new Promise((resolve,reject) => {
                //显示当前页面的转发按钮
                wx.showShareMenu({
                  withShareTicket:true,
                  success:(res)=>{
                    console.log("success", res);
                  },
                  fail:(res)=>{
                    console.log("fail", res);
                  },
                  complete:(res)=>{
                    console.log("complete",res);
                  }
                });
                //被动转发
                wx.onShareAppMessage(() => {
                  return {
                    title: '捷达小飞车',
                    imageUrl: 'openDataContext/assets/icon_first.png' // 图片 URL
                  }
                });
      
              });
          }
        //主动转发  
        shareAppMessage() {
          return new Promise((resolve, reject) => {
            wx.shareAppMessage({
              title: '捷达小飞车',
              imageUrl: 'openDataContext/assets/icon_first.png' // 图片 URL  
            })
          })
        }
      openDataContext = new WxgameOpenDataContext();
      
      }
      //下面代码自动生成不要动
      class WxgameOpenDataContext {
      
          createDisplayObject(type, width, height) {
              const bitmapdata = new egret.BitmapData(sharedCanvas);
              bitmapdata.$deleteSource = false;
              const texture = new egret.Texture();
              texture._setBitmapData(bitmapdata);
              const bitmap = new egret.Bitmap(texture);
              bitmap.width = width;
              bitmap.height = height;
      
              if (egret.Capabilities.renderMode == "webgl") {
                  const renderContext = egret.wxgame.WebGLRenderContext.getInstance();
                  const context = renderContext.context;
                  ////需要用到最新的微信版本
                  ////调用其接口WebGLRenderingContext.wxBindCanvasTexture(number texture, Canvas canvas)
                  ////如果没有该接口,会进行如下处理,保证画面渲染正确,但会占用内存。
                  if (!context.wxBindCanvasTexture) {
                      egret.startTick((timeStarmp) => {
                          egret.WebGLUtils.deleteWebGLTexture(bitmapdata.webGLTexture);
                          bitmapdata.webGLTexture = null;
                          return false;
                      }, this);
                  }
              }
              return bitmap;
          }
      
      
          postMessage(data) {
              const openDataContext = wx.getOpenDataContext();
              openDataContext.postMessage(data);
          }
      }
      window.platform = new WxgamePlatform();
      
      • 小游戏启动时要微信授权登录获取用户信息,以及被动分享,在Main.tsrunGame方法中添加:
          private async runGame() {
              //微信授权登录
              await platform.login()
              //如果已经授权登录就跳过授权登录步骤
              if(!egret.localStorage.getItem("nickName")||!egret.localStorage.getItem("avatarUrl")){
                   await this.loginAndGetUserInfo();
              }
              await this.loadResource();
              this.createGameScene();
               //游戏页面都加载完成后展示分享按钮
              await platform.showSharemMenu();
              const result = await RES.getResAsync("description_json")
              this.startAnimation(result);
              
              
          }
          //授权登录获取用户信息
          private async loginAndGetUserInfo() {
              const userInfo = await platform.getUserInfo();//微信授权登录
              console.log("userInfo",userInfo);
              if(userInfo){//名称和图片保存在本地
                  egret.localStorage.setItem("nickName",userInfo.nickName);
                  egret.localStorage.setItem("avatarUrl",userInfo.avatarUrl);
              }
          }
      

      微信登录授权获取用户信息效果如下:

      微信授权登录获取用户信息
      微信被动分享需要注意的是platform.showSharemMenu()必须在创建游戏场景后调用,否则会卡住;添加后会模拟器出现转发取消选项效果如下:
      在这里插入图片描述
    • 微信主动转发分享:即我们自己添加一个分享按钮点击后分享给好友,代码如下

      //转发按钮点击事件
      this.forwardBtn.addEventListener(egret.TouchEvent.TOUCH_TAP,()=>{
          //主动转发事件
          platform.shareAppMessage();
      },this);
      
    主动分享

    微信排行榜

    • 在共享获取微信相关信息和绘制排行榜,即在openDataContext文件夹的index.js文件中进行操作

    微信排行榜数据获取

    • 获取和写入用户托管数据到微信后台:即游戏结束时上传分数到微信后台,但要先判断是最大分数时才上传;wx.getUserCloudStorage方法获取用户托管信息,wx.setUserCloudStorage写入用户信息,相关代码,
          //获取当前用户托管数据当中对应 key 的数据。该接口只可在开放数据域下使用,比较当前分数是否比存储的分数大,如果大就更新数据
          function getUserCloudData(data) {
            nickName = data.nickName;
            myCurrentScore = data.wxgame.score;
            console.log("当前分数", myCurrentScore);
            wx.getUserCloudStorage({
              keyList: ["gameScoreData"],
              success: (re) => {
                console.log("[wx]success", re);
                let userData = re.KVDataList;
                console.log("userData", userData);
                myShowRank = ShowRanking.ShowLite;
                if (userData.length > 0) {
                  let value = userData[0].value;
                  let json = JSON.parse(value);
                  console.log("json", json);
                  maxScore = json.wxgame.score ? json.wxgame.score : 0;
                  console.log("maxScore", maxScore);
                  console.log("最大分数", maxScore);
                  if (maxScore < myCurrentScore) {//如果当前分数大于最大分数则更新最大分数
                    updateUserDataToCloud(data);
                  }else {
                    if(totalGroup.length <= 0){
                      preloadFriendData();
                    } else {
                      console.log("有数据并且不用更新数据,就直接绘制");
                      renderDirty = true;//重绘标记
                      requestAnimationFrameID = requestAnimationFrame(loop);//每一帧绘制
                    }
                  }
                } else { //第一次为空
                  updateUserDataToCloud(data);
                }
              }, fail: (re) => {
                console.log("[wx]fail", re);
              }, complete: (re) => {
                console.log("[wx]complete", re);
              }
            })
          }
          
          //对用户托管数据进行写数据操作。允许同时写多组 KV 数据。上传分数等信息到微信云
          function updateUserDataToCloud(data) {
            console.log("上传分数", data);
            let userKVData = {
              key: "gameScoreData",
              value: JSON.stringify(data)
            }
            console.log(userKVData);
            wx.setUserCloudStorage({
              KVDataList: [userKVData],
              success: (re) => {
                console.log("[wx]success", re);
                preloadFriendData();
              }, fail: (re) => {
                console.log("[wx]fail", re);
              }, complete: (re) => {
                console.log("[wx]complete", re);
              }
            });
          }
      
    • 获取好友列表信息wx.getFriendCloudStorage;下面是获取用户信息,并对用户图像进行预加载,然后根据分数排序相关代码
      //获取好友排行榜数据信息
      function preloadFriendData() {
        wx.getFriendCloudStorage({
          keyList: ["gameScoreData"],
          success: (re) => {
            console.log("[wx]success", re);
            let dataArr = re.data;
            var tempArr = [];
            let imagUrl = [];
            console.log("dataArr", dataArr);
            for (var i = 0; i < dataArr.length; i++) {
              let objc = dataArr[i];
              let oj = {};
              oj.key = i;
              oj.name = objc.nickname;
              oj.url = objc.avatarUrl
              oj.score = 0;
              if (objc.KVDataList.length > 0) {
                let userSetData = objc.KVDataList[0];
                let score = JSON.parse(userSetData.value).wxgame.score;
                console.log("分数", score);
                oj.score = score;
              }
              imagUrl.push(objc.avatarUrl);
              tempArr.push(oj);
            }
            console.log("tempArr", tempArr);
            totalGroup = null;
            totalGroup = tempArr;
            console.log("totalGroup1", totalGroup);
            if (imagUrl.length > 0) {
              preloadAvatarUrl(imagUrl);
            }
          }, fail: (re) => {
            console.log("[wx]fail", re);
          }, complete: (re) => {
            console.log("[wx]complete", re);
          }
        });
      }
      
      //先预加载图片资源
      function preloadAvatarUrl(avatarUrlList) {
        console.log("imgs", avatarUrlList);
        let preloaded = 0;
        let assetsAvatar = [];
        for (var j = 0; j < avatarUrlList.length; j++) {
          const img = wx.createImage();
          img.onload = () => {
            preloaded++;
            if (preloaded == avatarUrlList.length) {
              console.log("所有头像加载完成");
              console.log("图片", assetsAvatar);
              for (var i = 0; i < totalGroup.length; i++) {
                let objc = totalGroup[i];
                objc.img = assetsAvatar[i];
              }
              let data = totalGroup.sort(createComprisonFunction("score", false));
              for (var i = 0; i < totalGroup.length; i++) {
                let objc = totalGroup[i];
                objc.key = i + 1;
              }
              totalGroup = data;
              console.log("totalGroup2", totalGroup);
              renderDirty = true;//重绘标记
              requestAnimationFrameID = requestAnimationFrame(loop);//每一帧绘制      
            }
          }
          img.src = avatarUrlList[j];
          assetsAvatar.push(img);
        }
      }
      
      
      //对象数组,根据对象的key进行排序
      function createComprisonFunction(propertyName, isSequence) { //true为顺序,false为逆序
        return function (object1, object2) {
          var value1 = object1[propertyName];
          var value2 = object2[propertyName];
          if (isSequence == true) { //顺序排序
            if (value1 < value2) {
              return -1;
            } else if (value1 > value2) {
              return 1;
            } else {
              return 0;
            }
          } else {
            if (value1 < value2) {
              return 1;
            } else if (value1 > value2) {
              return -1;
            } else {
              return 0;
            }
          }
        }
      }
      

    微信好友排行榜绘制

    • 游戏结束主域创建离屏画布,并添加显示;官方示例该画布占满整个舞台,但其实可以是自定义一个group,添加到group中;但这group的必须是舞台的宽高的等比例缩放,不然在共响域绘制就没法计算出正确的位置;
      添加离屏画布的group
      可以看出在exml文件中,虽然我只需要在白色背景处绘制排行榜,但是由于会变形,真正要添加离屏画布的group比较长;
      在响应的ts文件中创建和添加离屏画布
      this.gameOverBitmap = platform.openDataContext.createDisplayObject(null, this.gameOverGroup.width, this.gameOverGroup.height);
      this.gameOverGroup.addChild(this.gameOverBitmap);
      

    主域名发送消息,共享域接受消息

    • 游戏结束主域发送分数等信息给共享域;共享域监听接受到消息后进行一系列的数据处理,排行榜绘制工作;
    • 游戏结束够主域创建添加离屏画布,发送分数给子域;
      //游戏结束绘制分数等信息
          private gameOverGroup: eui.Group;
          // private gameOVerMask: egret.Shape;
          private gameOverBitmap: egret.Bitmap;
          //上传分数到微信
          private upLoadMyScore() {
              egret.log("gameOverGroup",this.gameOverGroup);
              egret.log("gameOverGroup.width",this.gameOverGroup.width);
              if(this.gameOverGroup){
                  //gameOverBitmap 宽高比必须是stage的宽高比,否则变形
                  this.gameOverBitmap = platform.openDataContext.createDisplayObject(null, this.gameOverGroup.width, this.gameOverGroup.height);
                  this.gameOverGroup.addChild(this.gameOverBitmap);
              }
              //发送消息给子域
              platform.openDataContext.postMessage({
                     wxgame: {
                         score:this.score,
                         update_time:new Date().getTime(),
                      },
                      nickName: egret.localStorage.getItem("nickName"),
                      width:this.gameOverBitmap.width,
                      height:this.gameOverBitmap.height,
                      command: "updateMyScore"
                  });
          }
      
    • 共享域名监听接受消息
      /**
       * 增加来自主域的监听函数
       */
      function addOpenDataContextListener() {
        console.log('增加监听函数')
        wx.onMessage((data) => {
          console.log(data);
          if (data.command == 'open') {//打开微信好友排行榜
            if (!createScene()) return;
            myShowRank = ShowRanking.ShowAll;
            if (totalGroup.length > 0) {
              renderDirty = true;//重绘标记
              // requestAnimationFrameID = requestAnimationFrame(loop);//每一帧绘制  
            } else {
              preloadFriendData();
            }
          } else if (data.command == 'goBack' && requestAnimationFrameID) {
            console.log("goBack");
            //返回时重新绘制简易排行榜
            myShowRank = ShowRanking.ShowLite;
            renderDirty = true;//重绘标记
          } else if (data.command == 'loadRes' && !hasLoadRes) {
            /**
             * 加载资源函数
             * 只需要加载一次
             */
            // console.log('加载资源')
            preloadAssets();
          } else if (data.command == "updateMyScore") {//更新用户信息
            if (!createScene()) return;
            console.log("data", data);
            getUserCloudData(data);
      
          } else if (data.command == "loadLastPage") {//加载上一页
            if (page > 0) {
              buttonClick(0);
      
            }
          } else if (data.command == "loadNextPage") {//加载下一页
            //在next按钮的范围内
            if ((page + 1) * perPageMaxNum < totalGroup.length) {
              buttonClick(1);
            }
          } else if (data.command == "startPlayGame"){
            console.log("startPlayGame");
            //开始游戏,停止进行循环绘图
            cancelAnimationFrame(requestAnimationFrameID);
            requestAnimationFrameID = null
          }
        });
      }
      

      效果如下:


      微信排行榜

    其他注意事项

    • 展示排行榜时报错gameSubContextThirdScriptError Cannot read property 'width' of undefined;at requestAnimationFrame callback function TypeError: Cannot read property 'width' of undefined时必须在Main.ts加载资源时通知共享域预加载图片资源,及调用platform.openDataContext.postMessage({command:'loadRes'});.
      private async loadResource() {
              try {
                  const loadingView = new LoadingUI();
                  this.stage.addChild(loadingView);
                  await RES.loadConfig("resource/default.res.json", "resource/");
                  await this.loadTheme();
                  await RES.loadGroup("preload", 0, loadingView);//第三个参数只要实现RES.PromiseTaskReporter协议的onProgress方法既可以
                  this.stage.removeChild(loadingView);
                  //添加一行代码:加载排行榜资源,否则在展示排行榜时报错gameSubContextThirdScriptError Cannot read property 'width' of undefined;at requestAnimationFrame callback function TypeError: Cannot read property 'width' of undefined
                  platform.openDataContext.postMessage({command:'loadRes'});
              }
              catch (e) {
                  console.error(e);
              }
          }
      
    • canvas画布的大小是舞台大小,但实际上是有所缩放,所以我们需要通过比例系数计算真实的大小以方便绘制;
        //500x888 //画板区域
        //500x604 //绘制区域
        //500 x 255 蓝色图片
        // let scale = 500/640;
        let topBackGroundHeight = 255/888*sharedCanvas.height;
        //设置图尺寸乘以下面宽度放大的倍数就是真实尺寸
        let widthScale = sharedCanvas.width / 640;
        fontScale = sharedCanvas.height/1136;
      
    • loop方法是一个递归调用,开启会一直循环调用,只要renderDirty设置为true就会绘制图像
      /**
       * 循环函数
       * 每帧判断一下是否需要渲染
       * 如果被标脏,则重新渲染
       */
      function loop() {
        if (renderDirty) {
          context.setTransform(1, 0, 0, 1, 0, 0);
          context.clearRect(0, 0, sharedCanvas.width, sharedCanvas.height);
          if (myShowRank == ShowRanking.ShowLite) {
            console.log("绘制精简版排行榜");
            drawLiteRankPanel();
          } else if (myShowRank == ShowRanking.ShowAll) {
            console.log("绘制全部排行榜");
            drawRankPanel();
          }
      
          renderDirty = false;
        }
        // console.log("递归死循环调用");
        requestAnimationFrameID = requestAnimationFrame(loop);
      }
      
      所以在退出排行榜重新开始游戏时就停止循环绘制调用
      //开始游戏,停止进行循环绘图
        cancelAnimationFrame(requestAnimationFrameID);
        requestAnimationFrameID = null
      
    • 开始绘制调用requestAnimationFrame(loop)调用一次即可,renderDirty设置为true就会安装canvas去绘制,所以在涉及到绘制界面刷新改变时就将renderDirty设置为true即可.
    • 不管是本地图片资源还是网络图片资源在显示之前需要预加载,否则无法显示;微信提供了相关方法
      //创建图片容器
      const img = wx.createImage();
      //图片加载完成时回调
      img.onload = () => {
      }
      //本地图片资源通过图片传入图片路径,网络图片资源传入图片url
      img.src = assetsUrl[asset];
      

    相关文章

      网友评论

          本文标题:egret微信小游戏相关

          本文链接:https://www.haomeiwen.com/subject/ncstnctx.html