Sure, here is my Projectile.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RPG.Core; // for us to be able to access the Target's Health component
using RPG.Attributes; // to access 'Health.cs'
using UnityEngine.Events;
namespace RPG.Combat {
public class Projectile : MonoBehaviour
{
[SerializeField] bool isHoming = true; // determines if our missiles are homing (persuasive) or not
[SerializeField] float speed = 1.0f; // The speed of our Arrow being fired at its target
[SerializeField] GameObject hitEffect = null; // the hit effect when a projectile hits its target (particle system)
[SerializeField] float maxLifeTime = 10.0f; // the maximum lifetime of our Projectile
[SerializeField] GameObject[] destroyOnHit = null; // an array of objects to be destroyed once a Projectile hits an object
[SerializeField] float lifeAfterImpact = 2.0f; // the life of a Projectile after it has impacted a target
[SerializeField] UnityEvent onHit; // projectile hitting target audio
Health target = null; // The target of our Projectile must have health for us to attack them
GameObject instigator = null; // Instigator (for us to gain xp as we kill enemies)
float damage = 0.0f; // Damage done to the target of our projectile (passed in from 'Weapon.cs')
Vector3 targetPoint; // the target position of our Ranged Abilities
private void Start() {
// When firing an arrow, the first thing we want to do
// is look at our target (once only. If we put this
// in update, it will turn into a Homing Missile)
transform.LookAt(GetAimLocation());
}
// Update is called once per frame
void Update()
{
// This function shoots the arrow at a target. First, we check
// if we have a target or not. If there's no target,
// quit this function.
// If we have a target, and he's not dead, we want the arrow
// to look at our target, and then move towards it (on the Z-axis),
// via Vector3.forward, at the given Speed variable,
// over Time.deltaTime (time in frames per second)
// if (target == null) return;
if (target != null && isHoming && !target.IsDead()) {
transform.LookAt(GetAimLocation()); // makes our missile a homing missile
// (only if we assigned it as a homing missile)
// after we checked that the target is not dead,
// and our missile is homing
}
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
public void SetTarget(Health target, GameObject instigator, float damage) {
// This function calls 'SetTarget' below, so we can aim at our enemies using our abilities
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;
// our Target is now the GameObject holding this script
this.targetPoint = targetPoint;
// keep track of the target we are firing our Projectile abilities at
this.damage = damage;
// The damage dealt by our Projectile = damage from SetTarget ('float damage' argument)
this.instigator = instigator;
// The instigator is set to be an instance of whoever holds this script
Destroy(gameObject, maxLifeTime); // destroys our Projectile after its maximum lifetime (so it doesn't take a toll on our computer)
}
private Vector3 GetAimLocation() {
if (target == null) return targetPoint; // if there is no target, just head to the targetPoint
// This function aims the projectile at the heart (Z-Axis) of the target, by getting the target's
// Capsule Collider height, and then adding 1/2 of its height, so our arrow lands into the target's chest
// (midway through the CapsuleCollider). It also checks if the Target has a CapsuleCollider or not. If not, just aim the
// arrow at the target feet (its original aim point)
CapsuleCollider targetCapsule = target.GetComponent<CapsuleCollider>();
if (targetCapsule == null) {
return target.transform.position;
}
return target.transform.position + Vector3.up * targetCapsule.height / 2;
}
public void OnTriggerEnter(Collider other) {
// This function checks if what the Projectile hit has the same
// Health as our target or not (aka: if we hit something with
// health). If not, quit the function. If it has the same
// health, we apply damage, we play any particle effects of Impact
// (if they exist), and then we destroy the gameObject
// holding this script (arrow in this case) once it has hit
// the target (other collider)
Health health = other.GetComponent<Health>(); // the health our projectile is aiming to hurt
if (target != null && health != target) {
return; // if we haven't hit our target, don't explode (Ability)
}
/* if (target != null && target.IsDead()) {
// If our target is dead (and we have a target), we don't need to hit him (i.e: the
// arrow can just fly by into its straight path)
return;
} */
if (health == null || health.IsDead()) return; // avoid hitting obstacles in the way of the projectile
if (other.gameObject == instigator) return; // if our ability is hitting us, we dont want it to hurt us (the player)
health.TakeDamage(instigator, damage);
// the health of our target is what takes the damage, created by the instigator
speed = 0; // stops our Projectile from moving once it hit a target
onHit.Invoke();
if (hitEffect != null) {
// If there is a Particle Effect, we want to play that
Instantiate(hitEffect, GetAimLocation(), transform.rotation);
}
// The following lines of code destroy a projectile after it has either been alive for a few amount of
// 'lifeAfterImpact' seconds, or if it missed its target
foreach(GameObject toDestroy in destroyOnHit) {
Destroy(toDestroy);
}
Destroy(gameObject, lifeAfterImpact);
// (NOT IN COURSE): The following if statement Instantiates arrows to
// the ground, as long as they're not fireballs (aka: stop playing the
// weird magic effect for Fireballs when enemy is dead)
/* if (gameObject.tag != "Fireball") {
Instantiate(gameObject, other.transform.position, Quaternion.identity);
} */
}
}
}
Any modifications in this script should not affect anything but the Fire Spray ability though, right?