UI Tech Test
About the project
This is a small sample test I did for a mobile game development studio based in Barcelona.
WebGL Demo
Main Screen
Sprite Packing

Sprite packing in the project is done using the Sprite Atlas creation tool inside Unity, this is extremely important to have everything cached and batched inside a single draw call, so the UI draws in two batches: UI Sprites -> TextMeshPro Text.
Main UI Footer

Everything animated in the UI is done by scripting using the DOTween Pro package.
All the buttons are laid out using a Horizontal Layout, when activating a button, a tween changes the width of the corresponding Rect Transform component from 0 to its expanded value. Due to the change in size, the Horizontal Layout component automatically adjusts the other buttons positions.
Settings Screen
UI Blur

IEnumerator RecordFrame()
{
yield return new WaitForEndOfFrame();
var screenTex = ScreenCapture.CaptureScreenshotAsTexture();
screenShot = new Texture2D(screenTex.width, screenTex.height, TextureFormat.RGB24, true);
screenShot.SetPixels32(screenTex.GetPixels32());
screenShot.Apply();
uiSettingsScreenshotRawImage.texture = screenShot;
Destroy(screenTex);
OpenUISettingsPanel();
}
In order to avoid doing a blit operation I’ve decided to take an actual screenshot of the current state of the screen using a coroutine, after the screenshot is taken, I call the method to actually open the UI and do the scale tween and pass the screenshot as a texture to a panel with a blur shader.
In the shader:
void BoxBlur_half(in Texture2D MainTex, in SamplerState sampler_MainTex, in float2 uv, in float2 texelSize, in float radius, in float lod, out float4 result)
{
result = half4(0,0,0,0);
float diameter = (float(radius) * 2.0) + 1.0;
float numberOfSamples = 0;
float2 res = uv * (1/texelSize);
for (int y = -radius; y <= radius; y++)
{
for (int x = -radius; x <= radius; x++)
{
half2 pixelOffset = half2(x,y);
half distanceToPixel = length(pixelOffset);
if(distanceToPixel > float(radius))
{
continue;
}
half2 uvOffset = pixelOffset * (texelSize * (lod + 1));
result += SAMPLE_TEXTURE2D_LOD(MainTex, sampler_MainTex, uv + uvOffset, lod);
numberOfSamples++;
}
}
result /= numberOfSamples;
}
This is essentially abox blur with a 3×3 max sample radius. Some optimizations are applied,instead of doing a whole box I’m doing a circle, this creates a more pleasant effect and avoid a bunch of texture samples. To avoid having to do a bunch of samples and be able to limit it to 3 per-axis, I’m sampling the texture taking advantage of the mipmaps in the texture, by selecting a higher LOD while scaling the radius of the actual blur it gives the appearance of a higher radius because we’re sampling a lower resolution texture progressively.