Attack Function Not Activating

SamuraiRabbit

New member
Hello there!

I am very new to coding, so the mistakes I have made may be elementary. I'm using the Tactical pack with A*Pathfinding integration for a top-down 2D game. Here is my enemy behaviour tree:

Behaviour Tree.png

As you can see, it goes through to the attack node, but the attack function is not firing. I used the same code for my player melee attack (which works fine) but my test enemy does not damage the player. Could you help troubleshoot this please?

Here is my Enemy AI script (which implements the IAttackAgent):

C#:
using UnityEngine;

namespace BehaviorDesigner.Runtime.Tactical
{
    public class EnemyAI : MonoBehaviour, IAttackAgent
    {
        public GameObject meleeEffectPrefab;
        public Transform attackPoint;
        public float attackDistance = 10, repeatAttackDelay = 0.5f, attackAngle = 90, attackDamage = 50;
        private float _lastAttackTime;
        public LayerMask entityLayers;

        private void Awake()
        {
            _lastAttackTime = -repeatAttackDelay;
        }

        public float AttackDistance()
        {
            return attackDistance;
        }

        public bool CanAttack()
        {
            return true;
        }

        public float AttackAngle()
        {
            return attackAngle;
        }

        public void Attack(Vector3 targetPosition)
        {
            Debug.Log("Attacking");
            {
                GameObject meleeEffect = Instantiate(meleeEffectPrefab, attackPoint.position, attackPoint.rotation);
                Destroy(meleeEffect, 0.1f);

                Collider2D[] hitTargets = Physics2D.OverlapCircleAll(attackPoint.position, attackDistance, entityLayers);

                foreach (Collider2D player in hitTargets)
                {
                    if (player == null)
                        return;

                    player.GetComponent<IDamageable>().Damage(attackDamage);
                }
            }
        }
    }
}

And my Player Health script (Which implements the IDamageable):

Code:
using UnityEngine;
namespace BehaviorDesigner.Runtime.Tactical
{
    public class PlayerHealth : MonoBehaviour, IDamageable
    {
        [SerializeField] private float _maxhealth = 100f, _currentHealth;
        [SerializeField] private UIManager _uIManager;
        public GameObject deathEffect;

        public void Awake()
        {
            _currentHealth = _maxhealth;
        }

        public void Damage(float amount)
        {
            _currentHealth -= amount;
            Debug.Log("Taken Damage!");
            _uIManager.UpdateHealth(_currentHealth);

            if (_currentHealth <= 0)
            {
                Die();
            }
        }

        public bool IsAlive()
        {
            return _currentHealth > 0;
        }

        void Die()
        {
            deathEffect = Instantiate(deathEffect, transform.position, Quaternion.identity);
            Destroy(deathEffect, 0.1f);
            Destroy(gameObject);
        }
    }
}

None of the Debug.Log statements are firing either.

I love what I've achieved with Behaviour Designer so far, and this is the last piece of the puzzle I need for me being happy with my AI.

Thank you in advance.
 

Attachments

  • Behaviour Tree.png
    Behaviour Tree.png
    80.8 KB · Views: 6
Glad you're enjoying Behavior Designer!

If you set a breakpoint does CanAttack get called? Also, just to understand the flow, does the demo scene work correctly for you?
 
Hi Justin, thanks for the reply.

I added a breakpoint to the Attack task, and added another Debug.Log to the CanAttack method. Looks like it's not being called.

Yes, I've played around with different behaviours in the demo scene and it works fine.
 
If CanAttack isn't being called then it's not even hitting the interface. You could start debugging within the Attack task and step through it to see why the CanAttack method isn't being called.
 
I've added Debug.Logs to each method in the Attack Task script and my Enemy AI script. See below:

C#:
using UnityEngine;
using BehaviorDesigner.Runtime.Tasks;
using HelpURL = BehaviorDesigner.Runtime.Tasks.HelpURLAttribute;


namespace BehaviorDesigner.Runtime.Tactical.AstarPathfindingProject.Tasks
{
    [TaskCategory("Tactical/A* Pathfinding Project")]
    [TaskDescription("Moves to the closest target and starts attacking as soon as the agent is within distance")]
    [HelpURL("https://www.opsive.com/support/documentation/behavior-designer-tactical-pack/")]
    [TaskIcon("Assets/Behavior Designer Tactical/Editor/Icons/{SkinColor}AttackIcon.png")]
    public class Attack : IAstarAITacticalGroup
    {
        public override TaskStatus OnUpdate()
        {
            Debug.Log("TaskStatus");
            var baseStatus = base.OnUpdate();
            if (baseStatus != TaskStatus.Running || !started) {
                Debug.Log("BaseStatus");
                return baseStatus;
                
            }


            if (MoveToAttackPosition()) {
                Debug.Log("TryAttack");
                tacticalAgent.TryAttack();
            }


            return TaskStatus.Running;
        }
    }
}


C#:
using UnityEngine;

namespace BehaviorDesigner.Runtime.Tactical
{
    public class EnemyAI : MonoBehaviour, IAttackAgent
    {
        public GameObject meleeEffectPrefab;
        public Transform attackPoint;
        public float attackDistance = 10, repeatAttackDelay = 0.5f, attackAngle = 360, attackDamage = 50;
        private float _lastAttackTime;
        public LayerMask entityLayers;

        private void Awake()
        {
            Debug.Log("AttackAwake");
            _lastAttackTime = -repeatAttackDelay;
        }

        public float AttackDistance()
        {
            Debug.Log("AttackDistance");
            return attackDistance;
        }

        public bool CanAttack()
        {
            Debug.Log("Can Attack - Enemy AI");
            return _lastAttackTime + repeatAttackDelay < Time.time;
        }

        public float AttackAngle()
        {
            Debug.Log("AttackAngle");
            return attackAngle;
        }

        public void Attack(Vector3 targetPosition)
        {
            Debug.Log("Attacking");
            {
                GameObject meleeEffect = Instantiate(meleeEffectPrefab, attackPoint.position, attackPoint.rotation);
                Destroy(meleeEffect, 0.1f);

                Collider2D[] hitTargets = Physics2D.OverlapCircleAll(attackPoint.position, attackDistance, entityLayers);

                foreach (Collider2D player in hitTargets)
                {
                    if (player == null)
                        return;

                    player.GetComponent<IDamageable>().Damage(attackDamage);
                }
            }
        }
    }
}

From the console logs (see attached) it looks like it's checking for attack distance every tick, but it never progresses the task status from BaseStatus to TryAttack. Any thoughts? Like I said, I'm new to coding so apologies if I'm missing something obvious.
 

Attachments

  • Logs.png
    Logs.png
    48.6 KB · Views: 2
It looks like it's getting to this portion within TacticalGroup:

Code:
            if (!tacticalAgent.CanSeeTarget() ||
                    Vector3.Distance(tacticalAgent.TargetTransform.position, transform.position) > tacticalAgent.AttackAgent.AttackDistance()) {
                tacticalAgent.SetDestination(tacticalAgent.TargetTransform.position);
                tacticalAgent.UpdateRotation(true);
                tacticalAgent.AttackPosition = true;
            } else {
                tacticalAgent.Stop();

                if (use2D) {
                    return tacticalAgent.RotateTowardsPosition2D(tacticalAgent.TargetTransform.position, angleOffset2D.Value);
                } else {
                    return tacticalAgent.RotateTowardsPosition(tacticalAgent.TargetTransform.position);
                }
            }

If I were to guess my bet is that RotateTowardsPosition2D is returning false. Can you step into that method to see if that's the case?
 
Hi Justin, thanks for this. I've jumped into Tactical Group and the code in the method does not contain "RotateTowardsPosition2D". See below:

C#:
/// <summary>
        /// Moves the agent towards and rotates towards the target transform.
        /// </summary>
        protected bool MoveToAttackPosition()
        { Debug.Log("MovingToAttackPosition");
            FindAttackTarget();
            if (tacticalAgent.TargetTransform == null) {
                Debug.Log("Target Transform = Null");
                return false;
            }
            if (!tacticalAgent.CanSeeTarget() ||
                    Vector3.Distance(tacticalAgent.TargetTransform.position, transform.position) > tacticalAgent.AttackAgent.AttackDistance()) {
                tacticalAgent.SetDestination(tacticalAgent.TargetTransform.position);
                tacticalAgent.UpdateRotation(true);
                tacticalAgent.AttackPosition = true;
            } else {
                tacticalAgent.Stop();
                Debug.Log("Tactical Agent Stopped");
                return tacticalAgent.RotateTowardsPosition(tacticalAgent.TargetTransform.position);
            }
            return false;
        }

The Debug.Log("Tactical Agent Stopped") is being fired though.
 
Oh, hmm, RotateTowardsPosition2D should look like the following within TacticalAgent:

Code:
        public bool RotateTowardsPosition2D(Vector3 targetPosition, float angleOffset)
        {
            var position = targetPosition - transform.position;
            var angle = Mathf.Atan2(position.y, position.x) * Mathf.Rad2Deg;
            angle += angleOffset;
            var targetRotation = Quaternion.AngleAxis(angle, Vector3.forward);
            return RotateTowards(targetRotation);
        }

Since you are working with 2D I would try adding that to see if it works.
 
Hi Justin,

I've updated the TacticalAgent with the above code, and edited the TacticalGroup to match your example above.

Debug.Log("RotateTowardsPosition2D") is firing and returning a value.

Making headway, but we're still not progressing to the attack method though.
 

Attachments

  • Updated Logs.png
    Updated Logs.png
    17.6 KB · Views: 1
Last edited:
Does RotateTowards ever return true? It looks like I have an unreleased version that includes support for 2D so I'll try to package this up and release it on the store.
 
That's the issue!

I haven't used A* with 2D before but can you determine why it's not rotating the agent?
 
Top