0
\$\begingroup\$

I have a projectile and an enemy, both with colliders. On the projectile script, I'm destroying the projectile OnTriggerEnter:

public class bulletBehavior : MonoBehaviour
{
    private Rigidbody rb;
    public float smallGunSpeed = 500f;

    void Start()
    {
        rb = this.GetComponent<Rigidbody>();
        rb.AddForce(transform.forward * smallGunSpeed);
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Enemy")
        {
            Destroy(this.gameObject);
        }
    }   
}

On the Enemy Script I'm handling the health and the damage from the different projectiles using tags. Is this the right way to do it? I would much rather attach the damage amount to the projectile but I'm not sure how to "Transfer" the damage value to the NPC script from the projectile script? Storing them on the NPC script seems incredibly inefficient.

public class NPCscript : MonoBehaviour
{   
    public int maxHealth = 300;
    public int currentHealth;
    public HealthBar healthBar;
    private GameObject enemy;

    void Start()
    {
        currentHealth = maxHealth;
        healthBar.SetMaxHealth(maxHealth);

        StartCoroutine(AutoHeal());
    }

    void Update()
    {
        if (currentHealth <= 0)
        {
            Destroy(this.gameObject);
        }
    }

    void TakeDamage(int damage)
    {
        currentHealth -= damage;
        healthBar.SetHealth(currentHealth);
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Bullet")
        {
            TakeDamage(10);
        }
        else if (other.tag == "Grenade")
        {
            TakeDamage(75);
        }
    }   
}
```
\$\endgroup\$

2 Answers 2

0
\$\begingroup\$

That "NPC script" looks like it's all about managing health and damage. I'd be tempted to call it a Health component instead. The NPC's behaviour can live in another component, that can talk to the Health component when it needs to check its HP.

By separating the concept of having health and taking damage from being an NPC, you can re-use the same component on damageable player character or destructible objects too. 😀

Then your projectile can look like this - owning its own damage and the dealing of it:

public class bulletBehavior : MonoBehaviour
{
    private Rigidbody rb;
    public int damageAmount = 10;
    public float smallGunSpeed = 500f;

    void Start()
    {
        rb = this.GetComponent<Rigidbody>();
        // If you're going to call it "speed" then apply it as a speed
        // in meters per second, not a force in Newtons.
        rb.AddForce(transform.forward * smallGunSpeed, ForceMode.VelocityChange);
    }

    private void OnTriggerEnter(Collider other)
    {
        // Check if we hit something we can damage.
        if (other.TryGetComponent(out Health health)) {
            // If so, damage it and destroy ourselves!
            health.TakeDamage(damageAmount);
            Destroy (gameObject);
        }            

        // Depending on your game, you might also want to
        // destroy the projectile if you hit an indestructible wall.
    }   
}

Now the projectile knows about the Health component and damage-taking (kind of unavoidable - dealing damage is a projectile's whole job), but things that have Health don't need to know about all the (possibly many) things that can deal damage. We've simplified that relationship to just the simple contract of the TakeDamage method, (ie. "I'll tell you how much damage to take")

Your Health component doesn't need a trigger/collision callback method at all, since these other scripts will just call its public methods when they need it to do something.

This way, we can easily add lots of different things that have health, and lots of different things that deal damage, without our trigger methods becoming a giant mess of different tag checks.

\$\endgroup\$
3
  • \$\begingroup\$ Everything makes sense, and is wonderful. Only clarification I need to ask is in (other.TryGetComponent(out Health health)) - "Health" represents the component script name I believe, but what does the second "health" represent? Not getting it from doc: docs.unity3d.com/ScriptReference/Component.TryGetComponent.html \$\endgroup\$
    – benbastien
    Commented Jan 26, 2021 at 7:59
  • \$\begingroup\$ Like you said, the first "Heath" is the component name (classes should normally be named in uppercase) the second "health" is just the variable name. It would be the same if you had something like "public int myInt" where int is the type and myInt is the name. \$\endgroup\$ Commented Jan 26, 2021 at 8:06
  • \$\begingroup\$ @benbastien the syntax out Health health in this context is trying to load the component Health and assign it to the health variable. In the next line you see you can access it already with health.TakeDamage. It is a shorter version of Benjamin's getComponent and afterwards checking against null. \$\endgroup\$
    – Zibelas
    Commented Jan 26, 2021 at 11:45
0
\$\begingroup\$

Hey welcome to the game dev stack exchange. Don't worry about being new to Unity what you want to do is pretty simple and straight forward, plus you are almost ready doing it with "smallGunSpeed".

public class bulletBehavior : MonoBehaviour
{
    private Rigidbody rb;
    public float smallGunSpeed = 500f;
    public float damage = 10.0f;

    void Start()
    {
        rb = this.GetComponent<Rigidbody>();
        rb.AddForce(transform.forward * smallGunSpeed);
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Enemy")
        {
            Destroy(this.gameObject);
        }
    }   
}

public class NPCscript : MonoBehaviour
{   
    public int maxHealth = 300;
    public int currentHealth;
    public HealthBar healthBar;
    private GameObject enemy;

    void Start()
    {
        currentHealth = maxHealth;
        healthBar.SetMaxHealth(maxHealth);

        StartCoroutine(AutoHeal());
    }

    void Update()
    {
        if (currentHealth <= 0)
        {
            Destroy(this.gameObject);
        }
    }

    void TakeDamage(int damage)
    {
        currentHealth -= damage;
        healthBar.SetHealth(currentHealth);
    }

    private void OnTriggerEnter(Collider other)
    {
        bulletBehaviour bulletScript = other.transform.GetComponent<bulletBehaviour>();
        if (bulletBehaviour != null)
        {
            TakeDamage(bulletBehaviour.damage);
        }
    }   
}

Just a heads up (and I learned this today) it is recommended that most simple questions like this are best asked in the chat room for faster support (you may even get some additional tips). But there really is no harm posting a question either.

\$\endgroup\$
3
  • \$\begingroup\$ I'd recommend CompareTag() over tag ==, but here we actually don't need tags at all. \$\endgroup\$
    – DMGregory
    Commented Jan 26, 2021 at 4:19
  • \$\begingroup\$ Oh I just left most of the original code in there. Personally I would drop tags all together but I figured the person who asked might not be super familiar with coding and didn't want to overwhelm them. Personally I would also move the health check in update to the take damage function as well to improve performance a bit. Not to mention shuffling around some variables like healthbar and smallgun speed, but that's an architecture question for another day. \$\endgroup\$ Commented Jan 26, 2021 at 8:05
  • \$\begingroup\$ Appreciate all the feedback either way. \$\endgroup\$
    – benbastien
    Commented Jan 26, 2021 at 14:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .