基于Laya游戏引擎实现微信小游戏排行榜

我们都知道,微信小游戏和小程序目前风头十足,很多公司都逐渐增加了相关业务线来迅速推广自己的产品和抢占用户群。说到微信小游戏,就不得不提到排行榜这个功能,就目前游戏行业,似乎都离不开排行榜这个重要功能,用户很大一部分留存都是依仗这个看似不起眼的模块。那么,微信小游戏中具体该如何借助laya引擎实现排行榜这个功能呢?我们先来看一下最终的效果图:

按照微信官方的说法,如果我们要使用微信官方提供的好友关系链的数据,我们就不能直接在项目中绘制排行榜,我们需要借助于开放域来绘制排行榜:

? 如果想要展示通过关系链 API 获取到的用户数据,如绘制排行榜等业务场景,需要将排行榜绘制到 sharedCanvas 上,再在主域将 sharedCanvas 渲染上屏。简单来说,sharedCanvas 是主域和开放数据域都可以访问的一个离屏画布。在开放数据域调用 wx.getSharedCanvas() 将返回 sharedCanvas。更多相关详情可以去看看官网的介绍:https://mp.weixin.qq.com/debug/wxagame/dev/tutorial/open-ability/open-data.html

那么我们来实际动手操作一下吧。

主域绘制

通过效果可以看出来,我们排行榜是一个弹窗形式展示的,由于开放域只负责排行榜UI绘制,所以,除此以外的UI以及交互我们需要在主域绘制和处理。因此,这里的弹窗 dialog 需要在主域绘制,然后将对应的排行榜需要显示的位置信息和长宽映射到开放域,具体代码如下:


    /**
     * 显示排行榜数据
     */
    function onRankInfoLoad(){
        console.log("查看排行榜~");
        var dialog = new RankDialogUI();
        showShareCanvas();
        // 解决显示对象和鼠标错位而导致的排行榜滑动无效问题
        var globalPosition = dialog.ranking_list.localToGlobal(new Laya.Point());
        var originMatrix = Laya.stage._canvasTransform;
        var mat = new Laya.Matrix(originMatrix.a, 0, 0, originMatrix.d, globalPosition.x * originMatrix.a, globalPosition.y * originMatrix.d);
        wxPostMessage({
            command: 0,
            text: "设置开放域canvas大小",
            canvasData: {
                width: rankViewWidth * mat.a, height: rankViewHeight * mat.d, matrix: mat
            },
            isLoad: false
        }, null, function (message) {
            console.log("再次往开放域发请求");
            window['wx'].postMessage({
                command: 1,
                text: '开放域加载资源',
            });
        });

        Laya.stage.addChild(dialog);
        dialog.ranking_list.visible = false;
        dialog.popup();
         Laya.timer.once(400,this,function(){
            wxPostMessage({
                command: 3,
                text: "获取排行榜数据~"
            }, null, function (message) {
                console.log("获取排行榜的回调~");
            });
         });

        dialog.btn_rank_dialog_share.on(Laya.Event.CLICK, this, onGameRankShare);
        dialog.btn_rank_dialog_back.on(Laya.Event.CLICK, this, onDialogClose);
        function onDialogClose(){
            wxPostMessage({
                command: 4,
                text: "关闭排行榜~"
            }, null, function (message) {
                console.log("关闭排行榜的回调~");
            });
            dialog.close();
        }

        function onGameRankShare(){
            console.log("分享排行榜~");
            window['wx'].showShareMenu({
                withShareTicket:false,
                success:function(res){
                    console.log("开启转发成功~");
                },
                fail:function(res){
                    console.log("开启转发失败~");
                },
                complete:function(res){

                }
            });
            window['wx'].onShareAppMessage(function () {
                return {
                    title: '我在飞机大战游戏中排名又上升了,快来挑战我吧~'
                }
            })
            window['wx'].shareAppMessage({

                title: '我在飞机大战游戏中排名又上升了,快来挑战我吧~',
                imageUrl: canvas.toTempFilePathSync({
                    x: (screenWidth - rankViewWidth)/2 - 10,
                    y: (screenHeight - rankViewHeight)/2+80,
                    width: (rankViewWidth - 30)*4,
                    height: (rankViewHeight - 40)*4,
                    destWidth: 500,
                    destHeight: 600
                })
            });
        }
    }

    /**
     * 设置共享Canvas
     */
    function showShareCanvas(){
            window['sharedCanvas'].width = rankViewWidth;
            window['sharedCanvas'].height = rankViewHeight;
            //主域显示开放域内容???
            //window['sharedCanvas'].sharedCanvas = window['wx'].getOpenDataContext().canvas;
            Laya.timer.once(1000, this, function () {
                var sprite = new Laya.Sprite();
                sprite.zOrder = 1008;
                sprite.pos(0, 0);
                var texture = new Laya.Texture(window['sharedCanvas']);
                texture.bitmap.alwaysChange = true;//小程序使用,非常费
                sprite.graphics.drawTexture(texture, (screenWidth - rankViewWidth)/2, (screenHeight - rankViewHeight)/2, texture.width, texture.height);
                Laya.stage.addChild(sprite);
            });
    }

/**
     * 向开放域发送消息,并接收开放域返回过来的数据,
     * 可根据发送参数和接收数据在主域这边进行下步处理
     * @param message
     * @param caller
     * @param callback
     */
    function wxPostMessage(message, caller, callback){
        window['wx'].postMessage(message);
        Laya.timer.once(400, this, function (){
            //回调处理
            if (caller == null || caller == undefined) {
                callback(message);
            } else {
                caller.callback(message);
            }
        });
    }

这边主要发送的消息体中携带了command字段,用于在开放域执行不同的功能代码,这边大体分为:资源加载命令、初始化排行榜大小命令、获取关系链(排行榜)数据命令以及关闭排行榜的命令,大家可以根据业务具体需要适当增减。

开放域绘制

根据官方的说明,开放数据域 是一个封闭、独立的 JavaScript 作用域。要让代码运行在开放数据域,需要在 game.json 中添加配置项 openDataContext 指定开放数据域的代码目录。添加该配置项表示小游戏启用了开放数据域,这将会导致一些 限制。这些限制主要包含:

  • 无法设置sharedCanvas的宽高
  • 只能使用有限的接口(如逐帧动画、Timer、触摸事件以及获取和设置关系链数据等接口)

下面我们一步步来在开放域绘制排行榜。

  1. 首先,需要新建一个项目作为开放域,这个项目目录是与主域项目平行的(主域就是未做排行榜之前的项目目录),比如我的项目目录是这样的:

  2. 接着,我们需要在开放域中接收主域发送的消息并处理不同的功能命令,UI就不放了,看看具体代码吧:
    /**
         * 监听从主域发过来的消息
         */
        function wxOnMessage(){
            if(window['wx'] != undefined){
                window['wx'].onMessage(function (message){
                    dispatchMessage(message);
                });
            }else{
                console.log("微信接口无法使用~");
            }
        }
        /**
         * 处理消息
         * @param {*} message
         */
        function dispatchMessage(message){
            switch(message.command){
                //设置开放域画布大小
                case 0:
                    sample.setCanvasSize(message.canvasData);
                    break;
                //加载资源
                case 1:
                    sample.loadResource();
                    break;
                //写入排行榜数据
                case 2:
                    sample.writeRankingData(message.rankingData);
                    break;
                //获取微信排行榜数据
                case 3:
                    sample.getRankingData();
                    break;
                //关闭排行榜
                case 4:
                    sample.closeRankingDialog();
                    break;
                default:
    
                    console.log(JSON.stringify(message));
            }
        }
        /**
         * 设置开放域画布大小
         * @param {*} size
         */
         _proto.setCanvasSize = function(size){
            console.log("设置开放域canvas大小~");
            window['sharedCanvas'].width = size.width;
            window['sharedCanvas'].height = size.height;
            //Laya.stage.width = size.width;
            //Laya.stage.width = size.height;
            /**
             * 将主域的canvasTransform映射到开放域
             */
            if(size.matrix!=null){
                console.log("收到主域的同步canvasTransform了~");
            }
            var mainMatrix = size.matrix;
            var openMatrix = new Laya.Matrix();
            openMatrix.a = mainMatrix.a;
            openMatrix.b = mainMatrix.b;
            openMatrix.c = mainMatrix.c;
            openMatrix.d = mainMatrix.d;
            openMatrix.tx = mainMatrix.tx;
            openMatrix.ty = mainMatrix.ty;
            //重置矩阵
            Laya.stage._canvasTransform = openMatrix;
            //监听舞台的鼠标移动事件
            Laya.stage.mouseEnabled = true;
        }
    
        /**
         * 用户自己的排名
         */
        var myRanking = -1;
        /**
         * 获取微信排行榜数据
         */
        _proto.getRankingData = function(){
            window['wx'].getUserInfo({
                openIdList: ['selfOpenId'],
                success: (userRes) => {
                    console.log('success', userRes.data);
                    //索引代表各个好友0为自己
                    let userData = userRes.data[0];
                    console.log("取信息索引0" + userData.nickName);
                    //取出所有好友数据
                    window['wx'].getFriendCloudStorage({
                        keyList: [
                            //'击杀排行',
                            '第1关',
                            '第2关',
                            '第3关'
                        ],
                        success: res => {
                            console.log("wx.getFriendCloudStorage success", res);
                            let data = res.data;
                            /*data.sort((a, b) => {
                                if (a.KVDataList.length == 0 && b.KVDataList.length == 0) {
                                    return 0;
                                }
                                if (a.KVDataList.length == 0) {
                                    return 1;
                                }
                                if (b.KVDataList.length == 0) {
                                    return -1;
                                }
                                return b.KVDataList[0].value - a.KVDataList[0].value;
                            });*/
                            for (let i = 0; i < data.length; i++) {
                                var playerInfo = data[i];
                                var currentPlayer = res.data[i].nickname;
                                console.log("当前排行玩家昵称为=>"+res.data[i].nickname);
                                var kvList = playerInfo.KVDataList;
                                var scoreSum = 0;
                                if(kvList.length>0){
                                    for(var j = 0;j<kvList.length;j++){
                                        if(kvList[j].key != null){
                                            //将value转化为int再累加
                                            scoreSum+=Number(kvList[j].value);
                                        }
                                    }
                                }
    
                                if (data[i].avatarUrl == userData.avatarUrl) {
                                    //获取群好友的时候,没有自己的名字??
                                    data[i].nickName = userData.nickName;
                                    myRanking = i+1;
                                    console.log("此ID为自己,当前排名第"+myRanking);
    
                                }
                                //填充总分信息
                                sortData.push({
                                    nickName: currentPlayer,
                                    avatarUrl: data[i].avatarUrl,
                                    totalScore: scoreSum
                                });
                            }
                            sortData.sort((a, b) => {
                                var score1 = Number(a.totalScore);
                                var score2 = Number(b.totalScore);
                                if (score1 > score2) {
                                    return -1;
                                }else if(score1 < score2){
                                    return 1;
                                }else{
                                    return 0;
                                }
                            });
                            showRankingDialog();
    
                        },
                        fail: res => {
                            console.log("拉取好友信息失败", res);
                        },
                    });
                },
                fail: (res) => {
                    console.log("拉取个人信息失败")
                }
            });
        }
        /**
         * 加载资源
         */
        _proto.loadResource = function(){
            Laya.loader.load(["comp/bg_line.png","comp/ranking1.png","comp/ranking2.png",
            "comp/ranking3.png","comp/userholder_img.png"], Laya.Handler.create(null,function(){
                console.log("开放域资源加载完毕~");
                sample.rankView = new RankingViewUI();
                Laya.stage.addChild(sample.rankView);
            }));
    
        }
    
        /**
         * 写入排行榜数据
         */
        _proto.writeRankingData = function(rankingData){
            console.log("写入排行榜数据~");
            //KVDataList代表排行数据,可以为多个,多个代表多个排行
            //key-排行类型,value-排行分数
            window['wx'].setUserCloudStorage({
                KVDataList: [
                    //{ key: '击杀排行', value: "" + 1 },
                    { key: '第'+rankingData.fightLevel+'关', value: rankingData.fightScore+"" },//需要改成动态的值
                ],
                success: function (res) {
                    console.log('setUserCloudStorage', 'success', res)
                },
                fail: function (res) {
                    console.log('setUserCloudStorage', 'fail')
                }
            });
        }
        var sortData = [];
        /**
         * 渲染排行榜列表
         */
        function showRankingDialog(){
            console.log("拿到好友排行榜信息", sortData);
            sample.rankView.ranking_list.vScrollBarSkin = "";
            sample.rankView.ranking_list.array = sortData;
            sample.rankView.ranking_list.renderHandler = new Laya.Handler(this, onRender);
            sample.rankView.ranking_list.selectHandler = new Laya.Handler(this, onSelect);
    
        }
    
        var lastRenderIndex = -1;
        function onRender(cell, index){
            if (index == lastRenderIndex) {
                return;
            }
            lastRenderIndex = index;
            //根据子节点的name获取子节点对象
            var name = cell.getChildByName("item_rank_name");
            var ranking = cell.getChildByName("item_rank_text");
            var userlogo = cell.getChildByName("item_rank_logo");
            var score = cell.getChildByName("item_rank_score");
         var rank_icon = cell.getChildByName("item_rank_icon");
    
            name.text = sortData[index].nickName;
            console.log("渲染排行榜当前的用户名为="+sortData[index].nickName+",渲染索引:"+lastRenderIndex);
            ranking.text = (index+1)+"";
            userlogo.skin = sortData[index].avatarUrl;
            score.text = sortData[index].totalScore+"分";
            if(lastRenderIndex === 0 || lastRenderIndex === 1 || lastRenderIndex === 2){
             ranking.visible = false;
             rank_icon.visible = true;
             rank_icon.skin = "comp/ranking"+(lastRenderIndex+1)+".png";
         }else{
             rank_icon.visible = false;
                ranking.visible = true;
            }
    
        }
    
        function onSelect(index){
            console.log("当前选择的索引是:"+index);
    
        }
    
        /**
         * 关闭排行榜
         */
        _proto.closeRankingDialog = function(){
            //dialog.close();
         console.log("关闭排行榜~");
            sortData = [];
            lastRenderIndex = -1;
            sample.rankView.removeChildren();
            sample.rankView = null;
    
        }
    
        _proto.start = function(){
            console.log("开始接收主域的消息~");
            wxOnMessage();
        }
    }
    

    可以看到,我们这里通过 wx.onMessage 方法来获取主域发送的数据,然后借助 dispatchMessage 方法作消息的分发处理。值得注意的是,我们在设置开放域canvas大小的时候,需要重置坐标矩阵,将主域的排行榜显示位置映射到开放域中来,否则会发生滑动无效的问题。我这里设置的排行榜数据有三条,大家可以根据具体需求来传入,获取到排行榜数据后,需要对它进行排序处理并展示,这里直接借助于laya中的 List 控件展示就可以了,对于它用法不熟悉的可以去laya官网了解一下:https://ldc.layabox.com/doc/?nav=zh-js-6-0-0

  3. 开放域图片加载问题:
    用过laya游戏引擎的都知道,我们一般用官方推荐的打包图集的方式来加载游戏中的图片资源,这种方式在主域中是可行的,然而,在开放域中却不能成功加载图片资源。因此,开放域中,我们不需要将图片资源打包成图集,只要像下面这样直接加载图片即可:
 /**
     * 加载资源
     */
    _proto.loadResource = function(){
        Laya.loader.load(["comp/bg_line.png","comp/ranking1.png","comp/ranking2.png",
        "comp/ranking3.png","comp/userholder_img.png"], Laya.Handler.create(null,function(){
            console.log("开放域资源加载完毕~");
            sample.rankView = new RankingViewUI();
            Laya.stage.addChild(sample.rankView);
        }));

    }

然后,我们还需要将对应的图片文件夹拷贝到wx_publish目录下,否则会提示找不到图片资源。

合并主域和开放域

主域和开放域功能代码实现了之后,我们就需要打包成微信小游戏项目了。首先,我们需要先将主域项目发布成微信小游戏,发布目录直接为项目的根目录,如上图的 wx_publish 目录。然后,在该目录下创建src/myOpenDataContext目录。接着,我们需要将开放域项目也发布成微信小游戏,目录可以选择桌面,名称为 wx_open,发布成功后,进入该目录,将code.js、weapp-adapter.js以及index.js文件复制到 wx_publish/src/myOpenDataContext 目录下,并在 game.json 文件中增加开放域映射目录:


{
  "deviceOrientation": "portrait",
  "showStatusBar": "false",
  "networkTimeout": {
    "request": 10000,
    "connectSocket": 10000,
    "uploadFile": 10000,
    "downloadFile": 10000
  },
  "openDataContext": "src/myOpenDataContext"
}

到这里,微信小游戏排行榜功能就算实现了,到头来发现,其实实现起来并不难,难的是缺乏资料,此文仅用来抛砖引玉,如有问题欢迎提出。

原文地址:https://www.cnblogs.com/moosphon/p/11565820.html

时间: 2024-08-05 23:05:07

基于Laya游戏引擎实现微信小游戏排行榜的相关文章

使用Laya引擎开发微信小游戏(上)

本文由云+社区发表 使用一个简单的游戏开发示例,由浅入深,介绍了如何用Laya引擎开发微信小游戏. 作者:马晓东,腾讯前端高级工程师. 微信小游戏的推出也快一年时间了,在IEG的游戏运营活动中,也出现了越来越多的以小游戏作为载体运营的活动类型,比如游戏预约,抢先试完等等,都收到了非常良好的效果. 在支持微信小游戏的游戏引擎中,Cocos,Egret,Laya都对小游戏的开发提供了很多强大的支持.前段时间正好抽空研究了一下这块的内容,现做一个总结,针对如何使用Laya引擎开发微信小游戏给大家做一下

这一次,让创意成为小游戏的品牌 | 微信小游戏推出四大创意鼓励措施

小游戏的本质是创意. 玩法革新亦或独特的剧情设定,有创意的小游戏总能令人眼前一亮,更易受到用户的喜爱,也是我们一向认可和鼓励的. 为发现优质的创意小游戏.鼓励对游戏创意的深耕和打磨,现推出四大鼓励措施,解决小游戏用户识别难.发现难等痛点,帮助开发者获得更多收益. 凡是在玩法.美术.剧情.音乐方面拥有高创新性的小游戏均可申请,通过专业评审后即有机会获得创意小游戏认证. 四大创意鼓励措施 01 创意标识 我们将为创意小游戏加上创意标识,让用户可以在多个场景下清晰地识别出创意小游戏. 02 初始用户

[小游戏] 微信小游戏开发源码_教程_工具_资源最新集合

[小游戏资源] 微信小游戏开发资源目录 一.微信官方游戏教程 小游戏简易教程 小游戏API大全 小游戏开发工具 二.微信小游戏图标资源 Game-icons.net 三.微信小游戏图片资源 Super Game Asset GameDev Market envato market Game Art Partners KENNEY 四.微信小游戏音频资源 工具类 Audacity 9 款音频压缩软件推荐 7 款混音软件推荐 7 款降噪软件推荐 资源类 爱给音效库 freesound Soundim

一、微信小游戏开发 --- 初次在微信开发者工具里跑Egret小游戏项目

尝试下Egret的小游戏开发,学习,学习,干IT,不学习,就得落后啊... 相关教程: Egret微信小游戏教程 微信公众平台-微信小游戏教程 微信公众平台-微信小游戏接入指南 开发版本: Egret Engine 5.1.11 Egret Wing 4.1.5 微信开发者工具 1.02.1803210 开发流程: 一. 注册微信小程序账号. 二. 下载安装微信开发者工具. 三. Egret创建微信小游戏项目 一.注册微信小程序账号 前往 微信公众平台,按照小程序注册教程注册账号. 二.下载安装

【开发记录】微信小游戏开发入门——俄罗斯方块

叨叨 我在前一阵子,打算做一个微信小游戏,当然是单机的,只是为了了解小游戏开发的过程,最终选择了俄罗斯方块这一经典小游戏作为demo,源代码已托管值github,当然,这个游戏demo对用不并不友好,但是已经可以让我入门小程序开发了XD. demo地址:https://github.com/nbclw/Laya_Brick 准备 在任何开发前都需要对要开发的东西有一定的了解.准备: 小游戏原理:微信小游戏是属于H5游戏的一种吧,我是这样理解的:在H5中,有一个叫Canvas(画布)的存在,与电脑

微信小游戏入门与实战 引爆朋友圈

第1章 准备工作(需要ES5,ES6基础)学员作品演示:https://pan.baidu.com/s/1gEMWzujg72soj0cEUOtJ2A 密码:uy2n,本章首先介绍课程目标,学习收获等,然后通过与APP.小程序的对比,让大家知道什么是微信小游戏以及微信小游戏前景如何,有哪些特点,然后带大家搭建微信小游戏的开发环境和调试环境,之后会对微信小游戏开发前注意事项与准备工作进行讲解,目...1-1 课程导学1-2 小游戏官方开发工具快速体验1-3 小游戏开发测试环境搭建和工具链使用1-4

微信小游戏入门与实战 刷爆朋友圈

第1章 课程介绍与准备工作本章首先介绍课程目标,学习收获等,然后通过与APP.小程序的对比,让大家知道什么是微信小游戏以及微信小游戏前景如何,有哪些特点,然后带大家搭建微信小游戏的开发环境和调试环境,之后会对微信小游戏开发前注意事项与准备工作进行讲解,目的是为了让大家在开发微信小游戏的时候可以跳过不必要遇到的坑,为后面的小游戏... 第2章 微信小游戏开发原理与JS面向对象初步填坑本章会带大家快速创建第一个微信小游戏项目,并跑起来,让大家快速的体验和熟悉小游戏开发和运行的整体流程,之后会对微信小

微信小游戏发布注意事项

微信小游戏上传基本流程: 1.首次提审资质文档 1.提审版本->2.提审页面上传资质->3.提交审核->4.审核受理 小程序注册完成后,登录微信公众平台(mp.weixin.qq.com),进入公众平台点击左侧的“开发管理”->再找到所需提交的版本->“提交审核” 勾选“已阅读并了解平台审核规则” ->点击下一步 ->进入提交审核信息填写界面(可提交相关的游戏资质文档.填写小游戏类目.游戏引擎.提供游戏测试号等)->上传资质文档 ->提交审核 说明:游

【微信小游戏】排行榜实战版

一.前提 微信小游戏主打社交玩法,为了丰富社交玩法我们肯定会用到关系链数据来做好友排行帮,群排行榜等功能.本篇主要介绍Cocos Creator中排行榜的实现,上一篇微信小游戏排行版概念篇. 二.准备 工具:cocos creator 版本:v1.9.1 语言:JavaScript 介绍: 1)cocos creator v1.9.1 版本,构建发布中增加了子域的概念,这里的子域对应的就是小游戏中的开放数据域.如图1 2)子域代码目录:指的就是小游戏game.json中subContext的路径