title: How to stretch a sprite mask programatically across the screen
body: <p>I am trying to make a sprite mask cover the bottom 90% of my screen, so that my UI which sits on the top 10% can be clearly seen. To do this I am using Unity's SpriteMask script. The problem I am getting is that the only way that I can scale the SpriteMask is by changing its LocalScale, and I am unsure of the calculation required to convert my screen size to the local scale required for this mask.</p>
  <p>I tried using the pixels per unit value but I cant seem to figure out how to relate this to the pixel value of the actual mask's size on screen. When comparing the bounds of the sprite to the various screen width values I can calculate I am struggling to find any relation that would let me calculate them.</p>
  <p>My current scaling script is as follows (and doesn't work)</p>
  <pre><code>using UnityEngine;
  
  [ExecuteInEditMode]
  public class SpriteMaskScreenSizer : ScreenSizer
  {
      [SerializeField] private SpriteMask _mask;
      [SerializeField] private Vector2 _scaleFactor = Vector2.one;
  
      protected override void UpdateSize()
      {
          if (_mask == null)
          {
              _mask = gameObject.GetComponent&lt;SpriteMask&gt;();
          }
  
          float ppu = _mask.sprite.pixelsPerUnit;
          Vector3 newScale = transform.localScale;
          float desiredWidth, xScale, desiredHeight, yScale;
          switch (_stretchDirection)
          {
              case StretchDirection.Horizontal:
                  // Grab the width
                  desiredWidth = ScreenSize.x;
                  xScale = desiredWidth / ppu;
                  newScale.x = xScale * _scaleFactor.x;
                  break;
              case StretchDirection.Vertical:
                  // Grab the height
                  desiredHeight = ScreenSize.y;
                  yScale = desiredHeight / ppu;
                  newScale.y = yScale * _scaleFactor.y;
                  break;
              case StretchDirection.Both:
              {
                  // Grab the width
                  desiredWidth = ScreenSize.x;
                  xScale = desiredWidth / ppu;
                  newScale.x = xScale * _scaleFactor.x;
                  // Grab the height
                  desiredHeight = ScreenSize.y;
                  yScale = desiredHeight / ppu;
                  newScale.y = yScale * _scaleFactor.y;
                  break;
              }
              case StretchDirection.None:
              default:
                  // Do nothing.
                  break;
          }
          transform.localScale = newScale;
      }
  }
  </code></pre>
  <p>The ScreenSizer is a base class that just handles calling update or registering with my resolution change watcher as I have a number of different screen sizers for various types of objects.</p>
  <pre><code>using UnityEngine;
  
  public class ScreenSizer : MonoBehaviour
  {
      protected enum StretchDirection
      {
          Horizontal,
          Vertical,
          Both,
          Custom,
          None
      }
  
      [SerializeField] protected StretchDirection _stretchDirection = StretchDirection.None;
      [SerializeField] private bool _shouldRegisterForResolutionChange = false;
      [SerializeField] private bool _shouldRunOnUpdate = false;
  
      public void Awake()
      {
          if (_shouldRegisterForResolutionChange)
          {
              BaseGame.Instance.ResolutionService.RegisterAction(OnResolutionChanged);
          }
      }
  
      public void Update()
      {
          if (_shouldRunOnUpdate || (!Application.isPlaying &amp;&amp; _shouldRegisterForResolutionChange))
          {
              UpdateSize();
          }
      }
  
      public void OnDestroy()
      {
          if (_shouldRegisterForResolutionChange)
          {
              BaseGame.Instance.ResolutionService.DeregisterAction(OnResolutionChanged);
          }
      }
  
      private void OnResolutionChanged(Vector2 resolution)
      {
          UpdateSize();
      }
  
      protected Vector2 ScreenSize
      {
          get
          {
              Vector2 result = Vector2.zero;
              Vector3 topRight = Camera.main.ViewportToWorldPoint(Vector3.one);
              Vector3 bottomLeft = Camera.main.ViewportToWorldPoint(Vector3.zero);
              result.x = topRight.x - bottomLeft.x;
              result.y = topRight.y - bottomLeft.y;
              return result;
          }
      }
  
      protected virtual void UpdateSize()
      {
          throw new System.NotImplementedException(&quot;Must implement UpdateSize for any screen sizer.&quot;);
      }
  }
  </code></pre>
  <p>I created some info scripts just to visualize in the editor the screen size (two different calculations):</p>
  <pre><code>using UnityEngine;
  
  [ExecuteInEditMode]
  public class ScreenSizeInfo : MonoBehaviour
  {
      [SerializeField] private Vector2 _screenSize = Vector2.zero;
      [SerializeField] private Vector2 _cameraScreenSize = Vector2.zero;
  
      public void Update()
      {
          _screenSize.x = Screen.width;
          _screenSize.y = Screen.height;
  
          Vector3 topRight = Camera.main.ViewportToWorldPoint(Vector3.one);
          Vector3 bottomLeft = Camera.main.ViewportToWorldPoint(Vector3.zero);
          _cameraScreenSize.x = topRight.x - bottomLeft.x;
          _cameraScreenSize.y = topRight.y - bottomLeft.y;
      }
  }
  </code></pre>
  <p>And the extents of the sprite mask (and also of the sprite just to check any relationships):</p>
  <pre><code>using UnityEngine;
  
  [ExecuteInEditMode]
  public class SpriteMaskInfo : MonoBehaviour
  {
      [SerializeField] private SpriteMask _mask;
      [SerializeField] private Vector2 _spriteBounds;
      [SerializeField] private Vector2 _spriteExtents;
      [SerializeField] private Vector2 _maskBounds;
      [SerializeField] private Vector2 _maskExtents;
  
      public void Update()
      {
          if (_mask == null)
          {
              _mask = gameObject.GetComponent&lt;SpriteMask&gt;();
              return;
          }
          _maskBounds = _mask.bounds.size;
          _spriteBounds = _mask.sprite.bounds.size;
          _maskExtents = _mask.bounds.extents;
          _spriteExtents = _mask.sprite.bounds.extents;
      }
  }
  </code></pre>
  <p>This is a screenshot of my editor with my various scripts on the masking object:</p>
  <p><a href="https://i.stack.imgur.com/JWUse.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/JWUse.png" alt="SpriteMash Inspector" /></a></p>
  <p><a href="https://i.stack.imgur.com/LjyQX.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/LjyQX.png" alt="Camera Inspector" /></a></p>
  <p>I would really appreciate any help on what the calculation I need to use to connect these various values if anyone has run into this similar problem.</p>