Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Create a copy of (not reference to) Prefab/GameObject in script.

Discussion in 'Scripting' started by AlanGameDev, Jun 30, 2014.

  1. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Hello there.

    I have a reference to a prefab that is used for instantiation. I need to modify the PropertyBlocks of the SpriteRenderer in that prefab, however, first I need to somehow get a copy of that prefab I think, because my attempts to modify that failed miserably and if I do for example SpriteRender.enabled=false, it will change the prefab itself.

    I know something like Resources.Load() will make copies of the stuff in memory, however, I don't want to use that.

    Any help is appreciated. Thank you in advance.
     
  2. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    I think even if you manage to do that, this functionality will fail when it comes to game release.
    Why don't you instantiate prefab, then change needed property in instantiated gameObject.
    Then you can use this gameObject as a prefab for further cloning.
     
  3. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    I think that's exactly what he is asking, but has no idea how to say it properly.

    Code (csharp):
    1.  
    2. GameObject instance = GameObject.Instanciate(myPrefabReference) as GameObject;
    3.  
     
    Greviouss likes this.
  4. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Thank you guys, that's not what I want though. Instantiate() doesn't work as Resources.Load() because the object is instantiated into the scene, as opposite to Resources.Load() in which the object exists only in memory.

    However, even using Resources.Load(), the thing doesn't work, I've tried:

    Code (JavaScript):
    1. var bh : GameObject = Resources.Load("bh4");
    2.  
    3. var block : MaterialPropertyBlock = new MaterialPropertyBlock();
    4. block.AddTexture("_MainTex", sceneText);
    5.  
    6. bh.GetComponent(SpriteRenderer).SetPropertyBlock(block);
    7. Instantiate(bh, bh.transform.position + Vector2(Random.Range(-5f,5), 0), Quaternion.identity);
    and

    Code (JavaScript):
    1. myMaterial.SetTexture("_MainTex", sceneText);
    2. bh.GetComponent(SpriteRenderer).material = myMaterial;
    and

    Code (JavaScript):
    1. bh.GetComponent(SpriteRenderer).sharedMaterial = material;
    and none of them works, however, if I set the MaterialPropertyBlock AFTER instantiating the GameObject it works:
    Code (JavaScript):
    1. var x : GameObject = Instantiate(bh, bh.transform.position + Vector2(Random.Range(-5f,5), 0), Quaternion.identity);
    2. x.GetComponent(SpriteRenderer).SetPropertyBlock(block);
    3.  
    but that's not what I want, I don't want to set the property block every time I instantiated the object, I want it to have that property block.
     
    ow3n likes this.
  5. TylerPerry

    TylerPerry

    Joined:
    May 29, 2011
    Posts:
    5,577
    Why don't you want it in the scene? Cant you just Instantiate the GameObject, then add the things, then instantiate the clones and disable the original?
     
  6. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    For Unity, there's no real difference between "in memory" and "on the scene". Item that you want "in memory" simply have some hide flags set and are disabled.
     
  7. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Well, even when the object is on the scene, the instances are 'reverted' to the prefab and do not represent the object I'm instantiating from... pretty crazy stuff if you ask me:

    Code (JavaScript):
    1.     var block : MaterialPropertyBlock = new MaterialPropertyBlock(); //create a property block
    2.     block.AddTexture("_MainTex", sceneText);    //set its texture (texture assigned in inspector)
    3.     var bh : GameObject = Resources.Load("bh4"); // load prefab in memory
    4.     var x : GameObject = Instantiate(bh, Vector2(0,0), Quaternion.identity); //instantiate prefab at 0,0 and assign to x
    5.     x.GetComponent(SpriteRenderer).SetPropertyBlock(block); //set the property block of x so its texture changes
    6.     Instantiate(x, Vector2(1,0), Quaternion.identity); //instantiate x, however, the texture gets reverted to the old one
    Interesting. So, for example, a pooling system would simply enable and disable the objects? Thank you.
     
  8. TylerPerry

    TylerPerry

    Joined:
    May 29, 2011
    Posts:
    5,577
    It might be something to do with the MaterialPropertyBlock, I'd just use a Material. this works fine:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Example : MonoBehaviour
    5. {
    6.     public GameObject prefab;
    7.     public GameObject instantiatePrefab;
    8.     public Texture2D texture;
    9.  
    10.     void Start ()
    11.     {
    12.      
    13.         instantiatePrefab = GameObject.Instantiate (prefab) as GameObject;
    14.         instantiatePrefab.GetComponent<SpriteRenderer> ().material.SetTexture("_MainTex", texture);
    15.  
    16.         for (int i = 0; i < 100; i ++)
    17.         {
    18.             GameObject.Instantiate (instantiatePrefab);
    19.         }
    20.     }
    21. }
    But I'm pretty sure that makes like lots of materials behind the scenes. So you probably want something like:
    Code (CSharp):
    1.  
    2. public class Example : MonoBehaviour
    3. {
    4.     public Material mat;
    5.     public GameObject prefab;
    6.     public GameObject instantiatePrefab;
    7.     public Texture2D texture;
    8.  
    9.     void Start ()
    10.     {
    11.      
    12.         instantiatePrefab = GameObject.Instantiate (prefab) as GameObject;
    13.         instantiatePrefab.GetComponent<SpriteRenderer> ().material = mat;
    14.  
    15.         for (int i = 0; i < 100; i ++)
    16.         {
    17.             GameObject.Instantiate (instantiatePrefab);
    18.         }
    19.     }
    20. }
    And then just make the material how ever and just assign it.

    (Sorry, I didn't see you were using UnityScript but I'm to tired to do the conversion but it's pretty much the same)
     
  9. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    Docs about SetPropertyBlock:
    If it's not duplicated, then could be it's not cloned properly with Instantiate method.
     
  10. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Thank, but it doesn't work... I think it works for other stuff but not for sprites, here is the code I've used:
    Code (JavaScript):
    1. #pragma strict
    2.  
    3. var mat : Material; //assigned in inspector (default sprite mat)
    4. var prefab : GameObject; //assigned in inspector
    5. var instantiatePrefab : GameObject;
    6. var texture : Texture2D ; //new texture assigned in inspector
    7.  
    8. function Start ()
    9. {
    10.  
    11.     instantiatePrefab = Instantiate(prefab);
    12.     instantiatePrefab.transform.Translate(Vector2(1,0));
    13.     instantiatePrefab.GetComponent(SpriteRenderer).material = mat;
    14.    
    15.     var newInst : GameObject = Instantiate(instantiatePrefab);
    16.     newInst.transform.Translate(Vector2(1,0));
    17.    
    18.     //both sprites have the same texture as the original prefab
    19.     //not the 'texture' assigned in inspector
    20.    
    21. }
    So, is there a way to change the sprites textures without having to change the PropertyBlock for each object?

    I just want to re-use a portion of my texture atlas. This is something quite basic if you ask me. Half of my texture atlas is the same for all levels of my game (chars, weapons, etc), but the other half changes, if I use 2 atlases, the draw calls go crazy because the objects are not ordered in a way that allows them to be batched efficiently, so the only option is to simply re-use a portion of my sprite atlas for each level, however, there's no way in hell to do that... Sprite.Create could be an option, but there's no way to get the pivot point of the sprites, changing the material doesn't work also because the texture of the sprite is independent... you can change the materials as much as you like but you're not changing the actual 'sprite', just the shader...

    The only way to actually change the texture of a sprite I could find is by changing the mainTexture of the material it's using, because I guess when you assign a material and a 'sprite' to a SpriteRenderer, Unity creates a new material that consists of the shader of your material and the texture of the sprite. The problem though is you can't instantiate from that, so you have to set the PropertyBlock each time you instantiate an object. That's not actually a problem for some of my objects, because I could simply loop all the objects in scene, which includes the bullets and particles in my objects pools, however, for some things like bullet holes I can't use object pools because as the character can't turn back, I'm destroying everything behind it in order to free memory and alleviate physics processing (checking colliders that are not going to be used), and as the bullet holes are parented to the objects, they are destroyed also. I know I could make a very sophisticated pooling system for that but that's not what I want. I just need a 'decent' way to re-use half of my spritesheet (/texture atlas) without having to map the whole shebang all the time... something like TexturePacker could do the job I think because I could simply change all the sprites based on their names, but TexturePacker won't allow me to set the pivot points for sprites.
     
  11. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    Try like this
     

    Attached Files:

  12. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Thank you, but that's not what I want, I don't want to change the sprite itself, but the texture (atlas) being used by the sprite, so, for example, I have a texture atlas of a character with some clothes and another texture atlas with the same coordinates but the clothes have other colors, how can I keep the same prefabs and stuff and only change the texture atlas being used by the sprites? PropertyBlock works for that, but as I said, when you instantiated from that it gets reverted back to the original texture.
     
  13. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    Ah that's simple too, but you need to define your own sprites I think.
    Try this.
     

    Attached Files:

  14. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    Make some XML description for your predefined sprites or something and then just build sprites in code from your definition in XML or whatever for every texture you need.
     
  15. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Here you go
     

    Attached Files:

  16. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Yeah, I could do that, however, I'd need to make a sprite editor and stuff, the purpose of using a game engine is to avoid that kinda trouble...
    I could re-create the sprites if there was a way to get the pivot of the sprites from the spritesheet, but that's not possible, you can set the pivot, but not get it.

    Honestly, Unity is too much trouble for 2d games... I thought it would be a good idea to use it since I know it already but it's not a good option... it simply sucks to use the z-axis or those 'layers' for draw ordering, you always end up with problems that you have no idea how happened (how that object ended up in z 2312?) and the thing is not very performant compared to the other 2d-only engines.

    I just hope I can finish this game, even if I have to workaround the whole shebang and duct tape the entire thing, and I'm surely going to give the other alternatives a try for my next 2D projects.
     
  17. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    You can always set texture after you instantiate object. Even if there are many of them it shouldn't be a problem.

    For my 2D projects I still use 3D Unity limited to X and Z axis. 2D in Unity is very fresh and still in development. It's already good for simple games, but when it comes to lots of sprites, especially with colliders... Performance is poor compared to 3D.
     
  18. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Yeah, there's another problem with that also... whenever you deactivate and activate the gameObject, the texture is reverted back to the old one... I think the only 'safe' way is actually creating new sprites from the other texture atlas, but that's not possible because you can't get the pivot points.

    I wonder if *no_one* is reusing sprites in Unity... specially for mobile, that's a must-have feature imho.

    About performance, Unity is quite decent for 3D games, however, there are some things I simply can't understand... in Unity I get stutters with 2k rigidbodies (boxes), while in Blender (Game) I can have like 8k rigidbodies and the thing is still much more fluid, even with the same physics settings (when possible) and still, the physics in Blender are less jumpy (more accurate). I know Blender uses Bullet and Unity Physx, but I thought the main advantage of Physx was the performance. The tests were performed on a modern Haswell CPU with the camera facing away from the meshes.

    I just hope there's a way to solve the sprites problem easily... I mean, perhaps I could simply make a program to change the metadata of the spritesheets directly... The problem is when you update the thing... you have to go through all the spritesheets and somehow determine what should be changed... it's going to be a pain in the ass.
     
  19. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    Whats the problem with pivot? When you create a sprite there is a Vector2 parameter to setup a pivot. It doesn't have presets, but I guess calculating center or corner is not a problem.

    PhysX is cool with top NVIDIA GPU, but using software emulation on CPU (as it does in Unity) PhysX is not the best solution.
    Bullet plugin for Unity seems to be much better.
     
  20. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    The problem is you can't 'get' the pivot point of a sprite using textureX to re-create that sprite in textureY.
     
  21. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    I'm trying to replace all the sprites on the scene but I'm experiencing a major problem... for some reason the changes are permanent; when I stop the game all the objects keep the other sprite in the editor.

    I'm stuck on this game with so many issues :(. No matter what I try it never works as expected...

    Code (JavaScript):
    1. for (var spr : SpriteRenderer in Resources.FindObjectsOfTypeAll(SpriteRenderer))
    2.     spr.sprite = spridx;
     
  22. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    It's stored in "bounds" property, but unfortunately it's already in usable for unity form. So might need some math to convert it into pivot usable for "Create" method.