Author Topic: Changing UITextures at runtime  (Read 9928 times)

AtomicBob

  • Jr. Member
  • **
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 69
    • View Profile
Changing UITextures at runtime
« on: June 12, 2013, 03:06:41 PM »
I've run into a bit of a problem using UITextures and hoping someone can point me in the right direction. I'm instantiating 6 copies of a prefab with a UITexture on it. Then I'm choosing which texture should be used on each item, of which there are 2 placeholder textures and the rest are downloaded from the internet. What I'm seeing is things working precisely as I expect for a while, but then after running the game enough times in the editor, changing the texture on one instance causes all of the other instances to change too. I'm trying to prevent this from happening.

I'm guessing it has something to do with the materials being used for the UITextures. Possibly that they all have the same material. However, I thought UITextures generated their materials automatically.

Some extra info: the prefab has the Unlit/Transparent Colored shader assigned. The prefab does not have any texture assigned (though I noticed the prefab actually get's updated based on which textures are set at runtime, odd). I'm using mUITexture.mainTexture = downloadedTexture to change the textures.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Changing UITextures at runtime
« Reply #1 on: June 13, 2013, 03:55:54 AM »
If you duplicate a UITexture within the editor, it will use the same material until you hit Play. If you assign a material instead of assigning a texture, it will always keep using that material (which will then be shared between different UITextures). Otherwise UITextures should have different materials, generated dynamically.

If you run into any issues, just create your own material (mat = new Material()) and assign it to the UITexture instead of setting its mainTexture.

AtomicBob

  • Jr. Member
  • **
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 69
    • View Profile
Re: Changing UITextures at runtime
« Reply #2 on: June 13, 2013, 10:34:19 AM »
Thanks for the response. I'm a bit unclear on a couple of your points.

  • If you duplicate a UITexture within the editor, it will use the same material until you hit Play.
    So if I copy and paste a UITexture from in the hierarchy, they will use the same material. Thus, changing one texture changes all of them. But when I hit play, they will all get separate materials and can be changed independently. Testing bears this out.

  • If you assign a material instead of assigning a texture, it will always keep using that material (which will then be shared between different UITextures).
    What do you mean it will be shared between different UITextures? If I assign a material to one UITexture, will all other UITextures start using that material?

  • Otherwise UITextures should have different materials, generated dynamically.
    Wouldn't instantiating copies of prefab at runtime fall under this category? If so, I shouldn't be seeing all UITextures change when I change just one, correct? As far as I can tell through testing, if a texture and shader are chosen on the prefab, a material is created for the prefab. If the prefab is instantiated at runtime, it keeps it's material and all instances will share the same material. Changing one changes them all unless a new material is explicitly applied.

    Here is where I believe the problem lies. This isn't consistent. When I first load Unity and play my game, every instance acts like it has it's own material. Changing one texture does not change them all. After a few minutes or playing the game a few times (not sure which) they suddenly start acting like all share a material. Changing one texture changes them all.

I've worked around the problem by assigning a new material to each UITexture as they are instantiated, but I still want to understand how this is supposed to work. I can see arguments for instances of prefabs sharing the original material and for having their own, however, changing the texture on one should be consistent. The question is, should changing the texture create a new material so it can't affect other instances, or should changing one instance be able to change all of them? We can obviously assign a new material explicitly when we want to ensure we don't affect other instances, however this could be done automatically with, say, a boolean createMaterialOnChange.

PS While testing this, I noticed other odd behaviors.
  • I have a background panel with a UITexture and a second panel with a z = -1 offset that I'm testing on. I'm getting z fighting issues between the two separate panels. If I put a widget on the second panel, which is above the background, and I offset the z value of the UITextures by -1, the UITextures draw below the background, but only if using the same material and only in the editor. The panel tool reports 3 draw calls.

  • When the material is auto-generated the shader and texture are not shown in the editor. This makes it difficult to test things in the editor, as textures changes must be done in code.

  • Changing the texture or shader of an instance of a prefabbed UITexture at runtime, without giving it a new material, causes the prefab to change. Since the instance of the prefab keeps the same material and apparently it is serialized.
« Last Edit: June 18, 2013, 02:47:30 PM by AtomicBob »

AtomicBob

  • Jr. Member
  • **
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 69
    • View Profile
Re: Changing UITextures at runtime
« Reply #3 on: June 18, 2013, 02:49:37 PM »
Still curious about this if anyone has any insight.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Changing UITextures at runtime
« Reply #4 on: June 18, 2013, 11:58:44 PM »
First thing first -- what version of NGUI are you using?

2. Assigning a material to a UITexture means it will use that material. Other UITextures will only use that material if you duplicate the UITexture, or assign the same material to others manually.

3. Theoretically it should be consistent, but it might depend on what info Unity stores on the prefab. To be safe, clear the texture reference from your UITexture on the prefab since you are assigning them manually anyway.

AtomicBob

  • Jr. Member
  • **
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 69
    • View Profile
Re: Changing UITextures at runtime
« Reply #5 on: June 19, 2013, 09:58:56 AM »
2.6.1e. I haven't gotten around to upgrading to 2.6.3 yet.

Regarding clearing the texture from the prefab, I threw together a test scene and it looks like that may be the solution. I tested this before but it didn't work because the other issues were interfering. Here's what happens:

Case 1: Adding a new UITexture component to the prefab will have no material, texture, or shader. If copies are instantiated at this point, everything behaves correctly.

Case 2: Assigning a texture to the UITexture prefab will auto generate a material and choose a shader. If copies are instantiated at this point, changing the texture on any instance changes every instance, like one would expect.

The unintuitive part:
Case 3: Removing the texture (and/or shader) from the UITexture prefab does not remove the autogenerated material. If copies are instantiated at this point, one would expect things to act like Case 1 above, but instead changing the texture on any instance changes every instance.

The only way to distinguish when Case 3 will occur is when the Material is hidden on the UITexture prefab. There is no way to get back to Case 1 without removing the UITexture component and readding it.

I can't test this in the original scenario at the moment, so I don't know why it would work for a while and then suddenly stop, but I suspect it has something to do with the prefab being updated with changes to instances.