首页 > 其他分享 >【Unity】UGUI模拟NGUI的UISprite-->LImage

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

时间:2024-10-30 21:31:18浏览次数:1  
标签:Sprite -- LImage Unity SpriteAtlas 序列化 sprites 图集 sprite

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,减少参与序列化的资源信息,及时清理内存;

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

 

标签:Sprite,--,LImage,Unity,SpriteAtlas,序列化,sprites,图集,sprite
From: https://www.cnblogs.com/lovewaits/p/18516658

相关文章

  • KMC数据库设计
    存一下密钥管理中心数据库,免得找不到了具体表格:表名列名数据类型主键外键KeysKeyIDINTPKKeyValueVARBINARY(256)KeyTypeVARCHAR(50)CreatedAtDATETIMEExpirationDateDATETIMEStatusENUMKey_PoolsPoolIDINTPKPoo......
  • Linux Centos7 同步服务器时钟为北京时间
    Linux服务器时间不准确,容易造成日志时间错误、数据统计时间不准确等问题,因此需要将服务器时间设置准确并实时更新。操作步骤如下:1、安装ntp使用date命令查看时间是否准确:date如果不准确,使用ntp同步最新网络时间,安装ntp:yuminstall-yntp2、启动ntp服务执行命令:systemctl......
  • CF做题日记1
    当一个题贪心和\(dp\)都可能,而又无法证明贪心假了,可以通过数据范围推测用什么,实在不行可以先尝试\(dp\),看看能否优化。一个题中给的文字条件,可以先尝试将它转化为数学或计算机语言,这样可能会发现一些性质。咱们举个例子CF1842A,这题是个红题应该很简单(但我没做出来)假设所......
  • D. Speedbreaker
    题意:给定长为n的数组a,a[i]属于[1,n]。开始时时刻为1,可以选定其中一个数,接下来每个时刻选定与已选定的数相邻的其他数,要求每个数被选定的时刻小于等于它的值。求满足这一条件的起点的数目。解:对于每个a[i],其要求起点的范围为[i-a[i]+1,i+a[i]-1],将n个区间取交集,即为可能的答案区......
  • 10.28 ~ 11.3
    10.28返校日。晚上到了校之后就开始看大家的游击,然后\(\text{Huge}\)让我们写总结;于是就顺便把游记写了......
  • 【设计模式】异步阻塞、异步回调模式
    1 前言为什么要看这个异步回调呢?是因为我上节在看RocektMQ发送消息的时候,它支持同步、异步、一次性的模式,后两者不会阻塞当前线程,但是看这两者都没用到线程池,那它是如何处理的呢?我们看下三者最后的落点,都是在 NettyRemotingAbstract这个类里://NettyRemotingAbstract#inv......
  • 把代码绑定到WPF中的textblock中
    在WPF中,将数据绑定到TextBlock控件中是一个常见的操作,这样可以动态显示数据源中的数据。以下是如何将数据绑定到TextBlock的步骤:定义数据源:首先,你需要有一个数据源,它可以是一个属性,这个属性需要实现INotifyPropertyChanged接口以便在数据变化时通知UI更新。设置DataContex......
  • 大模型项目实战零基础入门到精通,非常详细收藏我这一篇就够了!
    Part.1什么是生成式AI?**“所有产品都值得用大模型重做一次。”**是近几年在AI圈子非常火爆的观点。当大家都在热议大模型和生成式AI时,怎么让这些炫酷的技术快速落地,真正帮到商业和社会,成了个大难题。不过,AWS已经把大模型和生成式AI的门槛大大降低了。什么是生成式AI?生......
  • 日常工作中,你是如何优化SQL的?
    如何优化SQL加索引避免常见的索引不生效场景避免返回不必要的数据减少不必要的逻辑分批量进行思想读写分离优化sql结构分库分表性能优化分析神器—explain慢SQL排查思路1.加索引很多时候,我们的慢查询,都是因为历史原因没有加索引,或者忘记加索引导致的......
  • 【系统设计】高效的分布式系统:使用 Spring Boot 和 Kafka 实现 Saga 模式
    在现代分布式系统中,管理跨多个服务的长事务至关重要。传统的分布式事务解决方案往往面临性能瓶颈和复杂性问题,而Saga模式作为一种灵活高效的解决方案,逐渐受到开发者的青睐。本文将探讨如何利用SpringBoot和Kafka实现Saga模式,并详细介绍事务补偿机制,帮助你构建稳定......