DEV Community

Getinfo Toyou
Getinfo Toyou

Posted on

Building a Zero-Bloat Android Runner: Object Pooling, Low-Latency Input, and Optimization

Modern mobile gaming often comes with an unspoken tax: 500MB download sizes, endless splash screens, and aggressive background services that drain batteries. When you just want a quick three-minute distraction while standing in line or riding the train, waiting through loading bars and shader pre-compilations ruins the experience.

I built Echo Runner to solve that specific annoyance. It is a lean, fast-paced endless runner designed to launch instantly, run at a locked 60 frames per second on budget devices, and deliver straightforward arcade reflex gameplay without unnecessary bloat.

Here is a look at the technical decisions behind Echo Runner, the engineering challenges of low-spec Android optimization, and who gains the most from this approach.


Who Benefits Most From This Architecture?

Before diving into the code, it helps to understand the target profile. Echo Runner was designed specifically for two groups:

  1. Commuters and casual mobile gamers on budget or aging hardware: Players who do not own flagship phones, have limited storage, or deal with spotty network connections. They need a responsive game that opens in two seconds, consumes minimal battery, and does not hitch when an obstacle appears.
  2. Players who value reflex-driven gameplay over pay-to-win systems: Many modern runners introduce artificial speed caps or energy meters that recharge with microtransactions. Echo Runner is built on clean, pure skill progression where level difficulty scales mathematically rather than commercially.

The Tech Stack

To balance rapid development with low-level control, I used:

  • Engine: Unity (stripped-down Universal Render Pipeline with all post-processing passes removed except minimal bloom).
  • Language: C# using strictly allocation-free patterns during the main loop.
  • Target Platform: Android (targeting API level 34, backward-compatible to Android 8.0).
  • Asset Pipeline: Low-poly meshes, unlit vertex-colored shaders, and compressed audio buffers to keep total APK download size low.

Technical Challenges and Solutions

1. Eliminating Garbage Collection Spikes

In an endless runner, spawning and destroying platforms, obstacles, and pick-ups dynamically is the standard pattern. In C#, frequent calls to Instantiate() and Destroy() trigger the Mono runtime's garbage collector. When the GC runs on low-end Android hardware, it causes noticeable micro-stutter (50-100ms frame drops)—which instantly kills a player in a precision reflex game.

To fix this, I implemented an aggressive generic object pooling system:

public class ObjectPool<T> where T : Component
{
    private readonly Queue<T> _availableObjects = new();
    private readonly T _prefab;
    private readonly Transform _parent;

    public ObjectPool(T prefab, int initialSize, Transform parent = null)
    {
        _prefab = prefab;
        _parent = parent;
        for (int i = 0; i < initialSize; i++)
        {
            T instance = Object.Instantiate(_prefab, _parent);
            instance.gameObject.SetActive(false);
            _availableObjects.Enqueue(instance);
        }
    }

    public T Rent()
    {
        T item = _availableObjects.Count > 0 ? _availableObjects.Dequeue() : Object.Instantiate(_prefab, _parent);
        item.gameObject.SetActive(true);
        return item;
    }

    public void Return(T item)
    {
        item.gameObject.SetActive(false);
        _availableObjects.Enqueue(item);
    }
}
Enter fullscreen mode Exit fullscreen mode

Every track segment, barrier, and power-up is pre-warmed during initial scene setup. During active gameplay, allocations per frame drop to zero bytes.

2. Input Latency Calibration

Touch latency on Android varies wildly across manufacturers due to display refresh rates and touch-polling implementations. In a runner game where a lane switch requires split-second timing, queued touch events caused players to feel like controls were sluggish.

Instead of reading touch inputs in Unity's standard Update() loop with raw delta checks, I migrated the input stack to Unity's modern Input System, consuming tap and swipe events directly from the hardware queue during FixedUpdate() sync phases. This cut perceived input lag significantly on 60Hz panels.


Lessons Learned

  1. Profiling on real low-end hardware is mandatory: Testing on modern Snapdragon processors hides memory leaks and fill-rate limits. Testing on an entry-level MediaTek chip exposed visual bottlenecks in the particle systems within five minutes.
  2. Simplicity beats feature bloat: Trimming complex UI menus and third-party analytics SDKs dropped load times from four seconds to under one second.

Try It Out

If you want a lightweight, distraction-free arcade game for your daily commute, you can download Echo Runner directly on Google Play:

Feedback on performance across different Android hardware configurations is always welcome in the comments.

Top comments (0)