Extending Survival Shooter Tutorial Using RaycastAll to attack all enemies in a line.

Preface: I started with the Survival Shooter tutorial and added a ton of stuff. Here I’m trying to add a power-up that will shoot through every enemy in a line. I have put in a counter so later on when I want to tweak the power-ups I can select how many enemies should be shot through.

The problem: In the game and scene view I can see the Raycast (in the form of a line render implemented in the tutorial) pass through multiple enemies or even single ones and not return that it has hit. I have tested it by playing the game and shooting from different angles and ranges. It seems that the Raycast will hit the enemy about 50% or less of the time. This is in the PlayerShooting script.

The tutorial’s working code in C# that hits only 1 enemy at a time 100% of the time:

if (Physics.Raycast(shootRay, out shootHit, range, shootableMask))
        {
            //Debug.Log(shootHit.transform.name);
            //Debug.Log(shootHit.transform.position);
            // Get the enemy health script in order to apply damage.
            // The thing that has been hit might not be an enemy.
            EnemyHealth enemyHealth = shootHit.collider.GetComponent<EnemyHealth>();

            // So first check if the script is empty or not, otherwise an error or crash will occur.
            if (enemyHealth != null)
            {
                damagePerShot = Random.Range(damageMin, damageMax);
                //Debug.Log(damagePerShot);
                //Debug.Log(enemyHealth.transform);

                // Damage done to an enemy causes the amount of damage done to be displayed on the canvas. 
                FloatingTextManager.CreateFloatingText("-" + damagePerShot.ToString() + " HP", enemyHealth.transform);
                enemyHealth.TakeDamage(damagePerShot, shootHit.point);
            }
            // End the line render when the line hits an object.
            gunLine.SetPosition(1, shootHit.point);
        }
        else
        {
            // Draw a line that's 100 units.
            gunLine.SetPosition(1, shootRay.origin + shootRay.direction * range);
        }

Testing non-working code in C# that hits all enemies in a line <50% of the time:

RaycastHit[] hits = Physics.RaycastAll(shootRay, range, shootableMask);
        
        int i = 0; 
        while(i < hits.Length)
        {
            //Debug.Log(hits*.transform.name);*

EnemyHealth enemy = hits*.transform.GetComponent();*
//Debug.Log(enemy);
Debug.Log(hits*.point);*
if (enemy != null)
{
counter += 1;
gunLine.SetPosition(1, hits*.point);*
enemy.TakeDamage(damageMin, hits*.transform.position);*
}
else
{
gunLine.SetPosition(1, shootRay.origin + shootRay.direction * range);
return;
}
i++;
}
Debug.Log(counter);
counter = 0;
If you need more information or the entire script, please let me know. I’ve tried for a couple hours and nothing I’ve looked up or tried has worked.

A basic ,pretty much what you did, approach would be something like this:

RaycastHit[] hits = Physics.RaycastAll(shootRay, range, shootableMask);

            if (hits.Length != 0)      //might need (hits != null && hits.Length !=0) don't remember if it returns empty array or null
            {
                foreach (RaycastHit hit in hits)    //may be replaced with for loop
                {
                    EnemyHealth enemyHealth = hit.collider.GetComponent<EnemyHealth>();
                    if (enemyHealth != null)
                    {
                        enemyHealth.TakeDamage(100, hit.point);
                    }
                    gunLine.SetPosition(1, hit.point);
                }
            }
            else
            {
                gunLine.SetPosition(1, shootRay.origin + shootRay.direction * range);
            }

Don’t use a while loop when you can use for /foreach loops. But that “solution” has a major bug it will ignore bolders. That s due to the fact that the hits array’s elements are in “random” order.

To achieve a proper solution some “easy” ways would be

  1. pick up the raycast result array sort it according to distance and then iterate though it, damage all objects till you reach a non-enemy one (emenyHealth == null) then stop the process. In this solution you can apply aslo consecutive damage reduction.
  2. pick up the raycase array find the closest bolder element (enemyHealth== null/ or with tags) store the distance re iterate through the array kill all enemies with distance lower than the stored one. With this you may not apply concecutive dmg reduction as you don’t know the order of the elements.

In general the first will give you better control of the situation but its performance highly depends on the sorting algorithm you ll use, the second one might be more performance friendly as it will iterate only twice on any senario but won’t allow special behaviours to applied. Cheers!