Professional case study

UGUI Rendering Optimization

Live-game UI performance engineering across multiple companies and titles

  • Bagelcode
  • StudioZoo
  • SZ Code Lab
  • Unity
  • UGUI
  • NGUI
  • C#
  • HLSL
  • Unity Profiler
  • Frame Debugger
Avg FPS
60 (on low-end devices)
Peak Draw Calls
≈ 100
Reference Device
iPhone 5s · low-end Android
Live Titles
8 shipped games

Challenge

Across eight live slot and casual titles — Party Slots, Vegas Party Slots, Club Vegas, Epic Diamond Slots, Jackpotjoy, Starspins, Slot & Dragons, City of Holdem, and Golden Mango Casino — the same pattern recurred. Out-game lobbies accumulated dense UI/UX requirements, and in-game features added real-time masking/clipping. The result was draw-call explosions and broken batching, which produced critical frame drops on low-end devices. The breaking point came fast on legacy targets like the iPhone 5s.

Solution

Custom UGUI components

I subclassed and rewrote UGUI components to handle complex hierarchies and real-time clipping/masking without breaking compatibility with the rest of the project. New patterns plugged into the existing prefab graph and still benefited from optimized batching.

A repeatable bottleneck-analysis routine

Every UI change went through a short checklist: profile draw calls, set-pass calls, and batches with Unity Profiler + Frame Debugger. Documenting that loop kept the bar consistent as the team grew.

Canvas hierarchy redesign

Instead of one giant canvas, I split UI into static / dynamic / overlay layers and reorganized the global atlas by layer and usage frequency, so batching collapsed naturally on most screens.

graph TD Canvas["Root Canvas"] StaticLayer["Static Layer
Background · Frames · Headers
(Batched aggressively)"] DynamicLayer["Dynamic Layer
Counters · Animations · Tooltips
(Re-batched per frame)"] OverlayLayer["Overlay Layer
Popups · Toasts
(On-demand canvas)"] AtlasGroup["Global Atlas Groups
per layer · per usage frequency"] Canvas --> StaticLayer Canvas --> DynamicLayer Canvas --> OverlayLayer StaticLayer --> AtlasGroup DynamicLayer --> AtlasGroup OverlayLayer --> AtlasGroup

Lightweight custom shaders

For UI elements that didn’t need full alpha testing or generic UI features, I replaced the default UI shader with lightweight HLSL shaders, cutting overdraw and shader switches on hot paths.

Tooling for non-engineers

To keep new assets from breaking the batching structure, I shipped editor inspector tooling that designers and artists could use to test placements themselves. Mistakes were caught at the moment assets were added, not at build time.

Achievements

  • Rock-solid 60 FPS on low-end devices. Slot & Dragons held a steady 60 FPS with draw calls kept around ≤100 on iPhone 5s-class hardware.
  • Same playbook applied across eight live titles. Bagelcode → StudioZoo → SZ Code Lab — each new project converged on the optimized structure faster than the last.
  • Productivity multiplier for non-engineers. Inspector-driven scripts let design and art teams validate UI placements without an engineer in the loop, freeing core engineering time.

Shipped games

Public code excerpts

← Back to portfolio