经验分享:Creator 原生平台截屏方案

赞 

请问为什么不直接就用这一句呢,不管有没有cc.Mask?

这个比较消耗内存一些,所以没有 mask 节点的截图,为了省内存,还是不要这样做。

好的,谢谢!

那个,我的需求比较多一点。之前我测试过截取某个node的图像。我想问问。

第一: 这个只是截屏,就是完整的一个屏幕。即position在0 0点。
但是如果我只想接截取某个其他位置的node的contensize区域呢?

比如我有一个node放在屏幕中央,大小只有100x100。然后我就想截取这个node和它上面所有子节点的图像,只需要node那100x100的范围。

我测试过,如果position不在 0 0点,大小不是全屏,那还挺麻烦的。
用这个老方法:render.setVirtualViewport(origin, fullRect, virtualViewPort)。我试了好久都没搞出来。

伪代码如下:
var render = new cc.RenderTexture(size.width, size.height, cc.Texture2D.PIXEL_FORMAT_RGBA8888, gl.DEPTH24_STENCIL8_OES);
var origin = cc.p(0, 0);
var fullRect = cc.rect(x,y, size.width, size.height);
var virtualViewPort = cc.rect(x, y, size.width * 2, size.height * 2);
render.setVirtualViewport(origin, fullRect, virtualViewPort);
render.begin();
this.backgroundLayer_.visit();
render.end();

setVirtualViewport我以为可以调整截取的区域,结果怎么都达不到效果。

第二,截取生成的renderTexture,我没找到方法来生成sprite使用。图像已经在内存中,但是没办法取出来吗?
第三,renderTexture和 texture2D是什么关系,如果能转换,就能生成spriteFrame,设定截取的rect。可以任意切割,自定义性就强很多。能做很多事情。

我用了这个代码截屏

screenShoot(func){
        if (!cc.sys.isNative) return;
        let dirpath = jsb.fileUtils.getWritablePath() + 'ScreenShoot/';
        if( !jsb.fileUtils.isDirectoryExist(dirpath)){
            jsb.fileUtils.createDirectory(dirpath);
        }
        let name = 'ScreenShoot-' + (new Date()).valueOf() + '.png';
        let filepath = dirpath + name;
        let size = cc.winSize;
        let rt = cc.RenderTexture.create(size.width, size.height);
        cc.director.getScene()._sgNode.addChild(rt);
        rt.setVisible(false);
        rt.begin();
        cc.director.getScene()._sgNode.visit();
        rt.end();
        rt.saveToFile('ScreenShoot/' + name, cc.IMAGE_FORMAT_PNG, true, function() {
            cc.log('save succ');
            rt.removeFromParent();
            if (func) {
                func(filepath);
            }
        });
        return filepath;
    },

截出来的图片是正确的,但是android平台,游戏界面往左下角缩小了,上面的按钮的响应区域没变。有人遇见过吗?

1赞

你把 let size=cc.winSize 改成 let size = cc.director.getVisibleSize()

2赞

修改后无效。

1赞

目前 native 的截屏可能有些 bug,Android 平台和 mac 模拟器截屏完后,viewport 的大小有问题,会导致游戏画面在截屏后显示在左下角:

可以通过修改 CCRenderTexture.cpp 里面的 onEnd 函数来 hack 一下:

void RenderTexture::onEnd()
{
    Director *director = Director::getInstance();

    glBindFramebuffer(GL_FRAMEBUFFER, _oldFBO);

    // restore viewport
    director->setViewport();
   
    //
    director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, _oldProjMatrix);
    director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _oldTransMatrix);

}

建议做成插件哈

正在考虑做成插件中。

1赞

子龙大大。我这边也一样也遇到这个问题了 版本1.3.1 查看RenderTexture::onEnd源码 director->setViewport();这句是已经有加的。但是还是截图后整个画面缩小到左下角。。请问还有什么方法可以解决这个问题。

复制我上面的代码就好了呀

现在源码就是这样的。但是截图还是缩小到坐下角了。。。。

你的代码跟我的不一样啊,去掉 glViewport 的调用。

ok了。谢谢。。我以为是要加上那句glViewport。。

厉害了 我的子龙大神~

请问第二问题解决了吗

我参照上面的方法 (截图的场景中含有 mask ) 也是用注释的部分来创建 renderTexture ;但是在大多手机上好的 在opp有的型号的手机上截屏 还是缺少元素,很多地方都是白色的 什么原因啊 求解决@子龙山人

非动态创建的sprite,可以从renderTexture获得sprite
但是动态创建的sprite,调用sprite.node._sgNode.visit()方法,没有从renderTexture获得sprite。不管是否设置Visible属性为true或者false。cocos新人请教。具体的代码再您的demo例子上修改的如下:
cc.Class({
extends: cc.Component,

properties: {
    richText: cc.Component,
    sprite4:cc.Component
},

// use this for initialization
onLoad: function () {

},

captureScreen: function () {
    //注意,EditBox,VideoPlayer,Webview 等控件无法截图

       var self=this;
       var node = new cc.Node();
       node.parent =this.node;
       node.position = cc.v2(0, 0);
       self.sprite = node.addComponent(cc.Sprite);
       self.sprite.setVisible(false);
       cc.loader.loadRes("style/0", cc.SpriteFrame, function (err,spriteFrame ) {
             self.sprite.spriteFrame = spriteFrame;
         })
 
       node = new cc.Node();
       node.parent =this.node;
       node.position = cc.v2(60, 60);
       self.sprite2 = node.addComponent(cc.Sprite);
       self.sprite2.setVisible(false);
       cc.loader.loadRes("style/1", cc.SpriteFrame, function (err,spriteFrame ) {
             self.sprite2.spriteFrame = spriteFrame;
         })
         
         
        //如果待截图的场景中含有 mask,请开启下面注释的语句
        // var renderTexture = cc.RenderTexture.create(1280,640, cc.Texture2D.PIXEL_FORMAT_RGBA8888, gl.DEPTH24_STENCIL8_OES);
        var renderTexture = cc.RenderTexture.create(1280,640);

        //把 renderTexture 添加到场景中去,否则截屏的时候,场景中的元素会移动
        this.richText.node.parent._sgNode.addChild(renderTexture);
        //把 renderTexture 设置为不可见,可以避免截图成功后,移除 renderTexture 造成的闪烁
        renderTexture.setVisible(false);
         
         
        //实际截屏的代码
        renderTexture.begin();
        //this.richText.node 是我们要截图的节点,如果要截整个屏幕,可以把 this.richText 换成 Canvas 切点即可
        this.sprite.node._sgNode.visit();
        this.sprite2.node._sgNode.visit();
        //this.richText.node._sgNode.visit();
      
        renderTexture.end();
        //非动态创建的sprite,可以从renderTexture获得sprite
        //问题,动态创建的sprite,调用sprite.node._sgNode.visit()方法,没有从renderTexture获得sprite。不管是否设置Visible属性为true或者false。
    
  
       node = new cc.Node();
       node.parent = this.node;
       node.position = cc.v2(0, 0);
       var sprite5 = node.addComponent(cc.Sprite);
       sprite5.spriteFrame=renderTexture.getSprite().getSpriteFrame();//这里必须重新创建精灵,否则会报错  
       sprite5.node.setAnchorPoint(cc.p(0.3,0.3));  
    

      renderTexture.saveToFile("demo.png",cc.IMAGE_FORMAT_PNG, true, function () {
            //把 renderTexture 从场景中移除
            renderTexture.removeFromParent();
           cc.log("capture screen successfully!");
        }); 
        //打印截图路径
   
  
}

});

2赞