Metal detector issue, not detecting items of multiple tags, please help!

Part of me doesn’t even know how to ask what I want to ask because I’ve been staring at this for so long trying to figure it out.

I’m making a little project, a metal detecting simulator of sorts, there are almost definitely better ways around it but I wanted to challenge myself and see what I could do, and I’ve managed to make it this far without asking for help.
HOWEVER. I have fallen flat on my face!.

Quick rundown, this script is responsible for the digging up of an item, It was all working perfectly not too long ago, but I wanted to try and add a Quality system (Junk, Common, so on), so I made an array (I’ve never used an array before believe it or not so that’s probably the issue!, I’ve read about them and seen them be used in videos, this is by far the most I’ve put into a project.

Anyway, the way it worked before was, the item to be dug up was tagged as “Ring”, if the collider from the detector collides with something tagged as “Ring” The detector would buzz, and you then knew where it was.

I wanted to have different items tagged as different qualities, and to be able to detect all of them, so I’ve tried nesting an If statement (Again not sure if this is the right way, just sort of made sense to me at the time).

So I’m asking for a fresh set of eyes, show me the way!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Trowel : MonoBehaviour
{
  
    public float treasureCount;
    public Text treasureText;
    public bool treasureContact = false;
    private bool keyDown;
    private bool isDug;
    public GameObject spawner;
    string[] quality = new string[]{"Junk", "Common", "Rare", "Treasure", "Quest Item"};
  

    public GameObject dirtPile;

  
  
    void Update(){
      
        treasureText.text =   "Quest item found: " +treasureCount.ToString() ;
      
        keyDown = Input.GetKey("e");

      
          

      
    }
  
  
    void OnTriggerStay(Collider col) 
        {
          
                                
            if(isDug == false && keyDown){
                if(col.gameObject.tag == (quality[0]) || col.gameObject.tag == (quality[1]) || col.gameObject.tag == (quality[2]) || col.gameObject.tag == (quality[3]) || col.gameObject.tag == (quality[4]))
                StartCoroutine(Dig());
                    isDug = true;
                    //print("Is this working?");

                  
            }
              
          


    IEnumerator Dig(){
        yield return new WaitForSeconds(0.1f);
            treasureCount +=1;
                        Destroy(col.gameObject);
                            DirtPile();
                            yield return new WaitForSeconds(1.5f);
                                print("You found a " + col.tag +" Item");
                          

    }


  
}
    void OnTriggerExit(Collider col){
        isDug = false;

    }

    void DirtPile(){
        Instantiate(dirtPile, spawner.transform.position, spawner.transform.rotation);
    }


    void itemPickUpNotification(){

    }
}

Sorry for the wall of text, help is appreciated! :slight_smile:

EDIT: I realise that col.tag should probably be, col.gameObject.tag Still doesn’t work, though.

There are definitely better ways of checking collections such as arrays and list for values vs having to compare against each individual value in one big long statement. Even a simple for loop would be better than having to do a long single if statement with all those or parts.(note, there are other ways as well, but most people know what a for loop is)

That being aside, you haven’t really said what your failure point is. I see you had a print statement that you commented out. Did that work when it was there? Did you add additional debug/print statements? Did you print out the value of col.gameObject.tag?

Staring at code will never get you anywhere except maybe cross eyed. Start throwing debug/print statements all over. Print out values. See what is going on. Remember there is no limit on these and you can remove them when you’re done with them!

Ha! That’s kinda cool. Never occurred to me to make a game out of that.

So here’s some general observations about your Trowel script above: it is doing too much. It appears to have stuff related to treasure and quality and even spawning.

This sort of do-it-all makes it hard to reason about what’s going on.

I would start with just “detector” that the user moves around, and when it is near metallic treasure it goes beep or whatever.

That alone will give you a good start on later on putting in something like a Trowel to dig the treasure up. Successful engineering is often done one step at a time.

And as Brath points out above, use lots of Debug.Log() always, which will give you critical insight into what is happening. Just because code is in your script, doesn’t mean it is actually doing anything useful. Here’s my standard debugging blurb:

You must find a way to get the information you need in order to reason about what the problem is.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

If your problem would benefit from in-scene or in-game visualization, consider using Debug.DrawRay() or Debug.DrawLine() to visualize things like raycasts or distances.

You can also put in Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target.

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

https://discussions.unity.com/t/839300/3

I would say your main issue with your code is that you don’t seem to understand why you may want to indent your code ^^. Your indentation is all over the place and is actually counterproductive. For example it hides the fact that your nested if statement only affects your StartCoroutine call, but not your isDug = true; line as you haven’t used curly braces after your nested if statement. So if your trigger is hitting an object that is not in your array, you would essentially dead-lock your system temporarily because you set isDug to true without calling the coroutine which usually takes care of deleting the object. The simplest fix for this is to move the “isDug = true” line into the coroutine itself.

You also accidentally used a relatively new feature of C# that allows to write nested methods and in this case a nested coroutine. While this is of course quite convenient, it comes with additional hidden overhead as the system has to create a closure which captures the local “col” variable which you use in the coroutine. The usual approach is to pass the relevant information as a parameter to the coroutine when you start it and have the coroutine to be a “normal” coroutine defined at the class level and not as a nested method.

Here’s how your current code would look like if it was formatted correctly:

    void OnTriggerStay(Collider col)
    {
        if(isDug == false && keyDown)
        {
            if(col.gameObject.tag == (quality[0]) || col.gameObject.tag == (quality[1]) || col.gameObject.tag == (quality[2]) || col.gameObject.tag == (quality[3]) || col.gameObject.tag == (quality[4]))
                StartCoroutine(Dig());
            isDug = true;
            //print("Is this working?");
        }
    
        IEnumerator Dig()
        {
            yield return new WaitForSeconds(0.1f);
            treasureCount +=1;
            Destroy(col.gameObject);
            DirtPile();
            yield return new WaitForSeconds(1.5f);
            print("You found a " + col.tag +" Item");
        }
    }

And this is what you probably want to do

    void OnTriggerStay(Collider col)
    {
        if(isDug == false && keyDown)
        {
            string tag = col.gameObject.tag;
            for(int i = 0; i < quality.Length; i++)
            {
                if (tag == quality[i])
                {
                    StartCoroutine(Dig(col.gameObject));
                    break;
                }
            }
        }
    }
    // this is now "outside" the OnTriggerStay method
    IEnumerator Dig(GameObject go)
    {
        isDug = true;
        yield return new WaitForSeconds(0.1f);
        treasureCount +=1;
        Destroy(go);
        DirtPile();
        yield return new WaitForSeconds(1.5f);
        print("You found a " + col.tag +" Item");
    }
1 Like

Those print statements did work, I’m happy to say it’s all working perfectly now! I can’t remember what the issue was unfortunetly, but thanks!

Very informative, I appreciate your input! every little helps me along this journey!

Wow I had never known of Debug.DrawRay/Line, or Debug.Break, those are very useful!.

I have it at a stage where it detect the items, and I can dig them up, UI text tells me the rarity of the item, so i got all of that working (I must say, it isn’t a pretty sight as you can imagine! BUT… It works, that’s good enough for me for now, it’s all a learning journey, and there a little satisfaction of getting it to work my way, no matter how horrific ‘my way’ is haha.

I’ve actually got a couple of friend helping me now, making models / mapping and making UI, Fun little not too serious project for us all to learn from

I’m now trying to figure out how I can get the metal detector to follow the contour of the ground rather than clipping through is as seen here https://i.gyazo.com/a646ec7a993f748630749e99094cbefc.mp4

We’ll get there!

Thanks for the help, it’s much appreciated!

1 Like

Well, one way is to put a raycasting script on it set up to detect the ground in 3 places and align to it.

You’re welcome to study the basics of such a thing here in my TripodFollowTerrain package. The same idea would apply to your detector coil to align it.

You would still have to do something about the pole and hand to really look right… such reverse pushback is called Inverse Kinematics and this package does not handle that part. :slight_smile:

7932280–1013464–TripodFollowTerrain.unitypackage (235 KB)

ooh, that’s a very interesting sounding way to do it… Thanks, i’ll have a gander :slight_smile: