Creator 原生无感截图

2020-07-23  本文已影响0人  Jaymz_2b11

接到需求,需要在原生中生成带二维码的海报,于是有了这篇记录
原生中不像微信小游戏那样,有离屏画布,所以必须要采用其他的方案来做

思路如下,使用双相机实现无感截图
主相机展示 UI界面,新增截图相机来做截图
具体实现,在需要截图的界面新增一个相机,同时新增一个sceneShot的层,截图相机渲染Group为sceneShot的元素
主相机渲染剔除sceneShot层,同时设置相机深度,sceneShot优先级比主相机优先级高即可

截图相机属性


image.png

主相机属性


image.png

后续就使用 cc.RenderTexture 组件,将截图相机中的内容送进来,然后使用RenderTexture实例 调用readPixels方法完成截图,截图时需要把二维码挂在上面所以,有一个等待机制,假设code 没加载出来,那么这截图没有任何意义,因为截图时间长,所以我做了缓存,做了缓存以后,发现需要更新截图的时候不方便(比如海报变动,或者code变动),我又做了版本比对,游戏启动会去读取配置信息,根据配置信息中的shareBgVersion 与 本地存储的shareBgVersion 进行对比,若服务器版本高且图片存在,那么会删除图片重新截图,同时把服务器版本保存到本地,
以备下一次更新,代码如下


const {ccclass, property} = cc._decorator;

@ccclass
export default class UISharePanel extends UI 
{

    @property(cc.Node) btn_close : cc.Node = null;

    @property(cc.Node) btn_friend : cc.Node = null;

    @property(cc.Node) btn_circle : cc.Node = null;

    @property(cc.Sprite) bg_shot : cc.Sprite = null;

    @property(cc.Camera) camera : cc.Camera = null;

    @property(cc.Sprite) headIcon : cc.Sprite = null;

    @property(cc.Sprite) inviteCodeImage : cc.Sprite = null;

    @property(cc.Node) content : cc.Node = null;

    @property(cc.Node) item : cc.Node = null;

    @property(cc.Node) loading : cc.Node = null;

    @property(cc.PageView) pageView : cc.PageView = null;

    private texture : cc.RenderTexture = null;

    private prefixPath = "";

    private width = 720;

    private height = 1280;

    private curIdx = 0; //当前索引

    private shotIdx = 1;

    private shotComplete : Function = null;

    private picDataList = [];

    private isCanShot = false; //是否可以截图

    private isNeedShot = false; //是否需要截图

    private pageSize = null;

    private shareBgList:string[] = 
    [
        "share_1.png",
        "share_2.png",
    ]

    public Init()
    {
        if(!CC_JSB)
        {
            return;
        }
        this.prefixPath = jsb.fileUtils.getWritablePath();
        if(cc.sys.os == cc.sys.OS_ANDROID)
        {
            this.prefixPath = '/sdcard/';
        }
        this.pageSize = this.item.getContentSize();
        this.texture = new cc.RenderTexture();
        let gl = cc.game._renderContext;
        this.texture.initWithSize(this.width,this.height,gl.STENCIL_INDEX8);
        this.camera.targetTexture = this.texture;
        this.UpdateShareInfo();
        this.pageView.setCurrentPageIndex(this.curIdx);
        this.curIdx ++;
    }

    //更新邀请信息
    public UpdateShareInfo()
    {
        //this.inviteCodeLabel.string = "<b><color=#000000>我的邀请码:"+ Player.Instance.playerInfo.invitationCode +"</c>";
        this.LoadImage(this.inviteCodeImage,Player.Instance.playerInfo.codeUrl,this.LoadFinished.bind(this));  
        this.LoadImage(this.headIcon,Player.Instance.playerInfo.hearImg,this.LoadFinished.bind(this));
    }

    private idx = 0;
    public LoadFinished()
    {
        this.idx ++;
        console.log("LoadFinished : " + this.idx);
        if(this.idx>=2)
        {
            console.error("图片加载完毕 可以截图");
            this.isCanShot = true;
            if(this.isNeedShot)
            {
                console.error("检测要需要截图,开始截图");
                this.ShotFunc();
            }
        }
    }    

    public LoadImage(image:cc.Sprite, url:string,callback:Function)
    {
        if(!url || url == "")
        {
            console.log("URL 为 null");
            return;
        }
        console.log("开始下载图片 : " + url);
        cc.loader.load({url: url, type: 'png'},(err,res)=>{
            if(err != null)
            {
                console.error("加载Image Error : " +JSON.stringify(err));
                return;
            }
            console.log("加载图片成功 : URL : " + url);
            image.spriteFrame = new cc.SpriteFrame(res);
            if(callback)
            {
                callback();
            }
        });
    }

    // 监听事件
    public OnPageEvent(sender, eventType) 
    { 
        // 翻页事件
        if (eventType !== cc.PageView.EventType.PAGE_TURNING) {
            return;
        }
        console.log("当前所在的页面索引:" + sender.getCurrentPageIndex());
        this.curIdx = sender.getCurrentPageIndex() + 1;
    }

    public GetCurShareURL()
    {
        return this.prefixPath + this.shareBgList[this.curIdx-1];
    }

    //检查分享背景图版本
    //返回值是否需要重新截图
    public CheckUpdateShareBg():boolean
    {
        let curVersion = cc.sys.localStorage.getItem("shareBgVersion");
        let serverVersion = HeroGame.Instance.GameConfigInfo.shareBgVersion;
        if(!curVersion)
        {
            return true;
        }
        else
        {
            if(serverVersion >= curVersion)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    public DeleteShareBg()
    {
        for(let i = 0;i<this.shareBgList.length;++i)
        {
            if (jsb.fileUtils.isFileExist(this.prefixPath + this.shareBgList[i])) 
            {
                console.error("删除图片 Path: " + this.prefixPath + this.shareBgList[i]);
                jsb.fileUtils.removeFile(this.prefixPath + this.shareBgList[i]);
                //cc.loader.releaseRes(this.prefixPath + this.shareBgList[i]);      
            }
        }
    }

    public CheckShareBgExist()
    {
        let isExist = true;
        for(let i = 0;i<this.shareBgList.length;++i)
        {
            if (!jsb.fileUtils.isFileExist(this.prefixPath + this.shareBgList[i])) 
            {
                isExist = false;
                break;
            }
        }
        return isExist;
    }

    //开始截图
    public StartSceneShot()
    {
        if(!CC_JSB)
        {
            this.loading.active = false;
            UIItem.InitList(this.content);
            return;
        }

        this.loading.active = true;
        UIItem.InitList(this.content);
        //首先检测一下是否图片已经存在 若存在直接读取
        let isExist = this.CheckShareBgExist();
        let isUpdate = this.CheckUpdateShareBg(); //检测是否需要更新
        if (isUpdate && isExist) 
        {
            console.error("图片存在且需要更新");
            this.DeleteShareBg();
            isExist = false;
        }
        
        if(isExist)
        {
            console.log("图片已经存在,直接读取");
            UIItem.InitList(this.content);
            this.pageView.removeAllPages();
            //读取展示
            for(let i = 0;i<this.shareBgList.length;++i)
            {   
                let sprite :cc.SpriteFrame = null;
                console.log(this.prefixPath+this.shareBgList[i]);
                cc.loader.load(this.prefixPath+this.shareBgList[i],(err,res)=>
                {
                    if(err)
                    {
                        console.error(err);
                        return;
                    }
                    else
                    {
                        console.log("加载成功");
                        sprite = new cc.SpriteFrame();
                        sprite.setTexture(res);
                        let node = cc.instantiate(this.item);
                        console.log(node.getContentSize());
                        node.name = "share_"+i;
                        this.pageView.addPage(node);
                        let Image = node.getComponent(cc.Sprite);
                        Image.spriteFrame = sprite;
                        node.active = true;
                    }
                })
            }
            let size = this.content.getContentSize();
            this.content.setContentSize(cc.size(200 + this.shareBgList.length* 534,size.height));
            this.loading.active = false;
        }
        else
        {
            console.log("图片不存在,需要重新截图");
            //图片不存在重新截图保存
            this.shotComplete = ()=>
            {
                console.log("截图全部完成");
                cc.sys.localStorage.setItem("shareBgVersion",HeroGame.Instance.GameConfigInfo.shareBgVersion+1); //更新背景标识
                UIItem.InitList(this.content);
                this.pageView.removeAllPages();
                //读取展示
                for(let i = 0;i<this.picDataList.length;++i)
                {
                    let picData = this.picDataList[i];
                    let texture = new cc.Texture2D();
                    
                    texture.initWithData(picData,32,this.width,this.height);
                    //创建主相机列表展示
                    let sprite = new cc.SpriteFrame(texture);
                    let node = cc.instantiate(this.item);
                    node.setContentSize(this.pageSize);
                    console.log("node size : " + node.getContentSize());
                    node.name = "share_"+i;
                    this.pageView.addPage(node);
                    let Image = node.getComponent(cc.Sprite);
                    Image.spriteFrame = sprite;
                    node.active = true;
                }
                let size = this.content.getContentSize();
                this.content.setContentSize(cc.size(200 + this.shareBgList.length* 534,size.height));
                this.loading.active = false;
            }
            this.isNeedShot = true;
            if(this.isCanShot)
            {
                console.error("调用截图时 图片已经加载完毕,可以截图");
                this.ShotFunc();
            }
            else
            {
                console.error("调用截图时 图片未加载完毕,需要等待");
            }
        }
    }

    public ShotFunc()
    {
        this.scheduleOnce(()=>
        {
            console.log("开始截图");
            let picData = this.InitImage();
            this.picDataList.push(picData);
            this.SaveFile(picData,this.shotIdx); //保存
            this.shotIdx++;
            if(this.shotIdx<this.shareBgList.length+1)
            {
                console.log("换背景继续截图 : " + this.shotIdx);
                cc.loader.loadRes("UI/Static/texture/share_"+this.shotIdx,cc.SpriteFrame,(err,res)=>
                {
                    if(err)
                    {
                        console.error("load share bg err : " + err);
                    }
                    else
                    {
                        this.bg_shot.spriteFrame = res;
                        this.ShotFunc();
                    }
                })
            }
            else
            {
                if(this.shotComplete)
                {
                    this.shotComplete();
                }
            }
        },0); //等一帧再次截图
    }

    private _width = 0;
    private _height = 0;

    public InitImage() : Uint8Array
    {
        let data = this.texture.readPixels();
        this._width =  this.texture.width;
        this._height = this.texture.height;
        let picData = this.filpYImage(data,this._width,this._height);
        return picData;
    }

    public filpYImage (data:Uint8Array, width:number, height:number) :Uint8Array
    {
        let picData = new Uint8Array(width * height * 4);
        let rowBytes = width * 4;
        for (let row = 0; row < height; row++) {
            let srow = height - 1 - row;
            let start = srow * width * 4;
            let reStart = row * width * 4;
            // save the piexls data
            for (let i = 0; i < rowBytes; i++) {
                picData[reStart + i] = data[start + i];
            }
        }
        return picData;
    }

    public SaveFile(picData:Uint8Array,idx:number)
    {
        if(CC_JSB)
        {
            let filePath = this.prefixPath + 'share_'+idx+'.png';
            let success = jsb.saveImageData(picData, this._width, this._height, filePath)
            if (success)
            {
                cc.log("save image data success, file: " + filePath);
            }
            else 
            {
                cc.error("save image data failed!");
            }
        }
    }
    
}
上一篇下一篇

猜你喜欢

热点阅读