Change Sprite of objects tagged "ball" [solved]

I’m attempting to change the sprites of the balls in play in my brick breaker game. I currently have a regular ball and, if the player gets the powerup, a couple other candy balls, all tagged “ball” that are able to break bricks. They both have the “Ball” script on them. I have an overall manager script that handles when powerups hit the paddle. I’m attempting to code a powerup that, when it hits the collider, it changes the tag of the balls in play from “ball” to “soft ball” (I’m not considering capitalization while writing these tags). That will stop them from being able to break bricks. THIS part of the code works. The problem comes when I try to change the sprite image. I want it to APPEAR as a “soft ball” while the powerup is active, then turn back. Here is the section of the code on the manager script:

    public void ActivePopcorn()
    {
        GameObject[] balls = GameObject.FindGameObjectsWithTag("Ball");
        foreach(GameObject sball in balls)
        {
            sball.transform.tag = "Soft Ball";
            ShowNextHitSprite();
        }
        StartCoroutine(UnactivePopcorn());
    }

    // Ienumerator UnictiveShield
    IEnumerator UnactivePopcorn()
    {
        // Wait seconds(shieldDuration) to execute next lines
        yield return new WaitForSeconds(popcornDuration);

        // Get Animator component and set this bool "on" to false and it unactive shield.
        GameObject[] balls = GameObject.FindGameObjectsWithTag("Soft Ball");
        foreach(GameObject sball in balls)
        {
            sball.transform.tag = "Ball";
            ReturnSprite();
        }
    }


    public void ShowNextHitSprite()
    {
        Debug.Log("1");
        snowBall.ChangeHitSprite();
        Debug.Log("2");
    }
    public void ReturnSprite()
    {
        Debug.Log("3");
        snowBall.ChangeBack();
        Debug.Log("4");
    }

When playing, the console only Debugs “1” so it doesn’t get very far into the script.
In the Ball script I have:

    public void ChangeHitSprite()
    {
        if(hitSprites[1] != null)
        {
            GetComponent<SpriteRenderer>().sprite = hitSprites[1];
        }
        else
        {
            Debug.Log("Block sprite is missing array" + gameObject.name);
        }
    }
    public void ChangeBack()
    {
        if(hitSprites[0] != null)
        {
            GetComponent<SpriteRenderer>().sprite = hitSprites[0];
        }
        else
        {
            Debug.Log("Block sprite is missing array" + gameObject.name);
        }
    }

And even though I’m getting a nullReference, at the " snowBall.ChangeHitSprite();" line from the manager script, the Debug in the ball script isn’t being thrown either. I have sprites set in the ball script component in the ball, and candy ball prefabs, and yet nothing. I can’t figure out what the issue is, any help would be great.

Hi Amber,

Try to add Debug.Logs at the top of the ChangeBack and the ChangeHitSprite method to see if the methods get executed. The Debug.Log in the else block gets called only when the ChangeBack and ChangeHitSprite method respectively was called and if-condition was not evaluated to true.

Try to figure out what works and where the code stops working.

What is the first script? I’m wondering why ActivePopcorn and UnactivePopcorn access various objects while ShowNextHitSprite and ReturnSprite seem to access only one ball: snowBall.

I debugged those areas but none of them were displayed in the console, only “1”. The reason I have a ball array but only one ball script is because all of the balls within the array have the ball script on them. But they are named and tagged different things (i.e. ball, and candy ball) I have this for other reasons in other scripts. So since every ball has a component ball script, then I figured I could access the script on every ball, and change the public sprite for each. I’ve successfully changed all of their tags, but it stops the second I attempt to access their ball scripts.

Please check whether all message types are enabled in your console. If “1” appeared, “2” should have appeared, too, unless the programm stopped working because of something that was caused during this method call: snowBall.ChangeHitSprite();.

If you cannot find the problem, I would suggest to rework your code a bit. Create a BallTypeHandler class (maybe you will come up with a better name) which is supposed to handle the logic when a ball is supposed to change its tag. It could also have a List or an array with all balls in the scene, and it accesses the respective Ball instances.

Your Ball class could contain the “inner logic” of the ball instance itself such as a method which changes the sprite or the tag when it gets called, and so on.

This will make debugging way easier because you could check the Ball instance and the BallTypeHandler instance seperately. If the Ball works, you could focus on the Handler, and vice versa.

Unless I’m misunderstanding something here, the logic is currently intertwined in one class. The approach itself is not wrong by any means but it is difficult to debug, especially when the logic becomes more complex.

Do you know method overloading? If not, look it up. Maybe it could help you.


Instead of working with tags, you could consider using an enum in your Ball class. The (single) Handler (and other things) could read the enum value assigned to the respective Ball instance.

An example (in a separate empty .cs script):

public enum BallType
{
    Soft,
    Hard
}

In your Ball.cs:

BallType type = BallType.Soft;

public void GetBallType()
{
    return type;
}

// method overloading
public void SetBallType (BallType type)
{
    this.type = type; // 'this' refers to the instance variable
}

For now I’ll rework some of my code. A lot of what you said is a bit beyond me, so if I can’t sort it, I’ll start researching the rest. Thank you, and thanks for at least letting me know that it doesn’t seem technically unusual and that I have the right idea.

If the enum part is too confusing, keep the tags. What I described are just a few ideas. In many cases, there are multiple ways to make something work in Unity, so do explore your own ideas. It’s better anyway to figure out by oneself what works and what doesn’t. And if something does not work well, you could test an alternative.

Good luck! :slight_smile:

And update, I’ve gotten the script to debug at every location, including directly after the change the sprite method in the ball script, so every location of the script is being called. The problem was that, despite locating the balls through their tag, the gameObject that was holding my manager script needed the ball and candyball prefabs attached. So now with everything being debugged, the sprites themselves still aren’t being changed. It flows with no blockage but the change still isn’t happening and I’ve run out of places to look.

Did you also add a Debug.Log to the code block where the sprite is supposed to get changed? If so and if the message appears, log the name of the sprite into your console. To get more information, you could log GetComponent<SpriteRenderer>().sprite.name into your console before and after the sprite changed. And you could log hitSprites[0].name into your console. Then you should have the relevant information.

There is a chance that your code works perfectly fine but the wrong sprite gets assigned. In that case, you would have to check the hitSprites array in the Inspector.

Another idea: If your sprites change based on the tag value, log the tag into your console after it was changed. It might be that the tag gets updated with a delay. It’s just a theory but definitely something one should check to be on the safe side.

When debugging, make assumptions and try to verify/falsify them with Debug.Logs. Never assume that something works just because your code appears to be correct because there is still Unity which creates things during runtime. And that’s something you cannot control, so you have to check it.

Okay, so I ended up reworking my code. I went into the ball script and put in update(), if this object’s tag is “Soft Ball” change the sprite to hitsprite[1], and if the tag is “ball” then change it back to hitsprite[0]. Basically I took the changing method call out of the manager, and left the manager only to change the tags. If there is a better place to put this method call other than update, please let me know any tips, otherwise, I’ll consider this solved.

Good job! Changing the sprite in Update is a fine solution. :slight_smile:

If you want to challenge yourself, you could create a public method in the Ball which changes the sprite depending on a string or enum value that gets passed on to it. Somewhere else in your code, you set the type of the ball (“soft ball”, “hard ball”, etc.). That’s the moment when you could call the aforementioned public method.

In my example, I named the method SetBallType. In the method code block, you could simply change the sprite and whatever property your ball needs to have changed depending on the passed on type.

This way, the code would get executed only when needed, not each frame.

However, this is a little game, so using Update and compare a tag with a string each frame will not have any noticeable impact on the performance. In larger games, this would be a candidate to optimise the performance.


See also:

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

Privacy & Terms