Search Unity

Question How to Scale a SpriteRenderer's Sprite in Code?

Discussion in 'Scripting' started by redapplesonly, May 25, 2023.

  1. redapplesonly

    redapplesonly

    Joined:
    Nov 8, 2021
    Posts:
    51
    I'm building a 2D space strategy game where the gameboard will be a large array of Prefabs. The Prefab has a child 2D Sprite Rectangle which is intended to hold a picture of a planet, and then a few other child objects, like a TextMeshPro for a little nametag. Here's the Prefab in the Hierarchy, note that I've highlighted the 2D Sprite, which is called "SOImage":


    For reasons too involved to discuss here, I don't preload my Sprites into my Unity project and then assign them to SOImage by dragging-and-dropping them. Instead, I want to load all images from a collection of .JPG files on my harddrive. To do that, I have a method in the Prefab's C# script which loads a file and converts it into a 2D Sprite:

    Code (CSharp):
    1.     private Sprite LoadImageFile(string path)
    2.     {
    3.         byte[] bytes = File.ReadAllBytes(path);
    4.         Texture2D texture = new Texture2D(2, 2);
    5.         texture.LoadImage(bytes);
    6.         return Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
    7.     }
    Then, in an outer method, I set the image. Note that I also want to scale the image, so that it fits perfectly inside SOImage:

    Code (CSharp):
    1.     private void SetPlanetImage(string PathToImageFile)
    2.     {
    3.         SpriteRenderer sr = this.GetComponent<SpriteRenderer>();
    4.         Sprite imageSprite = LoadImageFile(PathToImageFile);
    5.         sr.sprite = imageSprite;       // Set the Sprite into GameObject SOImage
    6.  
    7.         Debug.Log("BEFORE:  The image is: " + sr.transform.localScale.x * sr.sprite.bounds.size.x + " by " + sr.transform.localScale.y * sr.sprite.bounds.size.y);
    8.         sr.transform.localScale = new Vector3(.65f, .65f, 1);        // SOImage is .65f by .65f in dimension
    9.         Debug.Log("AFTER:   The image is: " + sr.transform.localScale.x * sr.sprite.bounds.size.x + " by " + sr.transform.localScale.y * sr.sprite.bounds.size.y);
    10.     }
    Everything works... except for that scaling part. Here's a screenshot of my gameboard; notice that in the squares that are supposed to host planet images, the image is there, but its clearly not scaling to .65f by .65f:



    I also see this in my Unity console log:

    BEFORE: The image is: 1.222 by 1.157
    AFTER: The image is: 1.222 by 1.157
    BEFORE: The image is: 2.054 by 2.0475
    AFTER: The image is: 2.054 by 2.0475
    BEFORE: The image is: 1.0985 by 1.144
    AFTER: The image is: 1.0985 by 1.144

    So this line:
    Code (CSharp):
    1. sr.transform.localScale = new Vector3(.65f, .65f, 1);
    is clearly not doing what I'd like it to do.

    Sooooooooooooo how might I fix this? I've looked in the MS Visual Studio debugger, and I do see that I'm setting that localScale variable:



    But obviously this isn't the correct way to do this. Does anyone see what I'm doing wrong? Thanks for reading the whole post.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    You want to avoid all RectTransform manipulation due to the complex nature of RectTransform and how it works.

    It is EXTREMELY counter-intuitive, and I have used a metric ton of UI and scaling architectures down the years.

    Instead, just fab what you need in the Editor, tune it all up, anchor, scale, etc., then in code instantiate what you need based on your read-in data stream. Any other method is madness, pure madness.

    See the enclosed simple example for how.
     

    Attached Files:

    redapplesonly likes this.
  3. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    Does not appear OP is using RectTransforms (and thus, not using UI) But it seems to be SpriteRenderer.

    For OP, If you manually add an object with a SpriteRenderer component on it to the scene and then adjust it's scale, you'll indeed see that the size of your sprite changes. So, with this in mind, you are doing things correctly.

    You could also just throw in an object with a SpriteRenderer all by it's lonesome and try to resize it's scale through script and see what happens. It will indeed resize. So, that leaves us with the conclusion that something else is overwriting the scaling so that the code doesn't change it, or, somehow you're targeting the wrong object. I would start my debugging with these in mind.
     
    redapplesonly likes this.
  4. redapplesonly

    redapplesonly

    Joined:
    Nov 8, 2021
    Posts:
    51
    @Brathnann @MaltsGangfoot Thanks you guys, your advice pushed me in the correct direction. I am using a SpriteRenderer, as @Brathnann pointed out, and your comments made me realize that my code WAS rescaling my images... just not by the right amount. A little mathematical manipulation fixed the ratio by how much I was rescaling:
    Code (CSharp):
    1. private void SetPlanetImage(string PathToImageFile)
    2.     {
    3.         SpriteRenderer sr = this.GetComponent<SpriteRenderer>();
    4.         Sprite imageSprite = LoadImageFile(PathToImageFile);
    5.         sr.sprite = imageSprite;       // Set the Sprite into GameObject SOImage
    6.         Debug.Log("BEFORE:  The image is: " + sr.transform.localScale.x * sr.sprite.bounds.size.x + " by " + sr.transform.localScale.y * sr.sprite.bounds.size.y);
    7.         // NEW STUFF HERE - - - - - - - - - - - - - - - - - - - - -
    8.         float Xwidth = .65f / imageSprite.bounds.size.x;
    9.         float Ywidth = .65f / imageSprite.bounds.size.y;
    10.         sr.transform.localScale = new Vector3(Xwidth, Ywidth, 1);
    11.         // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    12.         Debug.Log("AFTER:   The image is: " + sr.transform.localScale.x * sr.sprite.bounds.size.x + " by " + sr.transform.localScale.y * sr.sprite.bounds.size.y);
    13.     }
    Which led to this console output:

    BEFORE: The image is: 1.222 by 1.157
    AFTER: The image is: .65 by .65
    BEFORE: The image is: 2.054 by 2.0475
    AFTER: The image is: .65 by .65
    BEFORE: The image is: 1.0985 by 1.144
    AFTER: The image is: .65 by .65

    ...and...



    It looks GREAT!!! Thanks for your wisdom!!!
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    HA! Yer totally right, my bad.

    OP: glad you are operational... I love any game with sector galaxy maps!
     
    redapplesonly likes this.
  6. redapplesonly

    redapplesonly

    Joined:
    Nov 8, 2021
    Posts:
    51
    @MaltsGangfoot Thanks Malts! Yeah, I know that this game looks a little homemade. But I've only been learning Unity for a month now, so I'm really happy with the results thus far. Thanks for the nice comments, you've really boosted my confidence today