Search Unity

Hidden stuff in Unity (reflection is great)

Discussion in 'Made With Unity' started by half_voxel, Jun 23, 2010.

  1. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    Hi

    Using some reflection and editorscripts I have built a utility which can show the hidden functions in Unity, and I have found a lot of great stuff which I don't understand why it isn't documented.

    There is all this with the terrain system, and why it isn't documented, but I will leave that since there are reasons for keeping it undocumented.

    Here are instead some other goodies:

    1. Built in perlin noise : Yes Unity have built in perlin noise, it works really great too:
    Mathf.PerlinNoise (float x, float y);

    2. OnPreviewSettings and OnPreviewGUI:
    Want to be able to show fancy previews of your ScriptableObjects (custom made assets) like the ones for previewing textures and models.
    Now you can, simply add the function OnPreviewGUI to your editor script.
    Code (csharp):
    1. public void OnPreviewGUI (Rect r) {
    2.     GUI.Box (r,"Hello\nThis is my custom preview");
    3. }
    There is a function called OnPreviewSettings which you can implement too, but I'm not sure what it does.

    3. And some terrain stuff anyway:
    TerrainData.lightmap : The terrain lightmap

    Terrain.GetSteepness (float x,float y) : I haven't tried this but I guess it returns the angle of the terrain.

    UnityEditor.TerrainLightmapper.Generate (Vector3 terrainPosition, TerrainData terrainData, TerrainCollider collider, Light[] lights, Color ambient, int shadowSamples, Texture2D lightmap, Texture2D shadowMap);
    This generates a lightmap for the terrain, not very usefull, but anyway.

    TerrainLightmapper.SuperSampleShadowOffset (float shadowSize, int shadowSamples);
    Returns an array of Vector3s randomly distributed (can be some order in it to make it not too random), good if you need to anti-alias something.

    4. UnityEditor.InternalEditorUtility.HasPro () : Have you forgot if you have pro or the free version? No problem! Just call this to find out.
    But no, there is no function called ActivatePro :wink:

    You can also check if you have got the Unity Beta:
    UnityEditor.InternalEditorUtility.IsUnityBeta ();

    5. EditorGUILayout.ToolbarSearchField (string text);
    This is something I think UT should have documented, it shows a search field like the one you use in the project view to search for assets.
    Unfortunately that function is hidden (private), but using some reflection you can call it anyway :wink:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEditor;
    4. using System.Reflection;
    5. using System;
    6.  
    7. [CustomEditor(typeof(MyScript))]
    8. public class MyEditor : Editor {
    9.    
    10.     public string search = "Hello";
    11.    
    12.     public override void OnInspectorGUI () {
    13.         System.Object[] parameters = new System.Object[2] {search, new GUILayoutOption [0]};
    14.         search = (string)DoInvoke (typeof(EditorGUILayout),"ToolbarSearchField",parameters);
    15.     }
    16.    
    17.     public static System.Object DoInvoke (Type type,string methodName, System.Object[] parameters) {
    18.         Type[] types = new Type[parameters.Length];
    19.        
    20.         for (int i=0;i<parameters.Length;i++) {
    21.             types[i] = parameters[i].GetType ();
    22.         }
    23.        
    24.         MethodInfo method = type.GetMethod (methodName,(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public),null,types,null);
    25.         return DoInvoke2 (type,method, parameters);
    26.     }
    27.    
    28.     public static System.Object DoInvoke2 (Type type, MethodInfo method, System.Object[] parameters) {
    29.         if (method.IsStatic) {
    30.             return method.Invoke (null,parameters);
    31.         }
    32.        
    33.         System.Object obj = type.InvokeMember(null,
    34.         BindingFlags.DeclaredOnly |
    35.         BindingFlags.Public | BindingFlags.NonPublic |
    36.         BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new System.Object[0]);
    37.    
    38.         return method.Invoke(obj,parameters);
    39.     }
    40. }

    There's a lot more you can do, but I can't list everything here.

    Remember that all undocumented features are not fully supported, meaning that an update of unity or the webplayer can break any use of those features.
     

    Attached Files:

  2. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I noticed someone marketing their Unity product recently and noticed that fancy preview in the inspector. I was wondering how they did that and spent a few hours reading the docs trying to discover it... I failed :(

    OnPreviewGUI, huh?

    This renders to the Inspector? Interesting...
     
  3. Joe-Robins

    Joe-Robins

    Unity Technologies

    Joined:
    Apr 21, 2008
    Posts:
    430
    All undocumented features are undocumented as they are not fully supported. Using these features may break backwards compatibility in projects and support cannot be offered when using these features.
     
  4. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    Ah, should have added that to the first post.
    Thanks for posting Joe
     
  5. Sammual

    Sammual

    Joined:
    Oct 28, 2008
    Posts:
    176
    I am attempting to figure out how use Perlin Noise and fBM for heightmap generation, how do you use Mathf.PerlinNoise?

    Thanks,
    Sammual
     
  6. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    Hi Sammual

    The way to use Mathf.Perlin noise:
    If you want to, for example map it to a texture then you can do something like this (pseudocode):

    Code (csharp):
    1. //PixelPosX  Y should be in normalized coordinates (0...1)
    2. foreach (pixel) {
    3.     float greyscale = Mathf.PerlinNoise (pixelPosX,pixelPosY);
    4.     pixel = new Color (greyscale,greyscale,greyscale);
    5. }
    Note: I think this was how it worked, haven't used it for a long time.

    If you don't want to use Unity's built-in stuff which isn't official you can use this script on the Unify Community Wiki:

    http://www.unifycommunity.com/wiki/index.php?title=Perlin_Noise

    Here is another script which uses Mathf.PerlinNoise to generate a terrain heightmap:
    http://www.unifycommunity.com/wiki/index.php?title=TerrainPerlinNoise

    Regards,
    Aron
     
  7. duke

    duke

    Joined:
    Jan 10, 2007
    Posts:
    763
  8. Sammual

    Sammual

    Joined:
    Oct 28, 2008
    Posts:
    176
    The problems with all these examples is that I don't know how they work.
    See - http://answers.unity3d.com/questions/22069/need-help-with-undocumented-mathf-perlinnoise

    I want to generate them at run time and for a given seed to always create the game map. I don't see any options with Mathf.PerlinNoise other then the X and Y co-ords. Can I set a starting seed? The Frequency and Amplitude?

    The Unity Answers shows what I am looking for in terms of output and without being able to set these other arguments I can't see how to do this.

    Sammual
     
  9. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I just tried following the example in the docs and created this:
    Code (csharp):
    1.  
    2. @CustomEditor(crInventoryItem)
    3.  
    4. class crInventoryItem extends Editor {
    5.     override function OnInspectorGUI ()
    6.     {
    7.         GUILayout.Label("This is a Label in a Custom Inspector");
    8.     }
    9. }
    By messing up the class names used above, when I click on a prefab in the Projects tab, I get an error about no script using the class. When I rename the class names to the proper names it no longer gives me the error message but it also doesn't do what it is supposed to do. In fact what it does is absolutely nothing.

    I have a generic inventory class that holds a variety of specs but some of the drop downs, when selected, means some of the values are of no use. For instance, if you make the bool value for "RotateInScene" false then the value for RotateSpeed becomes rather useless, so too for "BounceInScene" and "ScaleUpDown"... and all of them are rather useless when you select false for "SpawnInScene"... So what I want to do is create a series of tabs and depending on which tab you select, only certain values are displayed for modification... The method above seems to be the way to go except when I look in the inspector I still see the normal inspector like it always looked...

    Any ideas on what I am doing wrong?
     
  10. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Proper name: as long as filename and class name are the same it should work.
    You might just need to explicitely declare the class in the file to get through with anti-standard names.

    Custom Editor:
    I assume this class is a monobehaviour and attached directly to a game object?

    otherwise you will need to write a custom editor that targets the monobehaviour this class is part of to extend it accordingly or alternatively doubleclick this object to get into a "specific just to this" editor. Custom Editors won't expand "just symbol to object" situations when they are embedded into others by being linked there, at least I never got this to work.
     
  11. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I am wondering, the @CustomEditor makes this syntax look like JS, but with all the classes and monobehaviors you mentioned in your post, I am wondering if this only works for c# classes?

    My crInventoryItem script is a normal JS file with no additional classes defined within it. All members are in the root. The script is dragged onto an empty GameObject and then populated with values from there.

    I just tried something silly and tried placing that code inside the actual crInventory class and expected to get some kind of warning about editor classes outside the Editor folder but instead my class is now an Editor class, not a MonoBehavior class so now my code fails at each reference to "transform".

    So yeah, I'm still stuck...

    I was planning on writing a custom editor but it seemed rather daft to have a custom editor that shows/hides certain values and the entire time you can see all the values in the inspector and just go modify it there... I will have to do so if push comes to shove but I would much rather prefer to get this version working...

    So what do you think? C# only?
     
  12. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    so you get no compile error at all?
    not even that editor class isn't found or not supported there?

    Editor scripting in unityscript definitely works.
     
  13. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    For the life of me, I cannot add 2 @'s in the same file....

    Code (csharp):
    1.  
    2. @CustomEditor(crInventoryItem);
    3. @import UnityEditor;
    4.  
    Unexpected token: ;.
    Expecting EOF, found '@'

    Code (csharp):
    1.  
    2. @CustomEditor(crInventoryItem)
    3. @import UnityEditor;
    4.  
    Expecting an identitfier, found 'import'


    Code (csharp):
    1.  
    2. @import UnityEditor;
    3. @CustomEditor(crInventoryItem)
    4.  
    Expecting EOF, found '@'

    If I remove the @UnityEditor then it compiles with no problems of any kind. Looking at a custom editor I built before, I never included that UnityEditor and it worked just fine so it seems that is not required for JS.

    Something that I picked up earlier, though...:
    If I add the
    Code (csharp):
    1. @CustomEditor(crInventoryItem)
    2.  
    3. class crInventoryItem extends Editor {
    4.     override function OnInspectorGUI ()
    5.     {
    6.         GUILayout.Label("This is a Label in a Custom Inspector");
    7.     }
    8. }
    directly into crInventoryItem.js then my class is seen as an Editor class and ll my references to transforms and gameObject etc fail. However, when I add this exact same code into a different script (inside the Editor folder) then I get no compilation errors but my class can now reference transforms, no problem.

    One thing I still have to try is to rename the file to crInventoryItem inside the Editor folder also but I expect I am going to get that ambiguous class name error since the filename is already in use...

    Okay, renamed the file to have the same name and it didn't give me any errors but also didn't do anything...
     
    Last edited: Feb 13, 2011
  14. Fenrisul

    Fenrisul

    Joined:
    Jan 2, 2010
    Posts:
    618
    import doesn't need an @
     
  15. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    also import are normally the first thing in the file for readability purpose as they affect the whole rest. they are not "attributes that impact the declaration after them"
     
  16. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Doesn't need the @? That was a shocker!
    Like I said before, though, my custom editors don't include that but work fine.
    Just for the sake of argument, though, I added it and got no errors, just a warning:
    WBC0008: WARNING: Duplicate namespace: 'UnityEditor'

    Okay, so let me start from the beginning and see if maybe I understand this wrong and am expecting a miracle.
    1. I create a normal script file just like any of the hundreds I have made before. No different. Let's call it "fileA.js"
    2. Inside the Editor folder I create a new file and give it any name I wish (since it's JS)
    3. Inside the new file I place the code from the docs and make sure I use "fileA" as both the CustomEditor and Class that gets extended
    4. Inside the new file I use normal EditorGUILayout commands to determine how I want my variables to display
    5. When I click on a prefab that contains 10 components, one of which is fileA, the inspector will show my custom coding instead of the normal list of values...

    Do I understand this all correctly or is there a "No, you idiot, this only works for prefabs that are inside the Editor folder also" or "Oh, for Pete's sake, don't you know it will only work for prefabs with the name 'foo' in the name?" or something else that I am missing?

    In fact, hold on, here is (an extract) one of the files I wanted to do:
    Assets/_Scripts/crInventoryItem.js
    Code (csharp):
    1.  
    2. @script AddComponentMenu("Inventory/Item");
    3.  
    4. var Icon                    : Texture2D;                        //Icon to show in inventory / HUD
    5. var ItemType                : eItemType = eItemType.Weapon;
    6. var ItemKind                : eItemKind = eItemKind.Melee;
    7. var ClothingType            : eClothingType = eClothingType.None;
    8. var Assignable              : eAssignable = eAssignable.All;
    9. var Race                    : eRace = eRace.Human;
    10. var Element             : eElement = eElement.Air;
    11. var SavePrefix              : String;
    12. var Description             : String;                           //General description
    13. var Model                   : Transform;                        //Model to spawn during discard / use
    14. var Level                   : int;                              //This item's power level
    15. var MinimumUserLevel        : int;                              //minimum level to be before item can be used
    16. var Stock                   : int;
    17. var Duration                : int = -1;                         //Should the effects last only a number of rounds/attacks?
    18. var MaxTargets              : int = 1;                          //Affect only one target or multiple targets?
    19. var QtyToCollect            : int;                              //How many are collected to inventory?
    20. var SoundToPlay         : int = 0;                          //Play sound to indicate collection
    21.  
    22. var Value                   : float = 50;                       //Purchase price
    23. var ResellLoss              : float = 0.15;                     //Resell value adjusts purchase price by this value
    24. var SpawnHeight             : float = 0.5;
    25. var RotateSpeed             : float = 180;
    26.  
    27. var ApplyToParty            : boolean;                          //Can this object be applied to the entire party at once?
    28. var collectToParty          : boolean = true;                   //does this go into the party bag or the player bag?
    29.  
    30. var InfiniteUse             : boolean = false;                  //should item qty count down on use?
    31. var SpawnedAsPickup     : boolean = false;
    32. var RotatePickup            : boolean = false;
    33.  
    34. var Sounds                  : AudioClip[];                      //list of sounds associated with this object
    35.  
    36. private var Model_          : Transform;
    37.  
    38.  
    and /Assets/Editor/DataSheet.js
    Code (csharp):
    1.  
    2. import UnityEditor;
    3. @CustomEditor(crInventoryItem)
    4.  
    5. class crInventoryItem extends Editor {
    6.     override function OnInspectorGUI ()
    7.     {
    8.         EditorGUILayout.BeginHorizontal ();
    9.         target.RotateDir = EditorGUILayout.Vector3Field("Speed", target.RotateDir);
    10.         EditorGUILayout.EndHorizontal ();
    11.     }
    12. }
    13.  
    ...what am I doing wrong? I am thinking the fact that I am declaring my own class with the same name as an existing file, that is causing some confusion... If I write
    Code (csharp):
    1.  
    2. RotateDir = EditorGUILayout.Vector3Field("Speed", RotateDir);
    3.  
    It complains that it doesn't know the variable RotateDir but when I do this:
    Code (csharp):
    1. target.RotateDir = EditorGUILayout.Vector3Field("Speed", target.RotateDir);
    2.  
    it is perfectly happy to accept the variable, even though that variable doesn't exist in crInventoryItem ...
     
    Last edited: Feb 13, 2011
  17. Josh_Amsterdam

    Josh_Amsterdam

    Joined:
    Feb 22, 2011
    Posts:
    68
    Did you ever solve this? The problem is this: you should have a normalScript.JS and an editorScript.JS. At the top of the editorScript.JS you write

    @CustomEditor (normalScript)
    class editorScript extends Editor {

    What you have is:

    @CustomEditor (normalScript)
    class normalScript extends Editor {
     
  18. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Actually, I had a harddrive crash and lost the project I used this in. Never got around to redoing that part again. It is still needed, though, so perhaps I should look into this again... Will give this a quick go tonight and see what's up. Thanks.

    I have to admit, though, if my problem all this time is as simple as the fact that editorScript should have had "editorScript extends" instead of "otherScript extends" I am going to be very amazed! I have tried every combination of everything I could think of and even had seasoned vets like Dreamora help me on this and if there was a clear and definite error like that and none of us saw it... Wow..! Again, thanks. I will give this a go and let you know the result :)
     
  19. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I cannot believe it!!!! One word and it works like a charm! Thanks ever so much :)