超絶簡単unityライフハック -スクリプト生成編-
こんにちは。mo-takusanです。
さて、いよいよ始まりましたKCSアドベントカレンダーです。
第一日目ということで今回はあまり難解なことは書かずに、知っていると便利な機能を紹介していこうと思います。
unityのC#テンプレートがやばい
皆さんも毎日のようにunityを使っていると思いますが、この見出しみたいなこと感じたことありませんか?
この図のようにunityのテンプレートはC#のコーディング規則に沿っておらずきれにするためにはいちいち書き換えなければなりません。このコメントアウトもいりませんし、ちゃんと名前空間も設定したいところです。さらにMonoBehaviourに限らずScriptableObjectなどもテンプレートとして用意されていれば便利そうです。
したがって今回の記事では、
こんな感じで「Create」->「MonoBehaviour」
でMonoBehaviourが作成され、いい感じのテンプレートで自動的に初期化してくれることを目標にします。ScriptableObjectも作成したいですが、冗長になるだけなので今回は割愛します。
今回の記事はこの記事を参考にしました。
オリジナルのテンプレートを作る
それではまずはオリジナルのテンプレートを作成することから始めましょう。
参考記事によるとwindowsでは
"unityのアプリケーション位置"\Unity\Editor\Data\Resources\ScriptTemplates\
に存在するテキストを編集することで簡単にテンプレートを作成できるようです。
今回は以下のように編集してみました。なおファイル名は81-MonoBehaviour-NewBehaviourScript.cs.txt
としました。これで「Create」->「MonoBehaviour」から「NewBehaviourScript.cs」を作成することができるようになります。
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
namespace Sakkun //#PROJECTNAME##DIRECTORYNAME# | |
{ | |
public class #SCRIPTNAME# : MonoBehaviour | |
{ | |
private void Start () | |
{ | |
#NOTRIM# | |
} | |
private void Update () | |
{ | |
#NOTRIM# | |
} | |
} | |
} |
参考ページに載っているようにテンプレートにはパラメータがいくつか用意されており、スクリプト名などを勝手に置き替えてくれるようです。しかし、5行目のコメントアウトから分かるようにプロジェクト名や作成したディレクトリ名もパラメータとして用意してあれば文句はなかったのですが、さすがにそこまで融通は利かないようです。(ちなみにディレクトリ名を名前空間として設定するってC#でもやるんですかね?)
そこで、次節ではこれらのパラメータも認識するように工夫していきます。
自作パラメータを置き換えさせる
ところが、先ほどのように単に一筋縄ではいきません。ちゃんとエディタ拡張をする必要があるようです。
unityではアセットを作成した際に対応したEndNameEditAction
抽象クラスのAction
メソッドが呼ばれるそうです。つまり、前節のようにしてテンプレートを作成すると、unity側が命名規則に従って自動的にデフォルトのAction
メソッドを呼び出してくれると考えることができます。パラメータを自作する場合はこのAction
メソッド内にパラメータを置き換えるコードを書くことになります。
それでは早速プロジェクトを作成します。
まず、プロジェクト内に「Editor」フォルダを作成し、ここに先ほど作成したテンプレートを入れてしまいます。この時、先ほどのコメントアウトは消しておきます。以降で作成するパラメータは他のプロジェクトでは機能しないのでこのようにして隔離しておく必要があります。
それではEditorファイル内にスクリプトを生成します。
using System.IO; | |
using System.Text; | |
using UnityEngine; | |
using UnityEditor; | |
using UnityEditor.ProjectWindowCallback; | |
public class CreateAssetsSample : EndNameEditAction | |
{ | |
public override void Action(int instanceId, string pathName, string resourceFile) | |
{ | |
var text = File.ReadAllText(resourceFile); | |
var pathes = Application.dataPath.Split('/'); | |
var name = Path.GetFileNameWithoutExtension(pathName); | |
var scriptName = name.Replace(" ", ""); | |
var projectName = "." + pathes[pathes.Length - 2]; | |
var directryName = Path.GetDirectoryName(pathName). // プロジェクトが保存されているパスを取得する | |
Replace("Assets", ""). // Assetsのパスを削除 | |
Replace("/Scripts", ""). // Scriptsのパスを削除 | |
Replace("/", "."); // "/"を"."に置き換える | |
text = text.Replace("#NAME#", name); | |
text = text.Replace("#SCRIPTNAME#", scriptName); | |
text = text.Replace("#PROJECTNAME#", projectName); | |
text = text.Replace("#DIRECTORYNAME#", directryName); | |
text = text.Replace("#NOTRIM#", "\n"); | |
var encoding = new UTF8Encoding(true, false); | |
File.WriteAllText(pathName, text, encoding); | |
AssetDatabase.ImportAsset(pathName); | |
var asset = AssetDatabase.LoadAssetAtPath<MonoScript>(pathName); | |
ProjectWindowUtil.ShowCreatedAsset(asset); | |
} | |
} |
23~27行目のようにしてパラメータに対応する文字列を入れ替えていきます。それ以降にも何やらごちゃごちゃと書いていますが、エディタ拡張職人になるわけではないので「このような手順を踏んでアセットが作成されるんだ」くらいの認識で良いと思います。
ところが、これだけではうまく動きません。先ほど述べたようにAction
メソッドは対応したアセットに対してのみ実行されるため、作成したC#スクリプトに結び付けてやる必要があるのです。そこで、メニューアイテムも自作し、EndNameEditAction
を紐づけます。メニューアイテムを自作するにはstaticメソッドにMenuItem
属性を付与する必要があります。ただし、このようにしてメニューアイテムを自作する場合、既存のC#スクリプトと競合させてしまうと色々と面倒だったため、別のメニューとして作成します。また、この際の紐づけには次のメソッドを利用します。
void ProjectWindowUtil.StartNameEditingIfProjectWindowExists(int instanceID, EndNameEditAction endAction, string pathName, Texture2D icon, string resourceFile) |
このように事細かく指定する必要があるのは、自作したメニューアイテムからではデフォルトのAction
メソッドだけでなく、アイコンを指定するメソッドや、アセット名を自動的に付けてくれる処理も呼ばれないためです。
完成したスクリプトは以下のようになります。
using System.IO; | |
using System.Text; | |
using UnityEngine; | |
using UnityEditor; | |
using UnityEditor.ProjectWindowCallback; | |
public class CreateAssetsSample : EndNameEditAction | |
{ | |
// 指定したパスのメニューアイテムを作成してくれる | |
[MenuItem("Assets/Create/Mono Behaviour", false, -1)] | |
private static void CreateMonoBehaviour() | |
{ | |
var resourceFile = Path.Combine( | |
Application.dataPath, | |
"Editor/81-MonoBehaviour-NewBehaviourScript.cs.txt"); | |
// unityで用意されているC#のアイコンを利用する | |
var csIcon = | |
EditorGUIUtility.IconContent("cs Script Icon").image as Texture2D; | |
// ScriptableObjectのインスタンスとして作成する | |
var endNameEditAction = | |
ScriptableObject.CreateInstance<CreateAssetsSample>(); | |
ProjectWindowUtil.StartNameEditingIfProjectWindowExists( | |
0, // 多分適当でも大丈夫 | |
endNameEditAction, // 実行するActionの指定 | |
"NewBehaviourScript.cs", // デフォルトのActionは呼ばれないため名前もここで指定 | |
csIcon, // アイコンの設定 | |
resourceFile); // 使用するスクリプトテンプレート | |
} | |
public override void Action(int instanceId, string pathName, string resourceFile) | |
{ | |
var text = File.ReadAllText(resourceFile); | |
var pathes = Application.dataPath.Split('/'); | |
var name = Path.GetFileNameWithoutExtension(pathName); | |
var scriptName = name.Replace(" ", ""); | |
var projectName = "." + pathes[pathes.Length - 2]; | |
var directryName = Path.GetDirectoryName(pathName). | |
Replace("Assets", ""). | |
Replace("/Scripts", ""). | |
Replace("/", "."); | |
text = text.Replace("#NAME#", name); | |
text = text.Replace("#SCRIPTNAME#", scriptName); | |
text = text.Replace("#PROJECTNAME#", projectName); | |
text = text.Replace("#DIRECTORYNAME#", directryName); | |
text = text.Replace("#NOTRIM#", "\n"); | |
var encoding = new UTF8Encoding(true, false); | |
File.WriteAllText(pathName, text, encoding); | |
AssetDatabase.ImportAsset(pathName); | |
var asset = AssetDatabase.LoadAssetAtPath<MonoScript>(pathName); | |
ProjectWindowUtil.ShowCreatedAsset(asset); | |
} | |
} |
これで見事今回の目標は達成となりました。
全てのプロジェクトで使いたい
しかし、このままでは今回作成したプロジェクト内でしか利用することができません。そこで、以降に作成するプロジェクト全てに自動的に反映されるように工夫しましょう。と言っても非常に簡単です。windowsでは
"unityのアプリケーション位置"\Unity\Editor\Data\Resources\PackageManager\ProjectTemplates\
にデフォルトのプロジェクトが用意されているので、ここに作成したファイルを入れてやるだけです。
今回はcom.unity.template.3d\Assets
に入れてみます。
それでは試にプロジェクトを作成してみましょう。
プロジェクトに先ほど作成したファイルが存在していることが確認できると思います。
おわりに
日々利用するアプリケーションだからこそ開発環境を自分好みにカスタマイズすることが大切ですね。unityのエディタ拡張は専用のメソッドが多く、わかりにくいように感じられますが、「全てを理解する必要はない」と割り切ってしまえばそう難しくもないと思います。実際エディタ拡張ばかりではいつになってもゲームは完成しませんしね。
それでは明日以降も部員たちによる個性的な記事が投稿されることを期待して本記事を締めさせて頂きます。
Posted on: 2018年12月1日, by : mo-takusan