I added this check to the On Trigger Enter function of my projectiles… basically if you line 3 enemies up and ranged attack the 3rd, your projectiles will go through the first two… I put in a check that sees if it hits the first one that if it has the same tag, fighter().CanAttack is true, and the Health component is not the one we were aiming for, then make this thing we just hit our new target and let the action scheduler know this is our new action (other wise your hero will auto fire through all the enemies until the original one you clicked is dead)
EDIT - I also added a check to make sure that we aren’t switching targets on a target behind a target we missed because they are moving… this adds a few more things in the vars setup and in update… basically it get the initial distance to the aim at location, and in update tracks the distance of travel from the projectile to the instigator… if the distance of travel is >= initial distance, the projectile has passed the target and the fails the new if statement… if you want that functionality I included my entire projectile script here…
new script here
using UnityEngine;
using RPG.Attributes;
using RPG.Stats;
namespace RPG.Combat
{
public class Projectile : MonoBehaviour
{
Health targetToHit;
[SerializeField] float speedOfTravel;
[SerializeField] float projectileBonusDamage;
public WeaponSO weaponSO;
bool hasHit = false;
[SerializeField] bool homingProjectile = false;
[SerializeField] float projectileLifetime = 6f;
[SerializeField] float intialTravelDistance;
[SerializeField] float distanceOfTravel;
[SerializeField] float travelDistance;
[SerializeField] GameObject hitEffect;
[Range(0.1f, 3f)]
[SerializeField] float aimLocationFactor = 2;
[SerializeField] GameObject[] destroyOnHit;
[SerializeField] float lifeAfterImpact = 2f;
GameObject instigator;
BaseStats baseStatsScript;
WeaponComponent weaponComponent;
[SerializeField] Vector3 initialEnemyPos;
[SerializeField] bool passedInitalEnemyPos = false;
private void Start()
{
transform.LookAt(GetAimLocation());
baseStatsScript = instigator.GetComponent<BaseStats>();
weaponComponent = GetComponent<WeaponComponent>();
intialTravelDistance = Vector3.Distance(targetToHit.transform.position, transform.position);
initialEnemyPos = targetToHit.transform.position;
}
private void Update()
{
if (targetToHit == null) return;
if (homingProjectile && !targetToHit.IsDead())
{
transform.LookAt(GetAimLocation());
}
transform.Translate(Vector3.forward * speedOfTravel * Time.deltaTime);
distanceOfTravel = Vector3.Distance(transform.position, instigator.transform.position);
CheckIfPassedEnemy();
Destroy(gameObject, projectileLifetime);
}
private void CheckIfPassedEnemy()
{
if (distanceOfTravel >= intialTravelDistance)
{
passedInitalEnemyPos = true;
}
}
private Vector3 GetAimLocation()
{
CapsuleCollider targetCapsule = targetToHit.GetComponent<CapsuleCollider>();
if (targetCapsule == null)
{
return targetToHit.transform.position;
}
return targetToHit.transform.position + Vector3.up * targetCapsule.height / aimLocationFactor;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag(targetToHit.gameObject.tag)
&& other.GetComponent<Health>() != targetToHit
&& instigator.GetComponent<Fighter>().CanAttack(other.gameObject)
&& !passedInitalEnemyPos)
{
SetTargetToHit(other.GetComponent<Health>());
instigator.GetComponent<Fighter>().Attack(other.gameObject);
}
if (other.GetComponent<Health>() == targetToHit)
{
if (targetToHit.IsDead()) return;
speedOfTravel = 0;
weaponComponent.OnHit();
targetToHit.PlayProjectileHit(weaponSO.GetIsMagic());
float damageTaken = baseStatsScript.GetStat(Stat.Damage) + projectileBonusDamage;
other.gameObject.GetComponent<Health>().TakeDamage(instigator, damageTaken);
print(instigator.name + " did " + damageTaken + " with " + name + " from a " + weaponSO.name);
if (hitEffect != null)
{
Instantiate(hitEffect, GetAimLocation(), transform.rotation);
}
foreach (GameObject fxToDestroy in destroyOnHit)
{
fxToDestroy.gameObject.SetActive(false);
}
Destroy(gameObject, lifeAfterImpact);
}
}
public Health SetTargetToHit(Health target)
{
targetToHit = target;
return null;
}
public GameObject SetInstigator(GameObject instigatorPassedIn)
{
instigator = instigatorPassedIn;
return null;
}
public float GetProjectileBonusDamage()
{
return projectileBonusDamage;
}
}
}
the first if statement is the added part to correct his
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag(targetToHit.gameObject.tag)
&& other.GetComponent<Health>() != targetToHit
&& instigator.GetComponent<Fighter>().CanAttack(other.gameObject))
{
SetTargetToHit(other.GetComponent<Health>());
instigator.GetComponent<Fighter>().Attack(other.gameObject);
}
if (other.GetComponent<Health>() == targetToHit)
{
if (targetToHit.IsDead()) return;
speedOfTravel = 0;
weaponComponent.OnHit();
targetToHit.PlayProjectileHit(weaponSO.GetIsMagic());
float damageTaken = baseStatsScript.GetStat(Stat.Damage) + projectileBonusDamage;
other.gameObject.GetComponent<Health>().TakeDamage(instigator, damageTaken);
print(instigator.name + " did " + damageTaken + " with " + name + " from a " + weaponSO.name);
if (hitEffect != null)
{
Instantiate(hitEffect, GetAimLocation(), transform.rotation);
}
foreach (GameObject fxToDestroy in destroyOnHit)
{
fxToDestroy.gameObject.SetActive(false);
}
Destroy(gameObject, lifeAfterImpact);
}