Fire Spray Issue

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?

I think I"m going to need to just look at it directly and see what’s going on. Zip up your project and upload it to https://gdev.tv/projectupload Be sure to remove the Library folder to conserve space.

done, please check

Hmmm… I think we miscommunicated somewhere along the way…
The spawn projectile effect was set to use target point, but we’re shooting at a character so when I wrote the filter to get the Unit under cursor, I didn’t set the targeted point.

So it looks like you want to fire a projectile if there is a target under the cursor, but you want to make it so that, by shooting at the target point instead of the target itself, that if the target moves, it will miss.

The easiest solution is to uncheck Use Targeted Point and make sure that the projectile is not set to homing. This would exactly mimic the behaviour I described above.

You could, however, also store the targeted point in the UnitsUnderCursor targeting. The same result will happen either way.

using System;
using RPG.Control;
using System.Collections.Generic;
using UnityEngine;

// This script is a filtering strategy, which scans under the 
// player cursor, and checks if there is a target enemy to fire an ability at.
// If there is, we return true and fire at him. If not, don't fire anything!

namespace RPG.Abilities.Targeting {

    [CreateAssetMenu(fileName = "UnitsUnderCursor", menuName = "Abilities/Targeting/UnitsUnderCursor", order = 0)]

    public class UnitsUnderCursorTargeting : TargetingStrategy {

        [SerializeField] LayerMask layerMask;
        [SerializeField] float groundOffset; 
        // The Targeting function below MUST EXIST IN THIS SCRIPT, because we are inheriting from 'TargetingStrategy':
        public override void StartTargeting(AbilityData data, Action finished)
        {
            data.SetTargets(GetGameObjectsUnderCursor());   // 1. Setting the targets under our cursor
            RaycastHit raycastHit;
            Ray ray = PlayerController.GetMouseRay();
            if (Physics.Raycast(ray, out raycastHit, 1000, layerMask)) {

                // Requires a bit of mathematical trignometry (Shops and Abilities Course, Lecture 57)
                data.SetTargetedPoint(raycastHit.point + ray.direction * groundOffset / ray.direction.y);

                // Debugging:

                Debug.Log($"Target point set to {data.GetTargetedPoint()}"); }
            finished?.Invoke();     // 2. When done setting the targets, Invoke this function (the callback method)
        }

        // Accumulating all the units (Enemies) under the cursor, using Physics.RaycastAll(), 
        // so we know who will be affected by the targeting ability:
        private IEnumerable<GameObject> GetGameObjectsUnderCursor() {

            RaycastHit[] hits = Physics.RaycastAll(PlayerController.GetMouseRay());
            foreach (var hit in hits) {

                yield return hit.collider.gameObject;

            }

        }

    }

}

Hi Brian. I have copied and pasted the script above to replace my UnitsUnderCursor.cs script, but there seems to be no change in the activity of the Fire Spray when it’s fired. It still only fires if there’s a target under the cursor, but it still only appears for a few moments (probably 1-2 seconds), and the direction is still about 45 degrees north east in the game

Also where is the “Use Targeted Point” boolean that needs to be deactivated?

In the Fireballs Effect ScriptableObject
image

Yup, that got it to work. Thanks Brian :slight_smile:

Privacy & Terms