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

A question about prefabs

Discussion in 'Scripting' started by frankiwinnie, May 8, 2022.

  1. frankiwinnie

    frankiwinnie

    Joined:
    Dec 1, 2020
    Posts:
    26
    Hello everyone,

    I am trying to learn Unity and programming and I have question about prefabs.

    I have a prefab in my scene and a class attached to it. I want to declare a gameobject in that cass script and assign a gameobject which is already in the scene. However as you all know we cannot assign gameobjects which are located in a scene to a prefab.

    So I am thinking to revert the gameobjet which is in the scene also to a prefab and than assign that gameobjects prefab to the actual prefab.

    So my question here is: If in runtime, I acess to the assigned prefab script via GetComponent, will I be able to access that scripts updated version in runtime? For example: if the script has a declared list and that list changes in the runtime, by GetComponent am I going to access the updated version of that list?

    Thanks,
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
    Are you asking if GetComponent<T>() works? It does.

    The update rules at runtime are basically what code you write.

    Otherwise the values will be provided by the usual cascade of Unity initializations, some or all of which may apply in your case:

    Serialized properties in Unity are initialized as a cascade of possible values:

    - what the class constructor makes (either default(T) or else field initializers)

    - what is saved with the prefab

    - what is saved with the prefab override(s)/variant(s)

    - what is saved in the scene and not applied to the prefab

    - what is changed in Awake(), Start(), or even later etc.

    So you want to make sure you only initialize things at ONE of the above levels, or if necessary, at levels that you specifically understand in your use case. Otherwise errors will seem very mysterious.
     
  3. Unrighteouss

    Unrighteouss

    Joined:
    Apr 24, 2018
    Posts:
    599
    In a scene, you can assign game objects as references to prefab instances. It won't update across every instance of the prefab, just the instance that you assign the reference to.

    I think what you want to do is find the references you need dynamically through code. Instead of assigning the references in the inspector, you can use methods like
    GameObject.Find()
    to find the game objects you need to reference at runtime.

    https://docs.unity3d.com/ScriptReference/GameObject.Find.html

    I'm a bit confused by the phrasing here, what are you asking exactly? If you're talking about messing with the master prefab (the one in the project tab), don't do that; you should only ever be referencing prefab instances in the scene.
     
  4. frankiwinnie

    frankiwinnie

    Joined:
    Dec 1, 2020
    Posts:
    26

    yeah thanks for the answer. I get the general idea of your explanation, however those seemed a little bit advanced for me. I am doing things with my way and see how it goes :D. Thanks for the answer anyways.
     
  5. frankiwinnie

    frankiwinnie

    Joined:
    Dec 1, 2020
    Posts:
    26
    Thank you very much for the answer.

    Yes, I know the GameObject.Find(), but as I read from sources that method is considered as an "expensive" transaction and I did not want to use that.

    Sorry that it is highly possible that I could not explain it well both in terms of english and coding explanation :D. Actually I am trying to make a card game and all my cards are different prefabs. Then, I declared my "card deck" as a list in a different script and that script is attatched to an empty gameobject in the scene. So form a script attached to my card prefabs, I want to access to that deck list that is located in a script attached to that empty gameobject. So here, I wanted to ask if I make that object also prefab, then attach it to my card prefabs, when I reach the list declared in it, will I get the first cards initialized in that list even if it has changed while the game is ongoing or can I be able to get the list as changed in runtime (for example when you draw a card the list is going to change).

    I am sorry if still it is confusing, I tried to do my best to explain. It is possible that I may be talking nonsense, so sorry about that.
     
  6. Unrighteouss

    Unrighteouss

    Joined:
    Apr 24, 2018
    Posts:
    599
    It can be expensive, but that doesn't mean you should never use it. You should avoid putting it in the
    Update()
    method, as you don't want to needlessly call it every frame; however, calling it once in
    Start()
    or
    Awake()
    really isn't a big deal. I would highly recommend using this to get the references you're missing.

    I think I know what you're trying to do here, and I don't think it's a good idea. First, just to make sure we're on the same page, what I think you're trying to do is use the "deck list" prefab in your project tab as a reference instead of an instance in the scene. I haven't tested doing this, but you're going to have issues, and that's if it's even possible.

    The best way to do things is to place an instance of the deck list prefab in the scene and get a reference to it using
    GameObject.Find()
    or some other method.
     
    Last edited: May 9, 2022
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
  8. frankiwinnie

    frankiwinnie

    Joined:
    Dec 1, 2020
    Posts:
    26
    Indeed, thats what I wanted to do exactly, so you get me correct. It also appeared me as a bad idea when I first think of it but still I wanted to ask whether it would work or not. For GameObject.Find(); it would work considering the thing that I want to do with my project, however I will first try different solutions and reserve it as a last implementation.

    Thanks for the recommendations and replies.
     
  9. Unrighteouss

    Unrighteouss

    Joined:
    Apr 24, 2018
    Posts:
    599
    Now that I think about it, I actually meant to recommend
    Object.FindObjectOfType<>()
    , not
    GameObject.Find()
    , but it suffers from most of the same issues listed in the linked article.

    My scenes are usually limited to a few hundred objects at most, which doesn't take Unity long to loop through. I was curious just how little of a performance impact using this was, so I created a test. I made a scene with over 20 thousand objects, and ran
    GameObject.Find()
    30 times every frame for objects scattered throughout the scene, and my FPS dropped from 1000 to 120. So obviously, you don't want to be running it 30 times a frame with thousands of objects, however, I really don't see how it could have a significant performance impact in
    Start()
    or
    Awake()
    in your average scene of a few hundred objects.

    Of course, that's not to say that there are no alternatives, and I understand that best practice would avoid using this at all.

    If you're looking for an alternative, there are many ways you can go about giving references to instantiated objects. One way is to simply assign the reference to the object when you instantiate it.

    I'm assuming you're instantiating your card prefabs, and those are what need a reference to the deck list. So, assign the deck list instance as a reference to whatever script is instantiating your cards (it could be the deck list itself, I don't know), then when that script instantiates a card, keep the card as a reference:
    GameObject cardInstance = Instantiate(cardPrefab);
    then since you have a reference to the card, you can then assign the deck list instance as a reference to that card. Just make sure the card has a public variable called "deckList" or something.

    Hopefully that makes sense.
     
    Last edited: May 9, 2022
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
    I get lazy too... I'd be lying if I said I never allowed a GameObject.Find() to ship out in one of my games.

    When I do it though I always feel a little dirty. But I do try to help out by prefixing the object name with
    Find:Player
    so that if I see it in the scene in the future, I will remember not to change it.

    As long as you're not doing it in Update, the issue is rarely performance but rather vulnerability to scene or prefab changes and renames, and the non-obvious nature of errors caused by this. Two years from now if you add an extra arm to your excavator robot and rename the old arm as Arm1 and the new one as Arm2 and a bunch of code starts misbehaving, it can be a little tricky to track it down.
     
    Unrighteouss likes this.