Hitting Dropper dosn't add to hits

after updating Object Hit to check to see if an object allready is tagged “Hit” before counting as a hit - when I hit the dropper it dosn’t add to the hits variable.
The dropper turns red when hit but the number of hits dosn’t get updated.
Is there a parameter on the Dropper 3D object I need to set in for it to work?
Even copied the scripts from github to be sure it wasn’t in the code.

Hi Cmkaa,

Welcome to our community! :slight_smile:

Could you please share screenshots of what you see in Unity? Select the same game objects as before. Maybe you simply selected the wrong game object.

Since you wrote that the dropper turned red when it got hit, your code appears to be working because game objects do not do anything themselves in Unity.

Hi Cmkaa

I had the same issue, the reason this is happening is that for some reason the evaluation order of the Scorer and ObjectHit scripts is reversed for the Dropper object. I’m pretty sure it is to do with the order we added the components. The simplest solution I found is select you’re dropper object, right click on the Rigidbody component and select remove.

Next add a new Rigidbody component, ensuring its Use Gravity option is not checked as the dropper script sets this programatically.


Hopefully this will solve your poblem.

Let me know if you still have issues :slight_smile:

2 Likes

Hi.

It’s also possible to put the code in the ObjectHit class in a new public function.
Then you can call this function from the Scorer class.

Eg:

public void PlayerHit()
    {
        if(gameObject.tag != "Hit") {
            GetComponent<MeshRenderer>().material.color = Color.black;
            gameObject.tag = "Hit";
        }
    }

Then call this from the player’s OnCollisionEnter method with:

other.gameObject.GetComponent<ObjectHit>().PlayerHit();

An added advantage is that you don’t have to check for the “Player” tag, because you only call the method from the Scorer script, which is on the player.

Note:
If you use this way of calling the function on ObjectHit, make sure you can only hit objects with this script. You could also use this null-safe version for calling the method:

ObjectHit objectHit = other.gameObject.GetComponent<ObjectHit>();
if (objectHit != null) {
    objectHit.PlayerHit();
}
1 Like

Thank you!

This was my exact issue. But could you please explain its cause in more detail?

Hi zak

Welcome to the community :smiley:

Just so you’re aware I’m pretty new to Unity myself so my understanding may be somewhat limited, however I will do my best to explain the cause of this issue as I currently understand it.

So behind the scenes Unity keeps track of all the components within your game.

These components are things such as the Rigidbody, Box Collider, even the C# scripts we add are components. As an example if you click Add Component in the inspector view (see image) you get a list of potential components Unity provides, there are quite a few haha. :sweat_smile:

I believe that as we add components, Unity stores them behind the scenes in a sort of list, the order we add them is the order they are placed into that list.

When you run your game Unity looks at all the components we added (including the scripts and Rigidbody’s) and evaluates them in order, here lies the issue. :space_invader:

Our ObjectHit and Scorer scripts both hook into the collision system via the OnCollisionEnter method. When a collision occurs we want the Scorer script to be evaluated first to increase our score, then the ObjectHit script to be evaluated to colour the object and mark it as hit.

The problem is the evaluation of these two scripts is the wrong way round for the dropper object. The Scorer script (which is attached to the player object) is running after the ObjectHit scrip (attached to the Dropper object).

So why is this the case for the Dropper and not the original Obstacle we created?

This is where that Rigidbody component I got us to remove and re-add comes in…

For the OnCollisionEnter method to work the object it is attached to must have a Rigidbody, this is because OnCollisionEnter only gets called when a Rigidbody starts touching another Rigidbody. You can read more about this method here on the Unity Documentation.

Both our Player object and the Dropper have Rigidbody components (which are in the list Unity keeps behind the scenes that I mentioned earlier).

For some reason when we added the Dropper its Rigidbody is getting evaluated by Unity before the Player Rigidbody. This means the OnCollisionEnter in the ObjectHit script (attached to the Dropper) is been run before the OnCollisionEnter in the Scorer script (attached to the Player). This is why the “Hit” tag is being set first, then the if statement in the Scorer script is returning false and not increasing the score.

The fix works because by removing the Dropper Rigidbody and adding it again we are forcing it to be the last component added to Unity’s list, meaning it gets evaluated after the Player Rigidbody. This ensures the Scorer script (attached to Player) is evaluated before the ObjectHit script (attached to Dropper).

Sorry this explanation was a bit long, I hope it helps with your understanding :slightly_smiling_face:

2 Likes

I also had this “problem” with my Dropper not counting as a hit.

Rick is probably just lucky that he didn’t have this happen to him as well. This is actually an excellent example of working with a game engine and not knowing everything about it yet. We are going to bump into these types of issues alot until we start asking questions like: “when does a script actually excecute and in what order?”. Learning what not to do is part of becoming a master of something and probably takes longer to learn than just what to do.

In this case as @orangeoz explained, we have two independent scripts on two independent gameobjects calling the same OnCollisionEnter() method. How do we know what order they will be called? Can we even guarantee a particular order? This usually doesn’t matter except that our two scripts are changing/using the same game state variable/object, and thus we have a sort of “race condition”. Which script will win the race, and it does matter to our program which one that is.

@orangeoz’s solution of deleting and re-adding components can work, but it isn’t the proper way. What if we had hundreds of different components and scripts all running willy-nilly and bugs just popping up and going away as we fiddle with things over and over again?

The solution is to be very mindful of how game state is changed and by whom. If the Scorer script is worried about whether the object has already been hit, then it should be up to the Scorer to change that state, but only after it has scored the hit. That means there is nothing between scoring the hit and changing the tag to interfere with the order they happen.

The proper way is to remove gameObject.tag = "Hit"; from the ObjectHit script and put it into Scorer.

if(other.gameObject.tag != "Hit")
        {
            other.gameObject.tag = "Hit";
            hits++;
            Debug.Log("Hit Count: " + hits);
        }
4 Likes

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms