egret微信小游戏相关

官方文档

微信小游戏快速上手
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];
    

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值