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, 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值