【Unity】UGUI模拟NGUI的UISprite-->LImage

UGUI本没有像NGUI方便使用图集的组件,之前也写过继承Image,加入SpriteAtlas作图集,切换图片显示的组件,现在弄一个3.0版本的
这个组件的诞生源于上一篇:

【Unity】Addressables下的图集(SpriteAtlas)内存优化

====================================================================================

1、首先是在Image的基础上加入图集信息和切换图片的操作

在上篇里面都有写,是通过序列化图集的路径和选择Sprite的Name,初始化时通过Addressables加载图集,并完成图片选择显示
2、在编辑器模式下,通过属性面板完成图集信息和图片的选择

[CustomEditor(typeof(LImage), true)]
[CanEditMultipleObjects]
public class LImageInspector : ImageEditor
SpriteAtlas m_SpriteAtlas;//临时变量
string atlasName = AddressableEditorHelper.GetAssetPathByAddressableName(m_Comp.AtlasName);
m_SpriteAtlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasName);
在属性面板的Enable方法里,获取已设置好信息的图集资源
m_SpriteAtlas = EditorGUI.ObjectField(rect, m_SpriteAtlas, typeof(SpriteAtlas), false) as SpriteAtlas;
将临时图集变量显示在属性面板,来进行替换
if (m_SpriteAtlas != null)
{
    m_NewAtlasPath = AddressableEditorHelper.GetAddressableNameByObject(m_SpriteAtlas);

    if (m_AtlasPath.stringValue!= m_NewAtlasPath)
    {
        m_AtlasPath.stringValue = m_NewAtlasPath;
        Object[] sprites = m_SpriteAtlas.GetPackables();
        Sprite newSprite = null;
        for (int i = 0; i < sprites.Length; i++)
        {
            if (sprites[i].name == m_Comp.SpriteName)
            {
                newSprite = AssetDatabase.LoadAssetAtPath<Sprite>(AssetDatabase.GetAssetPath(sprites[i]));
            }
        }

        m_Comp.overrideSprite = newSprite;
    }
}
//判断当临时图集发生更改时,修改参与序列化的图集地址信息
//根据选择的图片名称,在新图集里面尝试寻找资源并赋值给overrideSprite
//赋值给overrideSprite 是因为它不参与序列化,但又可以完成显示
if (GUILayout.Button("更换Sprite", EditorStyles.miniButton))
{
    if (m_SpriteAtlas != null)
    {
        SpriteSelector.Show(serializedObject, m_SpriteInfo, m_SpriteAtlas, m_Comp);
    }
    else
    {
        EditorUtility.DisplayDialog("错误", "请先设置图集!", "确定");
    }
}
//增加更改图片的按钮,调用打开切换图片窗口的接口

3、实现根据图集展示所有资源,并提供展示选择的窗口

public Sprite[] LoadSprites(SpriteAtlas tempAtlas, string spriteName)
{

    UnityEngine.Object[] sprites = tempAtlas.GetPackables();

    Sprite[] _spriteArray = new Sprite[sprites.Length];
    Texture tempTexture = null;
    Sprite tempSprite = null;
    for (int i = 0; i < sprites.Length; i++)
    {
        if ((tempTexture = sprites[i] as Texture) != null)
        {
            _spriteArray[i] = AssetDatabase.LoadAssetAtPath<Sprite>(AssetDatabase.GetAssetPath(tempTexture));
        }
        else if ((tempSprite = sprites[i] as Sprite) != null)
        {
            _spriteArray[i] = tempSprite;
        }
    }

    if (string.IsNullOrEmpty(spriteName))
    {
        return _spriteArray;
    }
    return Array.FindAll(_spriteArray, (a) => { return a.name.ToLower().Contains(spriteName.ToLower()); });
}

//更具图集获取它对应的所有Sprite,其实这里可以直接调用SpriteAtlas的GetSprites接口,我这里是之前采取源资源直接赋值给sprite属性的方案,现在clone的sprite都可以

然后用上面获取到的所有Sprite,在创建的窗口上面显示出来,并给所有图片一个点击事件

if (mImage != null)
{
    mImage.SetSpriteEditor(sprite);
    EditorUtility.SetDirty(mImage.gameObject);
}
//在点击事件中,将选中的Sprite传递给LImage组件,进行编辑模式切换Spirte处理
public void SetSpriteEditor(Sprite _sprite)
{
    overrideSprite = _sprite;
    if (_sprite != null)
    {
        m_SpriteName = _sprite.texture.name;
    }
}
//修改当前显示
//修改参与序列化的Sprite Name值

因为overrideSprite是不参与序列化的,在编辑器预览预设时,需要给LImage增加一个初始化显示的方法,和LImageInspector面板Enable时类似,更具图集地址获取本地图集资源,再根据选择SpriteName从图集资源中找到对应的资源,赋值给overrideSprite,完成预览显示

4、完结!!

解释一下为什么不采用Sprite源资源直接赋值给sprite的方法,Image的sprite字段是参与序列化的,然后结合我上一篇最后的那段话

在非运行情况下(编辑器模式下),将资源直接拖拽到Image组件的Sprite属性上后,该资源会被Unity序列化到场景或预制件中,即便在运行时销毁了Image组件本身,资源本体依然保存在序列化数据中,并不会被直接卸载。运行期间,Unity对拖拽到Inspector面板上的资源进行引用计数管理,即使Image组件被销毁,拖拽到Sprite属性中的资源依然占用内存,因为它在场景中存在序列化引用。

所以更改overrideSprtie,减少参与序列化的资源信息,及时清理内存;

但是上面这个处理方案可能会造成显示“延迟”,因为它需要再加载,可以修改上面对图集的处理,将它更改为参与序列化,但是要注意内存!

 

posted @ 2024-10-30 21:33  lovewaits  阅读(5)  评论(0编辑  收藏  举报