以编程方式设置和保存与导入资产相关的图标 [英] Programmatically setting and saving the icon associated with an imported asset

查看:215
本文介绍了以编程方式设置和保存与导入资产相关的图标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些自动生成的数据正在导出到我的Unity项目中.为了帮助我,我想为这些资产分配一个自定义图标以清楚地标识它们.当然,这可以通过编辑器本身简单地实现,但是理想情况下,我希望在导入时自动进行.

I have some auto-generated data being exported into my Unity project. To help me out I want to assign a custom icon to these assets to clearly identify them. This is of course simply possible via the editor itself, but ideally I'd like this to happen automatically on import.

为此,我编写了一个AssetPostProcessor,它应该为我解决这一问题.在下面的示例中(以MonoScripts为例,但可以应用于任何种类的资产),所有新导入的脚本都将分配有MyFancyIcon图标.此更新在脚本资产本身以及检查器中的MonoBehaviours上均可见.

To this effect I have written an AssetPostProcessor which should take care of this for me. In the example below (which applies to MonoScripts as an example but could apply to any kind of asset), all newly imported scripts will have the MyFancyIcon icon assigned to them. This update is both visible on the script assets themselves, as well as on the MonoBehaviours in the inspector.

using UnityEngine;
using UnityEditor;
using System.Reflection;

public class IconAssignmentPostProcessor : AssetPostprocessor
{
    static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        Texture2D icon = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Iconfolder/MyFancyIcon.png");
        foreach (string asset in importedAssets)
        {
            MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(asset);
            if(script != null)
            {
                PropertyInfo inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
                SerializedObject serializedObject = new SerializedObject(script);
                inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null);
                SerializedProperty iconProperty = serializedObject.FindProperty("m_Icon");
                iconProperty.objectReferenceValue = icon;
                serializedObject.ApplyModifiedProperties();
                serializedObject.Update();

                EditorUtility.SetDirty(script);
            }
        }

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }    
}

它工作正常,除了一个问题.关闭项目并重新打开时,不会保存更新.据我所知,EditorUtility.SetDirty(script);调用应该解决这个问题,或者至少是AssetDatabase.SaveAssets();调用.

And it works just fine, except for one problem. The updates aren't saved when closing the project and reopening it. To the best of my knowledge, either the EditorUtility.SetDirty(script); call should take care of this, or at the very least the AssetDatabase.SaveAssets(); call.

但是,查看手动分配图标(有效)和以编程方式进行操作之间的区别,与资产相关联的元文件中有一个icon字段,当手动分配图标时,该字段确实会设置,但没有就我的脚本而言. (在脚本化的情况下,甚至没有更新元文件)

However, looking at the difference between manually assigning an icon (which works) and doing it programmatically, there is an icon field in the meta files associated with the assets which does get set when manually assigning an icon, but not in my scripted case. (In the scripted case the meta files aren't even updated)

那有什么用呢?当(显然)仅是我要更改的元数据时,我是否需要做特别的事情?有什么简单的我可以忽略的吗?

So what gives? Do I have to do anything in particular when it's (apparently) only meta data I'm changing? Is there anything simple I'm overlooking?

推荐答案

对此代码进行了实验,得出的结论是这是一个错误.与Unity取得联系,这是他们的回复:

Experimented with this code and concluded that this is a bug. Made contact with Unity and this is their reply:

当前,这是一个已提交的错误,我们的开发人员团队是 调查它.似乎该错误似乎是由于 AssetDatabes.SaveAssets()不会保存更改.

Currently , it is a submitted bug and Our Developer Team is investigating it. It seems that this bug seems to happen because of AssetDatabes.SaveAssets() does not save changes.

解决方法是手动执行此操作.

The work around is to do this manually.

在调用OnPostprocessAllAssets时处理和保存数据:

1 .创建一个Json文件设置,如果该设置不存在,它将保存这些设置.

1.Create a Json file settings that will hold the settings if it does not exist.

2 .调用OnPostprocessAllAssets时,加载旧的Json文件设置.

2.When OnPostprocessAllAssets is called, load old Json file settings.

4 .将精美的图标应用于资产.

4.Apply fancy icon to the Asset.

5 .查看已加载的Json文件设置,并检查其是否包含来自importedAssets参数的文件.

5.Loop over the the loaded Json file settings and check if it contains the file from the importedAssets parameter.

如果它包含已加载的文件,请修改该设置并保存.如果没有,请将其添加到列表中,然后保存.

If it contains the loaded file, modify that setting and save it. If it does not, add it to the List then save it.

6 .使用File.Exists检查资产importedAssets在硬盘驱动器上是否不存在.如果不存在,请从已加载的Json文件设置列表中将其删除,然后保存.

6.Check if the asset importedAssets does not exist on the hard-drive with File.Exists. If it does not exist, remove it from the List of the loaded Json file settings then save it.

在Unity加载时自动重新应用精美的图标:

1 .向IconAssignmentPostProcessor类添加静态构造函数.加载Editor以及调用OnPostprocessAllAssets时,将自动调用此静态构造函数.

1.Add a static constructor to the IconAssignmentPostProcessor class. This static constructor will automatically be called when Editor loads and also when OnPostprocessAllAssets is invoked.

2 .调用构造函数时,创建一个Json文件设置,如果该设置不存在,它将保存这些设置.

2.When the constructor is called, create a Json file settings that will hold the settings if it does not exist.

3 .加载旧的Json文件设置.

3.Load the old Json file settings.

4 .通过循环加载的Json文件重新应用精美的图标.

4.Re-apply the fancy icons by looping through the loaded Json file.

5 .检查加载的Json文件是否仍然具有驱动器上没有的资产.如果是这样,请从列表中删除该资产,然后保存.

5.Check if the loaded Json file still have assets that is not on the drive. If so, remove that asset from the List then save it.

下面是新的IconAssignmentPostProcessor脚本的外观:

Below is what the new IconAssignmentPostProcessor script should look like:

using UnityEngine;
using UnityEditor;
using System.Reflection;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System;

public class IconAssignmentPostProcessor : AssetPostprocessor
{
    // Called when Editor Starts
    static IconAssignmentPostProcessor()
    {
        prepareSettingsDir();
        reloadAllFancyIcons();
    }

    private static string settingsPath = Application.dataPath + "/FancyIconSettings.text";
    private static string fancyIconPath = "Assets/Iconfolder/MyFancyIcon.png";

    private static bool firstRun = true;

    static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        prepareSettingsDir();

        //Load old settings 
        FancyIconSaver savedFancyIconSaver = LoadSettings();


        Texture2D icon = AssetDatabase.LoadAssetAtPath<Texture2D>(fancyIconPath);

        for (int j = 0; j < importedAssets.Length; j++)
        {
            string asset = importedAssets[j];

            MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(asset);
            if (script != null)
            {
                //Apply fancy Icon
                ApplyIcon(script, icon);

                //Process each asset 
                processFancyIcon(savedFancyIconSaver, fancyIconPath, asset, pathToGUID(asset));
            }
        }

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }


    public static string pathToGUID(string path)
    {
        return AssetDatabase.AssetPathToGUID(path);
    }

    public static string guidToPath(string guid)
    {
        return AssetDatabase.GUIDToAssetPath(guid);
    }

    public static void processFancyIcon(FancyIconSaver oldSettings, string fancyIconPath, string scriptPath, string scriptGUID)
    {
        int matchIndex = -1;

        if (oldSettings == null)
        {
            oldSettings = new FancyIconSaver();
        }

        if (oldSettings.fancyIconData == null)
        {
            oldSettings.fancyIconData = new List<FancyIconData>();
        }

        FancyIconData fancyIconData = new FancyIconData();
        fancyIconData.fancyIconPath = fancyIconPath;
        fancyIconData.scriptPath = scriptPath;
        fancyIconData.scriptGUID = scriptGUID;

        //Check if this guid exist in the List already. If so, override it with the match index
        if (containsGUID(oldSettings, scriptGUID, out matchIndex))
        {
            oldSettings.fancyIconData[matchIndex] = fancyIconData;
        }
        else
        {
            //Does not exist, add it to the existing one
            oldSettings.fancyIconData.Add(fancyIconData);
        }

        //Save the data
        SaveSettings(oldSettings);

        //If asset does not exist, delete it from the json settings
        for (int i = 0; i < oldSettings.fancyIconData.Count; i++)
        {
            if (!assetExist(scriptPath))
            {
                //Remove it from the List then save the modified List
                oldSettings.fancyIconData.RemoveAt(i);
                SaveSettings(oldSettings);
                Debug.Log("Asset " + scriptPath + " no longer exist. Deleted it from JSON Settings");
                continue; //Continue to the next Settings in the List
            }
        }
    }

    //Re-loads all the fancy icons
    public static void reloadAllFancyIcons()
    {
        if (!firstRun)
        {
            firstRun = false;
            return; //Exit if this is not first run
        }

        //Load old settings 
        FancyIconSaver savedFancyIconSaver = LoadSettings();

        if (savedFancyIconSaver == null || savedFancyIconSaver.fancyIconData == null)
        {
            Debug.Log("No Previous Fancy Icon Settings Found!");
            return;//Exit
        }


        //Apply Icon Changes
        for (int i = 0; i < savedFancyIconSaver.fancyIconData.Count; i++)
        {
            string asset = savedFancyIconSaver.fancyIconData[i].scriptPath;

            //If asset does not exist, delete it from the json settings
            if (!assetExist(asset))
            {
                //Remove it from the List then save the modified List
                savedFancyIconSaver.fancyIconData.RemoveAt(i);
                SaveSettings(savedFancyIconSaver);
                Debug.Log("Asset " + asset + " no longer exist. Deleted it from JSON Settings");
                continue; //Continue to the next Settings in the List
            }

            string tempFancyIconPath = savedFancyIconSaver.fancyIconData[i].fancyIconPath;

            Texture2D icon = AssetDatabase.LoadAssetAtPath<Texture2D>(tempFancyIconPath);
            MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(asset);
            if (script == null)
            {
                continue;
            }

            Debug.Log(asset);
            ApplyIcon(script, icon);
        }
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }

    private static void ApplyIcon(MonoScript script, Texture2D icon)
    {
        PropertyInfo inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
        SerializedObject serializedObject = new SerializedObject(script);
        inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null);
        SerializedProperty iconProperty = serializedObject.FindProperty("m_Icon");
        iconProperty.objectReferenceValue = icon;
        serializedObject.ApplyModifiedProperties();
        serializedObject.Update();
        EditorUtility.SetDirty(script);
        Debug.Log("Applied Fancy Icon to: " + script.name);
    }

    //Creates the Settings File if it does not exit yet
    private static void prepareSettingsDir()
    {
        if (!File.Exists(settingsPath))
        {
            File.Create(settingsPath);
        }
    }

    public static void SaveSettings(FancyIconSaver fancyIconSaver)
    {
        try
        {
            string jsonData = JsonUtility.ToJson(fancyIconSaver, true);
            Debug.Log("Data: " + jsonData);

            byte[] jsonByte = Encoding.ASCII.GetBytes(jsonData);
            File.WriteAllBytes(settingsPath, jsonByte);
        }
        catch (Exception e)
        {
            Debug.Log("Settings not Saved: " + e.Message);
        }
    }

    public static FancyIconSaver LoadSettings()
    {
        FancyIconSaver loadedData = null;
        try
        {
            byte[] jsonByte = File.ReadAllBytes(settingsPath);
            string jsonData = Encoding.ASCII.GetString(jsonByte);
            loadedData = JsonUtility.FromJson<FancyIconSaver>(jsonData);
            return loadedData;
        }
        catch (Exception e)
        {
            Debug.Log("No Settings Loaded: " + e.Message);
        }
        return loadedData;
    }

    public static bool containsGUID(FancyIconSaver fancyIconSaver, string guid, out int matchIndex)
    {
        matchIndex = -1;

        if (fancyIconSaver == null || fancyIconSaver.fancyIconData == null)
        {
            Debug.Log("List is null");
            return false;
        }

        for (int i = 0; i < fancyIconSaver.fancyIconData.Count; i++)
        {
            if (fancyIconSaver.fancyIconData[i].scriptGUID == guid)
            {
                matchIndex = i;
                return true;
            }
        }
        return false;
    }

    public static bool assetExist(string path)
    {
        return File.Exists(path);
    }

    [Serializable]
    public class FancyIconSaver
    {
        public List<FancyIconData> fancyIconData;
    }

    [Serializable]
    public class FancyIconData
    {
        public string fancyIconPath;
        public string scriptPath;
        public string scriptGUID;
    }
}

重新启动Unity时,它应保留精美的图标.

This should hold the fancy icons when Unity is restarted.

这篇关于以编程方式设置和保存与导入资产相关的图标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆