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

Question Changing details on a prefab in a built game

Discussion in 'Scripting' started by TiggyFairy, Jul 22, 2023.

  1. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Soooo. I normally use a script I can
    GetComponent()
    to set details like the Image and background on a button prefab once it is created. However. I've found what feels like a hacky 'easy way' that I'm sure is probably a bad idea: making fields for each of the things I want to set, dragging the prefab to them, and then setting things on the prefab itself before I
    Instantiate()
    it.

    Code (CSharp):
    1. [SerializeField] Image prefabImage;
    2. [SerializeField] cameObject prefab;
    3.  
    4. public void SetImage(Sprite newImage)
    5. {
    6.       prefabImage.sprite = newImage;
    7.       Instantiate(prefab, parent);
    8. }
    This neatly avoids an extra script. HOWEVER. While this works in editor, it feels like a hack. I know you can't actually alter a prefab once the game is built. But I do know you can set values on certain things that just aren't saved once you close, which is fine for this. Nothing needs saving.

    I'm not actually doing this in my game yet, but it is very tempting as it avoids a lot of messing about. So, my question is: how much of a giant mistake would this be?
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    You change the instance, not the prefab although I don't see how the above code relates to the prefab; maybe you made a mistake?
     
  3. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    So, just to check, I shouldn't do this?
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    Ready what I said. I don't see what you're doing in the code apart from changing the sprite of an image and separately instantiating a prefab. You're not changing the prefab in the example at all.

    Is "prefabImage" referencing a prefab component?
     
  5. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    The prefabImage is on the prefab in this example, and the script would set a new sprite to it then create the prefab. Which, in editor tests, results in a new button with the new sprite. It's a very cut down version of what I would actually do.
     
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    If this were me and I wanted to expose very specific parts of the prefab as if it were some kind of black-box with public properties/methods, I'd add that access as a component in the prefab root and put the properties/methods there. Then I can grab that "config" component and just ask it to change what I liked. It has references to the prefab parts which automatically become references to the instance.

    So same as above but without the instantiate, that's done completely separately then this method lives on the "config" component.
     
  7. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Oh. So get the prefab to instantiate a copy of itself? That's pretty clever; I'm going to try that.

    Say, is there an OnCreate() feature in Mobobehaviour? Sort of an opposite of OnDestroy?
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    No, you instantiate as you normally would but then grab the component on the instance and use its properties/methods to configure it i.e. call "SetImage()" etc without knowing where those components or what is required. You can also have more abstract things like "SetAsWarrior()" if you needed without needing to explicitly know what components to touch.

    The prefab knows how to modiify itself. All modify code stays in that single root component. External users after they've instantiated it, just call that component to get changes done.
     
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    This is a component on the prefab, maybe in the root:

    Code (CSharp):
    1. public class WarriorConfig : MonoBehaviour
    2. {
    3.     [SerializeField] Image prefabImage;
    4.  
    5.     public void SetImage(Sprite newImage)
    6.     {
    7.           prefabImage.sprite = newImage;
    8.     }
    9. }
    Set up your prefab by dragging the image component in it, it points to the prefab. When you instantiate your prefab, that field will "magically" point to the instance Image; use "GetComponent<WarriorConfig>" on the instance and call "SetImage" etc.

    There's a bunch of other ways to struct this too.
     
  10. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Ah, okay. So it wouldn't be good to do this?
    Code (CSharp):
    1. public class WarriorConfig : MonoBehaviour
    2. {
    3.     [SerializeField] Image prefabImage;
    4.     public void SetImage(Sprite newImage, Vector3 targ, Transform parent)
    5.     {
    6.           prefabImage.sprite = newImage;
    7.           Instantiate(this.gameObgect, parent, targ);
    8.     }
    9. }
    My plan being to then have the script, which is no longer needed, self destruct on the new gameObject.
     
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    That's the same as you had above. I don't see why you want to change the prefab when you can and should change the instance. Do you perhaps think that somehow this is faster because it isn't.

    Instantitate then configure, profit! :)

    Only very active components such as physics components can potentially have a performance impact if you change them but there's not many and you can always instantiate it inactive by default, configure it then activate it.

    A prefab is essentially a permanently inactive GameObject hierarchy btw.

    Always change the instance.
     
  12. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Sorry, I haven't slept well so I'm kinda slow today :p
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    It's okay, some patterns don't sit well in your mind. Trust me, this weekend is full Unity dev for me too and I'm currently struggling to get my head around something so the forums are my way of taking a break! :)
     
  14. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    I wish you luck! lol My trick is go for a walk or have a shower and just think about it.
     
    MelvMay likes this.
  15. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    In general when instantiating a prefab that's not in the scene you should probably need a reference.

    Code (CSharp):
    1. public GameObject myPrefab;
    2. GameObject newRef=Instantiate(myPrefab);
    3. newRef.transform.position+=Vector3.right;   // changes the position of the prefab in the scene
    Code (CSharp):
    1. public GameObject myPrefab;
    2. Instantiate(myPrefab); // red flag!
    3. myPrefab.transform.position+=Vector3.right;  // changes the position of the stored prefab
    If I see some code on the forums that has a lone Instantiate then I assume somebody has probably made a mistake.
     
    Last edited: Jul 22, 2023
  16. TiggyFairy

    TiggyFairy

    Joined:
    Dec 22, 2019
    Posts:
    413
    Thank you, but that's not code in my game. It's just an example.