Search Unity

Hardcoding the object name works, referencing the name in code doesn't

Discussion in 'Scripting' started by SerNick, Jun 16, 2018.

  1. SerNick

    SerNick

    Joined:
    Jan 24, 2016
    Posts:
    22
    Just found out that the problem that I am having is that when it is calling "interact()" it is automatically referencing the newest object in the scene with the tag "Interactable Object". Any solutions to this?


    I have two objects in my scene, a potion and a health pack. They are set up as individual prefabs, and are identical apart from they both share this script:
    using UnityEngine;


    Code (CSharp):
    1.  public class PickUpItem : Interactable
    2.     {
    3.  
    4.          private static InventoryController inventoryController;
    5.          private static ItemDatabase itemDatabase;
    6.         public static Item item;
    7.         public static Item itemSlug;
    8.  
    9.         public void Awake()
    10.          {
    11.             //Finds all relevant components
    12.             GameObject thePlayer = GameObject.Find("Player");
    13.             GameObject inventory = GameObject.Find("Inventory");
    14.             inventoryController = thePlayer.GetComponent<InventoryController>();
    15.          }
    16.  
    17.      
    18.  public override void Interact()
    19.     {
    20.         InventoryController.ItemSlug = gameObject.transform.name;
    21.        
    22.         InventoryController.GiveItem(InventoryController.ItemSlug);
    23.        // InventoryController.GiveItem(name);
    24.        // Destroy();
    25.     }
    26.      }
    27.  
    The interact class here overrides another which has no code in it.

    Both items have 2D colliders attached, and if "G" is pressed whilst inside this collider, it will (should) interact with that item.

    Collider Script:

    Code (CSharp):
    1.  void update()
    2.       {
    3.        if (interactWithObject == true)
    4.         {
    5.                   if (Input.GetKeyUp(KeyCode.G))
    6.                   {
    7.                         Debug.Log("G");
    8.                       interact.Interact();
    9.                       //interactWithObject = false;
    10.                   }
    11.          }
    12.       }
    13.      void OnTriggerEnter2D(Collider2D other)
    14.         {
    15.             if (other.gameObject.CompareTag("Interactable Object") && interactWithObject == false)
    16.             {
    17.                 interactWithObject = true;
    18.                 Debug.Log("Within Collider: " + interactWithObject);
    19.                 Debug.Log(other.gameObject.name);
    20.             }
    21.         }
    22.         private void OnTriggerExit2D(Collider2D collision)
    23.         {
    24.             interactWithObject = false;
    25.             Debug.Log("Within Collider: " + interactWithObject);
    26.         }
    27.     }
    28.  
    Example of title:
    THIS works:


    Code (CSharp):
    1. public override void Interact()
    2. {
    3.     InventoryController.ItemSlug = "potion_log";
    4.     InventoryController.GiveItem(InventoryController.ItemSlug);
    5.    // InventoryController.GiveItem(name);
    6.    // Destroy();
    7. }
    THIS doesn't...


    Code (CSharp):
    1.   public override void Interact()
    2. {
    3.     InventoryController.ItemSlug = gameObject.transform.name;
    4.     InventoryController.GiveItem(InventoryController.ItemSlug);
    5.    // InventoryController.GiveItem(name);
    6.    // Destroy();
    7. }
    tl;dr: When interacting with one prefab in my scene, it will instead interact with another one which shares nothing, except for one override script which should add it to my inventory and remove it from the scene. When I destroy the item then try and interact with the remaining one, it claims that the script which is shared has been removed from the scene.

    If you need any of the other scripts, let me know

    Thanks!
     
    Last edited: Jun 16, 2018
  2. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    From your description of the problem, the root cause is coming from line 8 in the Collider Script. What is
    interact
    set to, that is to ask, how is it initialised?
     
  3. SerNick

    SerNick

    Joined:
    Jan 24, 2016
    Posts:
    22
    It is initalized in start with
    Code (CSharp):
    1. interact = FindObjectOfType<Interactable>();
    2.  
    Links to a virtual class
     
  4. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    Ok, and what do you want this line to do? It will find an Interactable object but you want it to do something different?
     
  5. SerNick

    SerNick

    Joined:
    Jan 24, 2016
    Posts:
    22
    There is an override class on every item that I want that class to be on. Right now, there are three of them:

    JSON File of items: Dedeserialized -> ItemDatabase -> . into three classes (the one I'm using is PickUpitems) it has an Interact() override. Right now, it is finding the LAST object which I GAVE the interactable tag to. I did tests, and its finding the last item
     
  6. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    So you are adding 3 PickUpItems into the scene and it is finding the last one. What are you wanting to happen instead?
     
  7. SerNick

    SerNick

    Joined:
    Jan 24, 2016
    Posts:
    22
    Interact with the item collider that the player enters. There could be hundreds of these items in the scene, so. I need the player to enter an item collider, press a key and the interact calass is called
     
  8. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    I see. So maybe something like this might help :
    Code (CSharp):
    1. void update()
    2. {
    3.     if (Input.GetKeyUp(KeyCode.G) && m_interactWith != null)
    4.     {
    5.         m_interactWith.Interact();
    6.     }
    7. }
    8.  
    9. void OnTriggerEnter2D(Collider2D other)
    10. {   // If the other object has no Interactable, then m_interactWith will be set to null, which is fine.
    11.     m_interactWith = other.GetComponent<Interactable>();
    12. }
    13.  
    14. void OnTriggerExit2D(Collider2D other)
    15. {
    16.     m_interactWith = null;
    17. }
    18.  
    19. Interactable m_interactWith;
     
  9. SerNick

    SerNick

    Joined:
    Jan 24, 2016
    Posts:
    22
    I actually did something similar. I the end, I used GetComponent in OnTriggerEnter, then used a Foreach loop in my Interactable class to loop through objects. I'm not sure if it is the most efficient way of solving it, but it works
     
  10. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    Why do you need to loop through objects? If your code is similar to mine above, then line 11 just gives you the object reference immediately. No need to loop. :)
     
  11. SerNick

    SerNick

    Joined:
    Jan 24, 2016
    Posts:
    22
    I tried something which was almost identical,
    (in the entertrigger code)
    Code (CSharp):
    1. interact = other.gameObject.GetComponent<Interactable>();
    and it was still retreivig the object which had been tagged last, the only thing that I've found to have actually worked is looping
     
  12. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    That is more than identical- it is basically the exact same thing. :)

    Something is wrong / missing here then- unless, of course, you actually are always colliding with the last item added.

    Just to clarify this point then, when you say "looping", what exactly are you looking for in this loop? How do you know when you have found the right object if it is not the object that you originally collided with?
     
  13. SerNick

    SerNick

    Joined:
    Jan 24, 2016
    Posts:
    22
    All of my interactable objects, are tagged as such. For some reason the LAST object placed in the scene was activated NO MATTER WHAT item it was that I collided with. I think, what was happening is that when I collided with an item, the game didn't know/care which item that I was colliding with, so it just chose the last one. That's why it worked when it was hardcoded in; because it had direction. This is my PickUp script now, which works as I needed it to:

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class PickUpItem : Interactable
    5. {
    6.  
    7.     private static InventoryController inventoryController;
    8.     private static ItemDatabase itemDatabase;
    9.     public static Collider2D theItem;
    10.     public GameObject[] items;
    11.     SceneController scene = new SceneController();
    12.     [SerializeField]
    13.  
    14.     public void Awake()
    15.     {
    16.         //Finds all relevant components
    17.         GameObject thePlayer = GameObject.Find("Player");
    18.         GameObject inventory = GameObject.Find("Inventory");
    19.         inventoryController = thePlayer.GetComponent<InventoryController>();
    20.         items = GameObject.FindGameObjectsWithTag("InteractableObject");
    21.         scene = GetComponent<SceneController>();
    22.         //InventoryController.GiveItem();
    23.  
    24.     }
    25.     public override void Interact()
    26.     {
    27.         //Loops through all prefabs in the scene, finding the one which matters all of the perameters:
    28.         //The one which is in the collider
    29.         //The one which G is pressed on
    30.         foreach (Object InteractableObject in items)
    31.         {
    32.             Debug.Log("This object is: " + gameObject.name);
    33.             InventoryController.GiveItem(gameObject.name);
    34.             Destroy(gameObject);
    35.             //GetList();
    36.             return;
    37.         }
    38.        
    39.     }
    40. }
    This will find the item that I am colliding with, with no issues.
    I found this problem because there is an item in my scene, which doesn't have the above script attached, instead it has a scene controller, which was, for some reason auto activating the picup script
     
  14. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    The thing is, that is quite simply not the way Unity works. When 2 objects collide, you are specifically given the other object, not just some random object that is vaguely like that other object in some arbitrary way. :)

    From the code in comment #13 above, PickUpItem is an interactable object. That suggests that something else is the thing that will interact with it (the player maybe?). This makes it strange that a PickUpItem should then be looping through other interactables. It just looks like the logic is back to front there. Presumably the player is not an interactable object.

    To clarify, the script I placed in post #8 should be attached to the player. It will directly link the player to the interactable object that it has collided with (assuming there is no overlap of colliders of interactable objects as that code can handle at most a single collision per frame). No looping, no arbitrary picking of the first, last, middle or nth object or anything else. Nice and simple. :)
     
  15. SerNick

    SerNick

    Joined:
    Jan 24, 2016
    Posts:
    22

    Huh. So, I've just replaced my pickup code with basically what you are suggesting - removing the loop and just letting do its thing, and it IS actually working. I'm gonna be honest. I don't know if I've made just a small change this time, or wharever but it was not working last night/this morning.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PickUpItem : Interactable
    4. {
    5.     private static InventoryController inventoryController;
    6.     SceneController scene = new SceneController();
    7.  
    8.  
    9.     public void Awake()
    10.     {
    11.         //Finds all relevant components
    12.         GameObject thePlayer = GameObject.Find("Player");
    13.         GameObject inventory = GameObject.Find("Inventory");
    14.         inventoryController = thePlayer.GetComponent<InventoryController>();
    15.         scene = GetComponent<SceneController>();
    16.     }
    17.     public override void Interact()
    18.     {
    19.         InventoryController.GiveItem(name);
    20.         Destroy(gameObject);
    21.     }
    22. }
    The code is much simpler now, so I can't complain too much. I do get what you were saying, it wasn't making sense to me, really, but that's just the way it was working for me earlier.
    Thank you for your help - I'm going to go and sit in a corner and refrain from looking at anymore code!
     
    Doug_B likes this.
  16. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    Excellent news- glad you got it working.

    You are welcome. Enjoy your well earned rest. :)
     
  17. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    If I might make a small suggestion...Rather than use 'Find' for who knows how many objects, you could get the inventory controller when you do the interaction.
    I read some of the earlier posts, but not enough at the moment to make an exact version of the code.. however the idea would be to :
    a) pass the inventory controller to the interactable object, if it's a pickup, so it can use that reference to add it.
    b) return the item/name you want to add from the interactable object to the player's script (which could have a cached referenced) to use it that way.
     
  18. SerNick

    SerNick

    Joined:
    Jan 24, 2016
    Posts:
    22

    I've totally removed those 'Find' methods. They were completely uneccesary, as it turned out :)