1

In Unity3D my enemy is not taking damage upon colliding with my projectile explosion.

Although this is not the case as it the health variable is unaffected upon colliding with my projectile explosion.

My Enemy and Barrel classes inherit from Entity which handles the taking of damage (subtracting the damage variable from the health variable). Although only the barrel class is working as intended.

The tags are 100% correct and I would prefer to continue using inheritance so please no suggestions to change the method in which my classes take damage.

the class that Enemy and Barrel inherit from

using UnityEngine;
using System.Collections;

public class Entity : MonoBehaviour {

    public float health = 25;

// Use this for initialization
void Start () {
}

// Update is called once per frame
void Update () {
}

public virtual void takeDamage(float dmg){
    health -= dmg;

    if (health <= 0){


         Destroy(this.gameObject);
        }
    }
}

Enemy class

using UnityEngine;
using System.Collections;

public class Enemy : Entity {
    private NavMeshAgent agent;
    public GameObject target;
    // Use this for initialization
    void Start () {
        agent = GetComponent<NavMeshAgent> ();
    }

    // Update is called once per frame
    void Update () {
        agent.SetDestination (target.transform.position);
    }
}

Barrel class

using UnityEngine;
using System.Collections;

public class Barrel : Entity {

    private Transform myTransform;

    //Effects
    public GameObject barrelExplosion;
    public GameObject explosionDamage;
    public GameObject explosionSound;

    // Use this for initialization
    void Start () {
        myTransform = this.transform;
    }

    // Update is called once per frame
    void Update () {
    }

    public override void takeDamage(float dmg){
        health -= dmg;

        if (health <= 0){
            Instantiate(barrelExplosion, myTransform.position, myTransform.rotation);
            Instantiate(explosionSound, myTransform.position, myTransform.rotation);
            Instantiate(explosionDamage, myTransform.position, myTransform.rotation);
            Destroy(this.gameObject);
        }
    }
}

ExplosionAOE the class that sends the damage

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

public class ExplosionAOE : MonoBehaviour {

    public float damage = 100.0f;

    public float lifeTime = 0.05f;
    private float lifeTimeDuration;

    public List<GameObject> damageTargets = new List<GameObject>();

    public float radius = 15.0f;

    GameManager gameManager;

    void Start() {
        gameManager = GameObject.FindGameObjectWithTag("GameManager").GetComponent<GameManager>();

        //Destroy (this.gameObject, lifeTime);
        lifeTimeDuration = Time.time + lifeTime;

        transform.GetComponent<SphereCollider>().radius = radius;
    }


    void Update() {

        //Explosion finishes, damage targets and remove AOE field
        if (Time.time > lifeTimeDuration) {
            foreach (GameObject target in damageTargets) {
                if (target != null) {
                    //Calculate damage based on proximity to centre of explosion
                    float thisDamage = ((radius - Vector3.Distance(target.transform.position, transform.position)) / radius) * damage;
                    print(thisDamage);
                    target.GetComponent<Entity>().takeDamage(thisDamage);
                    //target.SendMessage("takeDamage", damage);   //<< This is not good code. Let's fix this!
                }
            }
            Destroy(this.gameObject);
        }
    }


    void OnTriggerEnter(Collider otherObject) {

        if (otherObject.gameObject.tag == "Enemy") {
            damageTargets.Add(otherObject.gameObject);
        }
        if (otherObject.gameObject.tag == "Player") {
            Vector3 jumpVector = (otherObject.transform.position - transform.position).normalized;
            jumpVector *= 25;
            otherObject.GetComponent<CharacterMotor>().SetVelocity(jumpVector);
        }
    }
}

Sorry this is a bit of a lengthy one and EVERYTHING is tagged correctly so that is not the issue, thanks.

Fattie
  • 27,874
  • 70
  • 431
  • 719
jozza710
  • 95
  • 1
  • 1
  • 12
  • just curious: is there a reason for inheriting from another class instead of using an interface like `IHittable` ? – garglblarg Jun 24 '16 at 11:45
  • 1
    @garglblarg I'm not sure, I'm a student studying game programming at uni and this is just the way we've been taught to do it. – jozza710 Jun 24 '16 at 11:49
  • what exactly doesn't work? does the health value of your enemy not decrease when `takeDamage` is called? – Mong Zhu Jun 24 '16 at 12:01
  • @MongZhu Yeah the health value of my enemy doesn't decrease, although the health value of the barrel does decrease. – jozza710 Jun 24 '16 at 12:05
  • @garglblarg , I would leave it as inheritance for now. it takes the right touch to write components in c# for Unity, which **has nothing to do with OO and no connection to inheritance**. – Fattie Jun 24 '16 at 12:07
  • I don't quite understand the connection between your objects. What is `GameObject` ? – Mong Zhu Jun 24 '16 at 12:18
  • @MongZhu GameObjects are the fundamental objects in Unity that represent characters, props and scenery. – jozza710 Jun 24 '16 at 12:19
  • 1
    @jozza710 - note where you say `gameManager = ...` There is a different idiom you usually use there in Unity. It's just `FindObjectOfType` Please go to [this answer](http://stackoverflow.com/a/35891919/294884) and scroll down to *"Fortunately it's no problem..."* Your code is looking really good, most students posting on here are total crap. – Fattie Jun 24 '16 at 13:00
  • 1
    @JoeBlow Thanks, I used the approach you suggested of using `Debug.Log` (one I should have taken initially) and found out that it was actually because my enemies didn't have rigid bodies, I'm not sure why that would stop my enemies from taking damage would you please be able to explain to me why the rigid bodies were necessary in order for the enemies to take damage? – jozza710 Jun 24 '16 at 13:16
  • @JoeBlow and thanks I will definitely try and implement all your suggestions into my code, I am always looking for ways to improve as a aspiring game programmer so thank you for helping me do that. – jozza710 Jun 24 '16 at 13:21
  • 1
    @jozza710 rigidbodies .. for sure, it is fully explained HERE: http://docs.unity3d.com/Manual/CollidersOverview.html go to "trigger action matrix". it is a very annoying aspect of Unity. every single time I use colliders and/or triggers, I have to refer to that chart! it's impossible to remember it or see clearly why it works the way it does. NOTE pls don't forget you will, absolutely, have to move to using **layers** in your collision physics. – Fattie Jun 24 '16 at 13:45
  • @JoeBlow Thanks, that clears everything up and I will definitely being moving to using layers with collision physics, something I will probably try to implement in the morning. Thanks again, really helpful! – jozza710 Jun 24 '16 at 14:43
  • no need to thank, I did nothing. have a nice day. dude you would enjoy this: http://stackoverflow.com/a/35629303/294884 and example http://stackoverflow.com/a/35167170/294884 – Fattie Jun 24 '16 at 21:21
  • also dude to blow your classmates out of the water, @jozza710 here is like advanced-advanced AAA Unity ;-) .... http://stackoverflow.com/a/37228628/294884 – Fattie Jun 24 '16 at 21:21
  • @JoeBlow thanks man you to, looking forward to the permanent psychological problems when trying to use Tweeng :P – jozza710 Jun 25 '16 at 02:14

2 Answers2

4

Problem 1.

Use "Debug.Log" everywhere

 void OnTriggerEnter(Collider otherObject) {
 Debug.Log("in trig");
 Debug.Log("otherObject.gameObject.tag is " + otherObject.gameObject.tag);

        if (otherObject.gameObject.tag == "Enemy") {
Debug.Log("a");
            damageTargets.Add(otherObject.gameObject);
        }
        if (otherObject.gameObject.tag == "Player") {
Debug.Log("b");
            Vector3 jumpVector = (otherObject.transform.position - 
                transform.position).normalized;
            jumpVector *= 25;
            otherObject.GetComponent<CharacterMotor>().SetVelocity(jumpVector);
        }
    }

In particular, in Entity and Enemy.

Questions such as this one are instantly answered by tracking with Debug.Log.


Problem 2.

It's a PITA getting the relationships between triggers, rigidbody, etc.

It's very likely that's a problem here.

http://docs.unity3d.com/Manual/CollidersOverview.html

Go down to the annoying "trigger action matrix" and work from there.


Problem 3.

As a rule, never use the "tags" feature in Unity. (They only added tags to help "hello world" tutorials.)

In practice you use layers everywhere and always:

enter image description here

(Layers are particularly essential in shooting games: every single category needs a layer.)


Problem 4.

The code shown is definitely looking good. Here's some example code not unlike yours for tips.

Trivial example, note the breakaway code (the returns) inside the OnTrigger, you should do that).

Also,

use extentions

everywhere and always in Unity. Quick tutorial

it's the #1 tip if you actually want to work professionally.

public class Enemy:BaseFrite
    {
    public tk2dSpriteAnimator animMain;
    public string usualAnimName;
    
    [System.NonSerialized] public Enemies boss;
    
    [Header("For this particular enemy class...")]
    public float typeSpeedFactor;
    public int typeStrength;
    public int value;
    
    // could be changed at any time during existence of an item!
    
    [System.NonSerialized] public FourLimits offscreen; // must be set by our boss
    
    [System.NonSerialized] public int hitCount;         // that's ATOMIC through all integers
    [System.NonSerialized] public int strength;         // just as atomic!
    
    [System.NonSerialized] public float beginsOnRight;
    
    private bool inPlay;    // ie, not still in runup
    
    void Awake()
        {
        boss = Gp.enemies;
        }
    
..........

    protected virtual void Prepare()    // write it for this type of sprite
        {
        ChangeClipTo(bn);
        // so, for the most basic enemy, you just do that.
        // for other enemy, that will be custom (example, swap damage sprites, etc)
        }
    
    void OnTriggerEnter2D(Collider2D c)
        {
        // we can ONLY touch either Biff or a projectile. to wit: layerBiff, layerPeeps
        
        GameObject cgo = c.gameObject;
        
        if ( gameObject.layer != Grid.layerEnemies ) // if we are not enemy layer....
            {
            Debug.Log("SOME BIZARRE PROBLEM!!!");
            return;
            }
        
        if (cgo.layer == Grid.layerBiff)    // we ran in to Biff
            {
            Gp.billy.BiffBashed();
            // if I am an enemy, I DO NOT get hurt by biff smashing in to me.
            return;
            }
        
        if (cgo.layer == Grid.layerPeeps)   // we ran in to a Peep
            {
            Projectile p = c.GetComponent<Projectile>();
            if (p == null)
                {
                Debug.Log("WOE!!! " +cgo.name);
                return;
                }
            int damageNow = p.damage;
            Hit(damageNow);
            return;
            }
        
        Debug.Log("Weirded");
        }
    
    public void _stepHit()
        {
        if ( transform.position.x > beginsOnRight ) return;
        
        ++hitCount;
        --strength;
        ChangeAnimationsBasedOnHitCountIncrease();
        // derived classes write that one.
        
        if (strength==0)    // enemy done for!
            {
            Gp.coins.CreateCoinBunch(value, transform.position);
            FinalEffect();
            
            if ( Gp.superTest.on )
                {
                Gp.superTest.EnemyGottedInSuperTest(gameObject);
                boss.Done(this);
                return;
                }
            
            Grid.pops.GotEnemy(Gp.run.RunDistance);     // basically re meters/achvmts
            EnemyDestroyedTypeSpecificStatsEtc();       // basically re achvments
            Gp.run.runLevel.EnemyGotted();              // basically run/level stats
            
            boss.Done(this);                            // basically removes it
            }
        }
    
    protected virtual void EnemyDestroyedTypeSpecificStatsEtc()
        {
        // you would use this in derives, to mark/etc class specifics
        // most typically to alert achievements system if the enemy type needs to.
        }
    
    private void _bashSound()
        {
        if (Gp.biff.ExplodishWeapon)
            Grid.sfx.Play("Hit_Enemy_Explosive_A", "Hit_Enemy_Explosive_B");
        else
            Grid.sfx.Play("Hit_Enemy_Non_Explosive_A", "Hit_Enemy_Non_Explosive_B");
        }
    
    public void Hit(int n)  // note that hitCount is atomic - hence strength, too
        {
        for (int i=1; i<=n; ++i) _stepHit();
        
        if (strength > 0) // biff hit the enemy, but enemy is still going.
            _bashSound();
        }
    
    protected virtual void ChangeAnimationsBasedOnHitCountIncrease()
        {
        // you may prefer to look at either "strength" or "hitCount"
        }
    
    protected virtual void FinalEffect()
        {
        // so, for most derived it is this standard explosion...
        Gp.explosions.MakeExplosion("explosionC", transform.position);
        }
    
    public void Update()
        {
        if (!holdMovement) Movement();
        
        if (offscreen.Outside(transform))
            {
            if (inPlay)
                {
                boss.Done(this);
                return;
                }
            }
        else
            {
            inPlay = true;
            }
        }
    
    protected virtual void Movement()
        {
        transform.Translate( -Time.deltaTime * mpsNow * typeSpeedFactor, 0f, 0f, Space.Self );
        }
......



/*
(frite - flying sprite)
The very base for enemies, projectiles etc.
*/

using UnityEngine;
using System.Collections;

public class BaseFrite:MonoBehaviour
    {
    [System.NonSerialized] public float mpsNow;
    // must be set by the boss (of the derive) at creation of the derive instance!
    
    private bool _paused;
    public bool Paused
        {
        set {
            if (_paused == value) return;
            
            _paused = value;
            
            holdMovement = _paused==true;
            
            if (_paused) OnGamePause();
            else OnGameUnpause();
            }
        get { return _paused; }
        }
    
    protected bool holdMovement;
    
    protected virtual void OnGamePause()
        {
        }
    protected virtual void OnGameUnpause()
        {
        }
    
    protected string bn;
    public void SetClipName(string clipBaseName)
        {
        bn = clipBaseName;
        }
    
    }
Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
0

Is more easy if in ExplosionAOE/OnTriggerEnter function you call the takeDamage function:

scriptCall = otherObject.GetComponent(EnemyScript);

scriptCall.takeDamage(damage);

PacoPGD
  • 26
  • 5