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

[CODE] LWRP/URP Flip ShadowCaster2D when flipping sprite renderer

Discussion in '2D' started by alesimula, May 26, 2020.

  1. alesimula

    alesimula

    Joined:
    Mar 27, 2019
    Posts:
    7
    I needed this to use sprites with a normal map without having to redraw the ShadowCaster shape;
    I made a small script that will simply mirror the ShadowCaster according to the Spriterenderer's flipX and flipY values, hope someone else finds this useful

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Experimental.Rendering.Universal;
    3. using System.Reflection;
    4.  
    5. [ExecuteInEditMode]
    6. public class ShadowCasterFlip : MonoBehaviour
    7. {
    8.     [SerializeField] [HideInInspector] private bool flipX = false;
    9.     [SerializeField] [HideInInspector] private bool flipY = false;
    10.     private SpriteRenderer spriteRenderer;
    11.     private ShadowCaster2D shadowCaster;
    12.     private static BindingFlags accessFlagsPrivate = BindingFlags.NonPublic | BindingFlags.Instance;
    13.     private static FieldInfo meshField = typeof(ShadowCaster2D).GetField("m_Mesh", accessFlagsPrivate);
    14.     private static FieldInfo shapePathField = typeof(ShadowCaster2D).GetField("m_ShapePath", accessFlagsPrivate);
    15.     private static MethodInfo onEnableMethod = typeof(ShadowCaster2D).GetMethod("OnEnable", accessFlagsPrivate);
    16.  
    17.     //Bounds bounds => GetComponent<Renderer>()?.bounds ?? GetComponent<Collider2D>()?.bounds ?? new Bounds(transform.position, Vector3.one);
    18.  
    19.     private void flipCaster(bool flipX, bool flipY) {
    20.         if (!flipX && !flipY) return;
    21.         var shapePath = shapePathField.GetValue(shadowCaster) as Vector3[];
    22.         var newShapePath = new Vector3[shapePath.Length];
    23.  
    24.         int newStartIndex = 0;
    25.         float? lowestAvg = null;
    26.  
    27.         for(int i = 0; i<shapePath.Length; i++) {
    28.             shapePath[i].x = shapePath[i].x * (flipX ? -1 : 1);
    29.             shapePath[i].y = shapePath[i].y * (flipY ? -1 : 1);
    30.             var newAvg = (shapePath[i].x+shapePath[i].y)/2;
    31.             if (lowestAvg == null || newAvg<lowestAvg) {
    32.                 newStartIndex = i;
    33.                 lowestAvg = newAvg;
    34.             }
    35.         }
    36.         if (flipX != flipY) for(int i = 0, ni = newStartIndex; i < shapePath.Length; i++, ni--) {
    37.             if (ni < 0) ni = shapePath.Length-1;
    38.             newShapePath[i] = shapePath[ni];
    39.         }
    40.         else for(int i = 0, ni = newStartIndex; i < shapePath.Length; i++, ni++) {
    41.             if (ni >= shapePath.Length) ni = 0;
    42.             newShapePath[i] = shapePath[ni];
    43.         }
    44.        
    45.         shapePathField.SetValue(shadowCaster, newShapePath);
    46.         meshField.SetValue(shadowCaster, null);
    47.         onEnableMethod.Invoke(shadowCaster, new object[0]);
    48.     }
    49.  
    50.     void Start() {
    51.         spriteRenderer = GetComponent<SpriteRenderer>();
    52.         shadowCaster = GetComponent<UnityEngine.Experimental.Rendering.Universal.ShadowCaster2D>();
    53.     }
    54.    
    55.     void Update() {
    56.         flipCaster((flipX != spriteRenderer.flipX), (flipY != spriteRenderer.flipY));
    57.         flipX = spriteRenderer.flipX;
    58.         flipY = spriteRenderer.flipY;
    59.     }
    60. }
     
  2. alesimula

    alesimula

    Joined:
    Mar 27, 2019
    Posts:
    7
    For some reason I cannot edit my post, here is a version that only does changes in inspector mode and saves your precious CPU cycles

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Experimental.Rendering.Universal;
    3. using System.Reflection;
    4.  
    5. [ExecuteInEditMode]
    6. public class ShadowCasterFlip : MonoBehaviour
    7. {
    8.     [SerializeField] [HideInInspector] private bool flipX = false;
    9.     [SerializeField] [HideInInspector] private bool flipY = false;
    10.     private SpriteRenderer spriteRenderer;
    11.     private ShadowCaster2D shadowCaster;
    12.     private static BindingFlags accessFlagsPrivate = BindingFlags.NonPublic | BindingFlags.Instance;
    13.     private static FieldInfo meshField = typeof(ShadowCaster2D).GetField("m_Mesh", accessFlagsPrivate);
    14.     private static FieldInfo shapePathField = typeof(ShadowCaster2D).GetField("m_ShapePath", accessFlagsPrivate);
    15.     private static MethodInfo onEnableMethod = typeof(ShadowCaster2D).GetMethod("OnEnable", accessFlagsPrivate);
    16.     private static bool isInspector =>
    17.     #if UNITY_EDITOR
    18.         !UnityEditor.EditorApplication.isPlaying;
    19.     #else
    20.         false;
    21.     #endif
    22.  
    23.     //Bounds bounds => GetComponent<Renderer>()?.bounds ?? GetComponent<Collider2D>()?.bounds ?? new Bounds(transform.position, Vector3.one);
    24.  
    25.     private bool flipCaster(bool flipX, bool flipY) {
    26.         if (!flipX && !flipY) return false;
    27.         var shapePath = shapePathField.GetValue(shadowCaster) as Vector3[];
    28.         var newShapePath = new Vector3[shapePath.Length];
    29.  
    30.         int newStartIndex = 0;
    31.         float? lowestAvg = null;
    32.  
    33.         for(int i = 0; i<shapePath.Length; i++) {
    34.             shapePath[i].x = shapePath[i].x * (flipX ? -1 : 1);
    35.             shapePath[i].y = shapePath[i].y * (flipY ? -1 : 1);
    36.             var newAvg = (shapePath[i].x+shapePath[i].y)/2;
    37.             if (lowestAvg == null || newAvg<lowestAvg) {
    38.                 newStartIndex = i;
    39.                 lowestAvg = newAvg;
    40.             }
    41.         }
    42.         if (flipX != flipY) for(int i = 0, ni = newStartIndex; i < shapePath.Length; i++, ni--) {
    43.             if (ni < 0) ni = shapePath.Length-1;
    44.             newShapePath[i] = shapePath[ni];
    45.         }
    46.         else for(int i = 0, ni = newStartIndex; i < shapePath.Length; i++, ni++) {
    47.             if (ni >= shapePath.Length) ni = 0;
    48.             newShapePath[i] = shapePath[ni];
    49.         }
    50.      
    51.         shapePathField.SetValue(shadowCaster, newShapePath);
    52.         shadowCaster.enabled = false;
    53.         meshField.SetValue(shadowCaster, null);
    54.         shadowCaster.enabled = true;
    55.         return true;
    56.     }
    57.  
    58.     #if UNITY_EDITOR
    59.     void Start() {
    60.         if (isInspector) {
    61.             spriteRenderer = GetComponent<SpriteRenderer>();
    62.             shadowCaster = GetComponent<UnityEngine.Experimental.Rendering.Universal.ShadowCaster2D>();
    63.         }
    64.     }
    65.     #endif
    66.  
    67.     #if UNITY_EDITOR
    68.     void Update() {
    69.         if (isInspector && flipCaster(flipX != spriteRenderer.flipX, flipY != spriteRenderer.flipY)) {
    70.             flipX = spriteRenderer.flipX;
    71.             flipY = spriteRenderer.flipY;
    72.             UnityEditor.EditorUtility.SetDirty(shadowCaster);
    73.             UnityEditor.EditorUtility.SetDirty(this);
    74.         }
    75.     }
    76.     #endif
    77. }
    78.  
     
    Last edited: May 27, 2020