Dialogue Actions Not Triggering AggroGroup

So, I ran into an issue when testing out whether I could die and respawn at the respawnPoint. It seems that the dialogue is no longer triggering AggroGroup to activate the fighter and combat target scripts to reactivate.

Now, I made some (likely bodgy) changes to the script to detect whether an enemy has taken damage (likely from an ability) and if so, they should reactivate and fight the player. Now, this is mostly working as intended, however it’ll only affect targets damaged (by design) and not factor in things like the shoutDistance to aggro other nearby guards.

using UnityEngine;
using RPG.Attributes;

namespace RPG.Combat
{
    public class AggroGroup : MonoBehaviour
    {
        [SerializeField] Fighter[] fighters;
        [SerializeField] bool activateOnStart = false;

        void Update()
        {
            Activate(activateOnStart);
        }

        public void Activate(bool shouldActivate)
        {
            foreach (Fighter fighter in fighters)
            {
                CombatTarget target = fighter.GetComponent<CombatTarget>();
                Health targetHealth = fighter.GetComponent<Health>();
                if (target != null)
                {
                    if (targetHealth.TakenDamage())
                    {
                        target.enabled = true;
                    }
                    else
                    {
                        target.enabled = shouldActivate;
                    }
                }
                if (targetHealth.TakenDamage())
                {
                    fighter.enabled = true;
                }
                else
                {
                    fighter.enabled = shouldActivate;
                }
            }
        }
    }
}

Now, the issue seems to be stemming from changing

void Start()
        {
            Activate(activateOnStart);
        }

into: - EDIT: I now see the error here, as noted below. :joy:

void Update()
        {
            Activate(activateOnStart);
        }

So that TakenDamage() can be checked. I’ll post that function below:

public bool TakenDamage()
        {
            if (healthPoints.value < GetInitialHealth())
            {
                return true;
            }
            return false;
        }

I know I’ve asked a lot of questions lately, but this one has me stumped. Is there a better way to reactivate the guards if they’ve taken damage?

Thanks in advance,
Mark.

You know when you come back and look at things with fresh eyes and you see something completely obvious? :joy:

By using Activate(activateOnStart); in Update, I’m essentially setting the fighter and combat target scripts to disabled every frame because of the [SerializedField].

Hm… so now I need another way of working in a check to see whether the enemy has taken damage.

My current quick-and-dirty solution is a second foreach loop that checks whether targetHealth.TakenDamage() is true and activates accordingly. This feels needlessly wasteful though, so if you’ve got any recommendations on combining them, or refactoring, that’d be great!

AggroGroup.cs

using UnityEngine;
using RPG.Attributes;

namespace RPG.Combat
{
    public class AggroGroup : MonoBehaviour
    {
        [SerializeField] Fighter[] fighters;
        [SerializeField] bool activateOnStart = false;
        bool activateOnDamage = false;

        void Start()
        {
            Activate(activateOnStart);
        }

        void Update()
        {
            ActivateIfDamaged(activateOnDamage);
        }

        public void Activate(bool shouldActivate)
        {
            foreach (Fighter fighter in fighters)
            {
                CombatTarget target = fighter.GetComponent<CombatTarget>();
                if (target != null)
                {
                    target.enabled = shouldActivate;                    
                    fighter.enabled = shouldActivate;
                }
            }
        }

        public void ActivateIfDamaged(bool activateOnDamage)
        {
            foreach (Fighter fighter in fighters)
            {
                CombatTarget target = fighter.GetComponent<CombatTarget>();
                Health targetHealth = fighter.GetComponent<Health>();
                if (target != null)
                {
                    if (targetHealth.TakenDamage())
                    {
                        target.enabled = true;
                    }
                }
                if (targetHealth.TakenDamage())
                {
                    fighter.enabled = true;
                }
            }
        }
    }
}

It’s actually much simpler than this:

We’ll start by adding a method to Fighter.cs:

public void ActivateOnHit()
{
    GetComponent<Health>().TakeDamageEvent.AddListener( ()=>
    {
        enabled = true;
    }
}

Now, we don’t know for sure whether or not Fighter is active in the beginning (you might have it disabled already)… so let’s take care of this in AggroGroup.Start() (you don’t want to do this in Activate because it could be called multiple times:

void Start()
{
    Activate(activateOnStart);  //This really belongs in Start(), not Update.
    foreach(Fighter fighter in fighter)
    {
         fighter.ActivateOnHit();
     }
}

Now if the Fighter’s Health is hit, the fighter will be activated regardless of whether or not the aggrogroup is active.

This looks great, thanks!

I’m having an issue implementing the above statement.

I’ve updated GetComponent().TakeDamageEvent to Health.TakeDamageEvent as I got an error: cannot reference a type through an expression. So that’s fine, but on the lambda I’m getting an error that Delegate ‘UnityAction’ does not take 0 arguments.

Could ActivateOnHit() be used in conjunction with autoattack target? So, if the player is attacked, he sets that target using target = FindNewTargetInRange();?

Thanks again!

Hehe, silly me, typing code on the fly.
The signature for the Unity Event is
TakeDamageEvent(float)
This means that the lambda expression wouldn’t quite be the best match for this… so…

void WasHit(float unused)
{
    enabled=true;
}

and now the ActivateOnHit will be

public void ActivateOnHit()
{
     GetComponent<Health>().TakeDamageEvent.AddListener(WasHit);
}
It doesn't matter that we won't use the damage, it's just to make the headers match.

So both of these go in the Fighter.cs? I’m still getting an error: "An object reference is required for the non-static field, method or property 'UnityEvent.AddListener(UnityAction). I thought a simple this.GetComponent().TakeDamageEvent.AddListener(WasHit) / gameObject.GetComponent would suffice, but Unity says no. :stuck_out_tongue:

Ok, took a closer look at Health, since I’m at my build computer this morning.

TakeDamageEvent is the special wrapper class we created (actually, that’s no longer needed since Unity 2019)… the actual event is takeDamage;

GetComponent<Health>().takeDamage.AddListener(WasHit);
1 Like

Ah, awesome. That did the trick! Thanks again. :grinning:

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

Privacy & Terms