Ryan Nielson

The personal site and blog of Ryan Nielson.

© 2014. Ryan Nielson All rights reserved.

Fix Blurry Pixel Art Style Fonts in Unity

The Unity UI system is great, but by default it doesn't tend to work well when using fonts that require very sharp edges like those seen in pixel art style games. Though the fonts look alright, upon closer inspection the edges of the characters are slightly blurry and sometimes the characters look off. In the image below, if you look closely, you can see some blurriness around the character edges.

Blurry Text

Switching the font's import setting Rendering Mode to Hinted Raster can help a bit with the blurriness, but it doesn't seem to reproduce the characters properly. This leads to letters that look slightly different than they do in the original font.

Luckily there's a way to fix this issue in the Unity editor that doesn't require too much additional work. Begin by bringing a font into your project like you normally would. For this example I'm using a font called Press Start. Next click on your font in the project window to open the inspector, change the Character dropdown to Unicode, and click Apply. This process prepares Unity for the next step.

Font Import Settings

Click on the cog in the top right of the font inspector, and select Create Editable Copy. In your project window you should now see a new material, texture, and font asset with similar names to your existing font. By default when Unity imports a font it doesn't give you the ability to modify the related material and texture. Create Editable Copy gives you these assets to modify to your heart's content.

Created Font Files

Now click on the newly created texture, which should open the texture inspector. Switch Texture Type to advanced and change the Filter Mode from Bilinear to Point. This should remove the blurriness from your font's texture. The settings I've used are in the image below. Sometimes it seems as though selecting the incorrect settings can cause issues when displaying your font, so if you are having issues just start the process over and try to match the settings below.

Texture Import Settings

Now click on your UI Text object in the hierarchy, and replace the existing font with the new font asset (PrStart_copy in my case). You should immediately see sharper text in your game view. The image below shows the old font on the top, and the new font on the bottom. Hopefully it's clear enough to illustrate the difference the above steps make.

Blurry and Sharp Text

The UI system, and text in particular, can be quirky in Unity. Though like most things, the options are there to modify assets to get the look you set out to create. If you have any further questions please feel free to message me on Twitter @RyanNielson or comment below.

2D Sprite Outlines in Unity

Unity provides a component to outline UI objects, but it doesn't work on world space sprites. This post will demonstrate a simple way to add outlines to sprites using an extended version of the default sprite shader along with a simple component. This could be used to highlight sprites on mouse over, highlight items in the environment, or just to make sprites stand out from their surroundings.

To begin, create a new shader in your project called Sprite-Outline. This shader provides all the functionality of the default sprite shader, with the additions to allow sprite outlines.

Shader "Sprites/Outline"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0

        // Add values to determine if outlining is enabled and outline color.
        [PerRendererData] _Outline ("Outline", Float) = 0
        [PerRendererData] _OutlineColor("Outline Color", Color) = (1,1,1,1)
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ PIXELSNAP_ON
            #pragma shader_feature ETC1_EXTERNAL_ALPHA
            #include "UnityCG.cginc"

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
            };

            fixed4 _Color;
            float _Outline;
            fixed4 _OutlineColor;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif

                return OUT;
            }

            sampler2D _MainTex;
            sampler2D _AlphaTex;
            float4 _MainTex_TexelSize;

            fixed4 SampleSpriteTexture (float2 uv)
            {
                fixed4 color = tex2D (_MainTex, uv);

                #if ETC1_EXTERNAL_ALPHA
                // get the color from an external texture (usecase: Alpha support for ETC1 on android)
                color.a = tex2D (_AlphaTex, uv).r;
                #endif //ETC1_EXTERNAL_ALPHA

                return color;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;

                // If outline is enabled and there is a pixel, try to draw an outline.
                if (_Outline > 0 && c.a != 0) {
                    // Get the neighbouring four pixels.
                    fixed4 pixelUp = tex2D(_MainTex, IN.texcoord + fixed2(0, _MainTex_TexelSize.y));
                    fixed4 pixelDown = tex2D(_MainTex, IN.texcoord - fixed2(0, _MainTex_TexelSize.y));
                    fixed4 pixelRight = tex2D(_MainTex, IN.texcoord + fixed2(_MainTex_TexelSize.x, 0));
                    fixed4 pixelLeft = tex2D(_MainTex, IN.texcoord - fixed2(_MainTex_TexelSize.x, 0));

                    // If one of the neighbouring pixels is invisible, we render an outline.
                    if (pixelUp.a * pixelDown.a * pixelRight.a * pixelLeft.a == 0) {
                        c.rgba = fixed4(1, 1, 1, 1) * _OutlineColor;
                    }
                }

                c.rgb *= c.a;

                return c;
            }
            ENDCG
        }
    }
}

Now create a new material called SpriteOutline and assign the newly created shader to it in the inspector.

Sprite Outline Material

Next create a new C# script and name it SpriteOutline. This component is going to handle updating our material in the editor and at runtime to toggle the outline off or on and also change the color. This component can also be targetted in an animation to enable or disable outlines for specific animation frames or to change the outline color.

using UnityEngine;

[ExecuteInEditMode]
public class SpriteOutline : MonoBehaviour {
    public Color color = Color.white;

    private SpriteRenderer spriteRenderer;

    void OnEnable() {
        spriteRenderer = GetComponent<SpriteRenderer>();

        UpdateOutline(true);
    }

    void OnDisable() {
        UpdateOutline(false);
    }

    void Update() {
        UpdateOutline(true);
    }

    void UpdateOutline(bool outline) {
        MaterialPropertyBlock mpb = new MaterialPropertyBlock();
        spriteRenderer.GetPropertyBlock(mpb);
        mpb.SetFloat("_Outline", outline ? 1f : 0);
        mpb.SetColor("_OutlineColor", color);
        spriteRenderer.SetPropertyBlock(mpb);
    }
}

Now that the hard work is done, add a few sprites to your scene. Change the material field of the SpriteRenderer component to the SpriteOutline material created above. You'll also want to add the SpriteOutline component to this game object to show a white outline by default. To hide the outline simply disable or remove the component.

Completed Sprite

With all that completed, you should now have a sprite with a white outline. In the inspector you can change the color to anything you'd like, independently from the SpriteRenderer color. The custom shader also maintains all existing functionality of the default sprite shader.

Completed Sprite

Please download the demo project and play around with it to get a better idea of how this technique looks and works. It contains a single scene with three examples of outlined sprites, one of which is animated.

Shaders can be complicated, but they are very powerful and make it quite easy to implement graphical features, even in 2D. If you have any further questions please feel free to message me on Twitter @RyanNielson or comment below.

Update (June 2, 2016)

Some people have been asking about how to change the thickness of the sprite outlines. Please download this new demo project with the changes to add this functionality. Changes were made to both the shader and component. Just adjust the outline size slider on the sprite outline component to change outline size. There is a limited outline size of 16 to prevent issues with shader for loop unrolling. It hasn't been tested throughly, so your results may vary, but it's probably a good place to start.

2D Splatter Effects in Unity Using the Stencil Buffer

While reading Zack Bell's excellent game development blog I was intrigued by the splatter effects used in his game called INK or in other games like Super Meet Boy. As an experiment I decided to try to find an easy way to pull off something similar in Unity. Based on some previous reading I've done regarding the stencil buffer, using it along with some custom shaders seemed like the simplest approach.

This is the kind of effect that's possible using only the stencil buffer and a few sprites.

Surface Material Shader

The stencil buffer is a pixel mask that can be used in shaders to save or discard pixels. It is simply a buffer where an integer is stored for each pixel. In your shader to can change the stencil buffer value, or optionally draw based on the stencil value. For more information on the stencil shader and various stencil operations, check out the Unity documentation. This post will be about achieving a specific effect using the stencil buffer, not a run-down of everything it has to offer.

Our shaders will be slight modifications on Unity's standard sprite shader to add the stencil operations. Luckily you can download all of the built-in shaders here so we don't have to write everything from scratch. These will be used as a base for these tutorials, so the rendering will be very close to standard Unity sprites.

Start by creating two new shaders in your project called Surface and Splatter. For the Surface shader, we want to add a Stencil block that sets the buffer value to 5 for any drawn surface pixel. Below is the Stencil block that we will be adding to the shader in its Pass block. The full shaders will be added further below. We also added an _AlphaCutoff property, which allows the splatter to draw properly on non-rectangular sprites like spikes.

Stencil
{
  Ref 5
  Comp Always
  Pass Replace
}

For the Splatter shader, we only want to draw splatter sprite pixels where the stencil buffer has been set to 5. This ensures that splatter is only drawn on surface pixels.

Stencil
{
  Ref 5
  Comp Equal
}
The full Surface.shader:
Shader "Splatter/Surface"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
        _AlphaCutoff("Alpha Cutoff", Range(0.01, 1.0)) = 0.01
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

        Pass
        {
            Stencil
            {
                Ref 5
                Comp Always
                Pass Replace
            }

        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ PIXELSNAP_ON
            #include "UnityCG.cginc"

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                half2 texcoord  : TEXCOORD0;
            };

            fixed4 _Color;
            fixed _AlphaCutoff;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif

                return OUT;
            }

            sampler2D _MainTex;
            sampler2D _AlphaTex;
            float _AlphaSplitEnabled;

            fixed4 SampleSpriteTexture (float2 uv)
            {
                fixed4 color = tex2D (_MainTex, uv);
                if (_AlphaSplitEnabled)
                    color.a = tex2D (_AlphaTex, uv).r;

                return color;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
                c.rgb *= c.a;

        // Discard pixels below cutoff so that stencil is only updated for visible pixels.
                clip(c.a - _AlphaCutoff);

                return c;
            }
        ENDCG
        }
    }
}
The full Splatter.shader:
Shader "Splatter/Splatter"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

        Pass
        {
            Stencil
            {
                Ref 5
                Comp Equal
            }

        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ PIXELSNAP_ON
            #include "UnityCG.cginc"

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                half2 texcoord  : TEXCOORD0;
            };

            fixed4 _Color;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif

                return OUT;
            }

            sampler2D _MainTex;
            sampler2D _AlphaTex;
            float _AlphaSplitEnabled;

            fixed4 SampleSpriteTexture (float2 uv)
            {
                fixed4 color = tex2D (_MainTex, uv);
                if (_AlphaSplitEnabled)
                    color.a = tex2D (_AlphaTex, uv).r;

                return color;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
                c.rgb *= c.a;
                return c;
            }
        ENDCG
        }
    }
}

Now we need to make two materials, Surface and Splatter, and assign the new shaders to them using their shader dropdowns.

Surface Material Shader

Splatter Material Shader

Finally, add two objects with sprite renderers to the scene. One named Surface, and the other named Splatter. Assign whatever sprites you want to them, and make sure Splatter is drawn above Surface by increasing its Order in Layer or adding it to a different Sorting Layer. Now, add the corresponding materials that were created earlier to each sprite renderer. If you move the splatter back and forth over the surface, it should only be drawn on the pixels of the surface and masked everywhere else.

Splatter Demo

Please download the demo project and play around with it to get a better idea of how this looks. It contains two scenes, Demo1 and Demo2. Demo1 contains a surface and a splatter sprite. Move the splatter sprite around to give you an idea of how this effect works. Demo2 contains many surfaces and the ability to shoot splatters as shown near the top of this post. Splatter will be shot from the center of the screen towards your mouse cursor when the mouse is clicked, and explode on the first surface that is hit.

The stencil buffer is a great feature that allows for all kinds of interesting effects, and makes it easy to draw simple paint and blood splatter using the approach above. Check out Prime31's post about sprite occlusion for another example of what the stencil buffer makes possible. If you have any further questions please feel free to message me on Twitter @RyanNielson or comment below.

Fixing Gaps Between Sprites - Better 2D in Unity Part 2

The first post in this series focused on the locking objects to the pixel grid. This time around I'll be focusing on issues related to side by side sprite rendering.

When working on 2D games in Unity using sprites, you may notice strange spaces or gaps between sprites. These are most notable when sprites are placed beside one another. This issue may not be noticeable immedately, but becomes most apparent at certain resolutions or when moving the camera as shown below.

Sprites With Gaps

This is quite easy to fix by using sprite packing software, which Unity 5 includes out of the box. The Unity sprite packer combines all sprites together into a single atlas, which can improve performance. Another side effect is that it duplicates the outer pixels of each sprite and adds them as padding in the packed atlas, solving the gap issue.

Enabling sprite packing is quite simple. First make sure the sprite packer is enabled by clicking on Edit > Project Settings > Editor in the toolbar and setting the sprite packer mode to "Always Enabled." Next, click on a sprite or sprite sheet in the project window, which will open the import settings in the inspector, and add a packing tag. In my case I just used the tag "Tiles", but you can use whatever you want. Sprites with identical packing tags will be added to the same atlas.

Sprite Inspector

You can view the atlas that Unity has created by clicking on Window > Sprite Packer in the toolbar. If there's nothing there, just click the pack button in the upper-left corner. Unity doesn't create the atlas until you manually click pack, or you run the game. Unity will regenerate atlases whenever play is pressed, or the game is built.

With an atlas created by setting a packing tag, the gaps between sprites are now gone as shown below. The gaps may still be shown in the game window when editing, but they will disappear when in playing the game. It seems that Unity only uses atlases in play mode.

Sprites With No Gaps

I've found that the Unity sprite packer works perfectly fine for all my needs. If you have any further questions please feel free to message me on Twitter @RyanNielson or comment below.

Controlling Object Visibility and Editability in Unity Using HideFlags

Unity game objects and components have an interesting feature called HideFlags. This allows you to control whether a game object or component is visible, editable, persistent, or any combination thereof.

Both GameObject and MonoBehaviour inherit from the Object class which contains the hideFlags property. This property can be set to any of the valid values in the HideFlags enumeration. By default it is set to HideFlags.None. Below are some examples and explanations of various options.

To run these examples, I've created a script that allows you to choose a HideFlags value and whether to set it on the game object or component. It sets the hideFlags property in OnEnable, so it will run when the game starts, or you can use it in combination with ExecuteInEditMode to run it while in the editor.

using UnityEngine;

public class HideFlagsSetter : MonoBehaviour
{
    public HideFlags customHideFlags;

    public enum Mode
    {
        GameObject,
        Component
    }

    public Mode setOn = Mode.GameObject;

    private void OnEnable()
    {
        if (setOn == Mode.GameObject)
        {
            gameObject.hideFlags = customHideFlags;
        }
        else if (setOn == Mode.Component)
        {
            hideFlags = customHideFlags;
        }
    }
}

HideFlags.HideInHierarchy

This option hides your game object in the hierarchy view, and makes it not selectable in the scene view. This has no effect if set on a component.

HideFlags.HideInHierarchy

HideFlags.HideInInspector

If set on a game object, this option hides all the components in the inspector.

HideFlags.HideInInspector

If set on a component, this option only hides the component in the inspector.

HideFlags.HideInInspector

HideFlags.NotEditable

If set on a game object, no components will be editable. The object will also not be selectable in the scene.

HideFlags.NotEditable

If set on a component, only that component will not be editable.

HideFlags.NotEditable

Other options include HideFlags.DontSaveInEditor, HideFlags.DontUnloadUnusedAsset, HideFlags.DontSaveInBuild, HideFlags.DontSave, and HideFlags.HideAndDontSave that mainly relate to scene and build persistence that I won't go into detail about here. More information about these can be found in the HideFlags Documentation.

You can download the example project to experiment with HideFlags using the script and example scene used in this post.

HideFlags can be a very useful tool in preventing the hierarchy from being cluttered and ensuring specific components are not editable. They're quite useful when working on assets in which the component values or game object structure are imporant. If you have any further questions please feel free to message me on Twitter @RyanNielson or comment below.