Search Unity

Object instance settings applied in designer mode...

Discussion in 'Scripting' started by Gphysono, Nov 19, 2015.

  1. Gphysono

    Gphysono

    Joined:
    Jul 24, 2015
    Posts:
    21
    I apologize if this is a stupid/repeated question.

    I'm not new to programming, but I am new to the class libraries of Unity. I’m trying to write a script that allows the user to set the model color at design time.

    I have the public color variable to hold the users selection and code that drills into the models material color and set it to the users selection. But the code is in the start event so the color change isn’t applied until runtime.

    I’m using color coding to distinguish between multiple instances of the object so I’d really like to be able to drag the model into the scene, set its color and have it applied immediately.

    What’s the technique for doing that?

    Thanks!
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    You can use the [ExecuteInEditMode] attribute to make Update run in the editor. Just be aware that this runs Update (which you only sometimes want to be the same code as during play mode), so when I use it, I make sure that Update looks like this:
    Code (csharp):
    1. void Update() {
    2. if (Application.isPlaying) {
    3. }
    4. else {
    5. //editor code here
    6. }
    7. }
    Just so I don't accidentally start putting code intended for the editor inside something that'll also get run during play mode, and vice versa.

    And be aware that Start/Awake won't necessarily be called when this is run in editor mode, so if you have to do stuff like caching GetComponent calls, make sure this is done in Update as needed rather than just in Start.
     
  3. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    When you do this, do you need to worry about this affecting all instances using the same material? Will it only affect that single instance of the material?
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Renderer.material is pretty clever and handles that for you - if other renderers use that material, then it will clone it and only change the clone. If you want more control (and in this case, there's a pretty good chance you do - it'll be messy to have a bunch of cloned materials floating around in the editor version of your scene), you should store the material within your script, and then manually clone it, change its properties, and assign it to the material in your Update. (This is one of those times you really want to make sure that this isn't being done at runtime - if you do it the straightforward way, it'd create a ton of garbage - which matters little in the editor, but will cause a bunch of framerate lag in runtime.)
     
  5. Gphysono

    Gphysono

    Joined:
    Jul 24, 2015
    Posts:
    21
    So if I understand correctly:

    By changing the material color the way I’m coding will cause that material to be cloned. But what if I’m reusing the same set of colors?

    I’m using mannequin models as stand-ins for prototyping. Green mannequin is good guy, red is bad guy, blue is neutral, that sort of thing. So there may be many mannequin instances, but only a small set of unique colors.

    So would all the like colored ones share a common material? (all the green ones share the same green material, all the red ones share the red material) or would each model instance have their own material instance?
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    They'd each have their own material instance. Which isn't the worst thing in the world, but it would be a bit of a waste of memory.

    In that case you'll want to control the cloning of materials yourself. You could create a static Dictionary<Color, Material>, and lookup the material on demand.
    (make sure you import System.Collections.Generic at the top)
    Code (csharp):
    1.  
    2. static Dictionary<Color, Material> materialsByColor;
    3. public Material baseMaterial; //assign this in the inspector
    4. public Color myColor = Color.white;
    5. void Update() {
    6. if (Application.isPlayMode) {
    7. }
    8. else {
    9. if (baseMaterial != null) {
    10. if (materialsByColor == null) materialsByColor = new Dictionary<Color, Material>();
    11. if (materialsByColor.ContainsKey(myColor) ) {
    12. renderer.sharedMaterial = materialsByColor[myColor];
    13. }
    14. else {
    15. Material newMat = Instantiate(baseMaterial) as Material;
    16. renderer.sharedMaterial = newMat;
    17. materialsByColor[myColor] = newMat;
    18. }
    19. }
    20. }
    21. }
    (untested code, of course)

    One potential pitfall is that if you change colors, then "drag" your color across the color wheel like we always do, it will create a bunch of materials that are never seen again but are retained in that dictionary. However, the static dictionary will be cleared out every time scripts are recompiled or the editor restarts (and, accordingly, won't persist into a build), so it probably doesn't matter.

    If you want to change the base material, you could propagate those changes to the clones by simply setting the dictionary to null. (You could do the same to clear out those unused clones.) It'll re-initialize it and all the instances will re-do their "make a copy or find the copy" procedure, and then garbage collection should clean up the Materials.
     
  7. Gphysono

    Gphysono

    Joined:
    Jul 24, 2015
    Posts:
    21
    Hmmm!!!

    So I’m starting to realize I should rethink the color selection implementation. Instead of the full blown color selector, maybe a list of predefined colors so you can’t end up with a ton entries containing unused shades of a color.

    Or maybe not even a color at all: a role list like “Good”,”Bad”,”Neutral” and let coding logic assign color based on that.

    However it gets reworked, I thank you for sharing your knowledge and code. I had no idea my implementation would have affected memory that way!
     
    Nigey likes this.