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

Unity实现自定义图集(四)

以下内容是根据Unity 2020.1.0f1版本进行编写的

在之前的篇章中已经把自定义图集在编辑器上的使用,以及运行时所需的信息都准备好了,接下来就是魔改UGUI的Image组件,使其能够像Image那样运行时如果引用的资源有打自定义图集,则加载对应自定义图集的Texture。

1、思路

在这里插入图片描述
如图,想要模仿Image组件是怎么判断一个资源是否从有打图集的,先直接在Image组件上搜SpriteAtlas,发现只能搜出如上图这几个结果。

看起来应该是有一个SpriteAtlas的管理类,在Image初始化的时候注册了一个RebuildImage的事件,每当资源变动/运行游戏等情况时,就会执行这个RebuildImage的方法,方法调用时会传入一个SpriteAtlas的参数,SpriteAtlas类中有个CanBindTo的方法,看来这个应该就是判断资源是否有打图集的方法了。
在这里插入图片描述
再点进去看发现是外部方法,于是打开反编译软件看看有没有具体的代码
在这里插入图片描述
反编译软件里显示代码是写到了C++的内部文件,这样就看不到具体的实现过程了。但是我们可以自己实现一个类似的。

我们可以在自定义图集中多保存一份图集内全部资源的guid对应路径的字典(去掉之前的guid列表),然后在自定义的Image组件中保存一份当前引用资源的guid,当在某一个图集中找到自定义Image组件中的guid时,则认为该Image组件引用的资源已经打了自定义图集。

接下来就是如何在运行时把有自定义图集的自定义Image组件中的Texture换成自定义图集的Texture,并将其uv设对。

这里先实现编辑器的自定义图集Texture加载。

在前面的文章中就已经准备好了自定义图集的Texture,并且会在每次Unity启动前更新,那么编辑器下运行时只需要把存放在Library文件夹的Texture读取出来就可以了
在这里插入图片描述
读取出来后,自定义图集MyAtlas中已经保存有对应资源的uv信息了,这时候看Image代码(如图),发现传进去的uv是一个0~1的值。因此自定义图集保存时还需要多保存width和height两个值。

注意;这里不直接将保存的uv信息从int改成float后double,一是不希望保存太多小数,二是考虑到width和height值可能会有被其它地方用到的时候。

数据都准备好了,这时候还有一个问题,Image组件中,如果在上图的GenerateSimpleSprite方法中将原有的设置顶点信息和uv的代码去掉,增加判断当前Image资源是否已打自定义图集,并且加载图集的Texture并设置为材质球的Texture,会发现运行时资源是无法知道其guid的,甚至路径也不知道。

获取guid或者资源路径的代码一般使用的是AssetDataBase类,但是这个类是编辑器类,不能打包后在其它设备使用,打包时也会报错。看了一下Image的代码,猜测是Unity内部有自己的一套资源管理系统,能在运行时获取资源的信息,但是没有开放接口。

于是直接使用简单的办法,在自定义的Image组件上加一个guid变量,并且在对应的Editor类中对于每次Image引用的资源有变动,就刷新一次其引用资源的guid即可。

2、实现

在这里插入图片描述
首先需要去掉项目原来的UGUI插件,并从Unity官方下载UGUI源码,并将源码复制到项目中。(我这里复制的是魔改UGUI代码工程里的源码)

然后新建一个MyImage类,复制Image的代码,并按上面的思路修改
在这里插入图片描述
增加guid属性和纹理图属性,用于保存当前引用的资源guid和运行时资源对应图集的纹理图
在这里插入图片描述
在这里插入图片描述
接下来应该改动mainTexture,当获取mainTexture时,如果当前引用的资源存在自定义图集,那么应该直接返回自定义图集的图。

但是mainTexture没有set方法,只有get方法,因此先需要在其基础类Graphic类中将mainTexture虚拟属性增加一个空的set方法。然后再在MyImage中的get方法判断是否存在自定义图集的纹理图,若存在则直接返回,否则就按原来的代码执行。set方法也比较简单,就是把值赋给新增的m_MainTexture属性就行。

protected void SpriteGUI()
        {
   
            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(m_Sprite, m_SpriteContent);
            if (EditorGUI.EndChangeCheck())
            {
   
                var newSprite = m_Sprite.objectReferenceValue as Sprite;
                if (newSprite)
                {
   
                    MyImage.Type oldType = (MyImage.Type)m_Type.enumValueIndex;
                    if (newSprite.border.SqrMagnitude() > 0)
                    {
   
                        m_Type.enumValueIndex = (int)MyImage.Type.Sliced;
                    }
                    else if (oldType == MyImage.Type.Sliced)
                    {
   
                        m_Type.enumValueIndex = (int)MyImage.Type.Simple;
                    }
                    //自定义改动
                    MyImage myImage = target as MyImage;
                    if(myImage.sprite)
                    {
   
                        string path = AssetDatabase.GetAssetPath(newSprite);
                        string guid = AssetDatabase.AssetPathToGUID(path);
                        m_guid.stringValue = guid;
                        Debug.Log("image editor set guid: " + guid);
                    }
                    else
                    {
   
                        m_guid.stringValue = "";
                    }
                    EditorUtility.SetDirty(myImage);
                }
                (serializedObject.targetObject as MyImage).DisableSpriteOptimizations();
            }

在Editor文件夹下新建MyImageEditor脚本,复制ImageEditor代码,增加每次资源变动获取资源的guid并赋值到MyImage类的guid属性上。(自定义改动部分)
在这里插入图片描述
接着在MyAtlas类实现一个类似SpriteAtlas的CanBindTo方法。用于判断资源是否存在自定义图集。这里仅实现了在编辑器模式下的图集纹理加载,即加载保存到Library文件夹下的纹理图。
在这里插入图片描述
同理,在自定义图集管理类中也需要加一个CanBindTo方法,用于全局调用。

最后是改一下生成VertexHelper的方法,对应有4个方法,分别对应Image Type中的simple、sliced、tiled、filled模式。其中,平铺图tiled模式,如果将平铺图资源打进图集,可能会导致顶点数大大增加,因此一般不会把平铺图打图集,此外平铺图的生成比较麻烦,所以这里的改动忽略了平铺图生成VertexHelper的方法(即使用tiled平铺图不会合批)。

代码如下:

Simple模式:

private void GenerateSimpleSprite(VertexHelper vh, bool lPreserveAspect)
        {
   
            Vector4 drawingDimensions = GetDrawingDimensions(lPreserveAspect);
            Vector4 vector = (activeSprite != null) ? DataUtility.GetOuterUV(activeSprite) : Vector4.zero;
            Color color = this.color;
            vh.Clear();
            //vh.AddVert(new Vector3(drawingDimensions.x, drawingDimensions.y), color, new Vector2(vector.x, vector.y));
            //vh.AddVert(new Vector3(drawingDimensions.x, drawingDimensions.w), color, new Vector2(vector.x, vector.w));
            //vh.AddVert(new Vector3(drawingDimensions.z, drawingDimensions.w), color, new Vector2(vector.z, vector.w));
            //vh.AddVert(new Vector3(drawingDimensions.z, drawingDimensions.y), color, new Vector2(vector.z, vector.y));

            //自定义改动
            if(Application.isPlaying)
            {
   
                MyAtlas atlas = MyAtlasManager.CanBindTo(this);
                Debug.LogError("atlas: " + atlas);
                if (atlas)
                {
   
                    Texture2D texture2D = atlas.GetTexture(OnSetMainTexture);
                    RectInfo rectInfo = atlas.GetRectInfo(guid);
                    if (texture2D && rectInfo != null)
                    {
   
                        mainTexture = texture2D;
                        int textureWidth = atlas.GetWidth();
                        int textureHeight = atlas.GetHeight();
                        float x = (float)(rectInfo.x + vector.x * rectInfo.width) / textureWidth;
                        float y = (float)(rectInfo.y + vector.y * rectInfo.height) / textureHeight;
                        float z = (float)(rectInfo.x+ vector.z * rectInfo.width) / textureWidth;
                        float w = (float)(rectInfo.y+ vector.w * rectInfo.height) / textureHeight;
                        vector = new Vector4(x, y, z, w);
                    }
                }
                else
                {
   
                    mainTexture = null;
                }
            }
            vh.AddVert(new Vector3(drawingDimensions.x, drawingDimensions.y), color, new Vector2(vector.x, vector.y));
            vh.AddVert(new Vector3(drawingDimensions.x, drawingDimensions.w), color, new Vector2(vector.x, vector.w));
            vh.AddVert(new Vector3(drawingDimensions.z, drawingDimensions.w), color, new Vector2(vector.z, vector.w));
            vh.AddVert(new Vector3(drawingDimensions.z, drawingDimensions.y), color, new Vector2(vector.z, vector.y));
            vh.AddTriangle(0, 

http://www.kler.cn/news/340811.html

相关文章:

  • 软件包与服务搭建
  • 滚雪球学Redis[3.1讲]:Redis的持久化机制
  • Scalable TCP 如何优化长肥管道
  • 《黑神话悟空:点燃朔州文旅之火》
  • 云服务器架构详解:X86计算_ARM_GPU/FPGA/ASIC_裸金属_超级计算集群
  • scrapy爬取汽车、车评数据【下】
  • 02. 上报自定义数据到 prometheus(使用 Python Client)
  • 【C++】set和multiset(关联式容器、键值对,set和multiset的基本特性、主要用途及常用操作)
  • 搜维尔科技:Haption远程操作项目模拟项目
  • Spring Validation —— 参数校验框架
  • Linux系统中,文件和文件夹的权限和所有权核心概念
  • Window系统编程 - 文件操作
  • 国庆档不太热,影视股“凉”了?
  • Win10之Ubuntu22.04(主机)与Virtual-BOX(宿主win10)网络互通调试(七十九)
  • k8s 中存储之 PV 持久卷 与 PVC 持久卷申请
  • 实现std::sort,replace,fill,accumulate,equal等函数
  • MyBatis之TypeHandler的自定义实现
  • Golang | Leetcode Golang题解之第462题最小操作次数使数组元素相等II
  • GNU/Linux - tarball文件介绍介绍
  • C#中Json序列化的进阶用法