Search Unity

  1. Unity 2018.3 is now released.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. We've updated our Terms of Service. Please read our blog post from Unity CTO and Co-Founder Joachim Ante here
    Dismiss Notice
  4. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  5. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

SpriteShape Preview Package

Discussion in '2D Experimental Preview' started by rustum, Mar 19, 2018.

  1. BTStone


    Mar 10, 2012
    Hey there,

    so I'm fooling around with the package and before I go into detail I'd like to know a specific thing:
    How is idea of the update of SpriteShape or packages in general? Now, I'm on Unity 2018.3.0f2 and I installed SpriteShape 1.0.12.
    I saw on this topic multiple posts of Unity devs stating that some things have been fixed and already available in the Alpha for Unity 2019.1. Does this mean the packages are still kind of linked with the Unity version? So in the future for example SpriteShape 1.1.8 will only be available for 2019.1 and not for...let's say 2018.3.X or 2018.4.X ?

    Alright, but here's some of my feedback so far:

    1) Problem with setting up corners

    So I have a general problem with corners. I don't get why there are only two modes: Disabled and Automatic. Especially automatic feels random. I know the requirements are that there is a straight line and both points are set to Linear Point Mode. In order to test that I created a new profile with the Sprites/Textures from the Castle-Examples.
    Here's the created Profile, I only added one range like this and set the Outer Top Left and Outer Top Right Corners.

    And here is my created SpriteShape in the scene. Only four node points and the top two are set to automatic, but the corner sprites don't show up. Why?

    Everything _seems_ setup right. Cornersprites are set up in the profile, corner nodes in the Controller are in linear point mode and aren't too close. The whole corner setup feels random, especially when you only can select between disabled corners or let the ShapeController do his automatic magic. Why isn't there another option like "Forced" which will display the corner no matter the angle? For now I only can do random guessing what went wrong with my setup and do nothing else but try&error with the setup or in this case: asking in the forums.

    And another thing, for this "problem" I did use the shapeprofiles and not my own: is there a specific reason why open ended shapes don't display the corners although both nodes are set to linear point mode and both are set to automatic borders? Have a look at the following screenshot, you'll see a closed shape with displaying borders and an open ended shape with only two points, but the open ended one doesn't display the corners, although it should. Bug or by design, and if by design: why?

    2) Problems with changing Point Modes

    For some reason the point mode is changing and with that the nodes are changing when I do something like this:

    Step 1: Creating a new ShapeController and going into Edit Mode

    Step 2: Creating a new node inbetween the top left and top right node in Linear Point Mode

    Step 3: Selecting the left corner node and changing that node to Mirrored Point Mode

    Step 4: Then when I create a new node between the top left node and the top center node for some reason it's created in Non-Mirrored Point Mode - why is this happening?

    Step 5: Then when I select the top left node again the point mode changed to Non-Mirrored Point Mode although I changed it before manually to mirrored point mode as described in Step 3 - why is this happening?

    I don't know if these are bugs or by design and if it is indeed by design I don't like this UX at all. Here's what I expected or would expect:

    - Creating a new node in a specific point mode or changing an existing one into another specific point mode is persistent and whenever I get back to that node that specific point mode gets selected
    - Is a specific node selected and a new node is created, it doesn't matter where on the shape, it get's created in the selected point mode and doesn't change right before creation of the new node

    4) Comparison to Ferr2D and general UX

    For our last project we used Ferr2D from it's 1.0 version almost to 2.0, lately I'm also fooling around with the latest Ferr2D release and I'm going to list some of the things I would like to be added or changed in SpriteShape.

    - almost the whole process of editing a Ferr2D Terrain in comparison to a SpriteShape in the scene has a better UX in Ferr2D:
    • the node points are big in Ferr2D while in the SpriteShape they are too small and it kinda needs more precision to select one. If you don't want to set the size yourself, maybe allow to set the size of the points in the preferences?
    • deleting a node point in Ferr2D needs only holding ALT and clicking on the node which one wants to destroy, in the SpriteShape I have to select a node point and hit CMD/CTRL+DELETE fearing I might delete the whole Shape...
    • adding a node outside of the spline is very simple in Ferr2D and doesn't seem to be possible at all in the SpriteShape, in Ferr2D I just have to hold SHIFT, have a look at this screenshot:

    - selecting multiple nodes and moving them is almost good, but instead of holding SHIFT when dragging multiple nodes around there shouldn't be the need to hold SHIFT imo, just selecting multiple nodes and then selecting one of those selected should give the ability to move all of them around

    - when adding a Collider2D one has to select the "Update Collider"-Checkbox in order to set the collider accordingly to the same shape as the SpriteShape itself. Is there any reason this can't happen the moment one adds the Collider2D component? I mean, if I add a PolygonCollider2D to a sprite it immediately takes shape of the sprite, why isn't this the case with the SpriteShape?

    - change the color from selected nodes - that yellow is barely visible, change it to something more distinctive like blue
    (or at least give the option to change them ourselves)

    - please give the option to actually show the indices of all the nodes themselves in the scene view, the indices could then be displayed around the nodes and why not also showing the index of the selected node in the inspector as read-only. This would be useful to work with the Scripting API so one could immediately see if the code is doing what it should do with the desired index/node. This is also possible in Ferr2D.

    That being said, what I really like is the ShapeProfile-Setup. Ferr2Ds terrain setup editor is sooo~ annoying, at least for me. It's cumbersome and it had been often the case that values haven't been saved and I had to do the whole setup again...
    The angle range view is a delight and feels intuitive, great job on that and of course the tool overall, if those listed things could be adressed it would be great tool and a also a great workflow since Ferr2D support is nonexistent for months :)
  2. indieDoroid


    Jan 25, 2016
    Hi @Venkify , just wondering how the progress is going with my weird bug? :) (Where rigid body2d objects fall through the edge collider 2d)
  3. Venkify


    Unity Technologies

    Apr 7, 2015
    @BTStone Many thanks for the detailed feedback.
    Corner sprites connect the edge sprites when they are present. As you only have defined angle range that covers the top, there are no edge sprites on the sides or bottom based on your spriteshape profile inspector screenshot. Hence there are no corner sprites in the posted sample. Hence the issue. Also please note that the pixel height of the corresponding edge sprite and the corner sprite should be given.

    By design, only closed shapes have corners. We can consider adding corners for open shapes too.Will update this thread regarding this.

    We are revisiting this part for the upcoming version. Thanks for the feedback.
    BTStone likes this.
  4. pcg


    Nov 7, 2010
    I'm creating a spriteshape from code by starting with 2 points and inserting / positioning new points on to the spline.
    I call BakeCollider() to ensure the collider is correct but the Sprite Renderer bounds are incorrect.
    Is there there a method available to update the bounds after I've updated the spline?
    And is there anything else I should be calling once i've updated the spline?
  5. pcg


    Nov 7, 2010
    The world UV calculation seems to be off when using a fill texture when your sprite shape is not scale 1,1,1.
  6. Venkify


    Unity Technologies

    Apr 7, 2015
    Colliders are updated in Editor mode (if UpdateCollider is enabled) automatically as you Edit.
    In Playmode, BakeCollider should be explicitly called after making changes to the Spline.
    For example :
    var spriteShapeController = gameObject.GetComponent<SpriteShapeController>();
    // .. make changes to spline.

    Could you please post a screenshot ?
  7. pcg


    Nov 7, 2010
    Thanks, yes I managed to work out the baking of the collider but I only have a fudge for the issue of not being able to update the SpriteRenderer bounds.

    This bounds issue can be easily recreated by creating a spriteshape with two points which are vertical to one another.
    Then add points to the spline which give it a decent width. Then run in the editor and move the camera. Because the bounds are not recalculated on the renderer when you procedurally amend the spline in code the sprite disappears from the camera when it should still be visible.

    The world UV calculation issue for fill textures is most easily recreated by creating a spriteshape object in the editor then changing the scale and moving it around the screen in scene view. The fill texture should appear as as static but the sprite shape move freely, however the texture appears to move slightly to the right everytime you move the sprite shape to the left and vice versa.

    I can provide an example project if required?
  8. nosajtevol


    Jun 28, 2012
    Is it possible to render holes in the sprite shape?
  9. pastaluego


    Mar 30, 2017
    There's probably a better way but you can use stencilmask to do what you want. I wanted to be able to put windows anywhere on a spriteshape texture and it's possible if you have the spriteshape shader write to a certain stencil buffer and then have a mask texture read from that buffer and ignore those pixels, and then you can put some sprite over that hole to cover up the cutout.
    nosajtevol likes this.
  10. nosajtevol


    Jun 28, 2012
    That's a great idea! I'm thinking using a stencil shader on a sprite shape so I can make custom holes in a sprite shape in the background. Then I can use that conforming sprite shape script (in the demos for the package on GitHub) to make the outline for the hole and make it look organic. I think that would be a good workaround! Thanks for the idea!

    Update: Just want to let you guys know this worked out very well. I used the conforming script for the outline of the mask and now I am able to use the sprite shapes for interiors! Wahoo!

    Attached Files:

    Last edited: Jan 11, 2019 at 4:16 PM
  11. nosajtevol


    Jun 28, 2012
    Is there a simple way to cut holes on the sides of the sprite shape at runtime? I.E some procedural destruction that automatically resets the sprite shape?

    Edit: Finding simple raycasting to find the position of the edge is working well. However I'm wondering if there is a simple way to see if a point is along the path of the spline, i.e to find which index I need to insert the point at at runtime.

    An even better option would be something like Ferr2d's auto add point, that automatically adds a node to the sprite shape with only position as the parameter.

    @Sergi_Valls I saw that you mentioned previously in the thread about iterating through all Bezier points to find the closest one. Could you possibly elaborate on that?

    Update: I've figured it out!

    This script will add a point to your sprite shape based on object position. You can easily modify it with any position at all, as well as include an offset so you actually move the point, up or down, which is useful for digging and such. I also added the ability to add icons for the nodes at runtime as well as their handles. This makes a point every time you reactivate the game object with the script based on the closest area on the sprite shape.

    To make holes I plan to make new points and push them downward, or in whichever direction I want to make holes. I might share that script here as well if people are interested.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.U2D;
    6. public class MakePointBasedOnPos : MonoBehaviour
    7. {
    8.     public SpriteShapeController spriteShape;
    9.     public Vector3[] nodes;
    10.     public Sprite nodeIcon;
    11.     public float progress;
    12.     public Vector3[] closestPointsForAllCurves;
    13.     public List<GameObject> icons = new List<GameObject>();
    14.     int closestNode = 0;
    15.     [Tooltip("Useful for moving the point")]
    16.     public Vector3 offsetPoint;
    18.     private void Start()
    19.     {
    20.         MakeIcons();
    21.     }
    23.     private void OnEnable()
    24.     {
    25.         MakeIcons();
    27.         MakePoint();
    28.     }
    30.     // Start is called before the first frame update
    31.     public void MakePoint()
    32.     {
    33.         //Get node positions
    34.         GetNodePositions();
    36.         //Find closest points for all curves
    37.         closestPointsForAllCurves = new Vector3[spriteShape.spline.GetPointCount()];
    39.         for (int i = 0; i < nodes.Length; i++)
    40.         {
    41.             closestPointsForAllCurves[i] = FindClosestPoint(i, transform.position);
    42.         }
    44.         //Find closest node out of all of those points
    45.         float distance = 10000;
    46.         int closestNode = 0;
    48.         for(int i=0; i< nodes.Length; i++)
    49.         {
    50.             float newDistance = Vector3.Distance(transform.position, closestPointsForAllCurves[i]);
    51.             if (newDistance < distance)
    52.             {
    53.                 closestNode = i;
    54.                 distance = newDistance;
    55.             }
    56.         }
    58.         Debug.Log(closestNode);
    60.         Vector3 closestPoint = closestPointsForAllCurves[closestNode];
    61.         transform.position = closestPoint;
    63.         //Now insert a new point there
    64.         closestPoint = closestPoint + offsetPoint; // add offset
    65.         spriteShape.spline.InsertPointAt(Mod(closestNode + 1, spriteShape.spline.GetPointCount()), closestPoint);
    67.         //Smooth out new point
    68.         spriteShape.spline.SetTangentMode(Mod(closestNode + 1, spriteShape.spline.GetPointCount()), ShapeTangentMode.Continuous);
    70.         //Set icons
    71.         MakeIcons();
    72.     }
    74.     public Vector3 FindClosestPoint(int startNodeIndex,Vector3 position)
    75.     {
    76.         Vector3 startPoint = spriteShape.spline.GetPosition(startNodeIndex);
    77.         Vector3 endPoint = spriteShape.spline.GetPosition(Mod(startNodeIndex + 1, spriteShape.spline.GetPointCount()));
    78.         Vector3 startTangent = spriteShape.spline.GetRightTangent(startNodeIndex);
    79.         Vector3 endTangent = spriteShape.spline.GetLeftTangent(Mod(startNodeIndex + 1, spriteShape.spline.GetPointCount()));
    81.         return BezierUtility.ClosestPointOnCurveFast(position,startPoint, endPoint, startPoint + startTangent, endPoint + endTangent, out progress);
    82.     }
    84.     private static int Mod(int x, int m)
    85.     {
    86.         int r = x % m;
    87.         return r < 0 ? r + m : r;
    88.     }
    90.     public void GetNodePositions()
    91.     {
    92.         //Get list of all nodes
    93.         nodes = new Vector3[spriteShape.spline.GetPointCount()];
    95.         for (int i = 0; i < nodes.Length; i++)
    96.         {
    97.             nodes[i] = spriteShape.spline.GetPosition(i);
    98.             GameObject icon = new GameObject();
    99.         }
    100.     }
    102.     public void MakeIcons()
    103.     {
    104.         GetNodePositions();
    106.         //Delete previous icons
    107.         foreach (GameObject icon in icons)
    108.         {
    109.             Destroy(icon);
    110.         }
    111.         icons.Clear();
    113.         //Get list of all nodes
    114.         nodes = new Vector3[spriteShape.spline.GetPointCount()];
    116.         for (int i = 0; i < nodes.Length; i++)
    117.         {
    118.             nodes[i] = spriteShape.spline.GetPosition(i);
    119.             GameObject icon = new GameObject();
    120.             icons.Add(icon);
    121.             icon.transform.position = nodes[i];
    122.    = i.ToString();
    123.             icon.AddComponent<SpriteRenderer>();
    124.             icon.GetComponent<SpriteRenderer>().sprite = nodeIcon;
    125.             icon.GetComponent<SpriteRenderer>().color =;
    126.             icon.transform.localScale = new Vector3(100, 100, 100);
    127.         }
    129.         //Set tangent representations
    130.         for (int i = 0; i < nodes.Length; i++)
    131.         {
    132.             GameObject rightTangentIcon = new GameObject();
    133.             icons.Add(rightTangentIcon);
    134.             rightTangentIcon.transform.position = spriteShape.spline.GetRightTangent(i) + spriteShape.spline.GetPosition(i);
    135.    = i.ToString() + " Right Tangent";
    136.             rightTangentIcon.AddComponent<SpriteRenderer>();
    137.             rightTangentIcon.GetComponent<SpriteRenderer>().sprite = nodeIcon;
    138.             rightTangentIcon.GetComponent<SpriteRenderer>().color = Color.yellow;
    139.             rightTangentIcon.transform.localScale = new Vector3(100, 100, 100);
    141.             GameObject lefTTangentIcon = new GameObject();
    142.             icons.Add(lefTTangentIcon);
    143.             lefTTangentIcon.transform.position = spriteShape.spline.GetLeftTangent(i) + spriteShape.spline.GetPosition(i);
    144.    = i.ToString() + " Left Tangent";
    145.             lefTTangentIcon.AddComponent<SpriteRenderer>();
    146.             lefTTangentIcon.GetComponent<SpriteRenderer>().sprite = nodeIcon;
    147.             lefTTangentIcon.GetComponent<SpriteRenderer>().color = Color.yellow;
    148.             lefTTangentIcon.transform.localScale = new Vector3(100, 100, 100);
    149.         }
    150.     }
    152. }
    Last edited: Jan 13, 2019 at 9:25 PM
  12. rootools


    Nov 25, 2013
    Good work! I have 2 issues:

    1) Editor on mac crashed when i add a material with normal map(Mobile/Bumped Diffuse for example). Bug report case - 1117034

    2) What about add opportunity set separate material to Fill sprite? This is need for correct normals work.

    3) Thank you =)
    Lars-Steenhoff likes this.
  13. nosajtevol


    Jun 28, 2012
    If anyone's curious, I've begun working on a digging script using my above script as a base. Here's the very rudimentary version:

    Edit: Version .2

    Last edited: Jan 16, 2019 at 9:15 PM
  14. alanmthomas


    Sep 7, 2015
    Nice idea. Needs work but I can see where you're going with it.

    Do we have any indication as to when this will move out of preview and into something that can be included in a build? I'm debating using this or Ferr2D for my project.
  15. Sergi_Valls


    Unity Technologies

    Dec 2, 2016
    Hi, thanks for sharing your progress. Yes, this is the way I was referring to on how to find the closest point in a spline.
    If you keep track of the closest curve index and the closest "progress" returned by BezierUtility.ClosestPointOnCurve instead of the point itself you can later use BezierUtility.SplitBezier(). That method will return you two curves, left and right.
    "leftEndPosition" will be equal to "rightStartPosition", which is your new point. Use the other control points to update the neighboring tangents using SetTangentMode/SetLeftTangent/SetRightTangent. If done correctly, you should end up with a new point without a visual change in the shape.