Here’s my code for targeting, SpawnProjectile and Projectile scripts.
FrontDirectionTargeting.cs
using System;
using UnityEngine;
namespace RPG.Abilities.Targeting
{
[CreateAssetMenu(fileName = "Front Direction Targeting", menuName = "Abilities/Targeting/Front Direction", order = 0)]
public class FrontDirectionTargeting : TargetingStrategy
{
[SerializeField] float groundOffset = 1;
public override void StartTargeting(AbilityData data, Action finished)
{
Vector3 targetPosition = data.GetUser().transform.position + data.GetUser().transform.forward;
data.SetTargetedPoint(targetPosition + (Vector3.up * groundOffset));
finished.Invoke();
}
}
}
SpawnProjectileEffect.cs
using System;
using RPG.Attributes;
using RPG.Combat;
using UnityEngine;
namespace RPG.Abilities.Effects
{
[CreateAssetMenu(fileName = "Spawn Projectile Effect", menuName = "Abilities/Effects/Spawn Projectile", order = 0)]
public class SpawnProjectileEffect : EffectStrategy
{
[SerializeField] Projectile projectileToSpawn;
[SerializeField] float damage;
[SerializeField] bool isRightHand = true;
[SerializeField] bool useTargetPoint = true;
public override void StartEffect(AbilityData data, Action finished)
{
Fighter fighter = data.GetUser().GetComponent<Fighter>();
Vector3 spawnPosition = fighter.GetHandTransform(isRightHand).position;
if (useTargetPoint)
{
SpawnProjectileForTargetPoint(data, spawnPosition);
}
else
{
SpawnProjectilesForTargets(data, spawnPosition);
}
finished();
}
private void SpawnProjectileForTargetPoint(AbilityData data, Vector3 spawnPosition)
{
Projectile projectile = Instantiate(projectileToSpawn);
projectile.transform.position = spawnPosition;
projectile.SetTarget(data.GetTargetedPoint(), data.GetUser(), damage);
}
private void SpawnProjectilesForTargets(AbilityData data, Vector3 spawnPosition)
{
foreach (var target in data.GetTargets())
{
Health health = target.GetComponent<Health>();
if (health)
{
Projectile projectile = Instantiate(projectileToSpawn);
projectile.transform.position = spawnPosition;
projectile.SetTarget(health, data.GetUser(), damage);
}
}
}
}
}
Projectile.cs
using UnityEngine;
using RPG.Attributes;
using UnityEngine.Events;
namespace RPG.Combat
{
public class Projectile : MonoBehaviour
{
[SerializeField] float speed = 1;
[SerializeField] bool isHoming = true;
[SerializeField] GameObject hitEffect = null;
[SerializeField] float maxLifeTime = 10;
[SerializeField] GameObject[] destroyOnHit = null;
[SerializeField] float lifeAfterImpact = 2;
[SerializeField] UnityEvent onHit;
Health target = null;
Vector3 targetPoint;
GameObject instigator = null;
float damage = 0;
private void Start()
{
transform.LookAt(GetAimLocation());
}
void Update()
{
if (target != null && isHoming && !target.IsDead())
{
transform.LookAt(GetAimLocation());
}
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
public void SetTarget(Health target, GameObject instigator, float damage)
{
SetTarget(instigator, damage, target);
}
public void SetTarget(Vector3 targetPoint, GameObject instigator, float damage)
{
SetTarget(instigator, damage, null, targetPoint);
}
public void SetTarget(GameObject instigator, float damage, Health target=null, Vector3 targetPoint=default)
{
this.target = target;
this.targetPoint = targetPoint;
this.damage = damage;
this.instigator = instigator;
Destroy(gameObject, maxLifeTime);
}
private Vector3 GetAimLocation()
{
if (target == null)
{
return targetPoint;
}
CapsuleCollider targetCapsule = target.GetComponent<CapsuleCollider>();
if (targetCapsule == null)
{
return target.transform.position;
}
return target.transform.position + Vector3.up * targetCapsule.height / 2;
}
private void OnTriggerEnter(Collider other)
{
Health health = other.GetComponent<Health>();
if (target != null && health != target) return;
if (health == null || health.IsDead()) return;
if (other.gameObject == instigator) return;
health.TakeDamage(instigator, damage);
speed = 0;
onHit.Invoke();
if (hitEffect != null)
{
Instantiate(hitEffect, GetAimLocation(), transform.rotation);
}
foreach (GameObject toDestroy in destroyOnHit)
{
Destroy(toDestroy);
}
Destroy(gameObject, lifeAfterImpact);
}
}
}
DelayCompositeEffect.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RPG.Abilities.Effects
{
[CreateAssetMenu(fileName = "Delay Composite Effect", menuName = "Abilities/Effects/Delay Composite", order = 0)]
public class DelayCompositeEffect : EffectStrategy
{
[SerializeField] float delay = 0;
[SerializeField] EffectStrategy[] delayedEffects;
[SerializeField] bool abortIfCancelled = false;
public override void StartEffect(AbilityData data, Action finished)
{
data.StartCoroutine(DelayedEffect(data, finished));
}
private IEnumerator DelayedEffect(AbilityData data, Action finished)
{
yield return new WaitForSeconds(delay);
if (abortIfCancelled && data.IsCancelled()) yield break;
foreach (var effect in delayedEffects)
{
effect.StartEffect(data, finished);
}
}
}
}
By the way I’m using the Delay launch fireball