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

由于官方还没有正式提供截图的组件,而不少小伙伴对于这个功能需求很大,
所以我抽空弄了一个小 demo 供大家参考:

项目运行结果:

截图的核心代码:

    captureScreen: function () {
        //注意,EditBox,VideoPlayer,Webview 等控件无法被包含在截图里面
       //因为这是 OpenGL 的渲染到纹理的功能,上面提到的控件不是由引擎绘制的

        if(CC_JSB) {
            //如果待截图的场景中含有 mask,请使用下面注释的语句来创建 renderTexture
            // var renderTexture = cc.RenderTexture.create(1280,640, cc.Texture2D.PIXEL_FORMAT_RGBA8888, gl.DEPTH24_STENCIL8_OES);
            var renderTexture = cc.RenderTexture.create(1280,640);

            //实际截屏的代码
            renderTexture.begin();
            //this.richText.node 是我们要截图的节点,如果要截整个屏幕,可以把 this.richText 换成 Canvas 切点即可
            this.richText.node._sgNode.visit();
            renderTexture.end();
            renderTexture.saveToFile("demo.png",cc.ImageFormat.PNG, true, function () {
                cc.log("capture screen successfully!");
            });
            //打印截图路径
            cc.log(jsb.fileUtils.getWritablePath());
        }
    }

测试项目:
ScreenCaptureDemo.zip (478.4 KB)

测试方法:

  1. 直接点击截图按键,截图成功后控制台会输出: capture screen successfully!

  2. 查看截图内容: 可以从控制台可以看到截图保存的路径。

比如,我用的 1.2 的 creator 测试,输出的 log 为:

Simulator: /Applications/CocosCreator.app/Contents/Resources/cocos2d-x/simulator/mac/Simulator.app/Contents/Resources/

然后大家到对应的目录查看截图即可,使用 iOS 和 Android 也可以输出截图路径。

如果是 Android 用户,需要 root 手机后,可以通过 adb shell 的方式连接手机,然后输入 su 取得超级权限,
再通过 cd /data/data/org.cocos2d.ScreenCaptureDemo/files/ 目录下面,使用 cp demo.png /sdcard

最后输入两次 exit 退出 adb shell,然后在控制台执行 adb pull /sdcard/demo.png 就可以把 demo.png 下载到当前 shell 执行所在的目录了。

#注意
如果要测试截 ScrollView,可以把项目中的 ScrollView 的结点激活,然后修改测试代码为:

         var renderTexture = cc.RenderTexture.create(1280,640, cc.Texture2D.PIXEL_FORMAT_RGBA8888, gl.DEPTH24_STENCIL8_OES);
          //  var renderTexture = cc.RenderTexture.create(1280,640);
44赞

赞 

请问为什么不直接就用这一句呢,不管有没有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有的型号的手机上截屏 还是缺少元素,很多地方都是白色的 什么原因啊 求解决@子龙山人