Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Trying to use foreach loop with a search for tags function

Discussion in 'Scripting' started by danmct1995, Sep 9, 2020.

  1. danmct1995

    danmct1995

    Joined:
    Apr 21, 2020
    Posts:
    70
    I have a powerup script where I'd like to multiply the amount of a gameobject by the amount of the gameobject currently existing. For example, I have 3 objects on the screen and the powerup is picked up, the game would multiply the amount of objects on the screen to 9, by instantiating two new objects to each existing object.

    I have done this successfully, but the problem I am running into is when I have clones on the screen and the powerup is received again. This causes an error to be thrown if the pre-existing object is destroyed so I am trying to assign tags to the objects to find them through GameObject.FindObjectsOfTag ("Object"). The problem is that Unity will not allow me to search objects with this tag because I am assigning the class "Ball". I want to search for the objects with a foreach loop and run my method upon picking up the powerup, but I am throwing an error in my script at obtaining the tags in the array to begin with.

    How would I get a reference to each object and run them through a foreach loop?

    What I have currently is & my error is thrown within the update method and at any line where the variable from my update method is called:

    Code (CSharp):
    1.     public void Update()
    2.     {
    3.     Ball[] balls = GameObject.FindGameObjectsWithTag("Ball");
    4.     }
    5.     public void OnTriggerEnter2D(Collider2D PowerUp)
    6.     {
    7.         if (PowerUp.gameObject.name == "Paddle")
    8.         {
    9.             foreach (Ball ball in balls)
    10.             {
    11.                 Multiply();
    12.             }
    13.             FindObjectOfType<ScoreManager>().PowerupPoints();
    14.             Destroy(gameObject);
    15.         }
    16.         else if (PowerUp.transform.CompareTag("DeathBarrier"))
    17.         {
    18.             Destroy(gameObject);
    19.         }
    20.     }
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    I see a few glaring issues. First, any find call in Update is not performant and is not suggested to be used.
    Next, you are creating a local variable in Update but not doing anything with it. It looks like you may have a variable as well in your script, so maybe you intended to overwrite it?

    If that is the case you should change your Update to this.
    Code (CSharp):
    1. public void Update()
    2.     {
    3.         balls = GameObject.FindGameObjectsWithTag("Ball");
    4.     }
    However, I once again caution against this. Instead, I would suggest you have a script managing your objects when they are spawned or destroyed and have things added to or removed from a list. Then instead of looping through, perhaps just grab a count and use some math to multiply it (unless you have some other logic going on, in which case, loop through the list)

    Note you could be running into other issues depending on how long your foreach loop takes to run (if you had a lot of objects and your multiply function were complex enough.)
     
    danmct1995 likes this.
  3. danmct1995

    danmct1995

    Joined:
    Apr 21, 2020
    Posts:
    70
    Moving my tag search seemed like a good call so I moved it right before it needs to be called only. The problem I'm running into is still that it "Cannot implicity convert UnityEngine.GameObject[] to Ball[]". I've taken a course on udemy for c#, but never came across foreach loops or really any type of loop in it, so my understanding is only from youtube videos. Am I initializing the "balls" variable wrong at the start of my class?

    My entire updated script for this powerup is here:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using SampleGame;
    5.  
    6. public class Multiball : MonoBehaviour
    7. {
    8.     Ball ball;
    9.     float speed = 4f;
    10.     //  Random Range Variables for instantiation of ball
    11.     [SerializeField] float LowVelocity = 9f;
    12.     [SerializeField] float HighVelocity = 11f;
    13.     // Other Ball opposite direction
    14.     [SerializeField] float LowVelocityNegative = -9f;
    15.     [SerializeField] float HighVelocityNegative = -11f;
    16.     Ball[] balls;
    17.     public void Start()
    18.     {
    19.         Vector3 direction = Vector3.down;
    20.         Vector3 velocity = speed * direction;
    21.         GetComponent<Rigidbody2D>().velocity = velocity;
    22.     }
    23.  
    24.     public void OnTriggerEnter2D(Collider2D PowerUp)
    25.     {
    26.         if (PowerUp.gameObject.name == "Paddle")
    27.         {
    28.             balls = GameObject.FindGameObjectsWithTag("Ball");
    29.             foreach (Ball ball in balls)
    30.             {
    31.                 Multiply();
    32.             }
    33.             FindObjectOfType<ScoreManager>().PowerupPoints();
    34.             Destroy(gameObject);
    35.         }
    36.         else if (PowerUp.transform.CompareTag("DeathBarrier"))
    37.         {
    38.             Destroy(gameObject);
    39.         }
    40.     }
    41.     public void Multiply()
    42.     {
    43.         if (balls != null)
    44.         {
    45.             Ball ball1 = Instantiate(ball, balls.transform.position, balls.transform.rotation);
    46.             ball1.GetComponent<Rigidbody2D>().velocity = new Vector2(balls.GetComponent<Rigidbody2D>().velocity.x + 1, balls.GetComponent<Rigidbody2D>().velocity.y + 1);
    47.  
    48.             Ball ball2 = (Ball)Instantiate(ball, balls.transform.position, balls.transform.rotation);
    49.             ball2.GetComponent<Rigidbody2D>().velocity = new Vector2(balls.GetComponent<Rigidbody2D>().velocity.x - 1, balls.GetComponent<Rigidbody2D>().velocity.y - 1);
    50.         }
    51.     }
    52.  
    53. }
     
  4. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    danmct1995 likes this.
  5. danmct1995

    danmct1995

    Joined:
    Apr 21, 2020
    Posts:
    70
    oooooooh I had tried the object of type before and i couldn't get it to work. It finally worked when a added the "S" to object. thank you lol!
     
  6. danmct1995

    danmct1995

    Joined:
    Apr 21, 2020
    Posts:
    70
    I do have one last problem in my code though in this section:

    Code (CSharp):
    1.     public void Multiply()
    2.     {
    3.         if (balls != null)
    4.         {
    5.             Ball ball1 = Instantiate(ball, balls.transform.position, balls.transform.rotation);
    6.             ball1.GetComponent<Rigidbody2D>().velocity = new Vector2(balls.GetComponent<Rigidbody2D>().velocity.x + 1, balls.GetComponent<Rigidbody2D>().velocity.y + 1);
    7.  
    8.             Ball ball2 = (Ball)Instantiate(ball, balls.transform.position, balls.transform.rotation);
    9.             ball2.GetComponent<Rigidbody2D>().velocity = new Vector2(balls.GetComponent<Rigidbody2D>().velocity.x - 1, balls.GetComponent<Rigidbody2D>().velocity.y - 1);
    10.         }
    11.     }
    How would I instantiate the clones at the objects? I can't reference my variable I made using the array because I can't reach its transform. I tried to use "balls.transform.position", but I think because its not accessing the individual balls, its not working.
     
  7. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    balls is an array. So you just need to access the index that you want to access. If you want it to spawn at the location of whatever ball you're on in the loop, then just pass that ball to the Multiply function.

    Code (CSharp):
    1. foreach (Ball ball in balls)
    2.             {
    3.                 Multiply(ball.transform);
    4.             }
    Code (CSharp):
    1. public void Multiply(Transform ballTransform)
    2.     {
    3.         if (balls != null)
    4.         {
    5.             Ball ball1 = Instantiate(ball, ballTransform.position, ballTransform.rotation);
    6.             ball1.GetComponent<Rigidbody2D>().velocity = new Vector2(ballTransform.GetComponent<Rigidbody2D>().velocity.x + 1, ballTransform.GetComponent<Rigidbody2D>().velocity.y + 1);
    7.             Ball ball2 = (Ball)Instantiate(ball, ballTransform.position, ballTransform.rotation);
    8.             ball2.GetComponent<Rigidbody2D>().velocity = new Vector2(ballTransform.GetComponent<Rigidbody2D>().velocity.x - 1, ballTransform.GetComponent<Rigidbody2D>().velocity.y - 1);
    9.         }
    10.     }
    *edited in forum post, so might be typos*
     
    danmct1995 likes this.
  8. danmct1995

    danmct1995

    Joined:
    Apr 21, 2020
    Posts:
    70
    Sorry to message again, but it seems to be working much better. I have no errors thrown anymore, but when I run the simulation, the object throws an error code when the powerup should be activated stating "ArgumentException: The Object you want to instantiate is null." . I assumed this was talking about my ball object so I tried playing around a bit, but can't get this error to go away and actually process the function.

    The error seems to come from the "Multiply" function on this specific line:
    Code (CSharp):
    1.             Ball ball1 = Instantiate(ball, ballTransform.position, ballTransform.rotation);
    I really appreciate your help on this. I feel like the loop should be working smoothly now, but it looks like the object isn't being recognized for some reason. Here is the full script I cleaned up a bit:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using SampleGame;
    5.  
    6. public class Multiball : MonoBehaviour
    7. {
    8.     float speed = 4f;
    9.     Ball[] balls;
    10.     Ball ball;
    11.  
    12.     public void Start()
    13.     {
    14.         Vector2 direction = Vector2.down;
    15.         Vector2 velocity = speed * direction;
    16.         GetComponent<Rigidbody2D>().velocity = velocity;
    17.     }
    18.     public void OnTriggerEnter2D(Collider2D PowerUp)
    19.     {
    20.         if (PowerUp.gameObject.name == "Paddle")
    21.         {
    22.             balls = FindObjectsOfType<Ball>();
    23.             FindObjectOfType<ScoreManager>().PowerupPoints();
    24.             foreach (Ball ball in balls)
    25.             {
    26.                 Multiply(ball.transform);
    27.             }
    28.             Destroy(gameObject);
    29.         }
    30.  
    31.         else if (PowerUp.transform.CompareTag("DeathBarrier"))
    32.         {
    33.             Destroy(gameObject);
    34.         }
    35.     }
    36.     public void Multiply(Transform ballTransform)
    37.     {
    38.             Ball ball1 = Instantiate(ball, ballTransform.position, ballTransform.rotation);
    39.             ball1.GetComponent<Rigidbody2D>().velocity = new Vector2(ballTransform.GetComponent<Rigidbody2D>().velocity.x + 1, ballTransform.GetComponent<Rigidbody2D>().velocity.y + 1);
    40.  
    41.             Ball ball2 = Instantiate(ball, ballTransform.position, ballTransform.rotation);
    42.             ball2.GetComponent<Rigidbody2D>().velocity = new Vector2(ballTransform.GetComponent<Rigidbody2D>().velocity.x - 1, ballTransform.GetComponent<Rigidbody2D>().velocity.y - 1);
    43.     }
    44. }
    45.  
     
  9. danmct1995

    danmct1995

    Joined:
    Apr 21, 2020
    Posts:
    70
    Nevermind! I figured out that I just needed to serialize and fill in the slot! Thank you so much for helping me through this!
     
  10. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    Glad you got it working!