Search Unity

Unable to swap GameObjects properly (and possible Unity 5 bug)

Discussion in 'Scripting' started by Valasty, Jun 29, 2015.

  1. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    Hello all!

    I'm having a lot of headache figuring out this one...
    So, I'm build a JRPG battle style, and this has an option to swap characters in battle with characters in reserve during it. The code is basically this:

    Code (CSharp):
    1. IEnumerator RetreatAction () {
    2.  
    3.         GameObject ReserveChar;
    4.         ReserveChar = CharactersInParty[5];
    5.         CharactersInParty[5] = characters[1].gameObject;
    6.         CharactersInParty[1] = ReserveChar;
    7.         yield return new WaitForSeconds(0.2f);
    8.         Destroy(characters[1].gameObject);
    9.         characters[1] = ((GameObject)Instantiate(ReserveChar, new Vector2(characters[1].transform.position.x, characters[1].transform.position.y), Quaternion.identity)).GetComponent<CharacterStats>();              
    10.     }
    What's happening above is basically swapping spots. CharactersInParty is a list of GameObjects Prefabs representing the total of characters that can be used, and "characters" are the 4 characters currently instantiated in battle (as CharacterStats class, not GameObjects). So I need to swap places in CharactersInParty, and I also need to instantiate a new GameObject (with CharacterStats component) in the replaced "characters".

    Code is working fine, but when Destroy triggers, it's not only destroying characters[1], it's also destroys CharactersInParty[5], and I can't figure out why! CharactersInParty is not touched anywhere else in the code.



    Above you can see the bug I mentioned, element 5 should be updated to null on Inspector as soon as Destroy triggers, but it does only if I click it (took me an hour to notice it...). In Debug.Log I'm able to see it as null correctly.

    If someone could please shed some light on this, I would appreciate it.

    Thank you in advance :)
     
  2. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    You set characers[1] to ReserveChar (which is CharactersInParty[5]), then destroy it (which would make it null) and then Instantiate using ReserveChar which is now null.
     
  3. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    I'm Destroying characters[1] AFTER I set up it in CharactersInParty[5], so it wasn't null at the point of setup.

    If I perform the same debug right after the code above, there's nothing null, everything is setup perfectly. By the end of the frame, when Destroy actually occours, then CharactersInParty[5] becomes null.

    Why is Destroy targeting CharactersInParty[5] AND characters[1]?
     
  4. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    You set CharactersInParty[5] to characters[1] and then destroy characters[1]. GameObjects are reference types so setting it to some index in an array doesn't copy it or anything.

    If you do this then you should get True in your console
    Code (csharp):
    1.  
    2. Debug.Log(CharactersInParty[5] == characters[1].gameObject);
    3.  
     
  5. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    I think you are confused about reference types and value types.
    GameObject is a class and classes are reference type. That means when you assign one into a variable there is no "point of setup" and there is no second object created. The only thing that is done is to copy the reference of a single object.

    Code (CSharp):
    1. IEnumerator RetreatAction () {
    2.         GameObject ReserveChar;
    3.         ReserveChar = CharactersInParty[5];
    GameObject is a reference type. That means CharactersInParty[5] is not actually a GameObject, it is a memory address which "points" to the GameObject. When you assign it to ReserveChar, the GameObject is not duplicated, instead the memory address is copied such that both variables now refer to the same object.

    Let's remove some of the code so it's clear what is happening:
    Code (CSharp):
    1. CharactersInParty[5] = characters[1].gameObject;
    2. // ...
    3. Destroy(characters[1].gameObject);
    4. // ...
    Since both variables refer to the same object, it should be clear that CharactersInParty[5] is going to get destroyed.
     
    Kiwasi likes this.
  6. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    @Eisen, that makes sense, I imaged it could be something like this.

    But then, how can I destroy the GameObject on the scene without also destroying it's reference?

    I suppose I could assign the Prefab to CharactersInParty (instead of the Instatiated GameObject), but I'm not a big fan of pulling Prefabs directly from code unless absolutely necessary.
     
  7. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    I think you've got your object lists mixed up. I can't be certain from just this snippit but I'm guessing CharactersInParty is supposed to be a list of templates which you copy into the battle. In that case, your mistake is on line 5.
    Code (CSharp):
    1. IEnumerator RetreatAction () {
    2.  
    3.         GameObject ReserveChar;
    4.         ReserveChar = CharactersInParty[5];
    5.         CharactersInParty[5] = characters[1].gameObject;
    6.         CharactersInParty[1] = ReserveChar;
    7.         yield return new WaitForSeconds(0.2f);
    8.         Destroy(characters[1].gameObject);
    9.         characters[1] = ((GameObject)Instantiate(ReserveChar, new Vector2(characters[1].transform.position.x, characters[1].transform.position.y), Quaternion.identity)).GetComponent<CharacterStats>();          
    10.     }
    You are overwriting the template item with the character in the battle. I think line 5 should be:
    Code (CSharp):
    1. CharactersInParty[5] = CharactersInParty[1];
    That said, I find it pretty strange that you need to swap the order of your characters at all..
     
  8. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    You're absolutely right! I should have thinked about that :)

    Thank you sir, appreciate it, everything works now :)