Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
10
Question by Dszordan · Jan 11, 2014 at 11:35 PM · editorfolderdefault4.3

Change Default Script Folder

Upon creating a new script through the Unity Editor, it places that script in /Assets folder. I want to place that script in /Assets/Scripts folder instead.

Is there a property I can change to set the default folder for scripts created through the Unity Editor?

Edit: I just want to clarify my process of creating a new script: I'm adding the new script through the inspector via the "Add Component" button. When I create the script from there it seems to default to /Assets folder. Is there a way I can change that?

Comment
Add comment · Show 1
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image chuwilliamson · May 16, 2015 at 08:50 PM 1
Share

Did you ever figure this out? I would also like to change the default directory that Add Component puts new script files.

5 Replies

· Add your reply
  • Sort: 
avatar image
7

Answer by Bunny83 · Sep 06, 2015 at 01:36 AM

I just had a look at the code of the AddComponentWindow which is responsible for creating the new scripts. It has a nested class called "NewScriptElement" which handles the specifics about the script creation and actually draws the edit box for the script name and the drop down for the language selection. It actually has a private variable called "m_Directory" which is treated as a partial path after "/Assets/". However it's initialized with an empty string and there's no code that actually changes that variable.

So they might had in mind to provide a way to specify a path where the new script should be created, but they never implemented any kind of GUI / interface to set that directory.

Reflection saves the day, However it's more ugly than i thought. The problem is that the AddComponentWindow is recreated each time it's opened. Also it's internal tree of "Elements" which contains the "NewScriptElement" instance is also recreated each time the window is opened. Since it's a drop-down window you can't click anywhere outside the window since that would close the window. So the only solution is to actually "poll" if the window is currently open and if so we "inject" our desired path.

I've just written an editor script which does that. Of course the script has to be placed in an editor folder. To use it, just click "Tools/Default script path settings" in the main menu to bring up the settings menu window. The window provides you three things:

  • A toggle button where you can enable / disable the background scanning for the add component window

  • A text field where you can enter the relative path where you want your new scripts to be saved to. That path uses forward slashes. You shouldn't add leading or trailing slashes. So if you want to use "/Assets/Scripts/" as folder just type "Scripts" (without the quotes) into the text field.

  • An indicator which shows you whether the background check has successfully set the directory or not.

The window must be open to be able to work. However it can be docked in any view and even be hidden somewhere as long as it's open. The window also saves the state of the toggle button as well as the path you enter in the EditorPrefs so it's automatically restored when Unity recompiles or when you reopen Unity.

edit The new version of this script doesn't require to have the window open. The window is now just a "settings" menu where you can specify the path and enable / disable the background check.

 //NewScriptDefaultPathWindow.cs
 using UnityEngine;
 using UnityEditor;
 using System.Collections;
 using System.Linq;
 using System.Reflection;
 
 [InitializeOnLoad]
 public class NewScriptDefaultPathWindow : EditorWindow
 {
     #region Reflection magic
     static System.Type AddComponentWindowType = null;
     static System.Type NewScriptElement = null;
     static FieldInfo s_AddComponentWindow = null;
     static FieldInfo m_Tree = null;
     static FieldInfo m_Directory = null;
 
     static EditorWindow m_CurrentWindow = null;
     static object m_CurrentElement = null;
 
     static NewScriptDefaultPathWindow()
     {
         var types = typeof(EditorWindow).Assembly.GetTypes();
         AddComponentWindowType = types.Where(t => t.Name == "AddComponentWindow").FirstOrDefault();
         s_AddComponentWindow = AddComponentWindowType.GetField("s_AddComponentWindow",BindingFlags.NonPublic | BindingFlags.Static);
         m_Tree = AddComponentWindowType.GetField("m_Tree",BindingFlags.NonPublic | BindingFlags.Instance);
         var nestedTypes = AddComponentWindowType.GetNestedTypes(BindingFlags.NonPublic);
         NewScriptElement = nestedTypes.Where(t => t.Name == "NewScriptElement").FirstOrDefault();
         m_Directory = NewScriptElement.GetField("m_Directory", BindingFlags.NonPublic | BindingFlags.Instance);
         EditorApplication.update += BackgroundCheck;
     }
 
     static EditorWindow GetAddComponentWindow()
     {
         var inst = (EditorWindow)s_AddComponentWindow.GetValue(null);
         return inst;
     }
 
     static object GetNewScriptElement()
     {
         var window = GetAddComponentWindow();
         if (window == null)
         {
             m_CurrentWindow = null;
             m_CurrentElement = null;
             return null;
         }
         if (window == m_CurrentWindow)
         {
             return m_CurrentElement;
         }
         m_CurrentWindow = window;
         System.Array a = (System.Array)m_Tree.GetValue(window);
         var list = a.OfType<object>().ToArray();
         for(int i = 0; i < list.Length; i++)
         {
             if (list[i].GetType() == NewScriptElement)
             {
                 m_CurrentElement = list[i];
                 return m_CurrentElement;
             }
         }
         return null;
     }
     static string Directory
     {
         get
         {
             var element = GetNewScriptElement();
             if (element == null)
                 return "";
             return (string)m_Directory.GetValue(element);
         }
         set
         {
             var element = GetNewScriptElement();
             if (element == null)
                 return;
             m_Directory.SetValue(element, value);
         }
     }
     #endregion Reflection magic
 
     [MenuItem("Tools/Default script path settings")]
     static void Init ()
     {
         var win = CreateInstance<NewScriptDefaultPathWindow>();
 
         win.ShowUtility();
     }
     static string dir = "";
     static string currentDir = "";
     static bool enableBackgroundCheck = false;
     static int counter = 0;
     static NewScriptDefaultPathWindow instance;
     static bool initialized = false;
 
     static void LoadSettings()
     {
         dir = EditorPrefs.GetString("NewScriptDefaultPath", "");
         enableBackgroundCheck = EditorPrefs.GetBool("NewScriptDefaultPath_Enabled", false);
     }
 
     static void BackgroundCheck()
     {
         if (!initialized)
         {
             initialized = true;
             LoadSettings();
         }
         if (enableBackgroundCheck)
         {
             // check only once a second
             if (++counter > 100)
             {
                 counter = 0;
                 Directory = dir;
                 if (instance != null)
                 {
                     string tmp = Directory;
                     if (tmp != currentDir)
                     {
                         currentDir = tmp;
                         instance.Repaint();
                     }
                 }
             }
         }
     }
 
     void OnEnable()
     {
         LoadSettings();
         instance = this;
         titleContent = new GUIContent("Edit default script path");
     }
 
     void OnDisable()
     {
         instance = null;
     }
     Color GetColor(bool aActive)
     {
         if (aActive)
             return Color.green;
         return Color.red;
     }
 
 
     void OnGUI ()
     {
         Color oldColor = GUI.color;
         GUI.changed = false;
         enableBackgroundCheck = GUILayout.Toggle(enableBackgroundCheck, "enable background check", "button");
         GUILayout.BeginHorizontal();
         GUILayout.Label("Default script save path:", GUILayout.Width(150));
         dir = GUILayout.TextField(dir);
         GUILayout.EndHorizontal();
         GUILayout.BeginHorizontal();
         GUILayout.Label("Background check is ");
         GUI.color = GetColor(enableBackgroundCheck);
         GUILayout.Label((enableBackgroundCheck ? "enabled" : "disabled"), "box");
         GUI.color = oldColor;
         GUILayout.FlexibleSpace();
         GUILayout.EndHorizontal();
 
         if (GUI.changed)
         {
             EditorPrefs.SetString("NewScriptDefaultPath", dir);
             EditorPrefs.SetBool("NewScriptDefaultPath_Enabled", enableBackgroundCheck);
         }
         if (enableBackgroundCheck && dir != "")
         {
             GUI.color = GetColor(currentDir == dir);
             if (currentDir == dir)
                 GUILayout.Label("Directory successfully set", "box");
             else
                 GUILayout.Label(" - Window currently not open - ", "box");
             GUI.color = oldColor;
         }
         GUILayout.FlexibleSpace();
         GUILayout.BeginHorizontal();
         GUILayout.FlexibleSpace();
         if(GUILayout.Button("Close"))
         {
             Close();
         }
         GUILayout.Space(15);
         GUILayout.EndHorizontal();
         GUILayout.Space(15);
     }
 }

Note: It's just a quick and dirty solution. We can be happy that they implemented the m_Directory field and actually use it internally. Without that it would be near to impossible to change anything since the path would be hardcoded. The only way would be to decompile the UnityEditor.dll, edit the desired parts and recompile it completely.

Also Note: This class uses reflection to get access to several internal classes and fields. Internal things can change at any time in the future whenever you update Unity. So it might no longer work in a distant future.

Comment
Add comment · Show 8 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Sep 06, 2015 at 01:38 AM 1
Share

@eshan.mathur:
Since you asked the duplicate question over here, i thought you might be interested in this.

Personally I never use that AddComponentWindow. I always create scripts by right clicking the appropriate folder and select Create -> C# Script. So i actually don't have any use for my tool ^^.

ps: The window will check if the AddComponentWindow is open every second. First i thought a quicker response would be required but it takes way longer to click through the menus and actually type a script name. Probably every 5 seconds would be enough ^^. You can change that by editing the "100" in the script. (500 == 5 seconds)

avatar image eshan-mathur · Sep 06, 2015 at 01:58 AM 0
Share

This is pretty great. Thanks for writing this! I'll try it out after the weekend.

avatar image Bunny83 · Sep 06, 2015 at 02:06 AM 0
Share

@eshan.mathur:
To be honest it's actually pretty ugly ^^. Also it's current usage is quite cumbersome as the window has to be open all the time. It might be possible to use the "InitializeOnLoad" attribute and hook up the update delegate inside the static constructor. That way the routine that does the magic would run automatically in the background.

I'll see if that works ^^.

edit
Just tested it and yes, it works ^^. So it's improving... I'll update my answer once i finished cleaning up the code.

avatar image eshan-mathur Bunny83 · Sep 08, 2015 at 07:10 PM 0
Share

Awesome. Do you have a Github repo for this?

avatar image eshan-mathur Bunny83 · Sep 10, 2015 at 07:20 PM 0
Share

Updated answer?

avatar image Bunny83 eshan-mathur · Sep 12, 2015 at 09:54 AM 0
Share

@eshan.mathur:

Yes, i have updated the answer but sortly after i posted the comment above ^^.

No, i don't have a git rep. for this. I just wrote it from scratch and posted it here ^^. At the moment i'm not at home. If i find the time i might create a reopsitory for it. However you are free to use it.

avatar image Positive7 · Sep 08, 2015 at 08:06 PM 0
Share

Why not just change it with ILSpy in the UnityEditor.dll at UnityEditor -> AddComponentWindow -> NewScriptElement ->

 private string TargetDir(){
     return Path.Combine("Assets/Scripts", this.m_Directory.Trim(this.kPathSepChars));}

Then save and overwrite in the Unity folder :D You just have to make sure that Scripts folder exist before using it.

avatar image Bunny83 Positive7 · Sep 12, 2015 at 10:02 AM 0
Share

@Positive7:
As you can see at the bottom of my answer i already mentioned decompiling the dll, modify and recompile it. However this is never a good solution. First of all you can't be sure that ILSpy decompiled everything "correctly". I've often had some DLLs where ILSpy failed to decompile certain things. It's more reliable to use IL as language.

Patching those DLLs is most likely against the EULA, however for private use probably nobody really cares. Redistribution of those DLLs (patched or not) most likely isn't allowed either.

A reflection based approach is usually the best and should work with new versions out of the box. Also the placement of scripts might depend on the project, so having a hardcoded path again makes only little sense to me.

avatar image
1

Answer by JoshuaMcKenzie · Jul 09, 2016 at 04:19 PM

So I came here because I had the same question, if there was a setting to specify a default location for new scripts when adding via the Add Component in the inspector button.

Since it seems that none exists, I sat and thought up my own solution. This one is using AssetPostProcessor to handle default locations for scripts

 using UnityEngine;
 using UnityEditor;
 using System.Collections.Generic;
 using System.IO;
 
 //purpose of this postprocessor is to ensure that listed file extensions
 // are not in certain filepaths, when they are they are moved to a 
 //specified default path
 public class FileImportHandler : AssetPostprocessor 
 {
     //only evaluate files imported into these paths
     static List<string> pathsToMoveFrom = new List<string>()
     {
         "Assets"
     };
 
     static Dictionary<string,string> defaultFileLocationByExtension = new Dictionary<string, string>()
     {
         {".mp4",   "Assets/StreamingAssets/"},//for IOS, movies need to be in StreamingAssets
 
         {".anim",   "Assets/Art/Animations/"},
         {".mat",    "Assets/Art/Materials/"},
         {".fbx",    "Assets/Art/Meshes/"},
 
         //Images has subfolders for Textures, Maps, Sprites, etc.
         // up to the user to properly sort the images folder
         {".bmp",    "Assets/Art/Images/"},
         {".png",    "Assets/Art/Images/"},
         {".jpg",    "Assets/Art/Images/"},
         {".jpeg",   "Assets/Art/Images/"},
         {".psd",    "Assets/Art/Images/"},
         
         {".mixer",    "Assets/Audio/Mixers"},
         //like images, there are sub folders that the user must manage
         {".wav",    "Assets/Audio/Sources"}, 


         //like images, there are sub folders that the user must manage
         {".cs",     "Assets/Dev/Scripts"}, 
         {".shader", "Assets/Dev/Shaders"},
         {".cginc",  "Assets/Dev/Shaders"}
     };
 
     static void OnPostprocessAllAssets (string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) 
     {
         foreach (string oldFilePath in importedAssets)
         {
             string directory = Path.GetDirectoryName(oldFilePath);
             if(!pathsToMoveFrom.Contains(directory))
                 continue;
             
             string extension = Path.GetExtension(oldFilePath).ToLower();
             if(!defaultFileLocationByExtension.ContainsKey(extension))
                 continue;
             
             string filename = Path.GetFileName(oldFilePath);
             string newPath = defaultFileLocationByExtension[extension];
             
             AssetDatabase.MoveAsset(oldFilePath, newPath +filename);
             
             
             Debug.Log(string.Format("Moving asset ({0}) to path: {1}", filename, newPath));
         }
     }
 }

remember that AssetPostprocessor scripts are Editor scripts (they are in the UnityEditor namespace) so they must be in the Editor folder to work

I just made this about 20 minutes ago and so far it seems to work just fine. though it seems it'll also force any file with the listed extensions that is moved to one of the "paths to move from" it will also force move the file back to a default path.

that said this script should be nice for enforcing a clean folder hierarchy. hence why I expanded it to include other file extensions so that our teams art lead can enforce that art assets are imported into the correct folder

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Kamyker · Aug 25, 2019 at 04:02 PM 0
Share

Doesn't work in 2019.1, it breaks adding the script

 NullReferenceException: Object reference not set to an instance of an object
     UnityEditor.AddComponent.NewScriptDropdownItem.Create (UnityEngine.GameObject[] gameObjects, System.String searchString) (at C:/buildslave/unity/build/Editor/$$anonymous$$ono/Inspector/AddComponent/NewScriptDropdownItem.cs:68)

avatar image
0

Answer by Eric5h5 · Jan 11, 2014 at 11:40 PM

Unity puts new scripts wherever you have selected, so if you select your Assets/Scripts folder first then new scripts will be placed there.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Dszordan · Jan 12, 2014 at 02:20 AM 1
Share

Hey Erich. I think you've solved my problem, but I want to clarify further:

When I'm adding a new script component to a Gameobject through the inspector, it chooses the /Assets folder to put it in.

avatar image
0

Answer by DaveA · Jan 12, 2014 at 12:06 AM

Right-click on the folder (Assets/Scripts) and create script from there. Unless you mean a default for the Mono Editor

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Dszordan · Jan 12, 2014 at 02:24 AM 0
Share

Hey Dave,

I haven't been doing that, but I can start doing so. The way I've been adding a script now is through the "Add component" button, and it chooses the /assets folder by default. If I can't change that default folder, I'll use the method you mentioned there.

avatar image
0

Answer by aksh2143 · Sep 12, 2015 at 10:11 AM

Right click on the folder from the project panel you want to add script in. Select creae script. Finish

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Sep 12, 2015 at 10:52 AM 2
Share

That's what Dave and Eric already said in their answers, so what's the point of your answer a year later? Also, if you had read the edit of the Question the OP uses the AddComponent button in the inspector to add a new script.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

28 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

default script editor now working anymore 0 Answers

Editor Script Reverts to Default State on Script Compilition 2 Answers

How do I enumerate the contents of an asset folder? 2 Answers

Perform action on save/load in editor 2 Answers

Custom Asset Icons? 2 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges