Why not having CombatTarget component on the base Character?

When setting the prefabs variations you’ve opted to have the CombatTarget component inside the Enemy Variant rather than in the Character Prefab, stating that this is because the player can’t attack itself. But won’t the player be needing that component as well, so that enemies can target him?
is this something that I’ll see we actually change back to the character further ahead in the course or are we planning on using different scripts for that?

As the structure is designed now, the enemy targets based on locating the player through the Player tag.

It won’t require the CombatTarget as it’s used in the course now.

I see, so… let’s say in the future I want to add some companions for my player, that enemies are able to attack as well, also maybe other NPCs that can be attacked by them, will the tag strategy play out well for that scenario?
Maybe I’ll understand it straight away when i get to that phase.

No, they won’t be detected or noticed by the enemies in this current scheme.

For that, you’ll need to implement the concept of teams in some way. This is something I probably would add to a CombatTarget… an Enum Enemy/Neutral/PlayerAlly

Then instead of the AI looking exclusively for the player, it would look for any CombatTargets that are PlayerAlly and attack the nearest one.

1 Like

Cool, your point makes complete sense, it was exactly what I had in mind (I believe), and that was actually my (bad formulated) question about the tag strategy, should have been “if we could target multiple tags”.
I’ve got to that part now and tested it out with some dummy, tagged, capsules. it works as I intended but I’ll share with you, just to know your opinion on if this is a good solution for the problem at hand or if it can become very expensive to always go through all the possible targets.

using System.Collections.Generic;
using UnityEngine;

namespace RPG.Control { 
    public class AIController : MonoBehaviour
    {
        [SerializeField] float chaseDistance = 5f;

        string[] tagNames = { "Player", "Target" };

        private void Update()
        {
            GameObject[] enemyTargets = GetAllTargets();

            GameObject closestTarget = ClosestRangeTarget(enemyTargets);

            if (IsInChaseDistance(closestTarget))
            {
                print(this + " chase " + closestTarget);
            }
        }

        private GameObject[] GetAllTargets()
        {
            List<GameObject> targets = new List<GameObject>();
            foreach (string tag in tagNames)
            {
                foreach (GameObject target in GameObject.FindGameObjectsWithTag(tag))
                {
                    targets.Add(target);
                }
            }

            return targets.ToArray();
        }

        private GameObject ClosestRangeTarget(GameObject[] targets)
        {
            GameObject closestTarget = null;

            foreach (GameObject target in targets)
            {
                if (closestTarget == null || GetTargetDistance(target) < GetTargetDistance(closestTarget))
                {
                    closestTarget = target;
                }
            }

            return closestTarget;
        }

        private bool IsInChaseDistance(GameObject target) { 
            return GetTargetDistance(target) <= chaseDistance;
        }

        private float GetTargetDistance(GameObject target)
        {
            return Vector3.Distance(target.transform.position, transform.position);
        }
    }
}```

Somehow, I missed this response…

This is a case where it would actually be better to add an enum field to the Combat Target, example

public enum Team
{
     Player,
     Ally,
     Enemy
}

Then when the player targets, only attack if the CombatTarget is an Enemy, and when the Enemy targets, he can gather the targets by CombatTarget

GameObject[] GetAllTargets()
{
    List<GameObject> targets = new List<GameObject>();
    foreach(CombatTarget target in FindObjectsOfType<CombatTarget>) //One Pass
    {
         if(target.tag==Team.Enemy) continue; //One comparison
         targets.Add(target.gameObject); //only add targets that are not enemy
    }
    return targets.ToArray();
}

Now, I’m going to reduce your entire search to one function call, using a library called Linq; (using System.Linq;)
The first step is to use CombatTarget[] instead of GameObject[] in all of your listed function calls.

CombatTarget[] GetAllTargets()
{
     return FindObjectsOfType<CombatTarget().Where(t=>t.tag!=Team.enemy)
                                            .Where(h=>h.GetComponent<Health>().IsAlive)
                                            .Where(i=>IsInChaseDistance(i))
                                            .OrderBy(d=>GetTargetDistance(d))
                                            .ToArray();
}

This will retrieve a list of CombatTargets which are not enemies (i.e. the player or his allies), that are alive, and that are within pursuit range.
The first element is the closes. Obviously, the Update would have to check to make sure this list wasn’t a 0 length array (in which case no suitable targets are within range)

This topic was automatically closed after 9 hours. New replies are no longer allowed.

Privacy & Terms