以下内容是根据Unity 2020.1.01f版本进行编写的

UGUI源代码之Image-Sliced模式

  • 1、目的
  • 2、参考
  • 3、代码阅读
  • 4、思考
  • 5、准备修改UGUI源代码
  • 6、自定义实现九宫图
  • 7、与NGUI对比
  • 8、最终效果
  • 9、项目工程源代码

1、目的


本文主要以研究UGUI中九宫图的实现过程,以及考虑能否优化(效果上的优化以及性能上的优化),最终实现出来的效果还需要进一步测试才能用于实际项目中

2、参考


本文参考Unity官方的UGUI源代码
Github地址:https://github.com/Unity-Technologies/uGUI

3、代码阅读


Image组件有4种模式,在这里我们只解析九宫模式的Image。

static readonly Vector2[] s_VertScratch = new Vector2[4];static readonly Vector2[] s_UVScratch = new Vector2[4];private void GenerateSlicedSprite(VertexHelper toFill){if (!hasBorder){GenerateSimpleSprite(toFill, false);return;}Vector4 outer, inner, padding, border;if (activeSprite != null){outer = Sprites.DataUtility.GetOuterUV(activeSprite);inner = Sprites.DataUtility.GetInnerUV(activeSprite);padding = Sprites.DataUtility.GetPadding(activeSprite);border = activeSprite.border;}else{outer = Vector4.zero;inner = Vector4.zero;padding = Vector4.zero;border = Vector4.zero;}Rect rect = GetPixelAdjustedRect();Vector4 adjustedBorders = GetAdjustedBorders(border / pixelsPerUnit, rect);padding = padding / pixelsPerUnit;s_VertScratch[0] = new Vector2(padding.x, padding.y);s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);s_VertScratch[1].x = adjustedBorders.x;s_VertScratch[1].y = adjustedBorders.y;s_VertScratch[2].x = rect.width - adjustedBorders.z;s_VertScratch[2].y = rect.height - adjustedBorders.w;for (int i = 0; i < 4; ++i){s_VertScratch[i].x += rect.x;s_VertScratch[i].y += rect.y;}s_UVScratch[0] = new Vector2(outer.x, outer.y);s_UVScratch[1] = new Vector2(inner.x, inner.y);s_UVScratch[2] = new Vector2(inner.z, inner.w);s_UVScratch[3] = new Vector2(outer.z, outer.w);toFill.Clear();for (int x = 0; x < 3; ++x){int x2 = x + 1;for (int y = 0; y < 3; ++y){if (!m_FillCenter && x == 1 && y == 1)continue;int y2 = y + 1;AddQuad(toFill,new Vector2(s_VertScratch[x].x, s_VertScratch[y].y),new Vector2(s_VertScratch[x2].x, s_VertScratch[y2].y),color,new Vector2(s_UVScratch[x].x, s_UVScratch[y].y),new Vector2(s_UVScratch[x2].x, s_UVScratch[y2].y));}}}


在这里简单介绍一下Unity GUI渲染的流程:
1、与渲染模型类似,实际上UGUI也是通过计算顶点和三角形生成网格(Mesh),再通过Renderer渲染出来
2、顶点数据中最主要的是位置,颜色,纹理坐标,法线等。也就是说顶点信息中保存着顶点位置、颜色、纹理坐标、法线信息等。
3、Unity中的图片渲染,实际上也是通过生成顶点信息,以及生成三角形等,再把生成的信息转换为Mesh,再进行渲染的过程

从代码可以看出,传进来的是一个VertexHelper的参数,VertexHelper是顶点辅助类,VertexHelper类封装了生成mesh的基本信息以及常用的方法。

if (!hasBorder)
{GenerateSimpleSprite(toFill, false);return;
}

这段代码的意思是,如果Sprite没有设置九宫范围,则直接生成Simple模式的图

Vector4 outer, inner, padding, border;if (activeSprite != null)
{outer = Sprites.DataUtility.GetOuterUV(activeSprite);inner = Sprites.DataUtility.GetInnerUV(activeSprite);padding = Sprites.DataUtility.GetPadding(activeSprite);border = activeSprite.border;
}
else
{outer = Vector4.zero;inner = Vector4.zero;padding = Vector4.zero;border = Vector4.zero;
}Rect rect = GetPixelAdjustedRect();
Vector4 adjustedBorders = GetAdjustedBorders(border / pixelsPerUnit, rect);
padding = padding / pixelsPerUnit;s_VertScratch[0] = new Vector2(padding.x, padding.y);
s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);s_VertScratch[1].x = adjustedBorders.x;
s_VertScratch[1].y = adjustedBorders.y;s_VertScratch[2].x = rect.width - adjustedBorders.z;
s_VertScratch[2].y = rect.height - adjustedBorders.w;for (int i = 0; i < 4; ++i)
{s_VertScratch[i].x += rect.x;s_VertScratch[i].y += rect.y;
}s_UVScratch[0] = new Vector2(outer.x, outer.y);
s_UVScratch[1] = new Vector2(inner.x, inner.y);
s_UVScratch[2] = new Vector2(inner.z, inner.w);
s_UVScratch[3] = new Vector2(outer.z, outer.w);

这段代码实际上的操作是,计算图片的位置信息,然后把4个顶点位置信息按顺序写进s_VertScratch数组中,还有,计算当前sprite的uv信息(包括图集中的uv和自身的uv),然后把4个顶点的uv信息按顺序写进s_UVScratch数组中,其顺序如图所示:

因此,知道这4个点的位置信息和UV信息即可知道全部点的位置和UV信息了

toFill.Clear();for (int x = 0; x < 3; ++x)
{int x2 = x + 1;for (int y = 0; y < 3; ++y){if (!m_FillCenter && x == 1 && y == 1)continue;int y2 = y + 1;AddQuad(toFill,new Vector2(s_VertScratch[x].x, s_VertScratch[y].y),new Vector2(s_VertScratch[x2].x, s_VertScratch[y2].y),color,new Vector2(s_UVScratch[x].x, s_UVScratch[y].y),new Vector2(s_UVScratch[x2].x, s_UVScratch[y2].y));}
}

最后,先清空VertexHelper里的顶点信息,然后把计算出来的顶点信息通过AddQuad方法加入到VertexHelper中

其中,VertexHelper是一个用于保存顶点和三角形的类,相当于顶点和三角形信息的容器,常用方法有AddVert添加顶点,以及AddTriangle添加三角形

static void AddQuad(VertexHelper vertexHelper, Vector2 posMin, Vector2 posMax, Color32 color, Vector2 uvMin, Vector2 uvMax)
{int startIndex = vertexHelper.currentVertCount;vertexHelper.AddVert(new Vector3(posMin.x, posMin.y, 0), color, new Vector2(uvMin.x, uvMin.y));vertexHelper.AddVert(new Vector3(posMin.x, posMax.y, 0), color, new Vector2(uvMin.x, uvMax.y));vertexHelper.AddVert(new Vector3(posMax.x, posMax.y, 0), color, new Vector2(uvMax.x, uvMax.y));vertexHelper.AddVert(new Vector3(posMax.x, posMin.y, 0), color, new Vector2(uvMax.x, uvMin.y));vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2);vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex);
}

AddQuad方法是为VetexHelper增加一个矩形,实际上是增加4个顶点,以及两个三角形面。如果不需要填充中心(FillCenter)的话,就不为中心的矩形添加顶点和三角形面就可以了
至此,一张九宫图就实现出来了。

4、思考


从第三部分源代码阅读可以看出,实现九宫图一共需要执行9次AddQuad方法,即一共会产生4x9=36个顶点,以及2x9=18个三角形面,就是说,每次使用九宫图都会比使用Simple模式的图增加32个顶点。(Simple模式的图只需要执行以此AddQuad方法,需要4个顶点和2个三角形)
但是,根据我画的示意图,实际上最少只需要16个顶点就能实现相同的九宫图,而UGUI的九宫图实际上有不少顶点是重复的,那么重复的顶点是否可以复用呢?

5、准备修改UGUI源代码


请看这篇:UGUI源代码之修改源代码的前期准备

已经准备过的同学可以跳过

6、自定义实现九宫图


实现自定义的九宫图,在这里只写重点部分:

static void AddVertexAndLine(VertexHelper vertexHelper, Vector2[] s_VertScratch, Vector2[] s_UVScratch, bool m_FillCenter)
{vertexHelper.AddVert(new Vector3(s_VertScratch[0].x, s_VertScratch[3].y), color, new Vector2(s_UVScratch[0].x, s_UVScratch[3].y));vertexHelper.AddVert(new Vector3(s_VertScratch[1].x, s_VertScratch[3].y), color, new Vector2(s_UVScratch[1].x, s_UVScratch[3].y));vertexHelper.AddVert(new Vector3(s_VertScratch[2].x, s_VertScratch[3].y), color, new Vector2(s_UVScratch[2].x, s_UVScratch[3].y));vertexHelper.AddVert(new Vector3(s_VertScratch[3].x, s_VertScratch[3].y), color, new Vector2(s_UVScratch[3].x, s_UVScratch[3].y));vertexHelper.AddVert(new Vector3(s_VertScratch[0].x, s_VertScratch[2].y), color, new Vector2(s_UVScratch[0].x, s_UVScratch[2].y));vertexHelper.AddVert(new Vector3(s_VertScratch[1].x, s_VertScratch[2].y), color, new Vector2(s_UVScratch[1].x, s_UVScratch[2].y));vertexHelper.AddVert(new Vector3(s_VertScratch[2].x, s_VertScratch[2].y), color, new Vector2(s_UVScratch[2].x, s_UVScratch[2].y));vertexHelper.AddVert(new Vector3(s_VertScratch[3].x, s_VertScratch[2].y), color, new Vector2(s_UVScratch[3].x, s_UVScratch[2].y));vertexHelper.AddVert(new Vector3(s_VertScratch[0].x, s_VertScratch[1].y), color, new Vector2(s_UVScratch[0].x, s_UVScratch[1].y));vertexHelper.AddVert(new Vector3(s_VertScratch[1].x, s_VertScratch[1].y), color, new Vector2(s_UVScratch[1].x, s_UVScratch[1].y));vertexHelper.AddVert(new Vector3(s_VertScratch[2].x, s_VertScratch[1].y), color, new Vector2(s_UVScratch[2].x, s_UVScratch[1].y));vertexHelper.AddVert(new Vector3(s_VertScratch[3].x, s_VertScratch[1].y), color, new Vector2(s_UVScratch[3].x, s_UVScratch[1].y));vertexHelper.AddVert(new Vector3(s_VertScratch[0].x, s_VertScratch[0].y), color, new Vector2(s_UVScratch[0].x, s_UVScratch[0].y));vertexHelper.AddVert(new Vector3(s_VertScratch[1].x, s_VertScratch[0].y), color, new Vector2(s_UVScratch[1].x, s_UVScratch[0].y));vertexHelper.AddVert(new Vector3(s_VertScratch[2].x, s_VertScratch[0].y), color, new Vector2(s_UVScratch[2].x, s_UVScratch[0].y));vertexHelper.AddVert(new Vector3(s_VertScratch[3].x, s_VertScratch[0].y), color, new Vector2(s_UVScratch[3].x, s_UVScratch[0].y));vertexHelper.AddTriangle(4, 0, 1);vertexHelper.AddTriangle(1, 5, 4);vertexHelper.AddTriangle(5, 1, 2);vertexHelper.AddTriangle(2, 6, 5);vertexHelper.AddTriangle(6, 2, 3);vertexHelper.AddTriangle(3, 7, 6);vertexHelper.AddTriangle(8, 4, 5);vertexHelper.AddTriangle(5, 9, 8);if (m_FillCenter){vertexHelper.AddTriangle(9, 5, 6);vertexHelper.AddTriangle(6, 10, 9);}vertexHelper.AddTriangle(10, 6, 7);vertexHelper.AddTriangle(7, 11, 10);vertexHelper.AddTriangle(12, 8, 9);vertexHelper.AddTriangle(9, 13, 12);vertexHelper.AddTriangle(13, 9, 10);vertexHelper.AddTriangle(10, 14, 13);vertexHelper.AddTriangle(14, 10, 11);vertexHelper.AddTriangle(11, 15, 14);
}

因为顶点信息和UV信息都计算好了,所以信息可以直接使用,修改的地方就是循环使用AddQuad方法的地方,改为使用上述方法。
可以看出,自定义实现的九宫图中,我没有循环使用AddQuad方法,而是改为使用AddVert方法和AddTriangle方法手动添加16个顶点和18个三角形面。

需要注意的是:这里自定义的SlicedImage是没有继承Graphic类的,而是继承MonoBehavior类,因此一些变量需要获取(如canvas,rectTransform等),在这里使用简单的挂载获取

7、与NGUI对比

void SlicedFill (List<Vector3> verts, List<Vector2> uvs, List<Color> cols)
{Vector4 br = border * pixelSize;if (br.x == 0f && br.y == 0f && br.z == 0f && br.w == 0f){SimpleFill(verts, uvs, cols);return;}Color gc = drawingColor;Vector4 v = drawingDimensions;mTempPos[0].x = v.x;mTempPos[0].y = v.y;mTempPos[3].x = v.z;mTempPos[3].y = v.w;if (mFlip == Flip.Horizontally || mFlip == Flip.Both){mTempPos[1].x = mTempPos[0].x + br.z;mTempPos[2].x = mTempPos[3].x - br.x;mTempUVs[3].x = mOuterUV.xMin;mTempUVs[2].x = mInnerUV.xMin;mTempUVs[1].x = mInnerUV.xMax;mTempUVs[0].x = mOuterUV.xMax;}else{mTempPos[1].x = mTempPos[0].x + br.x;mTempPos[2].x = mTempPos[3].x - br.z;mTempUVs[0].x = mOuterUV.xMin;mTempUVs[1].x = mInnerUV.xMin;mTempUVs[2].x = mInnerUV.xMax;mTempUVs[3].x = mOuterUV.xMax;}if (mFlip == Flip.Vertically || mFlip == Flip.Both){mTempPos[1].y = mTempPos[0].y + br.w;mTempPos[2].y = mTempPos[3].y - br.y;mTempUVs[3].y = mOuterUV.yMin;mTempUVs[2].y = mInnerUV.yMin;mTempUVs[1].y = mInnerUV.yMax;mTempUVs[0].y = mOuterUV.yMax;}else{mTempPos[1].y = mTempPos[0].y + br.y;mTempPos[2].y = mTempPos[3].y - br.w;mTempUVs[0].y = mOuterUV.yMin;mTempUVs[1].y = mInnerUV.yMin;mTempUVs[2].y = mInnerUV.yMax;mTempUVs[3].y = mOuterUV.yMax;}for (int x = 0; x < 3; ++x){int x2 = x + 1;for (int y = 0; y < 3; ++y){if (centerType == AdvancedType.Invisible && x == 1 && y == 1) continue;int y2 = y + 1;verts.Add(new Vector3(mTempPos[x].x, mTempPos[y].y));verts.Add(new Vector3(mTempPos[x].x, mTempPos[y2].y));verts.Add(new Vector3(mTempPos[x2].x, mTempPos[y2].y));verts.Add(new Vector3(mTempPos[x2].x, mTempPos[y].y));uvs.Add(new Vector2(mTempUVs[x].x, mTempUVs[y].y));uvs.Add(new Vector2(mTempUVs[x].x, mTempUVs[y2].y));uvs.Add(new Vector2(mTempUVs[x2].x, mTempUVs[y2].y));uvs.Add(new Vector2(mTempUVs[x2].x, mTempUVs[y].y));if (!mApplyGradient){cols.Add(gc);cols.Add(gc);cols.Add(gc);cols.Add(gc);}else{AddVertexColours(cols, ref gc, x, y);AddVertexColours(cols, ref gc, x, y2);AddVertexColours(cols, ref gc, x2, y2);AddVertexColours(cols, ref gc, x2, y);}}}
}

实际上,NGUI在九宫图的实现逻辑上和UGUI无异,同样是使用循环增加顶点和三角形面,增加的顶点数和三角形面也和UGUI的一致

8、最终效果



可以看到,自定义实现的九宫图比UGUI的九宫图少20个顶点


两张九宫图的顶点结构也是一样的

9、项目工程源代码

https://github.com/CHJ-Self/MyUGUI



大佬们找到问题欢迎拍砖~

UGUI源代码之Image-Sliced模式相关推荐

  1. Python版打字练习软件源代码,键盘练习软件源代码,含娱乐模式和训练模式

    Python版打字练习软件源代码,键盘练习软件源代码,含娱乐模式和训练模式 按ESC切换左手练习,右手练习.双手练习 完整代码下载地址:Python版打字练习软件源代码 核心代码: import ra ...

  2. image unity 拉伸_Unity UGUI基础之Image

    UGUI的Image等价于NGUI的Sprite组件,用于显示图片. 一.Image组件: Source Image(图像源):纹理格式为Sprite(2D and UI)的图片资源(导入图片后选择T ...

  3. Unity3D之UGUI基础3:Image图片

    一.Image基本使用 Image组件用于显示图片资源或者纯粹的颜色 在导入图片资源之前,可以先对图片进行以下操作: 必须:将图片的类型修改为"Sprite(2D and UI)" ...

  4. unity3d UGUI九宫格纹理拉伸的使用

    本篇文章我们来学习下在unity new ui即UGUI九宫格纹理拉伸的使用,不论是游戏中的UI,还是应用中的UI,纹理九宫格拉伸都是必不可少的,因为采用这种拉伸方式,可以最大化的节省纹理资源,任意缩 ...

  5. unity之UGUI系统基础

                                                                  UGUI系统基础 一.UGUI 简介: ①.Unity3D4.6 版本开始, ...

  6. 【Unity编辑器扩展】(三)PSD转UGUI Prefab, 一键拼UI解放美术/程序(完结)

    工具效果: 第一步,把psd图层转换为可编辑的节点树,并自动解析UI类型.自动绑定UI子元素: 第二步, 点击"生成UIForm"按钮生成UI预制体 (若有UI类型遗漏可在下拉菜单 ...

  7. 记录最全面的ugui优化策略

    UI 资源规范(内存优化) 客户端做任何的性能优化首先想到的都是规范美术资源,前期不给美术资源定制一定的规范,后期做优化性能会非常的被动.对于 UI 资源的规范,主要是考虑的内存优化. 1.任何的 U ...

  8. 如何对DevOps数据库进行源代码控制

    提纲: 包括索引在内的数据库模式需要进行源代码控制 诸如查询表这类用于控制业务逻辑的数据需要进行源代码控制 开发人员需要一种能够便捷地创建本地数据库的方法 共享数据库的更新只能通过构建服务器完成 健壮 ...

  9. 关于SDC沙盒源代码加密

    关键词:SDC沙盒. SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒.SDC沙盒 ...

  10. “实模式--保护模式--实模式”转换过程

    下面以pmtest2.asm为例,来讲述"实模式--保护模式--实模式"的转换过程. 1."实模式--保护模式--实模式"的转换过程. 2.介绍段描述符属性 3 ...

最新文章

  1. SQL Server中的几个方法和Transact SQL 常用语句以及函数[个人推荐]
  2. 控制src_【聚焦】仙知机器人基于SRC的激光SLAM自动叉车,满足各类场景需求
  3. mysql操作xml字段_SQL XML 字段操作
  4. C++类的继承与派生
  5. SSO单点登录学习总结(2)——基于Cookie+fliter单点登录实例
  6. Linux工作笔记032---Centos7.3 kill杀掉不用的进程_查看某个进程_某个进程占用的cpu,内存情况
  7. Linux 误删除 /boot分区 的解救办法
  8. /etc/init.crs enable\disable 状态
  9. Greg and Array CodeForces 296C 差分数组
  10. 阿里天池大数据竞赛(杂)
  11. 计算机义诊暑期社会实践报告,义诊社会实践报告
  12. pdf去除签名_扫描全能王一键识别图片/PDF/文档/文本文字随意转换
  13. 如何批量发送邮件?小白问百度,大神秀操作,网友惊呼:666
  14. Ubuntu18.04安装搜狗输入法无法切换中英文
  15. 《从前慢》 ----- 作者:木心
  16. 和弦笔记:和弦组成音/和弦命名规律/sus和add的区别
  17. Joomla建站之幻灯片
  18. 50条有趣的Python一行代码,建议收藏!
  19. json 跟着黑马打的代码 但还是undefined。求解,
  20. 护眼灯显色指数怎么选择?护眼灯显色指数80和90的区别是什么

热门文章

  1. 项目经历——地图定位神器
  2. 关于hhkb pro2键盘在mac上串键问题
  3. Altium Designer四层板设计教程
  4. python数据分析怎么画_跟小白学Python数据分析——绘制维恩图
  5. pix4d无人机影像处理_PhotoScan和Pix4Dmapper的无人机影像快速处理模式对比实验
  6. OpenCV 调用手机摄像头
  7. 使用chrome-har导出浏览器HAR数据
  8. python实战项目词云生成器(wordcloud+jieba+pyinstaller打包)——词云生成软件【Pyinstaller打包问题解决】
  9. 点云IO篇之stl文件读写
  10. 教你一招恢复100分信用分,新手违规被扣40分,还有救吗?