news 2026/8/23 2:04:00

cocos 截图 节点截图 保存图片翻转y轴否则是倒立的

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
cocos 截图 节点截图 保存图片翻转y轴否则是倒立的

我在B站(我有一个小喷壶)持续更新Shader特效视频,所有代码免费,欢迎关注
这是视频演示
炼丹房·第19炉】截图功能:截屏+抠道具透明背景,代码免费拿
说下思路是看的这个
截图!长按保存分享!Cocos Creator
创建截图相机 一个rendertexture 截图相机渲染到这个rendertexture 然后创建一个sprite显示这个rendertexture
节点截图是获取节点坐标 width height

获取像素保存图片记得翻转y轴 否则图片是倒立的

// 方法3:使用更高效的像素操作(使用 TypedArray 视图)saveWithEfficientFlip(ctx:CanvasRenderingContext2D,pixels:Uint8Array,width:number,height:number){constimageData=ctx.createImageData(width,height);constoutput=imageData.data;// 使用 set 方法逐行复制,效率更高for(let y=0;y<height;y++){constsrcY=height-1-y;constsrcOffset=srcY*width*4;constdstOffset=y*width*4;// 复制整行output.set(pixels.subarray(srcOffset,srcOffset+width*4),dstOffset);}ctx.putImageData(imageData,0,0);this.downloadCanvas(ctx.canvas);}

这是截屏

import{_decorator,Camera,Component,Node,RenderTexture,Sprite,SpriteFrame}from'cc';const{ccclass,property}=_decorator;@ccclass('oo')export class oo extends Component{@property(RenderTexture)renderTex:RenderTexture=null;@property(Camera)captureCamera:Camera=null!;// 专用的截图相机(必须为正交相机)start(){// 延迟读取,确保渲染完成this.scheduleOnce(()=>{//this.captureAndSave();},0.5);}captureAndSave(){// 关键修改:使用世界坐标设置相机位置constwidth=this.renderTex.width;constheight=this.renderTex.height;constpixels=newUint8Array(width*height*4);// 读取像素this.renderTex.readPixels(0,0,width,height,pixels);console.log('Pixel data length:',pixels.length);// 转换为图片并下载this.savePixelsAsImage(pixels,width,height);}savePixelsAsImage(pixels:Uint8Array,width:number,height:number){constcanvas=document.createElement('canvas');canvas.width=width;canvas.height=height;constctx=canvas.getContext('2d');if(!ctx){console.error('Failed to get canvas context');return;}// 方法1:使用 Canvas 变换同时翻转 X 和 Ythis.saveWithEfficientFlip(ctx,pixels,width,height);// 或者使用方法2:手动翻转像素数据(更可靠)// this.saveWithManualFlip(ctx, pixels, width, height);}// 方法1:使用 Canvas 变换saveWithTransform(ctx:CanvasRenderingContext2D,pixels:Uint8Array,width:number,height:number){// 创建原始图像数据constimageData=ctx.createImageData(width,height);imageData.data.set(pixels);// 同时翻转 X 和 Y 轴ctx.save();ctx.scale(-1,-1);// 同时翻转 X 和 Yctx.translate(-width,-height);// 将图像移回可视区域ctx.putImageData(imageData,0,0);ctx.restore();this.downloadCanvas(ctx.canvas);}// 方法2:手动翻转像素数据(最可靠)saveWithManualFlip(ctx:CanvasRenderingContext2D,pixels:Uint8Array,width:number,height:number){constimageData=ctx.createImageData(width,height);constoutput=imageData.data;constrowSize=width*4;// 创建行的视图,避免复制for(let y=0;y<height;y++){constsrcRow=height-1-y;output.set(pixels.subarray(srcRow*rowSize,(srcRow+1)*rowSize),y*rowSize);}ctx.putImageData(imageData,0,0);this.downloadCanvas(ctx.canvas);}// 方法3:使用更高效的像素操作(使用 TypedArray 视图)saveWithEfficientFlip(ctx:CanvasRenderingContext2D,pixels:Uint8Array,width:number,height:number){constimageData=ctx.createImageData(width,height);constoutput=imageData.data;// 使用 set 方法逐行复制,效率更高for(let y=0;y<height;y++){constsrcY=height-1-y;constsrcOffset=srcY*width*4;constdstOffset=y*width*4;// 复制整行output.set(pixels.subarray(srcOffset,srcOffset+width*4),dstOffset);}ctx.putImageData(imageData,0,0);this.downloadCanvas(ctx.canvas);}// 下载 Canvas 内容downloadCanvas(canvas:HTMLCanvasElement){// 生成 DataURLconstdataUrl=canvas.toDataURL('image/png');// 下载constlink=document.createElement('a');link.href=dataUrl;link.download='render_texture_capture.png';document.body.appendChild(link);link.click();document.body.removeChild(link);}update(deltaTime:number){}}

这是节点截图

import{_decorator,Camera,Color,Component,director,Node,Rect,rect,RenderTexture,UITransform,Vec2,Vec3,view}from'cc';const{ccclass,property}=_decorator;@ccclass('o')export class o extends Component{@property(Camera)captureCamera:Camera=null!;// 专用的截图相机(必须为正交相机)@property(Node)targetNode:Node=null!;// 要截图的节点@property(RenderTexture)renderTex:RenderTexture=null!;// 渲染纹理@property({tooltip:'留白边距,1.0表示不留白,1.1表示留10%'})margin:number=1.1;// 留白边距// 是否自动设置相机的 cullingMask(如果已经在编辑器中手动设置好,可以关闭)@property({tooltip:'是否自动设置相机的可见层(如果已在编辑器中设置好,可关闭)'})autoSetVisibility:boolean=true;capture(){if(!this.targetNode){console.error('targetNode 未设置');return;}if(!this.captureCamera){console.error('captureCamera 未设置');return;}if(!this.renderTex){console.error('renderTex 未设置');return;}constbounds=this.getWorldBounds(this.targetNode);if(!bounds){console.error('无法获取节点包围盒,请确保节点或子节点包含 UITransform 组件');return;}constcenterX=(bounds.min.x+bounds.max.x)/2;constcenterY=(bounds.min.y+bounds.max.y)/2;constnodeWidth=bounds.max.x-bounds.min.x;constnodeHeight=bounds.max.y-bounds.min.y;// 关键修改:使用世界坐标设置相机位置this.captureCamera.node.setWorldPosition(centerX,centerY,1000);// 设置透明背景this.captureCamera.clearFlags=Camera.ClearFlag.SOLID_COLOR;this.captureCamera.clearColor=newColor(0,0,0,0);// 可选:自动设置可见层(如果已在编辑器中设置好,可注释掉或关闭开关)//if (this.autoSetVisibility) {//this.captureCamera.visibility = 1 << this.targetNode.layer;//}// 计算 orthoHeight(保持不变)consttexW=this.renderTex.width;consttexH=this.renderTex.height;consttexAspect=texW/texH;constnodeAspect=nodeWidth/nodeHeight;let orthoHeight=nodeHeight/2;if(nodeAspect>texAspect){orthoHeight=(nodeWidth/texAspect)/2;}orthoHeight*=this.margin;this.captureCamera.orthoHeight=orthoHeight;// 7. 延迟一帧读取像素(确保 GPU 渲染完成)this.scheduleOnce(()=>{constpixels=newUint8Array(texW*texH*4);this.renderTex.readPixels(0,0,texW,texH,pixels);this.saveImage(pixels,texW,texH);},0.1);}/** * 递归获取节点及其所有子节点的世界包围盒(轴对齐,忽略旋转) * 返回 { min: Vec3, max: Vec3 } 或 null(如果没有 UITransform) */privategetWorldBounds(node:Node):{min:Vec3;max:Vec3}|null{constmin=newVec3(Infinity,Infinity,Infinity);constmax=newVec3(-Infinity,-Infinity,-Infinity);let hasValid=false;consttraverse=(n:Node)=>{constui=n.getComponent(UITransform);if(ui){constpos=n.worldPosition;consthalfW=ui.width/2;consthalfH=ui.height/2;constleft=pos.x-halfW;constright=pos.x+halfW;constbottom=pos.y-halfH;consttop=pos.y+halfH;if(left<min.x)min.x=left;if(right>max.x)max.x=right;if(bottom<min.y)min.y=bottom;if(top>max.y)max.y=top;// 2D 游戏通常忽略 Z,但为了完整性也记录if(pos.z<min.z)min.z=pos.z;if(pos.z>max.z)max.z=pos.z;hasValid=true;}for(let child of n.children){traverse(child);}};traverse(node);returnhasValid ?{min,max}:null;}/** * 将像素数据保存为 PNG 图片(处理 Y 轴翻转) */privatesaveImage(pixels:Uint8Array,width:number,height:number){constcanvas=document.createElement('canvas');canvas.width=width;canvas.height=height;constctx=canvas.getContext('2d');if(!ctx){console.error('无法创建 canvas 上下文');return;}constimageData=ctx.createImageData(width,height);constrowSize=width*4;// 翻转 Y 轴:因为 readPixels 原点在左下角,Canvas 原点在左上角for(let y=0;y<height;y++){constsrcRow=height-1-y;constsrcStart=srcRow*rowSize;constsrcEnd=srcStart+rowSize;imageData.data.set(pixels.subarray(srcStart,srcEnd),y*rowSize);}ctx.putImageData(imageData,0,0);// 下载图片constdataUrl=canvas.toDataURL('image/png');constlink=document.createElement('a');link.href=dataUrl;link.download=`capture_${Date.now()}.png`;document.body.appendChild(link);link.click();document.body.removeChild(link);}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/23 2:03:03

手把手教你用OpenSSL生成EAP-TLS证书(附FreeRADIUS配置指南)

从零构建企业级EAP-TLS认证体系&#xff1a;OpenSSL与FreeRADIUS实战全解析 在构建现代企业网络或物联网设备接入方案时&#xff0c;如何确保接入设备身份的绝对可信&#xff0c;同时兼顾自动化与安全性&#xff0c;是每一位架构师和技术负责人必须直面的核心挑战。传统的用户名…

作者头像 李华
网站建设 2026/7/14 16:42:07

零配置训练PETRV2-BEV模型:星图AI平台开箱即用体验

零配置训练PETRV2-BEV模型&#xff1a;星图AI平台开箱即用体验 1. 从“不敢动”到“跑起来”&#xff1a;我的BEV模型训练初体验 如果你和我一样&#xff0c;第一次接触BEV&#xff08;鸟瞰图&#xff09;三维感知模型时&#xff0c;内心是崩溃的——光是看到那些复杂的配置文…

作者头像 李华
网站建设 2026/7/14 16:42:19

手把手教你用CODESYS将旧笔记本改造成软PLC(含证书配置避坑指南)

手把手教你用CODESYS将旧笔记本改造成软PLC&#xff08;含证书配置避坑指南&#xff09; 手头那台闲置的旧笔记本&#xff0c;除了偶尔翻出来怀念一下青春&#xff0c;是不是感觉食之无味、弃之可惜&#xff1f;别急着让它进储藏室吃灰&#xff0c;今天我们来玩点硬核的——把…

作者头像 李华
网站建设 2026/7/14 16:42:19

龙虾部署成功,你会了吗?

同时也对接了钉钉、飞书等你会部署open claw了吗&#xff0c;不会我帮你。私信我帮你部署。R-y_y0907

作者头像 李华
网站建设 2026/7/14 16:42:21

LangGraph开发RAG智能客服:从零构建与生产环境避坑指南

最近在做一个智能客服项目&#xff0c;客户要求系统能基于内部知识库进行精准问答。一开始用传统方式开发&#xff0c;发现流程编排和状态管理特别头疼&#xff0c;响应速度也上不去。后来尝试了LangGraph框架&#xff0c;整个开发体验顺畅了不少。今天就把我的实践过程整理出来…

作者头像 李华