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];
    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章