├── README_IMG ├── ExampleImage.png └── EditTemplateWindow.png ├── Assets ├── EditorDefaultResources │ ├── ScriptTemplates │ │ ├── PlainClassTemplate.cs.txt │ │ ├── ScriptableObjectTemplate.cs.txt │ │ ├── PlainClassTemplate.cs.txt.meta │ │ └── ScriptableObjectTemplate.cs.txt.meta │ └── ScriptTemplates.meta ├── EditorDefaultResources.meta ├── UnityScriptTemplates.meta └── UnityScriptTemplates │ ├── UnityScriptTemplates.asmdef.meta │ ├── UnityScriptTemplates.asmdef │ ├── CustomScriptTemplates.cs.meta │ ├── EditScriptTemplates.cs.meta │ ├── CustomScriptTemplates.cs │ └── EditScriptTemplates.cs ├── .gitignore ├── LICENSE └── README.md /README_IMG/ExampleImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baste-RainGames/Unity-Script-Templates/HEAD/README_IMG/ExampleImage.png -------------------------------------------------------------------------------- /README_IMG/EditTemplateWindow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baste-RainGames/Unity-Script-Templates/HEAD/README_IMG/EditTemplateWindow.png -------------------------------------------------------------------------------- /Assets/EditorDefaultResources/ScriptTemplates/PlainClassTemplate.cs.txt: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class #SCRIPTNAME# { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Assets/EditorDefaultResources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0bbf06b7a73cda44bb6fcca5633dad7a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/EditorDefaultResources/ScriptTemplates/ScriptableObjectTemplate.cs.txt: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class #SCRIPTNAME# : ScriptableObject { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Assets/UnityScriptTemplates.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dd2798cce67f68043a743ab0ffd7c2fc 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/EditorDefaultResources/ScriptTemplates.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f4716693e94bbdf48a4cabc107a9cdc6 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/UnityScriptTemplates/UnityScriptTemplates.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c1245470ff0492e46974db618a2b6119 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Assets/EditorDefaultResources/ScriptTemplates/PlainClassTemplate.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 32e65c2dfa411d64ab5f4e7b90a9443c 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Assets/EditorDefaultResources/ScriptTemplates/ScriptableObjectTemplate.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c90bf1d0ee944e64aa54bb630b20dab8 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Assets/UnityScriptTemplates/UnityScriptTemplates.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UnityScriptTemplates", 3 | "references": [], 4 | "optionalUnityReferences": [], 5 | "includePlatforms": [ 6 | "Editor" 7 | ], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false 10 | } -------------------------------------------------------------------------------- /Assets/UnityScriptTemplates/CustomScriptTemplates.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d9e30413a131c57448917ff239c0bfc2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/UnityScriptTemplates/EditScriptTemplates.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2918bf85b812a18428fc67ebaf14cfa9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | [Bb]uild/ 5 | [Bb]uilds/ 6 | Assets/AssetStoreTools* 7 | 8 | # Visual Studio cache directory 9 | .vs/ 10 | 11 | # Jetbrains Rider plugin 12 | .idea/ 13 | Assets/Jetbrains* 14 | 15 | # Don't include Packages or ProjectSettings, they are irrelevant to the project 16 | Packages/ 17 | ProjectSettings/ 18 | 19 | # Autogenerated VS/MD/Consulo solution and project files 20 | ExportedObj/ 21 | .consulo/ 22 | *.csproj 23 | *.unityproj 24 | *.sln 25 | *.suo 26 | *.tmp 27 | *.user 28 | *.userprefs 29 | *.pidb 30 | *.booproj 31 | *.svd 32 | *.pdb 33 | *.opendb 34 | 35 | # Unity3D generated meta files 36 | *.pidb.meta 37 | *.pdb.meta 38 | 39 | # Unity3D Generated File On Crash Reports 40 | sysinfo.txt 41 | 42 | # Builds 43 | *.apk 44 | *.unitypackage 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Update 16/01/19 2 | 3 | This is utterly redundant, due to an undocumented feature in Unity. 4 | If you put script templates in Assets/ScriptTemplates, Unity will use those instead of the built-in ones. So the best way to do this is to: 5 | - copy the files from UnityInstall/Editor/Data/Resources/ScriptTemplates to Project/Assets/ScriptTemplates 6 | - edit as you see fit 7 | - reopen project. 8 | 9 | Thanks to [jura_z](https://forum.unity.com/threads/how-to-create-your-own-c-script-template.459977/#post-3898429) from Unity for pointing this out. 10 | 11 | # Unity-Script-Templates 12 | Custom templates for creating new scripts in Unity. 13 | 14 | This project adds some entires under Create->Script Template for creating ScriptableObject and plain class files. 15 | There's also a window for editing custom and built-in templates. 16 | 17 | ![example](https://github.com/Baste-RainGames/Unity-Script-Templates/blob/master/README_IMG/ExampleImage.png) 18 | 19 | # Usage 20 | 21 | The custom templates work exactly like "Create->C# Script", except that the templates are placed in the Assets folder (in EditorDefaultResources) rather than in the Unity install folder. You can edit the templates however you like. To add a new template, see UnityScriptTemplates/CustomScriptTemplates.cs. It should be pretty easy to edit, as I've commented the code extensively. You should be able to add your own templates as needed, it only requites a couple of lines of code and a new template file. You can also just use the code as an example to create your own tooling. Go wild. 22 | 23 | If you want to edit a template, open Window->Edit Script Templates: 24 | ![example](https://github.com/Baste-RainGames/Unity-Script-Templates/blob/master/README_IMG/EditTemplateWindow.png) 25 | 26 | You can edit both the custom templates included in the project and the built-in ones. If you move your custom templates to somewhere else than the standard folder, you have to update EditScriptTemplates.cs. 27 | Note that in order to edit built-in templates (like Create->C# Script), you have to launch Unity as Administrator if your Unity install is in a protected folder (like Program Files on Windows). 28 | 29 | Both the custom and built-in templates work by doing some very light find-and-replace to the text content in the template before it's copied. You can check the behaviour by looking up ProjectWindowUtil.CreateScriptAssetFromTemplate from Unity's C# reference, but here's an overview: 30 | #NOTRIM# is replaced with empty space. To quote Unity, " #NOTRIM# is a special marker that is used to mark the end of a line where we want to leave whitespace. prevent editors auto-stripping it by accident." 31 | #NAME# is replaced with the name you entered for the file 32 | #SCRIPTNAME# is replaced with the name you entered for the file, with spaces removed 33 | #SCRIPTNAME_LOWER# is strange: 34 | - if the script name starts with an upper case letter, it is replaced with the script name in all lower cases. 35 | - if the script name starts with a lower case letter, it is prefixed with "my", and the old first letter is now upper case. So "thisIsAnExample" is turned into "myThisIsAnExample". No clue what this behaviour is good for. 36 | 37 | # Notes 38 | Public domain lincense, so do whatever. 39 | I've been wanting this for a while, and after yet another person asked about it, I went and looked through the public C# source. Turns out it's not so hard to do this! Thanks to Unity for making the source available. 40 | I've tried to write this OS-independent, but I haven't tested it for OSX/Linux, so please tell me if it's broken on those platforms. 41 | 42 | If you've got bugs/issues/feature requests, create an issue! If you have more general questions, you can reach me at baste@rain-games.com, or as "Baste" at the Unity forums. 43 | -------------------------------------------------------------------------------- /Assets/UnityScriptTemplates/CustomScriptTemplates.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Reflection; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | namespace UnityScriptTemplates { 7 | 8 | /// 9 | /// This file adds a menu item just below "Create->C# Script" for creating some different script templates. 10 | /// You can add your own templates by copy-pasting the code in this file and editing the paths.a 11 | /// 12 | public static class CustomScriptTemplates { 13 | /// 14 | /// Path to the template file for a ScriptableObject. 15 | /// Using .cs.txt extension to match Unity's built-in templates, but this is _not_ neccessary. 16 | /// 17 | private static string TemplatePathScriptableObject { 18 | get { return Application.dataPath + "/EditorDefaultResources/ScriptTemplates/ScriptableObjectTemplate.cs.txt"; } 19 | } 20 | 21 | /// 22 | /// Path to the template file for a Plain C# class. 23 | /// 24 | private static string TemplatePathPlainClass { 25 | get { return Application.dataPath + "/EditorDefaultResources/ScriptTemplates/PlainClassTemplate.cs.txt"; } 26 | } 27 | 28 | /// 29 | /// ProjectWindowUtil.CreateScriptAsset is the method that makes the magic happen. 30 | /// It has two parameters: 31 | /// - templatePath, the absolute path to the template file. 32 | /// - destName, the suggested file name for the new asset. 33 | /// 34 | /// It seems like this method is usually called from c++; it's a private method, and nothing in ProjectWindowUtil calls it, but if you add a breakpoint 35 | /// in it when hitting Create-C# script, the breakpoint is hit, with no stack trace. 36 | /// 37 | private static MethodInfo CreateScriptAsset { 38 | get { 39 | var projectWindowUtilType = typeof(ProjectWindowUtil); 40 | return projectWindowUtilType.GetMethod("CreateScriptAsset", BindingFlags.NonPublic | BindingFlags.Static); 41 | } 42 | } 43 | 44 | /// 45 | /// Adds a menu item for creating a new ScriptableObject file. 46 | /// 47 | [MenuItem("Assets/Create/Script Template/ScriptableObject", priority = 81)] // Create/C# Script has priority 80, so this puts it just below that. 48 | public static void CreateScriptableObject() { 49 | CreateScriptAsset.Invoke(null, new object[] { TemplatePathScriptableObject, "NewScriptableObject.cs" }); 50 | } 51 | 52 | /// 53 | /// Validates that the ScriptableObject template exists. 54 | /// It's pretty important to check that the template path exist. If you call this method with an empty template Unity creates an invisible asset you cannot 55 | /// interact with, which will stick around until you restart Unity. 56 | /// 57 | [MenuItem("Assets/Create/Script Template/ScriptableObject", true, priority = 81)] 58 | public static bool CreateScriptableObjectValidate() { 59 | return File.Exists(TemplatePathScriptableObject) && CreateScriptAsset != null; 60 | } 61 | 62 | /// 63 | /// Adds a menu item for creating a new plain class file. 64 | /// 65 | [MenuItem("Assets/Create/Script Template/Class", priority = 81)] 66 | public static void CreatePlainClass() { 67 | CreateScriptAsset.Invoke(null, new object[] { TemplatePathPlainClass, "NewClass.cs" }); 68 | } 69 | 70 | /// 71 | /// Validates that the plain class template exists. 72 | /// 73 | [MenuItem("Assets/Create/Script Template/Class", true, priority = 81)] 74 | public static bool CreatePlainClassValidate() { 75 | return File.Exists(TemplatePathPlainClass) && CreateScriptAsset != null; 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /Assets/UnityScriptTemplates/EditScriptTemplates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using UnityEditor; 6 | using UnityEngine; 7 | 8 | namespace UnityScriptTemplates { 9 | public class EditScriptTemplates : EditorWindow { 10 | [MenuItem("Window/Edit Script Templates")] 11 | public static void OpenWindow() { 12 | GetWindow(); 13 | } 14 | 15 | public List customScriptTemplates; 16 | public List builtInScriptTemplates; 17 | 18 | //serialized by Unity so selection can survive script reload 19 | public bool selectedIsBuiltIn; 20 | public int selectedIndex = -1; 21 | 22 | private GenericMenu selectTemplateMenu; 23 | private ScriptTemplateFile selectedTemplate; 24 | private bool hasWriteAccessToBuiltins; 25 | 26 | private string builtInPath; 27 | private string customPath; 28 | private Vector2 scroll; 29 | 30 | private void OnEnable() { 31 | builtInPath = EditorApplication.applicationContentsPath + "/Resources/ScriptTemplates"; 32 | customPath = Application.dataPath + "/EditorDefaultResources/ScriptTemplates"; 33 | builtInScriptTemplates = FindScriptTemplates(builtInPath, true); 34 | customScriptTemplates = FindScriptTemplates(customPath, false); 35 | 36 | var listToSelectIn = selectedIsBuiltIn ? builtInScriptTemplates : customScriptTemplates; 37 | if (selectedIndex > 0 && selectedIndex < listToSelectIn.Count) 38 | selectedTemplate = listToSelectIn[selectedIndex]; 39 | 40 | if (builtInScriptTemplates.Count > 0) { 41 | try { 42 | builtInScriptTemplates[0].SaveToFile(); 43 | hasWriteAccessToBuiltins = true; 44 | } 45 | catch (UnauthorizedAccessException) { 46 | hasWriteAccessToBuiltins = false; 47 | } 48 | } 49 | 50 | selectTemplateMenu = new GenericMenu(); 51 | foreach (var template in customScriptTemplates) { 52 | selectTemplateMenu.AddItem(new GUIContent("Custom/" + template.name), false, TemplateSelected, template); 53 | } 54 | 55 | foreach (var template in builtInScriptTemplates) { 56 | selectTemplateMenu.AddItem(new GUIContent("Built in/" + template.name), false, TemplateSelected, template); 57 | } 58 | } 59 | 60 | private void TemplateSelected(object template) { 61 | selectedTemplate = (ScriptTemplateFile) template; 62 | } 63 | 64 | private void OnGUI() { 65 | scroll = EditorGUILayout.BeginScrollView(scroll); 66 | DrawTemplateSelection(); 67 | 68 | if (selectedTemplate != null) { 69 | EditorGUILayout.Space(); 70 | DrawSelectedTemplate(); 71 | } 72 | 73 | EditorGUILayout.EndScrollView(); 74 | } 75 | 76 | private void DrawTemplateSelection() { 77 | EditorGUILayout.LabelField("Selected template:"); 78 | string text; 79 | if (selectedTemplate == null) { 80 | text = ""; 81 | } 82 | else { 83 | if (selectedTemplate.isBuiltIn) 84 | text = "Built in/" + selectedTemplate.name; 85 | else 86 | text = "Custom/" + selectedTemplate.name; 87 | } 88 | 89 | if (GUILayout.Button(text, GUILayout.Width(450f))) { 90 | selectTemplateMenu.ShowAsContext(); 91 | } 92 | 93 | if (selectedTemplate != null) { 94 | EditorGUILayout.TextField("Path: " + selectedTemplate.path, GUI.skin.label); //textField so it's selectable for copy+pasting 95 | } 96 | 97 | } 98 | 99 | private void DrawSelectedTemplate() { 100 | var disabled = !hasWriteAccessToBuiltins && selectedTemplate.isBuiltIn; 101 | if (disabled) { 102 | var style = new GUIStyle(GUI.skin.label) { 103 | normal = { 104 | textColor = Color.red 105 | } 106 | }; 107 | EditorGUILayout.LabelField("Cannot edit built in templates! You're probably not running Unity as administrator/root", style); 108 | EditorGUILayout.LabelField("Either relaunch as administrator/root, or edit the files directly in their folder", style); 109 | } 110 | 111 | EditorGUI.BeginDisabledGroup(disabled); 112 | selectedTemplate.text = EditorGUILayout.TextArea(selectedTemplate.text); 113 | 114 | EditorGUILayout.BeginHorizontal(); 115 | 116 | if (GUILayout.Button("Save to file", GUILayout.Width(120f))) { 117 | selectedTemplate.SaveToFile(); 118 | } 119 | 120 | if (GUILayout.Button("Discard changes", GUILayout.Width(120f))) { 121 | selectedTemplate.DiscardChanges(); 122 | GUI.FocusControl(null); //The text area doesn't update while not selected 123 | Repaint(); 124 | } 125 | 126 | GUILayout.FlexibleSpace(); 127 | 128 | EditorGUILayout.LabelField("Set indent style:", GUILayout.Width(100f)); 129 | if (GUILayout.Button("Spaces")) { 130 | selectedTemplate.text = selectedTemplate.text.Replace("\t", " "); 131 | } 132 | 133 | if (GUILayout.Button("Tabs")) { 134 | selectedTemplate.text = selectedTemplate.text.Replace(" ", "\t"); 135 | } 136 | 137 | EditorGUI.EndDisabledGroup(); 138 | 139 | EditorGUILayout.EndHorizontal(); 140 | } 141 | 142 | private static List FindScriptTemplates(string templateFolder, bool isInternal) { 143 | return Directory.GetFiles(templateFolder) 144 | .Where(file => file.EndsWith(".cs.txt")) 145 | .Select(file => new ScriptTemplateFile(file, isInternal)) 146 | .ToList(); 147 | } 148 | } 149 | 150 | public class ScriptTemplateFile { 151 | public readonly string path; 152 | public readonly string name; 153 | public readonly bool isBuiltIn; 154 | 155 | private string fileText; 156 | public string text; 157 | 158 | public ScriptTemplateFile(string path, bool isBuiltIn) { 159 | this.path = path.Replace("\\", "/"); 160 | this.isBuiltIn = isBuiltIn; 161 | name = Path.GetFileNameWithoutExtension(path); 162 | fileText = File.ReadAllText(path); 163 | text = fileText; 164 | } 165 | 166 | public void SaveToFile() { 167 | File.WriteAllText(path, text); 168 | fileText = text; 169 | if (!isBuiltIn) { 170 | var assetDatabasePath = "Assets" + path.Substring(Application.dataPath.Length); 171 | AssetDatabase.ImportAsset(assetDatabasePath); 172 | } 173 | } 174 | 175 | public void DiscardChanges() { 176 | text = fileText; 177 | } 178 | } 179 | } --------------------------------------------------------------------------------