Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

SpriteShape: modifying the shape at runtime -- the sprite does not show

Discussion in '2D Experimental Preview' started by DerickTP, Nov 21, 2018.

  1. DerickTP

    DerickTP

    Joined:
    Jul 28, 2014
    Posts:
    8
    EDIT: By setting spline quality to medium in the spriteshapecontroller inspector, it seems to work... But i still have to go to the scene window to show the mesh the first time. However the collider is there and my character is floating in thin air...

    Hi there,

    I have beed trying to use the Sprite shape package to make some kind of platformer like the sort of Alto. My idea was to build a few sprite shape controllers ahead of time and repurpose these as needed. So here is what I did:
    - at startup I instanciate 3-4 game objects with sprite shapes and colliders attached (one edge collider for the geometry and one box trigger collider for other purposes). I then modify the shapes as needed to fit my needs.
    - When the object becomes invisible, it is 'repurposed', i.e. I modify the spline in order to continue the path at the end of the currently generated path.

    Basically, I see the first repurposing instance for each object in the logs, but when I reach the end of the path, the path is empty. However the colliders are there, cause my object goes on. By going to the scene window and dezooming, I see the objects pop up on the screen and in my game window... I even tried to delete the objects and recreate them instead and I had no luck there... Here is a link to a gif showing the problem I hope :

    http://i.imgur.com/GB6nfx6.gifv

    And here is the code I use:
    Class that creates the objects and modify the spline
    Code (CSharp):
    1. public class WorldBuilding : MonoBehaviour
    2. {
    3.     public static WorldBuilding instance = null;
    4.     public SpriteShapeController prefab;
    5.  
    6.     [Header("World generic data")]
    7.     public float m_fSectionSize = 10f;
    8.     public float m_fSectionHeight = 100f;
    9.     [Range(3,10)]
    10.     public int m_iNumberOfPreloadedSections = 3;
    11.  
    12.     SpriteShapeController[] controllers;
    13.     int nextSection;
    14.  
    15.     Queue<int> repurposeList = new Queue<int>();
    16.  
    17.     // Use this for initialization
    18.     void Start ()
    19.     {
    20.         if(instance ==null)
    21.         {
    22.             instance = this;
    23.         }
    24.         else
    25.         {
    26.             DestroyImmediate(gameObject);
    27.             return;
    28.         }
    29.         nextSection = m_iNumberOfPreloadedSections;
    30.         CreateShapes();
    31.     }
    32.  
    33.     void CreateShapes()
    34.     {
    35.         controllers = new SpriteShapeController[m_iNumberOfPreloadedSections];
    36.         for (int i = 0; i < m_iNumberOfPreloadedSections; ++i)
    37.         {
    38.             CreateShape(i, i, i == 0);
    39.         }
    40.     }
    41.  
    42.     void CreateShape(int idx, int sectionNumber, bool isFirst = false)
    43.     {
    44.         controllers[idx] = GameObject.Instantiate<SpriteShapeController>(prefab, transform);
    45.         controllers[idx].splineDetail = 32;
    46.  
    47.         MakePath(idx, sectionNumber, isFirst);
    48.     }
    49.  
    50.     void MakePath(int idx, int sectionNumber, bool first = false)
    51.     {
    52.         Spline s = controllers[idx].spline;
    53.         s.Clear();
    54.  
    55.         float startPos = sectionNumber * m_fSectionSize;
    56.         float endPos = (sectionNumber + 1) * m_fSectionSize;
    57.  
    58.         int offset = 0;
    59.         s.InsertPointAt(offset++, new Vector3(endPos, -m_fSectionHeight, 0));
    60.         s.InsertPointAt(offset++, new Vector3(startPos, -m_fSectionHeight, 0));
    61.  
    62.  
    63.         s.InsertPointAt(offset++, new Vector3(startPos, 0, 0));
    64.         if (!first)
    65.         {
    66.             s.SetTangentMode(offset - 1, ShapeTangentMode.Continuous);
    67.             s.SetLeftTangent(offset - 1, new Vector3(-1, 0, 0));
    68.             s.SetRightTangent(offset - 1, new Vector3(1, 0, 0));
    69.         }
    70.  
    71.         s.InsertPointAt(offset++, new Vector3(0.5f * (startPos + endPos), 2f, 0));
    72.         s.SetTangentMode(offset - 1, ShapeTangentMode.Continuous);
    73.         s.SetLeftTangent(offset - 1, new Vector3(-1, 0, 0));
    74.         s.SetRightTangent(offset - 1, new Vector3(1, 0, 0));
    75.  
    76.         s.InsertPointAt(offset++, new Vector3(endPos, 0, 0));
    77.         s.SetTangentMode(offset - 1, ShapeTangentMode.Continuous);
    78.         s.SetLeftTangent(offset - 1, new Vector3(-1, 0, 0));
    79.         s.SetRightTangent(offset - 1, new Vector3(1, 0, 0));
    80.  
    81.         //controllers[idx].BakeMesh(); // Gives an error on splinedetails which sould be between 4 and 32. Setting it right before this call does not change the problem.
    82.         //controllers[idx].spriteShapeRenderer.bounds.SetMinMax(new Vector3(startPos, -m_fSectionHeight, 0), new Vector3(endPos, m_fSectionHeight, 0));
    83.         controllers[idx].BakeCollider();
    84.  
    85.         BoxCollider2D box = controllers[idx].GetComponent<BoxCollider2D>();
    86.         box.size = new Vector2(m_fSectionSize, m_fSectionHeight * 2);
    87.         box.offset = new Vector2(0.5f * (startPos + endPos), 0);
    88.  
    89.         WorldSection section = controllers[idx].GetComponent<WorldSection>();
    90.         section.Index = idx;
    91.         section.WasInside = false;
    92.  
    93.         controllers[idx].gameObject.SetActive(true);
    94.     }
    95.  
    96.     public void Repurpose(int idx)
    97.     {
    98.         Debug.Log("Repurposing " + idx);
    99.         repurposeList.Enqueue(idx);
    100.     }
    101.  
    102.     // Update is called once per frame
    103.     void Update ()
    104.     {
    105.         RebuildPathAhead();
    106.     }
    107.  
    108.  
    109.     void RebuildPathAhead()
    110.     {
    111.         while(repurposeList.Count > 0)
    112.         {
    113.             int idx = repurposeList.Dequeue();
    114.             Debug.Log("Rebuild " + idx);
    115.             DestroyImmediate(controllers[idx].gameObject);
    116.             CreateShape(idx, nextSection++);
    117.             //MakePath(idx, nextSection++, false);
    118.         }
    119.     }
    120. }
    121.  
    Class that handles the becameinvisible part and is attached to the object that has the sprite shape controller component attached to it:
    Code (CSharp):
    1. public class WorldSection : MonoBehaviour
    2. {
    3.     int index;
    4.     public int Index
    5.     {
    6.         get { return index; }
    7.         set { index = value; }
    8.     }
    9.  
    10.     bool wasInside = false;
    11.     public bool WasInside
    12.     {
    13.         get { return wasInside; }
    14.         set { wasInside = value; }
    15.     }
    16.  
    17.     // Use this for initialization
    18.     void Start ()
    19.     {
    20.  
    21.     }
    22.  
    23.     // Update is called once per frame
    24.     void Update ()
    25.     {
    26.  
    27.     }
    28.  
    29.     private void OnTriggerStay2D(Collider2D collision)
    30.     {
    31.         //Debug.Log("Inside section");
    32.         wasInside = true;
    33.     }
    34.  
    35.     public void OnBecameInvisible()
    36.     {
    37.         Debug.Log("BecameInvisible");
    38.         if (!wasInside) return;
    39.         WorldBuilding.instance?.Repurpose(index);
    40.         gameObject.SetActive(false);
    41.     }
    42. }
    43.  
    Thanks for any help that you could give and ask me if you need more infos!.
     
    Last edited: Nov 22, 2018
  2. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    @DerickTP Could you please file a bug report with a simple repro case ? We will take a look asap as it does look like a bug.
     
  3. DerickTP

    DerickTP

    Joined:
    Jul 28, 2014
    Posts:
    8
    Thanks, done it. Bug number is 1103327.
     
  4. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    @DerickTP Thanks for submitting the bug report. While we investigate the issue (is most likely related to regarding Bounds), as a workaround, please try to modify the SpriteShape in the Prefab to be of much larger size. This should fix the issue. Please do let us know if this works.
     
    DerickTP likes this.
  5. DerickTP

    DerickTP

    Joined:
    Jul 28, 2014
    Posts:
    8
    Hi, thanks it seems to work by increasing the size of the sprite shape for now. Thanks for the responsivenes!
     
  6. squarelover

    squarelover

    Joined:
    Nov 14, 2012
    Posts:
    31
    still not resolved. i dont get what do you both mean by using much bigger size SpriteShape ?
     

    Attached Files:

  7. danBourquin

    danBourquin

    Joined:
    Nov 16, 2016
    Posts:
    34
    I have the same issue
     
  8. Npicouet

    Npicouet

    Joined:
    May 17, 2018
    Posts:
    30
    This issue still exists when instantiating a sprite shape controller from prefab.
    There should be an option to "Always Render" like on animator.
     
  9. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    This issue has been fixed and will be available soon. Will post an update here.
    However please do help us reporting it as a bug with a simple repro project. Will take a look asap to see if its something unrelated. Thanks.
     
  10. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    I think I have the same problem, is this issue been fixed and released?

    Also, is there any way to force update mesh building and adjusting its bounds during runtime?
     
  11. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Any news with this? Is this fixed? My spriteshape sometimes does not render and then when I move camera a bit , suddenly it starts to render. I think this may got to do with the bounds, but I am not sure how to properly set the bound after calling RefreshMesh or BakeMesh. I thought that the bounds are supposed to be calculated properly upon generating the mesh for it?
     
  12. UnityIco

    UnityIco

    Joined:
    Jan 5, 2017
    Posts:
    18
    We got the same issue here. Was the fix updated to some Unity version?
    Our prefab with spriteshape doesn't show up, until we zoom in on it in scene view.
     
    castor76 likes this.
  13. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Is there public issue tracker for this?
     
  14. UnityIco

    UnityIco

    Joined:
    Jan 5, 2017
    Posts:
    18
    If I call SpriteShapeController ForceBakeMesh it will start working. But I don't know if that is the way to go. For now, I will use it.
     
  15. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    I will try this as well...
     
  16. itsonh2610

    itsonh2610

    Joined:
    Jul 15, 2017
    Posts:
    9
    I have the same issue right now with newest version :(
    Any news with this?
     
  17. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    where is ForceBakeMesh API?
     
  18. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    "Available Soon" ? That was 3 months ago? Is this even implemented yet? Any news?
     
  19. UnityIco

    UnityIco

    Joined:
    Jan 5, 2017
    Posts:
    18
    It's in SpriteShapeController script but it isn't easily accessible because it is in the package. So I took what I needed for my SpriteShape to show up, and put it into my script. I call this only once when I need the spriteshape toappear :

    Code (CSharp):
    1. if (spriteShapeRenderer != null)
    2.         {
    3.  
    4.             controller.BakeMesh();  //this is the SpriteShapeController
    5.  
    6.             UnityEngine.Rendering.CommandBuffer rc = new UnityEngine.Rendering.CommandBuffer();
    7.             rc.GetTemporaryRT(0, 256, 256, 0);
    8.             rc.SetRenderTarget(0);
    9.             rc.DrawRenderer(spriteShapeRenderer, spriteShapeRenderer.sharedMaterial);
    10.             rc.ReleaseTemporaryRT(0);
    11.             Graphics.ExecuteCommandBuffer(rc);
    12.  
    13.         }
     
  20. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    I am already doing .BakeMesh command. I was wondering if there was another one called "ForceBakeMesh" API :D

    However, I am interested to find out the rendering part... using command buffer. I am assuming that this is only for your specific project need?

    For me, I do call BakeMesh, but the renderer still does not render them right. I have to move my camera around a bit to probably trigger the rendering. Possibly to do with bounds. So I am assuming that this is still not fixed or fix is still not in the 2021.1.6f1 at least. ?

    @Venkify can you confirm please?
     
  21. UnityIco

    UnityIco

    Joined:
    Jan 5, 2017
    Posts:
    18
    Yeah I know about the BakeMesh but it didn't do the trick for me.

    That's why I am using ForceBakeMesh, it's where I got the rendering part from. But you can't just call it from your code even if you extend the function to be public it will reset on Unity Restart because it is inside the Packages\2D SpriteShape\Runtime\SpriteShapeController.

    So I took the rendering code out and placed it in my class, and It works for me. We got a vine that we spawn from player to position, and this vine is a sprite shape so we can customize it. Each time I spawn a new vine I force it to render with that piece of code. Hope it helps.
     
  22. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Are you calling it per every frame? I am asking because the rendering code seems like it needs to be doing per frame and with 256 x 256 fixed buffer size, how do you deal with different sized sprite shape?
     
  23. UnityIco

    UnityIco

    Joined:
    Jan 5, 2017
    Posts:
    18
    No, I am actually calling it only once when we fire the vine from the player to the target point.
    And you don't need to call it every frame cause from that point on it's drawn just fine on the screen. I am just using it to get it to render for the first time.

    I am not sure about huge sprite shapes, but our vine is kinda flexible and it seems to work fine on different sized vines.
     
  24. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Right... but I still think the size somehow should matter, so I will hold on to the official fix. This should really be fixed and 2Drenderer team should communicate more often about what is going on.
     
  25. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    @castor76 Agreed, we have added a new API to set custom Bounds in 2021.2 to support fixing the above.

    https://docs.unity3d.com/2021.2/Doc...nce/U2D.SpriteShapeRenderer.SetLocalAABB.html

    We are fixing the above issue in an upcoming release. Until then, please use the Render Mesh workaround or set custom Bounds to reflect any changes made to SpriteShape object on Runtime.
     
  26. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Please let us know which version of Unity is going to fix the issue, or which SpritShape package version number.
     
  27. Kothered

    Kothered

    Joined:
    Mar 5, 2019
    Posts:
    7
    I have the same issue.. And BakeMesh() doesn't work for me :(
     
  28. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Also, how do you set custom bounds? How do you calculate it?...
     
    Last edited: May 21, 2021
  29. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    @Venkify, it has been more than a month since last time you said the bug will be fixed. Is there any news to this yet?
     
  30. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    Sorry for the delay. Please use the SetLocalAABB API to ensure that any runtime modification to the SpriteShape updates the Bounds.

    An example script that can be attached to GameObject (that has dynamically modified SpriteShapeController) is as follows :

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.U2D;
    6.  
    7. public class SpriteShapeUpdateBounds : MonoBehaviour
    8. {
    9.     int boundsHash = 0;
    10.     SpriteShapeController sc;
    11.     SpriteShapeRenderer sr;
    12.  
    13.     void Start()
    14.     {
    15.         sc = gameObject.GetComponent<SpriteShapeController>();
    16.         sr = gameObject.GetComponent<SpriteShapeRenderer>();
    17.     }
    18.  
    19.     // Update is called once per frame
    20.     void Update()
    21.     {
    22.         Bounds bounds = new Bounds();
    23.         for (var i = 0; i < sc.spline.GetPointCount(); ++i)
    24.             bounds.Encapsulate(sc.spline.GetPosition(i));
    25.         bounds.Encapsulate(transform.position);
    26.      
    27.         if (boundsHash != bounds.GetHashCode())
    28.         {
    29.             sr.SetLocalAABB(bounds);
    30.             boundsHash = bounds.GetHashCode();
    31.         }
    32.     }
    33. }
    34.  
    This works for versions Unity 2021.2 and above. For older versions, please use the workaround suggested here : https://forum.unity.com/threads/tel...-with-sprite-shape.686305/page-3#post-6083610
     
    FaithlessOne and Kei12345 like this.
  31. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    For Generate Sprite Shape code it basically assumes that the size if within 256x256?

    Why can't the above fix be backported to 2021.1 ?
     
  32. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    No, the bounds calculation is fine. Setting the bounds simply makes the object to be visible so geometry generation is triggered. It's the same case for SkinnedMeshRenderer or 2D animation etc where Mesh is deformed and may need update of bounds.
    The API is only available since 2022.2
    Thanks.
     
  33. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Yeah, I am not talking about 2021.2 api add on. I am talking about the 2021.1 work around. The generatespriteshape code seems like it is assuming the size of rendering to 256x256. Which is the problem i was talking about. And not sure if it will work with 2drenderer? Also the api seems to be on spriteshape so why cant sprite shape package be updated and still be used for 2021.1 ?
     
    Last edited: Jun 26, 2021
  34. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    @castor76 Just noticed when responding to a similar issue :
    Rendering through CommandBuffer does not involve Culling and hence will always render. Hence this will force generating the geometry and update the exact bounds. The 256x256 is just a temporary RT to render to which will be released anyway. This does not affect the actual rendering later when the bounds are now accurate. Sorry for the delayed response.
     
  35. OlderFox

    OlderFox

    Joined:
    Sep 18, 2022
    Posts:
    3
    @Venkify I still need the localAABB workaround, even with version 9.0.0 of 2D Sprite Shape. Is that expected? I see that the SpriteShapeController detects spline changes and does re-bakes internally. But sprites still disappear (get cullled?) unless I do the workaround.
     
  36. samanabo

    samanabo

    Joined:
    Mar 10, 2015
    Posts:
    51
    I'm having a similar issue when trying to modify the spriteshape points in the editor, by script. I'm trying to center the transform of the spriteshape based on the points.
    I've attempted to add these work arounds, but the spriteshape still does not render unless I zoom the scene view in and out a few times. Am I missing something here:
    Code (CSharp):
    1.  
    2. public static void CenterOrigin(this SpriteShapeController ss) {
    3.     Vector3 firstPos = ss.transform.TransformPoint(ss.spline.GetPosition(0));
    4.     Bounds bounds = new(firstPos, Vector2.zero);
    5.  
    6.     for(int i = 1; i < ss.spline.GetPointCount(); i++) {
    7.         Vector3 pos = ss.transform.TransformPoint(ss.spline.GetPosition(i));
    8.         bounds.Encapsulate(pos);
    9.     }
    10.  
    11.     Vector3 offset = ss.transform.position - bounds.center;
    12.  
    13.     for(int i = 0; i < ss.spline.GetPointCount(); i++) {
    14.         Vector3 newWorldPos = ss.transform.TransformPoint(ss.spline.GetPosition(i)) + offset;
    15.         ss.spline.SetPosition(i, ss.transform.InverseTransformPoint(newWorldPos));
    16.     }
    17.  
    18.     ss.transform.position = bounds.center;
    19.  
    20.     ss.RefreshSpriteShape();
    21.     JobHandle job = ss.BakeMesh();
    22.     ss.BakeCollider();
    23.     job.Complete();
    24.     ss.spriteShapeRenderer.SetLocalAABB(bounds);
    25. }
    26.  
     
    OmniDeveloper likes this.
  37. samanabo

    samanabo

    Joined:
    Mar 10, 2015
    Posts:
    51
    Ok, I did find a small issue with my previous post. The SetLocalAABB function requires local bounds (as the name suggests...). Updating the script to the following seems to work in the editor:

    Code (CSharp):
    1.  
    2. public static void CenterOrigin(this SpriteShapeController ss) {
    3.     Vector3 firstPos = ss.transform.TransformPoint(ss.spline.GetPosition(0));
    4.     Bounds bounds = new(firstPos, Vector2.zero);
    5.  
    6.     for(int i = 1; i < ss.spline.GetPointCount(); i++) {
    7.         Vector3 pos = ss.transform.TransformPoint(ss.spline.GetPosition(i));
    8.         bounds.Encapsulate(pos);
    9.     }
    10.  
    11.     Vector3 offset = ss.transform.position - bounds.center;
    12.  
    13.     for(int i = 0; i < ss.spline.GetPointCount(); i++) {
    14.         Vector3 newWorldPos = ss.transform.TransformPoint(ss.spline.GetPosition(i)) + offset;
    15.         ss.spline.SetPosition(i, ss.transform.InverseTransformPoint(newWorldPos));
    16.     }
    17.  
    18.     ss.transform.position = bounds.center;
    19.  
    20.     ss.RefreshSpriteShape();
    21.     JobHandle job = ss.BakeMesh();
    22.     ss.BakeCollider();
    23.     job.Complete();
    24.  
    25.     Bounds localBounds = new(ss.spline.GetPosition(0), Vector2.zero);
    26.     for(int i = 1; i < ss.spline.GetPointCount(); i++) {
    27.         Vector3 pos = ss.spline.GetPosition(i);
    28.         localBounds.Encapsulate(pos);
    29.     }
    30.  
    31.     ss.spriteShapeRenderer.SetLocalAABB(localBounds);
    32. }
    33.  
     
  38. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,490
    Please note that the SpriteShape is no longer an experimental feature so please use the main 2D sub-forum from now on.

    Thanks.
     
    samanabo likes this.