扩展Unity3D编辑器的脚本模板

??最近在学习Shader时感觉Shader语言参数众多、语法诡异,如果每次都从头开始写Shader一定是一件痛苦的事情。如果可以在本地定义好一组标准的Shader模板,这样当我们需要实现某些效果类似的Shader时,就可以在这个Shader模板的基础上进行修改。因为Shader文件是一个文本文件,所以我们可以非常容易地创建这样一个模板,在这个模板中我们可以进一步完善相关的参数注释,这样就不用每次写Shader的时候都需要查文档了,从这个角度出发,就进入了这篇文章的正题:扩展Unity3D编辑器的脚本模板。

按图索骥,模板在哪里?

??Unity3D默认的脚本模版位于/Editor/Data/Resources/ScriptTemplates/目录下,注意该目录相对Unity3D的安装目录而言,在这个目录中我们可以找到Unity3D中脚本模板的某些蛛丝马迹,首先,脚本模板是一个简单的文本文件,这个文本文件中预先填充了内容,我们在编辑器中创建模脚本或者Shader的时候实际上是读取这些文件然后在写入项目中的指定路径的。其次,这些模板文件中#SCRIPTNAME#或者#NAME#这样的标记,当我们在编辑器中创建文件的时候,这个标记会被替换成指定的文件名。比如Unity3D中继承自MonoBehaviour的脚本,有一个非常重要的特性是文件名必须和类名保持一致,这固然是Unity3D引擎的一个设定,可是在这里亦可以找到一个可以称得上理由的理由。我们注意到这些模板的文件名中都有一个独一无二的数字,比如C#脚本的模板中的数字是81、Shader模板中的数字是83,这些数字是什么呢,博主这里将其称为来自星星的黑科技。

来自星星的黑科技

??作为一个经常捣鼓Unity3D编辑器的人,如果说你不知道MenuItem、EditorWindow、ScriptableWizard这些黑科技,那么说明你不是一个喜欢折腾和探索的人。从Unity3D的API文档中,我们知道MenuItem的原型为:

MenuItem(string itemName,bool isValidateFunction,int priority) 

我知道我们通常使用MenuItem常常使用的是它的第一个参数,即定义一个菜单项的名称,我们可以使用”/”这样的分隔符来表示菜单的层级,MenuItem需要配合一个静态方法来使用,可以理解为当我们点击当前定义的菜单后就会去执行静态方法中的代码,因此MenuItem常常可以帮助我们做些编辑器扩展开发的工作。好了,第二个参数作为一个验证的标志,如果该标志为true,意味着我们定义的静态方法是一个验证方法在执行静态方法前会首先对方法进行验证,这个我们暂且不管,因为今天我们这个来自星星的黑科技主要和第三个参数有关,第三个参数表示一个优先级,它表示菜单项在菜单栏中的展示顺序,优先级大的菜单项会展示在优先级小的菜单项下面,由此我们就明白了了模板文件名中的类似81、83这样的数字的真实含义,注意到模板文件的排列顺序和编辑器中的菜单项顺序是一样的,我们做一个尝试,编写下面的代码:

[MenuItem("Assets/Create/Lua Scripts", false, 85)]
static void CreateLuaScripts()
{

}

[MenuItem("Assets/Create/固定功能着色器", false, 86)]
static void CreateFixedFunctionShader()
{

}

[MenuItem("Assets/Create/表面着色器", false, 87)]
static void CreateSurfaceShader()
{

}

[MenuItem("Assets/Create/可编程着色器", false, 88)]
static void CreateVertexAndFragmentShader()
{

}

注意到我们按照已知的优先级继续写了四个方法,现在我们在编辑器中可以发现默认的菜单栏发生了变化:

我们可以看到我们编写的这四个菜单都生效了,虽然它们暂时什么都做不了,但顺着这个方向去探索,我们是可以实现最初的梦想的。现在我们来思考如何根据模板来创建文件,这个对我们来说简直太简单了,通过StreamReader来读取模板,然后再用StreamWriter来生成文件就可以了。可是这样创建的文件的文件名是固定的,在创建文件的时候我们没法修改,而且即使修改了文件内定义的名字并不会改变啊。所以我们需要一个更好的解决方案。Unity3D提供了一个UnityEditor.ProjectWindowCallback的命名空间,在这个空间中提供了一个称为EndNameEditAction的类,我们只需要继承这个类就可以完成这个任务。这个类需要重写Action的方法,我们知道创建一个文件的完整步骤是创建文件然后使其高亮显示,因此这部分代码实现如下:

/// <summary>
/// 定义一个创建资源的Action类并实现其Action方法
/// </summary>
class CreateAssetAction : EndNameEditAction
{

    public override void Action(int instanceId, string pathName, string resourceFile)
    {
        //创建资源
        Object obj = CreateAssetFormTemplate(pathName, resourceFile);
        //高亮显示该资源
        ProjectWindowUtil.ShowCreatedAsset(obj);
    }

    internal static Object CreateAssetFormTemplate(string pathName, string resourceFile)
    {

        //获取要创建资源的绝对路径
        string fullName = Path.GetFullPath(pathName);
        //读取本地模版文件
        StreamReader reader = new StreamReader(resourceFile);
        string content = reader.ReadToEnd();
        reader.Close();

        //获取资源的文件名
        string fileName = Path.GetFileNameWithoutExtension(pathName);
        //替换默认的文件名
        content = content.Replace("#NAME", fileName);

        //写入新文件
        StreamWriter writer = new StreamWriter(fullName, false, System.Text.Encoding.UTF8);
        writer.Write(content);
        writer.Close();

        //刷新本地资源
        AssetDatabase.ImportAsset(pathName);
        AssetDatabase.Refresh();

        return AssetDatabase.LoadAssetAtPath(pathName, typeof(Object));
    }
}

这部分代码相对来说比较简单,就是读取本地模板文件然后生成新文件,在生成新文件的时候会将#NAME替换成实际的文件名,这样我们就完成了文件资源的创建。现在的问题是如何在创建文件的时候获取实际的路径,这部分代码实现如下:

private static string GetSelectedPath()
{
    //默认路径为Assets
    string selectedPath = "Assets";

    //获取选中的资源
    Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.Assets);

    //遍历选中的资源以返回路径
    foreach (Object obj in selection)
    {
        selectedPath = AssetDatabase.GetAssetPath(obj);
        if (!string.IsNullOrEmpty(selectedPath) && File.Exists(selectedPath))
        {
            selectedPath = Path.GetDirectoryName(selectedPath);
            break;
        }
    }

    return selectedPath;
}

现在解决了创建资源的问题,我们接下来只要调用ProjectWindowUtil的StartNameEditingIfProjectWindowExists方法即可,该方法需要传入一个继承自EndNameEditAction的类的实例、目标文件路径和模板文件的路径。例如要创建一个Lua脚本可以这样实现:

[MenuItem("Assets/Create/Lua Scripts", false, 85)]
static void CreateLuaScripts()
{
    ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0,
        ScriptableObject.CreateInstance<CreateAssetAction>(),
        GetSelectedPath() + "/NewLuaScript.lua", null,
        "Assets/Editor/Template/85-Lua-NewLuaScript.lua.txt");
}

小结

??现在有了这个黑科技以后,我们可以创建更多的模板来扩展编辑器的功能,比如对Shader而言,我们可以创建些基础性的Shader模板,然后每次需要写Shader的时候直接从模板库中选择一个功能类似的Shader然后在此基础上进行修改,这样比从头开始写一个新的Shader应该会轻松不少,这段时间学习Shader,感觉进程缓慢离图形学高手遥遥无期,行了,这篇博客就是这样了。

时间: 2024-08-15 07:30:57

扩展Unity3D编辑器的脚本模板的相关文章

《转》Unity3D研究院编辑器之创建Lua脚本模板

Unity里能创建 c#脚本模板,但是如果我想创建Lua脚本模板怎么办呢?拓展一下编辑器吧. 设置一下Lua脚本的模板地址 :  Assets/Editor/Lua/Template/lua.lua using UnityEngine; using UnityEditor; using System; using System.IO; using System.Text; using UnityEditor.ProjectWindowCallback; using System.Text.Reg

Unity3D编辑器扩展(四)——扩展自己的组件

前面已经写了三篇: Unity3D编辑器扩展(一)——定义自己的菜单按钮 Unity3D编辑器扩展(二)——定义自己的窗口 Unity3D编辑器扩展(三)——使用GUI绘制窗口 今天写第四篇,扩展自己的自定义组件. 通常我们使用继承自 Editor 的自定义编辑器类,来扩展自己的组件的检视面板和编辑器,并配合 CustomEditor 特性语法,附加该编辑器到一个自定义组件. 首先我们先定义一个组件 Player: 1 using UnityEngine; 2 3 public class Pl

Tips12: 私人定制 专属的Unity3D 脚本模板

在使用U3D的过程中,新建一个C#脚本,它包含着空的Start()和Update()函数.  根据个人习惯的不同,可能有些人有着自己的脚本风格,每次进去都增删改很麻烦,这里介绍一个更改新建脚本模板的方法. 首先关闭U3D并找到U3D的安装目录,依次打开目录:Editor/Data/Resources,打开ScriptTemplates 脚本模板文件夹,可以看到很多脚本模板: 选择要修改的模板,打开,这是我打开C#模板默认的样子: 1 using UnityEngine; 2 using Syst

Unity3d自定义脚本模板

这是一个小技巧,打开Unity安装目录,如: C:\Program Files (x86)\Unity\Editor\Data\Resources\ScriptTemplates /* * * Title:[标题] * * * Descripts: * [详细描述本脚本的作用] * * * Author:[Star.X] * * Date:2014-10-19 * * Version:0.1 * * Modify Record: * [描述版本修改记录] * */ 里面有几个脚本模板,直接改脚本

如何修改新建脚本模板-ScriptTemplates(Unity3D开发之十五)

猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/44957631 unity添加新脚本的时候,没有版权文件头信息,主要是没有脚本创建人的姓名,在项目开发中,如果想知道这个脚本是谁写的,呼来唤去搞半天才发现是自己写的!!! 用习惯了xcode,所以准备给unity的新建脚本添加一个信息头内容. Unity自己就有新建脚本模板文件,但是这个文件里面的预定义key很少

修改Unity脚本模板的方法合计

作为一个习惯于偷懒的程序,重复性的无聊内容是最让人无奈的事,就比如我们创建Unity脚本之后,需要手动调整生成的新脚本的格式.编码.内容:如果我们要编写的是编辑器或者服务器端脚本,需要修改的内容就会更多等等,因此我们要寻求一种偷懒的解决办法,针对这个问题,之前也使用过一些解决办法,今天就索性总结一下: 1.找到Unity版本对应的安装路径,修改对应的脚本模板: XXX\Editor\Data\Resources\ScriptTemplates\下的模板,直接修改其格式.编码,这样之后在使用时,就

UNITY3D编辑器插件编写教程

如何让编辑器运行你的代码如何让编辑器运行你的代码  Unity3D 可以通过事件触发来执行你的编辑器代码,但是我们需要一些编译器参数来告知编译器何时需要触发该段代码. [MenuItem(XXX)]声明在一个函数上方,告知编译器给Unity3D编辑器添加一个菜单项,并且当点击该菜单项的时候调用该函数.触发函数里 可以编写任何合法的代码,可以是一个资源批处理程序,也可以弹出一个编辑器窗口.代码里可以访问到当前选中的内容(通过Selection类),并据此来 确定显示视图.与此类似,[Context

在Unity3d编辑器中加入菜单以及菜单项

在引用UZGUI插件时,u3d编辑器的菜单条发生了变化,新增了菜单和菜单项,于是乎自己也像尝试一下,看了EZGUI的About_EZ_GUI脚本文件后,结果大出我所料,原来SO EASY! 1 using UnityEngine; 2 using UnityEditor; 3 4 public class Example { 5 6 [MenuItem("new Menu/new/new new/new item")] 7 static void showDialog() 8 { 9

大开测试:性能—如何调整经常用到的相关协议脚本模板(连载4)

7.4  如何调整经常用到的相关协议脚本模板 1.问题提出 在应用LoadRunner VuGen过程中,可能经常会用到一些非系统函数,同时想加入一些注解信息和日志输出信息,将输出日志信息条理化,方便调试和分析,那么有什么方法将我们经常用到的协议脚本模板调整变成符合要求的脚本模板呢? 2.问题解答 可以针对自己经常用到的协议,加入必要注解,引用经常会用到的函数库文件,条理化日志输出信息等.下面仅以调整Web(HTTP/HTML)协议脚本模板为例. 首先,找到LoadRunner安装目录下的Tem