Shapes2D for Unity¶
Thanks for using Shapes2D, a Unity asset for easily creating art assets in the Unity editor. You can use Shapes2D for creating PNG sprites, prototyping, game jams, UI, effects and more!
Shapes2D is available in the Unity asset store.
Getting Help¶
Check out the forums, the Discord, or email oliver@sub-c.org. Follow me on Twitter for occasional progress updates.
Features¶
- Extremely easy to use
- Shapes can act as sprites or Unity GUI objects
- Convert to Sprite button can turn shapes into a .PNG with one click
- Multiple layered shapes can be collapsed into a single .PNG
- Works with Unity GUI masking
Shapes can optionally be used procedurally at runtime (see Performance), meaning:
- Shapes can be scaled up without becoming pixellated
- Shapes can be modified/animated at runtime using the Scripting API
Base shape types are:
- Circles/Ellipses (with settings for donuts, arcs, pies and wedges)
- Paths (made of quadratic Bézier curves, up to 32 segments, optionally fill closed areas)
- Polygons (convex or concave, up to 64 vertices)
- Rectangles (and rounded rectangles)
- Triangles
Customizations include:
- Outlines
- Blur
- Gradient fills
- Texture fills
- Pattern fills (grids, stripes & checkerboard)
Usage¶
After importing Shapes2D, go to GameObject->Shapes2D
and choose one of
the template Shapes. Once your Shape is created, you can tweak its various
options from the Shape component in the Inspector.
In the Shape component, sizes are typically specified in world units and outlines will keep the same thickness no matter how they are scaled.
Unity GUI¶
To use Shapes2D with canvas game objects, just add a Shape component to them. In Unity, canvases may be scaled quite a lot to fit your screen, depending on your settings. That means the scale of units may be very different than when dealing with sprites (canvases do use world units, which is why they appear so huge in the editor, but they are scaled down by Unity at runtime to overlay your screen). So, you may have to scale values (like outline size) up quite a lot to get the same effect in a UI component as with a sprite.
Anti-Aliasing¶
If you set blur to 0, Shapes2D will add resolution-independent anti-aliasing to Shapes so that they will look smooth regardless of scaling - essentially it is just modifying the blur value on the fly as you scale in and out. If you set blur to some value greater than 0, then blur is specified in world units and will stay fixed.
Sorting¶
Shapes can act like sprites or UI components, and will obey the same sorting rules. For sprite-based Shapes, use the attached SpriteRenderer component to specify the sorting layer and order. For UI components, the parent canvas has sorting options and the children will render in the order they appear in the hierarchy.
Convert to Sprite¶
To convert a Shape component to a sprite, press the
“Convert to Sprite” button in the Shape component in the inspector. A PNG
asset will be created in a folder called /Assets/Resources/Shapes2D Sprites
,
and the object will be converted into a SpriteRenderer or Image, depending
on the object. Any active child Shapes will also be combined into the PNG,
allowing you to create complex layered Shapes. To revert the sprite
conversion, either use Unity’s Undo function or just re-enable the Shape
component and any child Shapes.
Sizing¶
Shapes2D defaults to 100 pixels per world unit to determine the size of the generated PNG, so if your Shape is 1x2 world units, a 100x200 pixel PNG will be created. However, for UI Shapes where the parent canvas is not in world mode, Shapes2D assumes the size in world units is already specified in pixels and just uses that value divided by the canvas’s scaleFactor.
You can change the pixels per world unit setting by going to /Shapes2D/Preferences
in your Project pane (assuming you kept the default import location).
Scaling UI¶
For rectangular UI components, after converting to a sprite you may want to convert the resulting PNG into a 9-Slice so your component can scale without having its borders stretched. To do that, just select the PNG asset in the Project window and use Unity’s Sprite Editor to bring in the borders a little (the green bars in the Sprite Editor). Hit Apply, and then change the Shape’s Image component to have an Image Type of “Sliced”. Now Unity will be able to tile the parts of the image outside the borders instead of stretching them.
Performance¶
If you convert your Shapes to sprites, then Shapes2D has no impact on the performance of your game.
However, before converting to sprite, Shapes are rendered on the fly using shaders, which is why they look so smooth when scaled up and can be changed at runtime via scripting. For simple Shapes like rectangles that isn’t such a big deal, but for complex shape types like polygons and paths it can be really expensive. Lots of math has to be done for each pixel, so the size of the Shape on the screen matters.
To make things worse, Shapes do not batch and require one Material per Shape. Changing any Shape properties via scripting causes properties to be re-sent to the GPU which is also a performance hit. Mobile in particular does not have very good performance, depending on what you’re doing.
Nevertheless, using the included Stress Test demo I am able to get 1000+ non-complex Shapes on-screen at once at 60fps on my MacBook Pro. Dynamically modifying Shape properties at runtime is slower, and I get about 500+ Shapes running at 60fps. On mobile, performance is much worse and I recommend testing on all target platforms before attempting to use Shapes2D procedurally. You should be able to get away with small numbers of non-complex shapes.
If performance becomes an issue, a good workflow is to rapidly create Shapes when prototyping and then either “Convert to Sprite” or replace them with artist-created assets later. You should test on each of your supported platforms and decide if you can afford to use a procedural Shape or if you should replace prototype Shapes with sprites (or perhaps an animated mesh, for certain types of effects).
Path Performance¶
Paths (even line paths) are the most expensive Shape type for performance, and I recommend always converting them to sprites unless you are very sure your target platform can handle it. The more segments you use, the more expensive rendering will be. If you do need dynamic procedural curves or paths at runtime, you might want to look into a vector solution which would convert your paths into a much more performance-friendly mesh. There are other assets on the Unity Asset Store for that!
On mobile, performance for paths is very bad - convert them to sprites first!
Polygon Optimization¶
Polygon Shapes have an additional “optimized” rendering mode which can be selected via a checkbox in the Unity Component Inspector.
Optimized polygons:
- Are about as fast as the other Shape types, even on mobile
- Are slow if you want to update their vertices dynamically via code (and slow to Instantiate)
- Will render fine for most polygons but will show rendering artifacts if vertices are too close to each other, if vertices are too close to polygon edges, or if polygon edges intersect each other
Non-optimized polygons:
- Are slow in general, and slower the more vertices you have
- Are very slow on mobile
- Can have their vertices updated quickly
- Should always render correctly
If you intend to Convert to Sprite anyway, go ahead and leave your polygons non-optimized so you don’t have to worry about rendering issues.
Supported Platforms¶
Shapes2D has been tested on OSX, Windows, iOS, Android and WebGL. For polygons
and paths with high numbers of vertices, Shapes2D shaders may not compile for
the Direct3D9 Graphics API
, meaning you should not target that platform if you
intend to use complex shapes procedurally (converting to sprite is fine because
the shaders aren’t required at runtime). If you notice any problems with Shapes
not appearing or appearing wrong, please let me know!
Scripting API¶
To modify Shapes from code, you can do things like this:
Shapes2D.Shape shape = GetComponent<Shapes2D.Shape>();
shape.settings.outlineSize += Time.deltaTime;
shape.settings.outlineColor = new Color(1, 1, 1, 1);
etc...
If you want to modify a Shape’s attributes from your Start()
method, you may
need to add a call to shape.ComputeAndApply()
to prevent a flicker of the
Shape’s initial attributes on the first frame.
See below for customizable settings.
API Documentation¶
UserProps (customizable settings)
Enums¶
-
enum
Shapes2D::
ShapeType
¶ Shapes2D base shape types.
Values:
-
Ellipse
¶
-
Polygon
¶
-
Rectangle
¶
-
Triangle
¶
-
Path
¶
-
The Shape Component¶
-
class
Shapes2D::
Shape
¶ The MonoBehaviour for a Shapes2D Shape. Can be used with either a SpriteRenderer or an Image. If the Object does not have either component, one will be added depending on if there is a Canvas in the parent hierarchy or not. This component will also replace the Material and Sprite of an existing SpriteRenderer or Image attached to the GameObject.
Inherits from MonoBehaviour, IMaterialModifier
Public Functions
-
void Shapes2D.Shape.ComputeAndApply(bool disableBlending = false)
Used internally by Shape and ShapeEditor. Computes and sends the Shape‘s attributes to the shader. Normally you don’t need to call this (even when changing settings on the fly), but you may need to call this manually to force an update if you change settings from Start() to avoid a flicker on the first frame.
-
UserProps (customizable settings)¶
-
class
Shapes2D::Shape::
UserProps
¶ Runtime-editable attributes of a Shapes2D Shape.
Property
-
property
Shapes2D::Shape::UserProps::blur
The amount of blur in world units applied to the Shape. If the Shape has an outline, blur is applied to both the inner and outer parts of the outline. Set to 0 to have Shapes2D dynamically apply a small amount of blur that varies with the screen resolution.
-
property
Shapes2D::Shape::UserProps::correctScaling
If shapeType is Ellipse and outlineSize > 0, enabling this will use a much more complicated (but glitchy) algorithm that tries to apply an equal thickness at all places along the outline. This only applies in cases when the algorithm decides it’s likely to be useful (very non-circular ellipses). With this disabled, a simpler algorithm is used that does a decent job, but outlines may have irregular thickness with thick outlines and very non-circular ellipses. I strongly recommend leaving this disabled unless you really need it, and are able to test that it works reliably in all situations and platforms you care about. The innerCutout and arcAngle properties do not work when correctScaling is turned on and the algorithm is applied.
-
property
Shapes2D::Shape::UserProps::endAngle
If shapeType is Ellipse, specifies the end angle in degrees of the visible arc area. Only areas of the ellipse between startAngle and endAngle will be visible.
-
property
Shapes2D::Shape::UserProps::fillColor
The Shape‘s fill color, depending on fillType.
-
property
Shapes2D::Shape::UserProps::fillColor2
For certain fill types, this is the second fill color used.
-
property
Shapes2D::Shape::UserProps::fillOffset
An offset applied to the fill in world units.
-
property
Shapes2D::Shape::UserProps::fillPathLoops
If shapeType is Path, this will cause any loops in the path to be filled.
-
property
Shapes2D::Shape::UserProps::fillRotation
The rotation of the fill, in degrees.
-
property
Shapes2D::Shape::UserProps::fillScale
A scale applied to the fill.
-
property
Shapes2D::Shape::UserProps::fillTexture
A Texture2D to use for the fill, if fillType is Texture.
-
property
Shapes2D::Shape::UserProps::fillType
The Shape‘s fill type, a Shapes2D.FillType.
-
property
Shapes2D::Shape::UserProps::gradientAxis
A Shapes2D.GradientAxis specifying the gradient direction, if the fillType is Gradient. The difference between this and simply using fillRotation to rotate a gradient fill is that Shapes2D uses the axis to determine the gradient’s start and end positions. When using a rotated gradient rather than specifying the axis directly, the gradient may not stretch the way you expect it to when you scale the Shape.
-
property
Shapes2D::Shape::UserProps::gradientStart
The gradient’s start position as a percentage (from 0-1) along its gradientAxis, if the fillType is Gradient.
-
property
Shapes2D::Shape::UserProps::gradientType
A Shapes2D.GradientType specifying the gradient type, if the fillType is Gradient.
-
property
Shapes2D::Shape::UserProps::gridSize
If fillType is Grid, Checkerboard or Stripes, gridSize is the size in world units of the grid (or the distance between lines).
-
property
Shapes2D::Shape::UserProps::innerCutout
If shapeType is Ellipse, setting this will cut out an inner “donut hole” of the ellipse. Each dimension is specified from 0-1 and is a percentage of the X and Y radii of the ellipse. Use by itself or in combination with startAngle and endAngle to turn a pie shape into an arc.
-
property
Shapes2D::Shape::UserProps::invertArc
If shapeType is Ellipse, this inverts startAngle and endAngle so that they describe the invisible area rather than the visible area of the ellipse.
-
property
Shapes2D::Shape::UserProps::lineSize
If fillType is Grid or Stripes, lineSize is the size in world units of the lines in the fill.
-
property
Shapes2D::Shape::UserProps::outlineColor
The color of the outline, if any.
-
property
Shapes2D::Shape::UserProps::outlineSize
Outline size in world units. Specifically, this is the thickness in world units of the shape’s outline on one side of the Shape.
-
property
Shapes2D::Shape::UserProps::pathSegments
If shapeType is Path, this holds a list of PathSegments. A segment is a quadratic Bézier curve, with each curve made of 3 points. The first and last point in each segment will be clamped to the range -0.5, 0.5. To make a connected path, set points of connected segments to be equal to each other. You can have from 1 to MaxPathSegments (currently 32). If you modify the returned array directly rather than setting a new array, call settings.pathSegments = settings.pathSegments anyway so the shape knows to update the shader and values are sanitized.
-
property
Shapes2D::Shape::UserProps::pathThickness
If shapeType is Path, this is the thickness of the path in world units.
-
property
Shapes2D::Shape::UserProps::polygonPreset
For use with shapeType Polygon, sets the vertices to a copy from the given Shapes2D.PolygonPreset.
-
property
Shapes2D::Shape::UserProps::polyVertices
If shapeType is Polygon, this holds its vertices normalized from -0.5 to 0.5 (verts will be clamped to this range). You can have from 3 to MaxPolygonVertices (currently 64). If you modify the returned vertex array directly rather than setting a new array, call settings.polyVertices = settings.polyVertices anyway so the shape knows to update the shader and values are sanitized.
-
property
Shapes2D::Shape::UserProps::roundness
If shapeType is Rectangle and roundnessPerCorner is false, this specifies the amount of the rectangle, along a single side, that will be rounded. For UI shapes this is in world units so borders stay the same as you scale, like a 9-slice. For sprite shapes it’s a percentage of the available area so shapes won’t change as they scale.
-
property
Shapes2D::Shape::UserProps::roundnessBottomLeft
If shapeType is Rectangle and roundnessPerCorner is true, this specifies the amount of the rectangle in world units, along a single side, that will be rounded.
-
property
Shapes2D::Shape::UserProps::roundnessBottomRight
If shapeType is Rectangle and roundnessPerCorner is true, this specifies the amount of the rectangle in world units, along a single side, that will be rounded.
-
property
Shapes2D::Shape::UserProps::roundnessPerCorner
If shapeType is Rectangle, set this to true if you want to use per-corner roundness attributes instead of a single value for all corners.
-
property
Shapes2D::Shape::UserProps::roundnessTopLeft
If shapeType is Rectangle and roundnessPerCorner is true, this specifies the amount of the rectangle in world units, along a single side, that will be rounded.
-
property
Shapes2D::Shape::UserProps::roundnessTopRight
If shapeType is Rectangle and roundnessPerCorner is true, this specifies the amount of the rectangle in world units, along a single side, that will be rounded.
-
property
Shapes2D::Shape::UserProps::shapeType
The base shape type, a Shapes2D.ShapeType.
-
property
Shapes2D::Shape::UserProps::startAngle
If shapeType is Ellipse, specifies the start angle in degrees of the visible arc area. Only areas of the ellipse between startAngle and endAngle will be visible.
-
property
Shapes2D::Shape::UserProps::triangleOffset
If shapeType is Triangle, this is position on the x axis of the topmost point as a percentage of its width, from 0-1. It may be a little clumsy but you should be able to get any triangle you want by using triangleOffset and rotating the Shape.
-
property
Shapes2D::Shape::UserProps::usePolygonMap
If shapeType is Polygon, enabling this will pre-compute some data allowing us to render much faster. Most polygons will render correctly using this optimization, but not all. In general, vertices cannot be too close to each other, and vertices cannot be too close to non-adjacent polygon edges, or you’ll see rendering issues. Also, polygon edges can’t intersect with each other. If you get good performance with leaving this off (or plan to Convert to Sprite anyway), then leave it off. On mobile, polygon rendering is very slow and you’ll probably need this optimization.
-
property