Unity3d 在代码中获取此脚本所在的路径

需求

Unity3d编辑器脚本时而不时会有数据持久化需求,换句话说就是写入数据、保存配置的需求,怎么保存数据先不谈,但我们都知道,想要自己写的工具不污染整个工程的结构,往往我们需要将配置文件(生成的新文件)跟工具脚本整一起,或上一个目录,或同级目录,或同级下的子目录。
于是,“在指定脚本的周边目录下动态创建文件夹”的需求就这样应运而生啦!

代码

简单的描述完这个需求的使用场景,下面我们直接上代码:

using System.IO;
using System.Linq;
#if UNITY_EDITOR
using ADB = UnityEditor.AssetDatabase;
#endif

public static class FolderMaker
{

    /// <summary>
    /// 编辑器下使用,给定一个类的对象,在这个类的同级目录下创建文件夹并返回路径
    /// </summary>
    /// <typeparam name="T">类</typeparam>
    /// <param name="script">对象</param>
    /// <param name="subPath">指定要创建的文件夹的名称</param>
    /// <returns>文件夹的相对路径,相对于Assets文件夹</returns>
    public static string Creat<T>(T script, string subPath) where T : UnityEngine.Object //class  
    {
        string newPath = "";
#if UNITY_EDITOR
        string path = ADB.FindAssets("t:Script")
            .Where(v => Path.GetFileNameWithoutExtension(ADB.GUIDToAssetPath(v)) == script.GetType().Name)
            .Select(id => ADB.GUIDToAssetPath(id))
            .FirstOrDefault()
            .ToString();
        //newPath = path.Remove(path.LastIndexOf("/") + 1, Path.GetFileName(path).Length) + subPath; 
        newPath =Path.Combine( Path.GetDirectoryName(path) ,subPath);

        if (!ADB.IsValidFolder(newPath))
        {
            newPath = ADB.GUIDToAssetPath(ADB.CreateFolder(path, subPath));
        }
#endif
        return newPath;
    }
}
    /// <summary>
    /// 编辑器下使用,给定一个类型,在这个类的周边目录下创建文件夹并返回路径
    /// </summary>
    /// <param name="script">对象</param>
    /// <param name="subPath">指定要创建的文件夹的名称</param>
    /// <returns>文件夹的相对路径,相对于Assets文件夹</returns>
    public static string Creat(Type script, string subPath)
    {
#if UNITY_EDITOR
        string path = ADB.FindAssets("t:Script")
            .Where(v => Path.GetFileNameWithoutExtension(ADB.GUIDToAssetPath(v)) == script.Name)
            .Select(id => ADB.GUIDToAssetPath(id))
            .FirstOrDefault()
            .ToString();
        path = Path.GetDirectoryName(path);  //去除文件名
        path = Path.GetFullPath(path + "/" + subPath); //整合到完整路径,使用"/../" 回退到上一目录
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
#endif
        return path;
    }

Tips:

  1. using的使用:AssetDatabase 这个类用的简直不要太频繁,使用using ADB = UnityEditor.AssetDatabase;给他取个别名“ADB”。
  2. 预编译指令的使用:预编译指令是必不可少的,如果这个脚本处于Editor文件夹下,继承MonoBehavior的类将无法访问。挪出来就挺不错,但此时如不使用预编译指令,打包便会报错~
  3. 泛型的使用:泛型的使用使得逻辑复用,同时也能类型安全,where T:UnityEngine.Object 限制了使用环境是Unity,如果限制仅仅为 class ,嗯,随便一个字符串就能打发第一个参数,当然运行也就得不到我们想要的结果,报错呢,也肯定是要报的了。
  4. 实现思路,就是轮番使用UnityEditor下的API,查找到这个脚本,拿到脚本所在的目录拼接并创建新的目录。

2019年1月11日 补充:

  • 可以看到笔者在上面提供的代码块中又重载了这个方法,通过这个重载,现在不提供实例也能获取路径啦。
  • 另外,IO操作大幅回归System.IO的API,主要是为了解决2个问题:
    1. 得到上一个文件夹路径 ,使用File.GetFullPath(path),只要在传的参数中拼接 “../” 就能得到上一级目录,拼接多个就能回退多步。
    2. 递归创建文件夹,只需给定一个路径就好。

使用方法:↓

//返回脚本所在的目录的上两级,然后创建2个文件夹
  var path =FolderMaker.Creat(typeof(TagEnumGenarator),@"/../../aa/Bb") + "/EnumTag.cs";

2019年1月18日 补充

    /// <summary>
    /// 编辑器下使用,给定一个类型,在这个类的周边目录下创建文件夹并返回路径
    /// </summary>
    /// <param name="script">对象</param>
    /// <param name="subPath">指定要创建的文件夹的名称</param>
    /// <returns>文件夹的相对路径,相对于Assets文件夹</returns>
    public static string Creat(MonoBehaviour script, string subPath)
    {
#if UNITY_EDITOR
        MonoScript m_Script = MonoScript.FromMonoBehaviour(script); //更新 使用UnityEditor API 
        string path = AssetDatabase.GetAssetPath(m_Script);
        path = Path.GetDirectoryName(path);  //去除文件名
        path = Path.GetFullPath(path + "/" + subPath); //整合到完整路径,使用"/../" 回退到上一目录
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
#endif
        return path;
    }

很多时候,Unity 要的是相对于Assets 文件夹的相对路径,加一句就行了,顺便改下方法名,感觉这个方法名与功能才是真的对口

    /// <summary>
    /// 编辑器下使用,给定一个类型,在这个类的周边目录下创建文件夹并返回相对路径
    /// </summary>
    /// <param name="script">ScriptableObject 对象</param>
    /// <param name="subPath">指定要创建的文件夹的名称</param>
    /// <returns>文件夹的相对路径,相对于Assets文件夹</returns>
    public static string AllocateLocalPath(ScriptableObject script, string subPath)
    {
#if UNITY_EDITOR
        MonoScript m_Script = MonoScript.FromScriptableObject(script); //更新 使用UnityEditor API 
        string path = AssetDatabase.GetAssetPath(m_Script);
        path = Path.GetDirectoryName(path);  //去除文件名
        path = Path.GetFullPath(path + "/" + subPath); //整合到完整路径,使用"/../" 回退到上一目录
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        path = path.Substring(path.IndexOf("Assets"));
#endif
        return path;
    }

Tips:

  • 在Unity编辑器,脚本文件也是一个资源,并且是一个文本资源,由MonoScript表示,通过 UnityEditor.MonoScript.FromMonoBehaviour(MonoBehaviour script)拿到。
  • Unity编辑器模式下,有一套资源管理体系,UnityEditor.AssetDatabase.GetAssetPath(Object assetObject)就可以拿到脚本资源的路径。
  • 从对上述提及的方法签名的描述可知,传入 MonoScript 实例就能得到脚本路径啦。

使用

使用没什么要说的,调用一下这个方法就好了,譬如:

using UnityEngine;
public class Car : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Debug.Log(FolderMaker.Creat(this,"HAHA"+Random.Range(1,150)));
        }
    }
}

但是需要了解的是,尽管这个返回的路径是相对值,但很够用呀,如果想要完整路径的,加上这句就好了:


2024年7月3日 更新

通过以下方式也能获得脚本路径,但是需要区分这个路径是系统路径还是编辑器规则下的路径

              var stack = new StackTrace(true);
              var path = stack.GetFrame(0).GetFileName();

比如,Packages 中的路径就很奇怪,物理路径跟编辑器内路径是不一致的,下面截图表示:

系统路径 Unity路径

在系统的文件系统中 路径是:E:\Unity\Test\TestBuildLinux\Library\PackageCache\com.unity.editorcoroutines@1.0.0/README.md
然而在 Unity 中,他的路径是:Packages/com.unity.editorcoroutines/README.md

动画

  1. 在上面的动画中,每一次点击鼠标左键便会创建一个文件夹,文件夹名称以“HAHA”开头加上一串随机数。
  2. 另外,笔者在Unity播放状态拖拽并移动了Car这个脚本的位置,但事实上不建议大家这么做。
  3. 其实,这个解决方案只是为了实现编辑器脚本配置文件自动跟随而作,动图演示的貌似用处不大。

总结

这是一个简单的笔记,希望遇到有需要的人~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容