Unity定制脚本模版--自动添加头部注释

此篇教程参考了很多内容,本体是siki学院的“暗黑战神”课程中某一节的内容,我将里面的细节进行了一番探索及拓展,现在记录下其中的过程:

 

在Unity中创建新的C#脚本时,会自动去加载某个路径下的模版,它的后缀为txt,创建的C#脚本初始自带的内容就是这个模版内的内容。

在Windows下,这个路径为:Unity安装目录\Editor\Data\Resources\ScriptTemplates\81-C# Script-NewBehaviourScript.cs.txt

在Mac下,这个路径为Unity.app/Contents/Resources/ScriptTemplates/81-C# Script-NewBehaviourScript.cs.txt

 

下面看下里面有什么:

尝试着修改一下,加一个myFunc函数:

此时在Unity中创建一个脚本看下:

可以看到,myFunc已经进去了。

 

那现在有一个问题:模版中有public class #SCRIPTNAME# : MonoBehaviour这一行,显然是在创建时根据为脚本取的名字,动态地替换掉了模版中的#SCRIPTNAME#。

由于Unity引擎不开源,我们无法了解它底层是怎么实现的,但现在如果我们要进一步对其拓展,比如在模版中写一段自己的信息,里面也有一段需要根据创建时信息临时修改的内容,比如#Modify#,我们要在创建脚本时,Unity也根据对应的信息,比如当前时间,动态地替换掉#Modify#中的内容,怎么做呢?

 

  这是通过实现UnityEditor.AssetModificationProcessor这个类下的OnWillCreateAsset方法实现的,看一下它们的文档:

文档对AssetModificationProcessor这个类的功能的描述不大容易理解,查阅了一些其它资料后总结一下:AssetModificationProcessor这个类可以用来监听Project视图中资源的创建、删除、移动、保存。类里有几个方法,既然要在创建脚本时替换掉模版中##内的关键词,那我们只需要关注OnWillCreateAsset方法就行了,按照翻译,这个方法会在资源被创建之前被调用,而且从例子中看到,这个方法是个static方法。

  注意到这个方法有参数string assetName,Unity底层调用这个方法时会把要创建资源的名字作为参数传入。

 

那么现在思路就比较明确了:Unity为我们在底层固化了这种创建新脚本时以文件名替换模版中#SCRIPTNAME#的功能,我们在对其拓展时要实现AssetModificationProcessor类下的OnWillCreateAsset静态方法,在里面根据传入的资源名,读取对应的文本文件,将其中指定的一些##字符串,替换成我们动态获取的信息,随后把文本重新写入到这个模版中。

  那现在有几个问题值得注意:

     1.首先,OnWillCreateAsset这个方法在AssetModificationProcessor类中,我们难道要去修改AssetModificationProcessor类吗?

  答案是不用,从上面的图中例子也可以看到,直接用一个类继承它即可,在继承的类中实现这个方法。

   2.我们读取对应的文本文件,需要文件名,可是按照之前的说法,OnWillCreateAsset这个方法是在资源被创建之前调用的,这时候脚本都没创建完成,哪来的文件名?又怎么读取呢?

  这是因为这个方法的解释不清楚,这个方法的解释是在资源被创建之前调用的,但其实更详细地说,它是在Unity内部将脚本模版的内容复制到新创建的脚本中,并将#SCRIPTNAME#替换为文件名之后被调用的。

  现在我们要选一个函数打开对应传入参数的文件,可以选用ReadAllText函数,它可以打开一个文本文件,将文件中所有文本读取到一个字符串中,然后自动关闭此文件,位于命名空间System.IO中,参考文档如下:

https://docs.microsoft.com/zh-cn/dotnet/api/system.io.file.readalltext?redirectedfrom=MSDN&view=netframework-4.8#System_IO_File_ReadAllText_System_String_

由于我们看不到.NET Framework的源代码,只能猜测这个方法应该是封装了类似open,read和close这样的系统调用。

  3.读取后的文本中要替换的内容从哪里取得?

  比如我们要取得用户的用户名,这时可以使用Environment.UserName这个属性,它位于System命名空间中,它可以获取到当前已登录到操作系统的人员的用户名,参考文档如下:

  https://docs.microsoft.com/zh-cn/dotnet/api/system.environment.username?redirectedfrom=MSDN&view=netframework-4.8

  文档中说了,在Unix平台上,这个属性封装了对getpwuid_r的调用,我没有细查getpwuid_r的信息,但我猜测它的实现可能类似于who指令那样,是通过读取utmpx这个文件来获取utmpx结构体数组,随后访问里面的ut_user条目来得到用户名的,有关who指令的实现,可以参考我的文章:

  https://www.cnblogs.com/czw52460183/p/10999434.html

  

  那假如替换的内容中有当前系统的时间呢?这个怎么取得?

  答案是可以用DateTime这个数据结构中的信息直接获取,具体就不描述了,参考文档如下:

  https://docs.microsoft.com/zh-cn/dotnet/api/system.datetime?view=netframework-4.8

 

  好,前戏结束,给出实际操作过程:

  1.我们不想对模版做太多改动,只在头部加入文件名,作者名(登录用户名),邮箱(因为实际项目中你写的文件出了问题,要留联系方式方便别人找到你),日期和功能。因此,先对81-C# Script-NewBehaviourScript.cs.txt这个文件内容修改如下: 

/****************************************************
    文件:#SCRIPTNAME#.cs
    作者:#CreateAuthor#
    邮箱: czw52460183@163.com
    日期:#CreateTime#
    功能:Nothing
*****************************************************/

using UnityEngine;

public class #SCRIPTNAME# : MonoBehaviour 
{

}

  2.此时在Unity中创建一个脚本测试一下,打开后结果如图:

  3.现在我们开始写替换脚本,在写之前先要明确一点:在Unity中,创建任何资源时,目录下不仅会生成该资源(包括脚本,文件夹等各种资源),还会生成对应的同名的.meta文件,meta文件本质是一个文本文档,关于它是什么,可以参考:

  https://blog.uwa4d.com/archives/USparkle_inf_UnityEngine.html

  举个例子:我们在Asset目录下同时创建一个新的脚本文件和一个新的文件夹,结果如下:

    

现在问题是,传入到OnWillCreateAsset的参数到底是文件名还是带.meta的名字?

我们做个实验,在Asset下创建一个脚本,名为testInfo:

/****************************************************
    文件:testInfo.cs
    作者:#CreateAuthor#
    邮箱: czw52460183@163.com
    日期:#CreateTime#
    功能:Nothing
*****************************************************/

using System;
using System.IO;
using UnityEngine;

public class testInfo : UnityEditor.AssetModificationProcessor 
{
    private static void OnWillCreateAsset(string path) {
        Debug.Log(path);
    }
}

此时重新创建testFolder文件夹和testScript脚本,Unity日志输出如下:

说明传入到OnWillCreateAsset的参数是带meta的,而且会带有路径。

 

因此我们处理传入的参数时,要先去掉这个meta后缀,此外,由于只为新建的脚本添加头部注释,因此也要限定必须是.cs文件才会进行处理。

删掉上面的文件,重新写脚本,命名为AddScriptInfo.cs,代码如下:

using System;
using System.IO;

public class AddScriptInfo : UnityEditor.AssetModificationProcessor 
{
    private static void OnWillCreateAsset(string path) 
    {
        //将.meta后缀屏蔽
        path = path.Replace(".meta", "");
        //只对.cs脚本作操作
        if (path.EndsWith(".cs")) 
        {
            //读取该路径下的.cs文件中的所有文本.
            //注意,此时Unity已经对脚本完成了模版内容的替换,包括#SCRIPTNAME#也已经被替换为文件名了,读取到的是替换后的文本内容.
            string str = File.ReadAllText(path);
            //获取用户名和当前系统时间并替换对应位置内容
            str = str.Replace("#CreateAuthor#", Environment.UserName).Replace(
                              "#CreateTime#", string.Concat(DateTime.Now.Year, "/", DateTime.Now.Month, "/",
                                DateTime.Now.Day, " ", DateTime.Now.Hour, ":", DateTime.Now.Minute, ":", DateTime.Now.Second));
            //重新将文本写入.cs文件
            File.WriteAllText(path, str);
        }
    }
}

此时在Asset目录下重新创建文件testAgain.cs来测试,结果如下:

可以看到,功能已经实现了。

 

有趣的是,在我做这个实验时,网上查了许多资料,发现这个教程挺多的,但很多都强调,这个修改##信息的脚本(对应图中的AddScriptInfo),需要放到Editor文件夹下,却又都没讲为什么。而从实际测试来看,不放也能正常工作,也不知道什么情况。

 

不过考虑到这其实是个工具插件,和业务代码无关,可以考虑放到某个文件夹下方便管理,我们就按视频里的方法做吧:Assets下创建个Plugins文件夹,里面创建个Editor文件夹,放入此脚本,这里专门放置插件,方便管理。

 

完结。

 

posted on 2019-06-12 14:00  暴躁法师  阅读(1580)  评论(0编辑  收藏  举报