当前位置: 首页 > news >正文

cocos creator使用mesh修改图片为圆形,减少使用mask,j减少drawcall,优化性能

cocos creator版本2.4.11

一个mask占用drawcall 3个以上,针对游戏中技能图标,cd,以及多玩家头像,是有很大优化空间

1.上代码,只适合单独图片的,不适合在图集中的图片

const { ccclass, property } = cc._decorator;const gfx = cc.gfx;
cc.Class({extends: cc.Component,properties: {radius: 100, // 圆的半径segments: 32, // 圆的细分段数(顶点数)/*** !#en The sprite frame of the sprite.* !#zh 精灵的精灵帧* @property spriteFrame* @type {SpriteFrame}* @example* sprite.spriteFrame = newSpriteFrame;*/spriteFrame: {default: null,type: cc.SpriteFrame},},onLoad() {let renderer = this.node.getComponent(cc.MeshRenderer);if (!renderer) {renderer = this.node.addComponent(cc.MeshRenderer);}renderer.mesh = null;this.renderer = renderer;let builtinMaterial = cc.MaterialVariant.createWithBuiltin("unlit");renderer.setMaterial(0, builtinMaterial);this._applySpriteFrame();this.setMesh();},setMesh(){// 创建 Meshlet mesh = new cc.Mesh();// 计算顶点和 UVlet positions = [];let uvs = [];let indices = [];let colors = [];// 圆心顶点positions.push(cc.v2(0, 0)); // 圆心uvs.push(cc.v2(0.5, 0.5));  // 圆心 UVcolors.push(cc.Color.WHITE); // 圆心颜色// 圆边缘顶点for (let i = 0; i <= this.segments; i++) {let angle = (i / this.segments) * Math.PI * 2; // 计算角度let x = Math.cos(angle) * this.radius; // 计算 x 坐标let y = Math.sin(angle) * this.radius; // 计算 y 坐标positions.push(cc.v2(x, y)); // 添加顶点uvs.push(cc.v2((x / this.radius + 1) / 2, 1-(y / this.radius + 1) / 2)); // 添加 UVcolors.push(cc.Color.WHITE); // 添加颜色}// 设置索引(三角形扇)for (let i = 1; i <= this.segments; i++) {indices.push(0); // 圆心indices.push(i); // 当前顶点indices.push(i + 1); // 下一个顶点}mesh.init(new gfx.VertexFormat([{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },{ name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },]), positions.length, true);mesh.setVertices(gfx.ATTR_POSITION, positions);mesh.setVertices(gfx.ATTR_UV0, uvs);mesh.setIndices(indices);this.renderer.mesh = mesh;},// 更新图片_applySpriteFrame() {// cc.log('_applySpriteFrame');if (this.spriteFrame) {const renderer = this.renderer;let material = renderer._materials[0];// Reset materiallet texture = this.spriteFrame.getTexture();material.define("USE_DIFFUSE_TEXTURE", true);material.setProperty('diffuseTexture', texture);}}
});

这个js组件,绑定到节点上,把要渲染的spriteFrame挂在上面,运行就可以了,这种方式只适合单独图片,不适合图集中的图片

运行效果,下面是对比了这个图片

说明:这种方式是直接修改图片的mesh网格结构,使用meshRenderer组件,不能挂载sprite组件,使用shader也可以达到效果,但是shader是在Gpu层修改显示,图片形状没有变,这个是运行的时候直接修改形状,而且shader修改的话会有问题,例如打断动态合批,如果项目勾选了动态合批或者图片在图集中,shader修改是无效的

这种方式可以降低mask增加的drawcall

2.工具式的,直接调用,升级版,可以修改图集中的某个图片的显示

const { ccclass, property } = cc._decorator;const gfx = cc.gfx;
cc.Class({extends: cc.Component,properties: {radius: 100, // 圆的半径segments: 32, // 圆的细分段数(顶点数)/*** !#en The sprite frame of the sprite.* !#zh 精灵的精灵帧* @property spriteFrame* @type {spriteFrame}*/spriteFrame: {default: null,type: cc.spriteFrame,},},/**设置数据显示 需要等spriteFrame加载完成后调用,可以拿到实际的图片* radius: 半径* segments: 圆细分段数,越多会越圆滑,但是性能消耗会更大* node:节点,这里需要使用mesheRenderer组件,所以需要把sprite剔除* isAtlas:是否是图集中的图片*/setDataShow(node, radius, segments, isAtlas) {// MeshRendererlet renderer = this.node.getComponent(cc.MeshRenderer);if (!renderer) {renderer = this.node.addComponent(cc.MeshRenderer);}renderer.mesh = null;this.renderer = renderer;let builtinMaterial = cc.MaterialVariant.createWithBuiltin("unlit");renderer.setMaterial(0, builtinMaterial);renderer.enabled = false;this.radius = radius;this.segments = segments;let sp = node.getComponent(cc.Sprite);if (sp) {this.spriteFrame = sp.spriteFrame;node.removeComponent(cc.Sprite);}// 把图片加载到renderer上的材质this.applySpriteFrame();// 设置meshif (isAtlas) {// 大图集中的texturethis.setMeshByAtlas();} else {// 单个图片this.setMesh();}// 这里必须延迟一帧,不然不会刷新mesh,显示不出来图片setTimeout(() => {if(cc.isValid(renderer)){renderer.enabled = true;}}, 100);},/**更新mesh,在图集中的 */setMeshByAtlas() {let uv = this.spriteFrame.uv;// 创建 Meshlet mesh = new cc.Mesh();// 计算顶点和 UVlet positions = [];let uvs = [];let indices = [];let colors = [];// 圆心顶点positions.push(cc.v2(0, 0)); // 圆心uvs.push(cc.v2((uv[6] + uv[0]) / 2, (uv[7] + uv[1]) / 2)); // 圆心 UV(取中心点)colors.push(cc.Color.WHITE); // 圆心颜色// 圆边缘顶点for (let i = 0; i <= this.segments; i++) {let angle = (i / this.segments) * Math.PI * 2; // 计算角度let x = Math.cos(angle) * this.radius; // 计算 x 坐标let y = Math.sin(angle) * this.radius; // 计算 y 坐标positions.push(cc.v2(x, y)); // 添加顶点// 计算 UV 坐标(根据图集的 UV 信息进行映射)let u = (x / this.radius + 1) / 2; // 归一化到 [0, 1]let v = (y / this.radius + 1) / 2; // 归一化到 [0, 1]let uvX = uv[0] + (uv[2] - uv[0]) * u; // 根据图集 UV 计算实际 UVlet uvY = uv[1] + (uv[5] - uv[1]) * v; // 根据图集 UV 计算实际 UVuvs.push(cc.v2(uvX, uvY)); // 添加 UVcolors.push(cc.Color.WHITE); // 添加颜色}// 设置索引(三角形扇)for (let i = 1; i <= this.segments; i++) {indices.push(0); // 圆心indices.push(i); // 当前顶点indices.push(i + 1); // 下一个顶点}mesh.init(new gfx.VertexFormat([{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },{ name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },]), positions.length, true);mesh.setVertices(gfx.ATTR_POSITION, positions);mesh.setVertices(gfx.ATTR_UV0, uvs);mesh.setIndices(indices);this.renderer.mesh = mesh;},// 更新mesh,单独图片的setMesh() {// 创建 Meshlet mesh = new cc.Mesh();// 计算顶点和 UVlet positions = [];let uvs = [];let indices = [];let colors = [];// 圆心顶点positions.push(cc.v2(0, 0)); // 圆心uvs.push(cc.v2(0.5, 0.5));  // 圆心 UVcolors.push(cc.Color.WHITE); // 圆心颜色// 圆边缘顶点for (let i = 0; i <= this.segments; i++) {let angle = (i / this.segments) * Math.PI * 2; // 计算角度let x = Math.cos(angle) * this.radius; // 计算 x 坐标let y = Math.sin(angle) * this.radius; // 计算 y 坐标positions.push(cc.v2(x, y)); // 添加顶点uvs.push(cc.v2((x / this.radius + 1) / 2, (y / this.radius + 1) / 2)); // 添加 UVcolors.push(cc.Color.WHITE); // 添加颜色}// 设置索引(三角形扇)for (let i = 1; i <= this.segments; i++) {indices.push(0); // 圆心indices.push(i); // 当前顶点indices.push(i + 1); // 下一个顶点}mesh.init(new gfx.VertexFormat([{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },{ name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },]), positions.length, true);mesh.setVertices(gfx.ATTR_POSITION, positions);mesh.setVertices(gfx.ATTR_UV0, uvs);mesh.setIndices(indices);this.renderer.mesh = mesh;},// 更新图片applySpriteFrame() {// cc.log('_applySpriteFrame');if (this.spriteFrame) {const renderer = this.renderer;let material = renderer._materials[0];// Reset materialmaterial.define("USE_DIFFUSE_TEXTURE", true);material.setProperty('diffuseTexture', this.spriteFrame.getTexture());}},});

外部调用这个组件的方法,setDataShow传对应的参数就可以,节点上需要挂sprite组件,sprite更新图片或者初始化加载的时候,调用这个方法setDataShow,同时兼容删除节点的sprite组件,如果不想挂载sprite组件,默认直接挂上meshRenderer组件,需要自己修改下代码,把参数node直接改成传对应的spriteFrame图片 

Cocos Creator 的纹理坐标系(UV 坐标系)的 Y 轴方向是 从上到下 的,如果结果图片y是反向的,可以设代码修改uvs中的y的取值

  • 将 v 的计算改为 1 - (y / radius + 1) / 2,即对 Y 方向取反。

相关文章:

cocos creator使用mesh修改图片为圆形,减少使用mask,j减少drawcall,优化性能

cocos creator版本2.4.11 一个mask占用drawcall 3个以上&#xff0c;针对游戏中技能图标&#xff0c;cd,以及多玩家头像&#xff0c;是有很大优化空间 1.上代码&#xff0c;只适合单独图片的&#xff0c;不适合在图集中的图片 const { ccclass, property } cc._decorator;c…...

C++ Qt开发成长之路,从入门到企业级实战项目,保姆级学习路线

Qt 介绍 Qt是一个跨平台的C图形用户界面应用程序开发框架&#xff0c;最初由挪威的Trolltech公司开发&#xff0c;后来被诺基亚收购&#xff0c;现在由Qt公司维护。它提供了丰富的工具和类库&#xff0c;使开发者能够轻松地创建各种类型的应用程序&#xff0c;包括桌面应用、移…...

JavaWeb后端基础(7)AOP

AOP是Spring框架的核心之一&#xff0c;那什么是AOP&#xff1f;AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;其实说白了&#xff0c;面向切面编程就是面向特定方法编程。AOP是一种思想&#xff0c;而在Spring框…...

Uniapp实现地图获取定位功能

摘要&#xff1a;本文将手把手教你如何在Uniapp项目中集成地图功能、实现定位获取&#xff0c;并解决微信小程序、APP、H5三端的兼容性问题&#x1f680;&#x1f680;&#x1f680; 一、环境准备 地图平台选择 微信小程序&#xff1a;腾讯地图&#xff08;强制使用&#xff09…...

批量将 Excel 转换 PDF/Word/CSV以及图片等其它格式

Excel 格式转换是我们工作过程当中非常常见的一个需求&#xff0c;我们通常需要将 Excel 转换为其他各种各样的格式。比如将 Excel 转换为 PDF、比如说将 Excel 转换为 Word、再比如说将 Excel文档转换为图片等等。 这些操作对我们来讲都不难&#xff0c;因为我们通过 Office 都…...

Flutter:StatelessWidget vs StatefulWidget 深度解析

目录 1. 引言 2. StatelessWidget&#xff08;无状态组件&#xff09; 2.1 定义与特点 2.2 代码示例 3. StatefulWidget&#xff08;有状态组件&#xff09; 3.1 定义与特点 3.2 代码示例 4. StatelessWidget vs StatefulWidget 对比 5. StatefulWidget 生命周期 5.1…...

Stream流学习

Stream流 把数据放进stream流水线&#xff0c;对数据进行一系列操作&#xff08;中间方法&#xff09;&#xff0c;最后封装&#xff08;终结方法&#xff09;。 Stream.of()允许传入任何参数 常见中间方法 可以对数据进行链式&#xff08;流水线&#xff09;操作&#xff0c;但…...

多视图几何--恢复相机位姿/内参的几种方法

恢复相机位姿的几种方法 1分解投影矩阵 1.1投影矩阵分解为相机内外参矩阵的完整解析 投影矩阵&#xff08;Projection Matrix&#xff09;是计算机视觉中将三维世界点映射到二维像素坐标的核心工具&#xff0c;其本质是相机内参矩阵&#xff08;Intrinsic Matrix&#xff09…...

[数据结构]堆详解

目录 一、堆的概念及结构 二、堆的实现 1.堆的定义 2堆的初始化 3堆的插入 ​编辑 4.堆的删除 5堆的其他操作 6代码合集 三、堆的应用 &#xff08;一&#xff09;堆排序&#xff08;重点&#xff09; &#xff08;二&#xff09;TOP-K问题 一、堆的概念及结构 堆的…...

领域驱动设计(DDD)与MVC架构:理念对比与架构选择

领域驱动设计&#xff08;DDD&#xff09;与MVC架构&#xff1a;理念对比与架构选择 一、架构之争的本质&#xff1a;业务复杂度驱动技术演进 在软件开发领域&#xff0c;没有银弹式的完美架构&#xff0c;只有适合当前业务场景的合理选择。MVC与DDD的区别本质上是业务复杂度与…...

牛客周赛:84:B:JAVA

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 import java.util.ArrayList; import java.util.Arrays; import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner scanner new Scanner(S…...

【理想解法学习笔记】

目录 理想解法原理简介算法步骤属性值规范化方法代码示例 理想解法 原理简介 TOPSIS(Technique for Order Preference by Simi larity to IdealSolution)法是一种逼近理想解的排序方法。其基本的处理思路是&#xff1a;首先建立初始化决策矩阵&#xff0c;而后基于规范化后的初…...

CI/CD—Jenkins配置一次完整的jar自动化发布流程

背景&#xff1a; 实现设想&#xff1a; 要创建自动化发布&#xff0c;需要准备一台测试服务器提前安装好java运行所需的环境&#xff0c;JDK版本最好和Windows开发机器上的版本一致&#xff0c;在Jenkins上配置将构建好的jar上传到测试服务器上&#xff0c;测试服务器自动启动…...

Magento2根据图片文件包导入产品图片

图片包给的图片文件是子产品的图片&#xff0c;如下图&#xff1a;A104255是主产品的sku <?php/*** 根据图片包导入产品图片&#xff0c;包含子产品和主产品* 子产品是作为主图&#xff0c;主产品是作为附加图片*/use Magento\Framework\App\Bootstrap;include(../app/boot…...

从零开始的python学习(五)P71+P72+P73+P74

本文章记录观看B站python教程学习笔记和实践感悟&#xff0c;视频链接&#xff1a;【花了2万多买的Python教程全套&#xff0c;现在分享给大家&#xff0c;入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…...

OpenHarmony5.0分布式系统源码实现分析—软总线

一、引言 OpenHarmony 作为一款面向万物互联的操作系统&#xff0c;其分布式软总线&#xff08;Distributed SoftBus&#xff09;是实现设备间高效通信和协同的核心技术之一。分布式软总线通过构建一个虚拟的总线网络&#xff0c;使得不同设备能够无缝连接、通信和协同工作。本…...

基于SpringBoot实现旅游酒店平台功能六

一、前言介绍&#xff1a; 1.1 项目摘要 随着社会的快速发展和人民生活水平的不断提高&#xff0c;旅游已经成为人们休闲娱乐的重要方式之一。人们越来越注重生活的品质和精神文化的追求&#xff0c;旅游需求呈现出爆发式增长。这种增长不仅体现在旅游人数的增加上&#xff0…...

代码随想录算法训练营第六十一天 | 108. 冗余连接 109. 冗余连接II

108. 冗余连接 题目链接&#xff1a;KamaCoder 文档讲解&#xff1a;代码随想录 状态&#xff1a;AC Java代码&#xff1a; import java.util.*;class Main {public static int[] father;public static void main(String[] args) {Scanner scan new Scanner(System.in);int n…...

RoboVQA:机器人多模态长范围推理

23 年 11 月来自 Google Deepmind 的论文“RoboVQA: Multimodal Long-Horizon Reasoning for Robotics”。 本文提出一种可扩展、自下而上且本质多样化的数据收集方案&#xff0c;该方案可用于长期和中期的高级推理&#xff0c;与传统的狭窄自上而下的逐步收集相比&#xff0c…...

TCP/IP原理详细解析

前言 TCP/IP是一种面向连接&#xff0c;可靠的传输&#xff0c;传输数据大小无限制的。通常情况下&#xff0c;系统与系统之间的http连接需要三次握手和四次挥手&#xff0c;这个执行过程会产生等待时间。这方面在日常开发时需要注意一下。 TCP/IP 是互联网的核心协议族&…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...