Failed attempt at implementing "range" in this shooter script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Shooter : MonoBehaviour
{
[SerializeField] GameObject projectile, Weapon;
AttackerSpawner myLaneSpawner;
Animator animator;
Attacker attacker;
[SerializeField] float Range;

private void Start()
{
    SetLaneSpawner();
    animator = GetComponent<Animator>();
    attacker = FindObjectOfType<Attacker>();
    
    
}
private void Update()
{
    if (IsAttackerInLane())
    {
        animator.SetBool("IsAttacking", true);
    }
    else
    {
        animator.SetBool("IsAttacking", false);
    }
}

private void SetLaneSpawner()
{
    AttackerSpawner[] attackerSpawners = FindObjectsOfType<AttackerSpawner>();

    foreach (AttackerSpawner spawner in attackerSpawners)
    {
        bool IsCloseEnough = (Mathf.Abs(spawner.transform.position.y - transform.position.y) <= Mathf.Epsilon);
        if (IsCloseEnough)
        {
            myLaneSpawner = spawner;
        }
    }

}

private bool IsAttackerInLane()
{
    var attackerPos = attacker.transform.position.x;
    
    if (myLaneSpawner.transform.childCount <= 0 &&
       attackerPos - gameObject.transform.position.x > Range)
    {
        return false;
    }
    else
    {
        return true;
    }



}

public void Fire()
{
    Instantiate(projectile, Weapon.transform.position, Weapon.transform.rotation) ;
}

}

This is from the glitch garden section of the 2d unity course
I want to implement range because i have some defenders that can only shoot at close range(eg knight)
Pls tell me what i did wrong! Im a beginner

Hi,

Welcome to our community! :slight_smile:

As I already suggested in your thread on Udemy: Check the attacker variable in IsAttackerInLane. Where does the object come from?


See also:

sorry! i cant understand what you mean by check
Like i have used FindObjectOfType instead of serialize field because then it would take a lot of time with many attackers
What are you asking me to do
I thought that i should be FindObjectsOfType since there are more than 1 attacker but then it shows an error
Please tell me what can i do

You could, for example, add if (attacker == null) { return; } at the top of the IsAttackerInLane code block to terminate the method if there isn’t assigned any object to attacker.

By default, all variables of value types are null. If you don’t assign anything to attacker, the variable is “empty”. Via myLaneSpawner, you could iterate over the children (given they exist) and compare their position to the shooter’s position.

No it still isnt working at all
I tried to turn attacker variable into array with the logic behind it that there are many attackers and not one (like in the case of spawners)
But then it showed a red squiggly line on transform
So i implemented a foreach loop like this…
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Shooter : MonoBehaviour
{
[SerializeField] GameObject projectile, Weapon;
AttackerSpawner myLaneSpawner;
Animator animator;
Attacker attacker;
[SerializeField] float Range;

private void Start()
{
    SetLaneSpawner();
    animator = GetComponent<Animator>();
    attacker = FindObjectsOfType<Attacker>();
    
    
}
private void Update()
{
    if (IsAttackerInLane())
    {
        animator.SetBool("IsAttacking", true);
    }
    else
    {
        animator.SetBool("IsAttacking", false);
    }
}

private void SetLaneSpawner()
{
    AttackerSpawner[] attackerSpawners = FindObjectsOfType<AttackerSpawner>();

    foreach (AttackerSpawner spawner in attackerSpawners)
    {
        bool IsCloseEnough = (Mathf.Abs(spawner.transform.position.y - transform.position.y) <= Mathf.Epsilon);
        if (IsCloseEnough)
        {
            myLaneSpawner = spawner;
        }
    }

}

private bool IsAttackerInLane()
{
    if (attacker == null) { return false; }
    foreach (Attacker attacker in attacker)
    {
        var attackerPos = attacker.transform.position.x;

        if (myLaneSpawner.transform.childCount <= 0 &&
            attackerPos - gameObject.transform.position.x > Range)
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    
    


    
}

public void Fire()
{
    Instantiate(projectile, Weapon.transform.position, Weapon.transform.rotation) ;
}

}

But then it says that the bool IsAttackerInLane isnt returning any value and now its total chaos
im really confused! pls help

You could try this to fix the compiler error:

private bool IsAttackerInLane()
{
    // When the method is supposed to return `false`
    if (
        attacker == null
        || myLaneSpawner == null 
        || myLaneSpawner.transform.childCount == 0
    ) { return false; }
    
    // Your code
    foreach (Attacker attacker in attacker)
    {
        var attackerPos = attacker.transform.position.x;

        if (attackerPos - gameObject.transform.position.x < Range)
        {
            return true;
        }
    }

    return false;
}

FindObjectOfType returns the “first” object it finds, whatever Unity defines as “first”. As aforementioned, you’ll have to check the children of the myLaneSpanwer object, not some arbitrary Attacker object.

Also check the foreach condition. What is supposed to happen there?

You could compare your code to Rick’s to learn how to iterate over the children of a Transform object. See, for example, the SetLaneSpawner method.


See also:

ok so now i did this

    private bool IsAttackerInLane()
    {
        Attacker attacker = myLaneSpawner.GetComponentInChildren<Attacker>();
        
        if (
        attacker == null
        || myLaneSpawner == null
        || myLaneSpawner.transform.childCount == 0
          ) { return false; }

        
        
       
        if (myLaneSpawner.transform.childCount <= 0
            && attacker.transform.position.x - gameObject.transform.position.x > Range)
            {
                return false;
            }
        else
            {
                return true;
            }
     }

and it still doesnt work unfortunately
but the code is perfect because i checked with this

if(attacker.transform.position.x - gameObject.transform.position.x > 2)
        {
            Debug.Log("Dont shoot");
        }
        else
        { Debug.Log("Shoot"); }

and it did exactly what i asked
so does && surely means “and” and not “or” ?
and if not then whats wrong…this is really frustrating

wait why didnt my code formatting work?

I fixed the formatting for you. Click on the orange pencil in the top right corner of your post, then on “Raw” to see what I changed.

That’s right. && means “and”, || means “or”.

I’m not exactly sure what you mean by “it still doesnt work” but GetComponentInChildren returns the first Attacker object it finds, whatever Unity defines as “first”. It might be that it returns the same object over and over again. And the “first” object could be the one at the end of the lane, not the one next to the shooter.

What you want is to check all attackers in the lane to determine whether the defender is supposed to shoot or not. That’s why I suggested to iterate over the children of the object referenced by myLaneSpanwer.

I mean that the attacker still shoots without considering its range

How do i do this
i could create an array but then i would have to use a foreach loop which shows an error than boolean isnt returning any value

Log the values of your variables into your console to figure out what might have gone wrong.

ok im back with yet another failed attempt

private bool IsAttackerInLane()
    {
        
          foreach (Transform child in myLaneSpawner.transform)
        {

             bool AttackerRangeConfirm = child.position.x - gameObject.transform.position.x > Range;
             if (AttackerRangeConfirm)
            {
                return false;
            }
            else { return true; }

        }
         
     }

it just says that not all code paths return a value and show a red line on IsAttackerInLane

this didnt work too
pls tell why nothing is working
its like bool method doesnt accept foreach loops

Shouldn’t the method return true if the child is within the range? And you certainly do not want to return false if the current child is not within range. Otherwise, the program will not check the other children.

    private bool IsAttackerInLane()
    {
        
        foreach (Transform child in myLaneSpawner.transform)
        {

             bool AttackerRangeConfirm = child.position.x - gameObject.transform.position.x > Range;
             if (AttackerRangeConfirm) { return true; }
        }

        return false;
         
     }

Remove the attacker == null from my if-conditions because you don’t use the attacker variable anymore.

omg it finally works thanks a lot
heres the final script incase someone wants it

private bool IsAttackerInLane()
    {
        
        if (
        myLaneSpawner == null
        || myLaneSpawner.transform.childCount == 0
          ) { return false; }

        foreach (Transform child in myLaneSpawner.transform)
        {

             bool AttackerRangeConfirm = child.position.x - gameObject.transform.position.x <= Range;
             if (AttackerRangeConfirm)
            {
                return true;
            }
            

        }
        return false;
        
         
     }

Awesome! :slight_smile:

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms