這個元件的誕生源於上一篇:
【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,減少參與序列化的資源資訊,及時清理記憶體;
但是上面這個處理方案可能會造成顯示“延遲”,因為它需要再載入,可以修改上面對圖集的處理,將它更改為參與序列化,但是要注意記憶體!