• 微信小游戏egret开发包括p2引擎小结


    用egret + p2 做一个类似投球的小游戏,坑大致如下:

    1、p2引擎与egret坐标不同注意转换,横坐标没什么,纵坐标egret.y = stageHeight - body.position[1]*factor

    2、p2物体的原点为中心点,而egret显示对象的原点为左上角,设置display.anchorOffsetX = display.width/2;display.anchorOffsetY = display.height/2

    3、p2的单位与egret像素单位的factor设置为50,计算body坐标或display坐标时注意实时更新

    4、p2的碰撞检测代码如下,注意一定要给body设置个id,而且id必须是number

    this._world.on("beginContact", this.contact);
    contact = (evt) => {
            var bodyA:p2.Body = evt.bodyA;
            var bodyB:p2.Body = evt.bodyB;
    
            if (!bodyA || !bodyB) {
                return;
            }
            var bodyIdA = bodyA.id;
            var bodyIdB = bodyB.id;
    //这里一定要用id来确定碰撞的body
        }
    

      

    5、摩擦系数、弹性等属性可通过设置不同材质来设置,全局反弹系数用defaultContactMaterial,材质定义个id,然后body设置其material即可

    private init(){
            // 创建物理世界
            this._world = new p2.World();
            this._world.gravity = [0, Constants.WORLD_GRAVITY];
            //默认所有弹性
            this._world.defaultContactMaterial.restitution = 0.5;
            //不同材质的系数
            let m1 = new p2.Material(Constants.BALL_MATERIAL); 
            let m2 = new p2.Material(Constants.NET_MATERIAL);
            let m3 = new p2.Material(Constants.BASKET_MATERIAL);        
            let m1_m2 = new p2.ContactMaterial(m1,m2,<p2.ContactMaterialOptions>{restitution:0.01,friction:0});
            this._world.addContactMaterial(m1_m2);
            let m1_m3 = new p2.ContactMaterial(m1,m3,<p2.ContactMaterialOptions>{restitution:0.6,friction:0.3});
            this._world.addContactMaterial(m1_m3);
        }
    
    createBody(){
            // 创建球
            let box = new p2.Body({mass:1});      
            var boxShape:p2.Shape = new p2.Circle({
                radius: 10,
                material: new p2.Material(Constants.BALL_MATERIAL)
            });
            box.addShape(boxShape);
            let sp = new egret.Shape();
            sp.graphics.beginFill(0xfff000);
            sp.graphics.drawCircle(0,0,10);
            sp.graphics.endFill();
            sp.anchorOffsetX = sp.width/2;
            sp.anchorOffsetY = sp.height/2;
            box.displays = [sp];
    
            //创建篮筐
            let basket = new p2.Body({mass:1});      
            var basketShape:p2.Shape = new p2.Circle({
                radius: 10,
                material: new p2.Material(Constants.BASKET_MATERIAL)
            });
            basket.addShape(basketShape);
            let sp1 = new egret.Shape();
            sp1.graphics.beginFill(0xfff000);
            sp1.graphics.drawCircle(0,0,10);
            sp1.graphics.endFill();
            sp1.anchorOffsetX = sp1.width/2;
            sp1.anchorOffsetY = sp1.height/2;
            basket.displays = [sp1];
        }

    6、可以将world.step()向前走一段时间,然后记录刚体位置,画出轨迹即可实现模拟运动,但是正常step的时候第一帧必须设置与模拟的step帧保持一致,否则可能会出现错位和模拟与实际位置不符情况

    /**
         * 模拟运动
         */
        private simulate_move():void {
            for (var i:number = 0; i < 10; i++) {
                this._world.step(40 / 1000);
                // 记录位置
                this._ball.updateDisplayPosition();
            }
        }
    //正常step
    private loop(timestamp:number):boolean {
            var pass = 40;
            if (this._timestamp > 0) {
                pass = timestamp - this._timestamp;
            }
    
            this._timestamp = timestamp;
            if (pass < 10) {
                return;
            }
    
            if (pass > 1000) {
                return;
            }
    
            this.step(pass);
        }

    7、可设置刚体的sleepSpeedLimit和sleepTimeLimit来判断刚体是否可处于sleeping状态,然后通过world.broadphase.result判断sleeping状态下与哪些body处于碰撞状态,注意这个result是不断变化的,最好在sleeping的第一次就判断

    if (this.sleepState == p2.Body.SLEEPING || this.sleepState == p2.Body.SLEEPY) {
                    let arr = this.world.broadphase.result;
                    let num = 0;
                    for(let i = 0;i < arr.length;i++){
                        if(arr[i].id == Constants.BODY_ID.BALL){
                            num++;
                        }
                        if(arr[i].id == Constants.BODY_ID.BASKET_1 + Constants.BODY_ID.NET){
                            num++;
                        }
                        if(arr[i].id == Constants.BODY_ID.BASKET_1 + Constants.BODY_ID.TOPMASK){
                            num++;
                        }
                        
                    }
                    if(num >= 3){
                        console.log("sleepy and reset 了");
                        this.resetStatus();
                    }
                }

    8、可通过applyImpulse给body一个初始速度,来实现发射状态,钢体之间碰撞与不碰撞用this._world.enableBodyCollision(body1, body2);和this._world.disableBodyCollision(body1,body2);来设置

    9、微信小游戏的sharedcanvas绘制可能会闪屏,解决方法game.js和egret都设置成60帧

    10、微信小游戏sharedCanvas绘画的时候最好用相对坐标,不然可能会出现缩放的问题,取windowWidth和windowHeight,然后根据主屏幕的宽高比计算排行榜的大小和位置,接着进行相对运算,下面的代码是根据

    屏幕宽度适配的(fixedWidth),为防止绘画出现高低不齐,故保持了宽高比的windowHeight,这里的排行榜我是放到egret的一个UI里的,不是主屏幕,如果是整个贴在主屏幕则可以不用保持主舞台宽高比的windowHeight,

    以下是整个openDataContext的index.js

    /**
     * 资源加载组,将所需资源地址以及引用名进行注册
     * 之后可通过assets.引用名方式进行获取
     */
    const assetsUrl = {
      line:"openDataContext/assets/line.png"
    };
    
    class openDataContextMain{
      constructor(){
        this.init();
      }
      
      init(){
        //获取canvas渲染上下文
        this.context = sharedCanvas.getContext("2d");
        this.context.globalCompositeOperation = "source-over";
        this.sWidth = sharedCanvas.width;
        //根据宽度适配的 防止画的排行榜出现向下错位
        this.sHeight = this.sWidth*1330/750;
        this.assets = {};
        this.userData = null;
        this.friendData = null;
        this.groupData = null;
        if(!this.hasLoadRes){
          this.preloadAssets();
        }
        else{
          this.addOpenDataContextListener();
        }
      }
      //资源加载
      preloadAssets(){
        let preloaded = 0;
        let count = 0;
        for (let asset in assetsUrl) {
          count++;
          const img = wx.createImage();
          img.onload = () => {
            preloaded++;
            if (preloaded == count) {
              // console.log("加载完成");
              this.hasLoadRes = true;
              this.addOpenDataContextListener();
            }
          }
          img.src = assetsUrl[asset];
          this.assets[asset] = img;
        }
      }
      //添加监听
      addOpenDataContextListener() {
        console.log('增加监听函数')
        wx.onMessage((data) => {
          console.log(data);
          if(data.command == 'init'){
            //获取用户游戏信息
            this.ownOpenId = data.openid;
            
            //获取用户好友游戏信息
            if(!this.friendData){
              wx.getFriendCloudStorage({
                keyList:['score'],
                complete: res => {
                  if (res.errMsg == "getFriendCloudStorage:ok"){
                    this.friendData = this.sortFriendData(res.data);
                  }
                }
              });
            }
            //获取用户群组里的排行
            // if(!this.groupData){
            //   wx.getGroupCloudStorage({
            //     success: res => {
            //       if (res.errMsg == "getGroupCloudStorage:ok"){
            //         this.groupData = data;
            //       }
            //     },
            //     fail:error=>{
            //       console.log(error);
            //     }
            //   });
            // }
          }
          else if (data.command == 'friend') {
              this.drawFriendRank(data.page);
          } else if (data.command == 'group') {
            this.drawGroupRank(data.page);
          }
        });
      }
      //对好友数据进行排序组装
      sortFriendData(arr){
        if(!arr || arr.length == 0) return null;
        let len = arr.length;
        for(let i = 0;i < len;i++){
          let data = arr[i];
          let klist = data.KVDataList;
          for(let j = 0;j<klist.length;j++){
            let obj = klist[j];
            if(obj.key == 'score'){
              data[obj.key] = obj.value;
              break;
            }
          }
        }
        //排序
        arr.sort((a,b)=>{
          if (a.score > b.score) return -1;
          if (a.score < b.score) return 1;
          return 0;
        });
    
        return arr;
      }
      //获取自己的排名信息
      getOwnRank(){
        let len = this.friendData.length;
        for(let i = 0;i < len;i++){
          if (this.friendData[i].openid == this.ownOpenId){
            return [i+1,this.friendData[i]];
          }
        }
        return null;
      }
      //画好友排行榜
      drawFriendRank(page = 1){
        this.context.clearRect(0,0,this.context.canvas.width,this.context.canvas.height);
        if(!this.friendData || this.friendData.length == 0) return;
        let len = this.friendData.length;
        if(page > Math.ceil(len/7)){
          page--;
        }
        
        let itemWidth = this.sWidth * 2/3;
        let itemHeight = this.sHeight * 900/1330;
        let mid = itemHeight/9.8;
        let lx = itemWidth/600;
        let ly = mid/10;
        let fontSize = itemWidth/25;
        let i = (page-1)*7;
        let max = page*7;
        for(;i < max;i++){
          let obj = this.friendData[i];
          if(!obj) break;
          let yy = (i%7) * mid;
          this.context.fillStyle = '#65b5f7';
          this.context.font = fontSize + 'px Arial bold';
          this.context.textAlign = 'center';
          this.context.fillText(''+(i+1),20*lx,yy+ly*5);
          let image = wx.createImage();
          image.src = obj.avatarUrl;
          this.drawImage(image,80*lx,yy + ly*1,70*lx,70*lx);
          this.context.fillStyle = '#a1a1a1';
          this.context.fillText(obj.nickname,250*lx,yy+5*ly);
          this.context.fillText(obj.KVDataList[0].value,500*lx,yy+5*ly);
          this.drawImage(this.assets['line'], 0, yy+ly*9,560*lx,ly/5);
        }
    
        //绘制自己的排名
        let rank = this.getOwnRank();
        if(!rank) return;
        let robj = rank[1];
        let myy = 8.4*mid;
        this.context.fillText('' + rank[0], 20 * lx, myy + ly * 5);
        let image = wx.createImage();
        image.src = robj.avatarUrl;
        this.drawImage(image, 80 * lx, myy + ly * 1, 70 * lx, 70 * lx);
        this.context.fillStyle = '#a1a1a1';
        this.context.fillText(robj.nickname, 250 * lx, myy + 5 * ly);
        this.context.fillText(robj.KVDataList[0].value, 500 * lx, myy + 5 * ly);
        this.drawImage(this.assets['line'], 0, myy + ly * 9, 560 * lx, ly / 5);
      }
      //画群排行榜
      drawGroupRank(page = 1){
        this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
        this.context.fillStyle = '#ff0000';
        this.context.font = '50px Arial';
        this.context.fillText('世界排行榜', 10, 10);
      }
      //画图片
      drawImage(image, x, y, width, height) {
        if (image.width != 0 && image.height != 0 && this.context)     {
          if (width && height) {
            this.context.drawImage(image, x, y, width, height);
          } else {
            this.context.drawImage(image, x, y);
          }
        }
      }
    }
    
    const openDCM = new openDataContextMain();
    openDCM.addOpenDataContextListener();

    11、微信的授权现在改成必须通过授权按钮才能授权,很多情况下,与后端通信的逻辑应该是先获取openId,然后传给自己的服务器,服务器返回一个自己服务器的id,这个过程一般会保存用户信息,但是wx.login()没有返回

    userinfo,只返回一个code,code用来获取openid的,怎么办呢?我的逻辑是:

    1. wx.login()获取code
    2. 通过code获取openid
    3. 通过wx.getSetting()判断是否授权,如果授权则通过openid去自己服务器获取userinfo信息,否则创建授权按钮,在用户点击允许的时候把返回的userinfo更新到自己的服务器

    部分代码:

    //前面省略login和get openid
    wx.getSetting({complete:(res)=>{
                if(res.errMsg == 'getSetting:ok'){
                    let obj = res.authSetting;
                    if(obj['scope.userInfo'] == true){
                        // console.log('已经授权');
                    }
                    else{
                        createAuthorButton();
                    }
                    console.log("getSetting:%o",res);
                }
                 else{
                     wx.showModal('错误信息',res.errMsg);
                 }
            }});
    
    createAuthorButton(x:number, y:number, number, height:number) {
            if (egret.Capabilities.runtimeType != "wxgame") {
                return;
            }
    
            let button = wx.createUserInfoButton({
                type: 'text',
                text: '',
                style: {
                    left: x,
                    top: y,
                     width,
                    height: height,
                    backgroundColor: '#ff0000',
                    color: '#ffffff',
                    textAlign: 'center',
                    fontSize: 16,
                    opacity: 0
                }
            });
            button.onTap((res) => {
                if(res.errMsg == 'getUserInfo:ok'){
                    button.hide();
                    button.destroy();
                    WxApi.showShareMenu();
                    updateUserInfo(res);
                }
            });
        }

    12、小游戏转发带参数与不带参数注意参数格式,包括主动转发和被动转发,查api这个都知道

    13、微信开发者工具预览上传的时候会报错资源上传失败等,解决方法重启开发者工具再预览上传

    14、egret发布的小游戏,egret.js 和 eui.js库比较大,解决方法是通过终端发布命令发布微信小游戏:egret publish --target wxgame

    15、忘记一个p2的问题,p2可能会undefine,更改p2引擎暴露下p2

    16、有时会遇到微信开发者工具不能编译,不能运行,重启右上角清缓存也不行,win10,我的解决方法是关闭工具并删除如下这个文件夹

    总结下来大致就这些破问题,避免踩坑,如果还不行的话,删库跑路也未尝不可,毕竟家里的几亩地在那闲着呢,要不搬砖的随时欢迎!扯远了,其实只要多角度,多方面去思考问题,解决的方法还是很多的。

  • 相关阅读:
    那些不能错过的Xcode插件
    iOS开发过程中使用Core Data应避免的十个错误
    define和typedef
    #号运算符
    第三方移动后端服务开发
    9款优秀的APP免费测试框架
    网络协议初探(二)
    iphone第三方库
    HTTP状态码
    JDK8新特性:在JDK8中,默认添加final
  • 原文地址:https://www.cnblogs.com/wangzisheng/p/9477550.html
Copyright © 2020-2023  润新知