Hey, check out my latest blog post: http://www.li0rtal.com/2014/03/12/code-generation-fun-with-unity/
Nice one! but I'm not a Fan of using 3rd Party programms all over the place. And because I was bored anyways I wrote this little script that does the same without the use of T4. This is also fully automatic. Just drop it in \Assets\Editor\ , and you are good to go. Code (csharp): using System.IO; using System.Text; using UnityEditor; using UnityEngine; using UnityEditorInternal; [InitializeOnLoad] public static class TagCodeGenerator { // an array that hold all tags private static string[] _tags; // a flag if the dataset has changed private static bool _hasChanged = false; // time when we start to count private static double _startTime = 0.0; // the time that should elapse between the change of tags and the File write // this is importend because changed are triggered as soon as you start typing and this can cause lag private static double _timeToWait = 1.0; static TagCodeGenerator() { //subscripe to event EditorApplication.update += Update; // get tags _tags = InternalEditorUtility.tags; // write file WriteCodeFile(); } private static void Update() { // returns if we are in play mode if( Application.isPlaying == true ) return; Wait(); // temp array that hold new tags string[] newTags = InternalEditorUtility.tags; // check if the lenght is not the same if ( newTags.Length != _tags.Length ) { _tags = newTags; _hasChanged = true; _startTime = EditorApplication.timeSinceStartup; return; } else { // loop thru all new tags and compare them to the old ones for( int i = 0; i < newTags.Length; i++ ) { if( string.Equals( newTags[i], _tags[i] ) == false) { _tags = newTags; _hasChanged = true; _startTime = EditorApplication.timeSinceStartup; return; } } } } private static void Wait() { // if nothing has changed return if(_hasChanged == false) return; // if the time delta between now and the last change, is greater than the time we schould wait Than write the file if (EditorApplication.timeSinceStartup - _startTime > _timeToWait) { WriteCodeFile(); _hasChanged = false; } } // writes a file to the project folder private static void WriteCodeFile() { // the path we want to write to string path = string.Concat( Application.dataPath, Path.DirectorySeparatorChar, "Tags.cs" ); try { // opens the file if it allready exists, creates it otherwise using ( FileStream stream = File.Open( path, FileMode.OpenOrCreate, FileAccess.Write ) ) { using( StreamWriter writer = new StreamWriter(stream) ) { StringBuilder builder = new StringBuilder(); builder.AppendLine("// ----- AUTO GENERATED CODE ----- //"); builder.AppendLine("public static class Tags"); builder.AppendLine("{"); foreach(string tag in _tags) { builder.AppendLine( string.Format("\tpublic static readonly string {0} = \"{0}\";", tag)); } builder.AppendLine("}"); writer.Write( builder.ToString() ); } } } catch(System.Exception e) { Debug.LogException( e ); // if we have an error, it is certainly that the file is screwed up. Delete to be save if(File.Exists( path ) == true) File.Delete( path ); } AssetDatabase.Refresh(); } }
Your code goes the extra mile to build the tags class automatically whenever there's an update, nice. Just to be clear, my solution uses T4 preprocessed templates, meaning that my template is a self-contained class that can be used at runtime, without any dependency on T4 whatsoever. The class itself already contains the text output of how to format a class, which "placeholders" that are replaced with the real tags that will be generated.
I know this is old topic, but since google brings me here I found it worthy that share my solution, which expanded element_wsc's one. Here is the repository. https://github.com/srndpty/CodeGen Notice or PR me if you find something to be improved.
I used this as a basis to create an auto generated enum from scriptable objects with stat names in them. This gives the designer a simple way to add enum values to the stats without needing to go into code (and the custom editor behaves in much the same way that Unity's tag and layer editor works, but that's another story. Thanks for this!
Oooooh, be careful! Are you emitting hard numbers for those as well as ensuring that those numbers never change into the future, surviving intact through removals and new additions? You should be! If your enum-emitter blasts them all out unnumbered and their order changes, you will be mysteriously very sad. In general, enums are bad bad bad in Unity3D if you intend them to be serialized. Here's why: https://forum.unity.com/threads/bes...if-not-do-something-else.972093/#post-6323361 https://forum.unity.com/threads/unity-card-game-structure.1006826/#post-6529526 It is much better to use ScriptableObjects for many enumerative uses. You can even define additional associated data with each one of them, and drag them into other parts of your game (scenes, prefabs, other ScriptableObjects) however you like. References remain rock solid even if you rename them, reorder them, reorganize them, etc. They are always connected via the meta file GUID. Collections / groups of ScriptableObjects can also be loaded en-masse with calls such as Resources.LoadAll<T>(). Best of all, Unity already gives you a built-in filterable picker when you click on the little target dot to the right side of a field of any given type... bonus!
Ordinarily, I would agree with that, and have actually used systems in the past for exactly that purpose (actually for my personal implementation of the RPG project from the course I'm a TA for). This particular setup is for integrating into a course that is specifically using Enums for stats, and some of those need to be fixed in stone. I've rigged the Editor to always include about a dozen "standard" stats, and once you've added a stat, you cannot delete it. (You can rename it, but the renamed stat would have the same ordinal number as the previous stat). Since the students will be starting with the entire project centered around an enum based stat, it would require more time explaining how to make ScriptableStats that are retrievable at runtime, and dealing with students who forgot that yes, they do need to attach the Health ScriptableStat to their Health component or it won't work, etc) than it will be to provide this StatEditor with a brief note on how to use it.
Actually, thinking about it, it wouldn't be that hard to add an auto-numbering scheme to the SO and editor, and then have the enum include the hard serialized number. And I do agree, enums in general have some issues when people go in and muck about changing/adding/subtracting (ugh) members over time. That's part of why I'm putting this together, because students have run head into the downsides of it.
Oh no, no, no, no, no! That completely defeats the purpose!!! Not only that but you have just opened your codebase up to a MASSIVE collection of hard-to-find pernicious bugs if you place any kind of duplicate primary indentifier inside a ScriptableObject!!! - the identifier changes - identifier collides with another - identifier incorrect - someone copies that identifier out, then it changes - someone copies that identifier out, but you realize you need to change it - etc... These are really really HARD AWFUL bugs to be avoided at all costs! These are the bugs that sink entire projects. The entire point of the ScriptableObject instance itself is that it IS the identifier. It is NOT that it CONTAINS an identifer, that would be pointless. The single point of truth (SPOT) is the unique instance of that ScriptableObject. The identifier IS the ScriptableObject instance. Full stop. Any other way lies madness and bugs. As a long-time engineer, I have committed and observed and repaired and retrofitted programs with ALL of these bugs. This is a class of bug along the lines of running with scissors or playing tiddlywinks in the middle of the freeway. AVOID!
There is ONE scriptableObject with the names of the enum and an index, basically a flat table with an autonumber like you would find in just about any database. The enum is generated reading that scriptableObject as soon as any entry changes. Entries cannot be deleted, and assigned numbers are read only. The editor only works on one instance of the ScriptableObject. The only way for the user to edit the index would be to go into the Yaml and change the number (and let's face it, if they're that dumb...). This is for a specific project, to make the easiest integration possible. To change the codebase from the existing Stat to a ScriptableStat for each stat is a massive undertaking. I know, because I have done it (and it works just fine), but I'll leave 90% of my students behind explaining the changes. If and when we rewrite the courses, I'll have a hand in it, and will be pushing to have all of the enums in the course replaced with ScriptableObjects.