Adding RigidBody2D to enemy disables "Take Damage" function?

Hi Everyone,

I’ve hit a problem that I need to fix before I can go any further.

If player collides with enemies without a RigidBody2D, player damage function works fine (player takes damage).

If I add a RigidBody2D to the enemy, the Take Damage function stops working partially — some of the function is still triggered (such as a “damage blink”).

Here’s a clip of this behavior.

  1. The first enemy has a rigidbody2d, the second one doesn’t.
  2. Notice how on the first enemy, when colliding, the health bar (red bar) does not go down. But on the second enemy, it does go down.
  3. BUT, in both instances, the damage blinking is working. So, just the “Take Damage” portion isn’t.

Here’s the Monster Damage code, followed by the PlayerHealth code…

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

public class MonsterDamage : MonoBehaviour
{
    public int damage; // declare a public integer called damage
    PlayerHealth ph; // declare the PlayerHealth  for assignment

    void Awake()
    {

        ph = GameObject.FindWithTag("Player").GetComponent<PlayerHealth>(); //we assigned the ph field from above
    }


    void start()
    {
        damage = 1; // set the damage for this monster
    }


private void OnCollisionEnter2D(Collision2D collision) // name of this private void function, it's a collision event
    {

        if (ph.noDamage == false) // if the "NoDamage" state from PlayerHealth is false...
        {

            if (collision.collider.gameObject.tag == "Player") // and collision with "Player"
            {
                ph.TakeDamage(damage); // Do the TakeDamage function
                return; // return
            }
       }

       else  if (ph.noDamage == true) // if the "NoDamage" state is true
        {
            ph.TakeDamage(0); // 0 damage is taken
        }

    }

}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerHealth : MonoBehaviour
{
    PlayerMovement pm; // declare the playermovement script
    HealthBar hb; // this script needs to modify the health bar
    SpriteRenderer sprite; // same

    [Range (0, 50)] public int maxHealth = 5; // declare and set an integer called Max Health to 5

    public int currentHealth; // declare a public integer for what we'll call Current Health

    public bool noDamage; // a public boolean called No Damage

    
    void Awake()
    {
        pm = GetComponent<PlayerMovement>(); // define what/where the playermovement script is
        hb = GameObject.FindWithTag("UIHealth").GetComponent<HealthBar>(); // same
        sprite = GetComponent<SpriteRenderer>(); // and again

    }

   
    void Start()
    {

        noDamage = false; // No Damage is set to false at start

        currentHealth = maxHealth; // Current Health is set to whatever the max health is defined as above

        hb.SetMaxHealth(maxHealth); // Access the Health Bar script, and set the function within it, SetMaxHealth, with to our Max Health integer from this script. 
    }


    public void TakeDamage(int damage) // a function called TakeDamage
    {

    if (noDamage == false) // for starters, only perform this function is No Damage is currently false

        {
            currentHealth -= damage; // current health will be reduced by whatever damage is sent over (Monster Damage script will send this integer, or Spike Damage, or Rock Damage..)
            StartCoroutine(NoDamage()); // start the NoDamage function above
            StartCoroutine(Blink()); // start the Blink function above
            hb.SetHealth(currentHealth); // At the end, make sure to update the on screen Health Bar with whatever our Current Health is

            //function is complete

        }

    }

    public IEnumerator NoDamage() // a public function caled NoDamage. Player cannot take damage with this function is running
    {

        //below instructions happen in order

        noDamage = true; // change the noDamage bool to true. Some actions will check for that bool. 
        yield return new WaitForSeconds(1.7f); // we are going to wait for 1 and almost 3 quarters of a  second
        noDamage = false; // return the noDamage bool to false
    }


    public IEnumerator Blink() // a public function called Blink
    {
        for (int i = 0; i < 10; i++) // this is a for-loop, 
        {

            sprite.enabled = false; // sprite renderer is disabled (because enabled parameter is set to false) 
            yield return new WaitForSeconds(0.08f); // wait for 8/100 of a second
            sprite.enabled = true; // reenable the sprite renderer
            yield return new WaitForSeconds(0.08f); // wait again.... after this, the code is complete (unless it hasn't finished the loop)
        }

    }




}

I will add this. Another symptom of this problem is in the way the enemies react when “dying”.

So, the way it’s supposed to work (for now, or for how I currently have it scripted), is how it is working on the enemy with the rigidbody (turns upside down, collider gets disabled, and it falls/disappears). I’ve never been able to get it to work like that, without the rigidbody2d being attached, even though the enemies are copies of eachother and have all the same components… So I guess I was pleasantly surprised when I saw that. Now if only I can get the damage to work?

Here’s an example of this happening too.

https://s4.gifyu.com/images/example2.gif

GonzoBonDonzo!!

I think you gotta problem at least here:

The correct spelling is void Start() so the above will never get executed by Unity.

Same goes for any other mis-spellings or mis-capitalizations.

If that’s not it, here is how to reprise your debugging adventures:

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.

You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as Debug.Log("Problem!",this);

If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

You can also call 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 can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

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, such as this answer or iOS: https://discussions.unity.com/t/700551 or this answer for Android: https://discussions.unity.com/t/699654

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

When in doubt, print it out!™

Note: the print() function is an alias for Debug.Log() provided by the MonoBehaviour class.

You did not post the movement code, but keep this in mind:

With Physics (or Physics2D), never manipulate the Transform directly. If you manipulate the Transform directly, you are bypassing the physics system and you can reasonably expect glitching and missed collisions and other physics mayhem.

Always use the .MovePosition() and .MoveRotation() methods on the Rigidbody (or Rigidbody2D) instance in order to move or rotate things. Doing this keeps the physics system informed about what is going on.

https://discussions.unity.com/t/866410/5

https://discussions.unity.com/t/878046/8

1 Like

Kurt,

Wow! Thank you so much!! I appreciate all the help.

It was the start/Start misspelling! I would have never caught that or thought that such a thing would cause an issue (and it’s still strange that the “take damage” function worked on non-rigidbody enemies??).

Fixing that, and it works :smile:

That might just have been coincidence… keep in mind your damage field is extremely misleading: you mark it public, so it shows up in the inspector… but in Start() you wipe it out by setting it to 1… with code we just determined was not running.

I bet in the other prefab you just have it already set to 1!

Here’s more on what you are doing:

Serialized properties in Unity are initialized as a cascade of possible values, each subsequent value (if present) overwriting the previous value:

  • 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. <---- you wipe out EVERYTHING above

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.

Field initializers versus using Reset() function and Unity serialization:

https://discussions.unity.com/t/829681/2

https://discussions.unity.com/t/846251/8

Awake and Start are optional, and the reason they exist is usually that you do internal setup per script in Awake, such as caching a variable, and so on… but Start to start talking to other scripts and so on, as Start happens after every script has called it’s Awake.

The only exception is when you instantiate things including loading them in, so I felt this needed clarification. For most cases where scripts won’t be accessing other scripts, you can just use Start for all things. But sometimes a script needs to set itself up before talking to others, and that’s Awake’s job.

As Kurt says though, watch your typos. If you are using Visual Studio, you will notice void Start() is a different colour to void start() as the IDE has determined one of them belongs to the library and the other is a local function, or method.

Little cues like that are why a good IDE like Visual Studio is worth getting used to.

Thanks again!

I’m going to go through my scripts and make sure I’m not inadvertently overwriting values that I did not intend to.

And for whatever reason (which I haven’t really paid much attention too), my Visual Studio doesn’t delineate all the colors that other people seem to have in their visual studios. This might seem pretty strange to ignore, but I figured, maybe I changed a display setting a long time ago, or maybe I just need to uninstall and get the most recent install (my VS install is about 3 or 4 years old — I’ve just been doing the regular in-app updates).

Visual Studios does this all the time and requires you to comfort and console it a little bit, tell it nice happy stories and make sure it’s not scared of the ghosts under its bed and then it will work again. It’s extremely fragile.

This may help you with intellisense and possibly other Visual Studio integration problems:

Sometimes the fix is as simple as doing Assets → Open C# Project from Unity. Other times it requires more.

Other times it requires you also nuke the userprefs and .vsconfig and other crufty low-value high-hassle files that Visual Studio tends to slowly damage over time, then try the above trick.

Barring all that, move on to other ideas:

https://discussions.unity.com/t/778503

Also, try update the VSCode package inside of Unity: Window → Package Manager → Search for Visual Studio Code Editor → Press the Update button

Also, this: https://discussions.unity.com/t/805330/7

Kurt,

Thanks one more time. The link you provided helped solve it, and the colors are appearing now. I’m much appreciative of all your help.