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

,

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

public class WorldBuilding : MonoBehaviour
{
    public static WorldBuilding instance = null;
    public SpriteShapeController prefab;

    [Header("World generic data")]
    public float m_fSectionSize = 10f;
    public float m_fSectionHeight = 100f;
    [Range(3,10)]
    public int m_iNumberOfPreloadedSections = 3;

    SpriteShapeController[] controllers;
    int nextSection;

    Queue<int> repurposeList = new Queue<int>();

    // Use this for initialization
    void Start ()
    {
        if(instance ==null)
        {
            instance = this;
        }
        else
        {
            DestroyImmediate(gameObject);
            return;
        }
        nextSection = m_iNumberOfPreloadedSections;
        CreateShapes();
    }
 
    void CreateShapes()
    {
        controllers = new SpriteShapeController[m_iNumberOfPreloadedSections];
        for (int i = 0; i < m_iNumberOfPreloadedSections; ++i)
        {
            CreateShape(i, i, i == 0);
        }
    }

    void CreateShape(int idx, int sectionNumber, bool isFirst = false)
    {
        controllers[idx] = GameObject.Instantiate<SpriteShapeController>(prefab, transform);
        controllers[idx].splineDetail = 32;

        MakePath(idx, sectionNumber, isFirst);
    }

    void MakePath(int idx, int sectionNumber, bool first = false)
    {
        Spline s = controllers[idx].spline;
        s.Clear();

        float startPos = sectionNumber * m_fSectionSize;
        float endPos = (sectionNumber + 1) * m_fSectionSize;

        int offset = 0;
        s.InsertPointAt(offset++, new Vector3(endPos, -m_fSectionHeight, 0));
        s.InsertPointAt(offset++, new Vector3(startPos, -m_fSectionHeight, 0));


        s.InsertPointAt(offset++, new Vector3(startPos, 0, 0));
        if (!first)
        {
            s.SetTangentMode(offset - 1, ShapeTangentMode.Continuous);
            s.SetLeftTangent(offset - 1, new Vector3(-1, 0, 0));
            s.SetRightTangent(offset - 1, new Vector3(1, 0, 0));
        }

        s.InsertPointAt(offset++, new Vector3(0.5f * (startPos + endPos), 2f, 0));
        s.SetTangentMode(offset - 1, ShapeTangentMode.Continuous);
        s.SetLeftTangent(offset - 1, new Vector3(-1, 0, 0));
        s.SetRightTangent(offset - 1, new Vector3(1, 0, 0));

        s.InsertPointAt(offset++, new Vector3(endPos, 0, 0));
        s.SetTangentMode(offset - 1, ShapeTangentMode.Continuous);
        s.SetLeftTangent(offset - 1, new Vector3(-1, 0, 0));
        s.SetRightTangent(offset - 1, new Vector3(1, 0, 0));

        //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.
        //controllers[idx].spriteShapeRenderer.bounds.SetMinMax(new Vector3(startPos, -m_fSectionHeight, 0), new Vector3(endPos, m_fSectionHeight, 0));
        controllers[idx].BakeCollider();

        BoxCollider2D box = controllers[idx].GetComponent<BoxCollider2D>();
        box.size = new Vector2(m_fSectionSize, m_fSectionHeight * 2);
        box.offset = new Vector2(0.5f * (startPos + endPos), 0);

        WorldSection section = controllers[idx].GetComponent<WorldSection>();
        section.Index = idx;
        section.WasInside = false;

        controllers[idx].gameObject.SetActive(true);
    }

    public void Repurpose(int idx)
    {
        Debug.Log("Repurposing " + idx);
        repurposeList.Enqueue(idx);
    }

    // Update is called once per frame
    void Update ()
    {
        RebuildPathAhead();
    }


    void RebuildPathAhead()
    {
        while(repurposeList.Count > 0)
        {
            int idx = repurposeList.Dequeue();
            Debug.Log("Rebuild " + idx);
            DestroyImmediate(controllers[idx].gameObject);
            CreateShape(idx, nextSection++);
            //MakePath(idx, nextSection++, false);
        }
    }
}

Class that handles the becameinvisible part and is attached to the object that has the sprite shape controller component attached to it:

public class WorldSection : MonoBehaviour
{
    int index;
    public int Index
    {
        get { return index; }
        set { index = value; }
    }

    bool wasInside = false;
    public bool WasInside
    {
        get { return wasInside; }
        set { wasInside = value; }
    }

    // Use this for initialization
    void Start ()
    {
 
    }
 
    // Update is called once per frame
    void Update ()
    {
 
    }

    private void OnTriggerStay2D(Collider2D collision)
    {
        //Debug.Log("Inside section");
        wasInside = true;
    }

    public void OnBecameInvisible()
    {
        Debug.Log("BecameInvisible");
        if (!wasInside) return;
        WorldBuilding.instance?.Repurpose(index);
        gameObject.SetActive(false);
    }
}

Thanks for any help that you could give and ask me if you need more infos!.

@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.

Thanks, done it. Bug number is 1103327.

@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.

1 Like

Hi, thanks it seems to work by increasing the size of the sprite shape for now. Thanks for the responsivenes!

still not resolved. i dont get what do you both mean by using much bigger size SpriteShape ?

6277598--693575--Снимок экрана 2020-09-04 в 13.40.08.jpg

I have the same issue

This issue still exists when instantiating a sprite shape controller from prefab.
There should be an option to “Always Render” like on animator.

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.

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?

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?

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.

1 Like

Is there public issue tracker for this?

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.

I will try this as well…

I have the same issue right now with newest version :frowning:
Any news with this?

where is ForceBakeMesh API?

“Available Soon” ? That was 3 months ago? Is this even implemented yet? Any news?

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 :

if (spriteShapeRenderer != null) 
        {

            controller.BakeMesh();  //this is the SpriteShapeController

            UnityEngine.Rendering.CommandBuffer rc = new UnityEngine.Rendering.CommandBuffer();
            rc.GetTemporaryRT(0, 256, 256, 0);
            rc.SetRenderTarget(0);
            rc.DrawRenderer(spriteShapeRenderer, spriteShapeRenderer.sharedMaterial);
            rc.ReleaseTemporaryRT(0);
            Graphics.ExecuteCommandBuffer(rc);

        }

I am already doing .BakeMesh command. I was wondering if there was another one called “ForceBakeMesh” API :smile:

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?