近期在学习Shader时感觉Shader语言參数众多、语法诡异,假设每次都从头開始写Shader一定是一件痛苦的事情。假设能够在本地定义好一组标准的Shader模板,这样当我们须要实现某些效果相似的Shader时,就能够在这个Shader模板的基础上进行改动。

由于Shader文件是一个文本文件,所以我们能够很easy地创建这样一个模板,在这个模板中我们能够进一步完好相关的參数凝视,这样就不用每次写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,感觉进程缓慢离图形学高手遥遥无期,行了,这篇博客就是这样了。

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

  1. Unity3D编辑器扩展(五)——常用特性(Attribute)以及Selection类

    前面写了四篇关于编辑器的: Unity3D编辑器扩展(一)——定义自己的菜单按钮 Unity3D编辑器扩展(二)——定义自己的窗口 Unity3D编辑器扩展(三)——使用GUI绘制窗口 Unity3D ...

  2. Unity3D编辑器扩展(一)——定义自己的菜单按钮

    Unity3D 引擎的编辑器拥有很强的扩展性,用的好可以帮我们省很多事情.在这里记录下如何去扩展 Unity3D 的编辑器,定制属于我们自己的开发环境. 本篇主要讲解在 Unity3D 引擎的各个窗口 ...

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

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

  4. Unity3D编辑器扩展(六)——模态窗口

    前面我们已经写了5篇关于编辑器的,这是第六篇,也是最后一篇: Unity3D编辑器扩展(一)——定义自己的菜单按钮 Unity3D编辑器扩展(二)——定义自己的窗口 Unity3D编辑器扩展(三)—— ...

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

    前面已经写了三篇: Unity3D编辑器扩展(一)——定义自己的菜单按钮 Unity3D编辑器扩展(二)——定义自己的窗口 Unity3D编辑器扩展(三)——使用GUI绘制窗口 今天写第四篇,扩展自己 ...

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

    在使用U3D的过程中,新建一个C#脚本,它包含着空的Start()和Update()函数.  根据个人习惯的不同,可能有些人有着自己的脚本风格,每次进去都增删改很麻烦,这里介绍一个更改新建脚本模板的方 ...

  7. Unity3d自定义脚本模板

    这是一个小技巧,打开Unity安装目录,如: C:\Program Files (x86)\Unity\Editor\Data\Resources\ScriptTemplates /* * * Tit ...

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

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/44957631 ...

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

    作为一个习惯于偷懒的程序,重复性的无聊内容是最让人无奈的事,就比如我们创建Unity脚本之后,需要手动调整生成的新脚本的格式.编码.内容:如果我们要编写的是编辑器或者服务器端脚本,需要修改的内容就会更 ...

随机推荐

  1. Oracle中DBMS_LOB包使用小结

    本文主要介绍oracle数据库中dbms_lob包的使用以及使用dbms_lob包来维护lob数据库类型的基本方法.随着社会的发展,在现代信息系统的开发中,需要存储的已不仅仅是简单的文字信息,同时还包 ...

  2. 制定ip池内随机生成ip地址

    ]) { +] = {}; unsigned mask = 0x0; sscanf(ip_pool, "%[^/]/%d", ip_addr, &mask); long l ...

  3. UVALIVE 2431 Binary Stirling Numbers

    转自别人的博客.这里记录一下 这题是定义如下的一个数: S(0, 0) = 1; S(n, 0) = 0 for n > 0;S(0, m) = 0 for m > 0; S(n, m) ...

  4. linux硬盘分区表为gpt

    由于mbr最大支持2T不够用,给5T的新硬盘弄成GPT的 yum install -y parted#指定硬盘parted /dev/#p 查看分区#rm 1p 删除指定分区#改成gptmklabel ...

  5. 传输网页数据的json与xml

    #转载请留言联系 1.json json是数据格式,经常用于在网络中,不同平台或者不同语言中进行数据的传输.json的文件后缀就是 .json.当然,也可以把json直接写在js文件中. json储存 ...

  6. 【原创】Win Server 2012R2 IIS 详细配置(多图详解)

    1. 前期准备 1) 2012系统的IIS安装的时候,需要系统安装盘里面的一些软件,因此需要在安装前将系统安装盘挂载到服务器的盘符上,以便使用. 2. 添加角色和功能 打开服务器管理器,点击管理菜单, ...

  7. AC日记——[SCOI2008] 着色方案 bzoj 1079

    1079 思路: dp: 我们如果dp方程为15维,每维记录颜色还有多少种: 不仅tle,mle,它还re: 所以,我们压缩一下dp方程: 方程有6维,第i维记录有多少种颜色还剩下i次: 最后还要记录 ...

  8. 牛客网 暑期ACM多校训练营(第二场)J.farm-STL(vector)+二维树状数组区间更新、单点查询 or 大暴力?

    开心.jpg J.farm 先解释一下题意,题意就是一个n*m的矩形区域,每个点代表一个植物,然后不同的植物对应不同的适合的肥料k,如果植物被撒上不适合的肥料就会死掉.然后题目将每个点适合的肥料种类( ...

  9. Codeforces Round #446 (Div. 2) B. Wrath【模拟/贪心】

    B. Wrath time limit per test 2 seconds memory limit per test 256 megabytes input standard input outp ...

  10. 洛谷——P1029 最大公约数和最小公倍数问题

    P1029 最大公约数和最小公倍数问题 题目描述 输入二个正整数x0,y0(2<=x0<100000,2<=y0<=1000000),求出满足下列条件的P,Q的个数 条件: 1 ...