Kembali ke Blog

Optimasi Scaling Mobile Game: Merancang Arsitektur Kota untuk 1 Juta+ Concurrent Players

Diterbitkan pada 21 Mei 2026
Optimasi Scaling Mobile Game: Merancang Arsitektur Kota untuk 1 Juta+ Concurrent Players

Ringkasnya

Artikel teknis ini membedah strategi optimasi scaling untuk mobile game skala besar, dengan fokus pada arsitektur kota yang mendukung jutaan pemain secara simultan. Pembahasan mencakup teknik spatial hashing di sisi server, network interest management untuk efisiensi bandwidth, hingga penggunaan HLODs dan ASTC untuk mengatasi limitasi RAM pada perangkat mobile. Panduan ini memberikan wawasan praktis bagi developer untuk membangun infrastruktur multiplayer yang stabil dan berperforma tinggi menggunakan pendekatan berbasis distributed backend.

Setiap multiplayer developer tahu persis momen ketika arsitektur mobile game mereka mulai retak. Anda merancang lingkungan urban yang luas dan indah. Anda melakukan testing secara lokal dengan 10 simulasi client, dan build berjalan mulus di 60 FPS. Kemudian, Anda merilisnya ke live environment dengan 1.000 concurrent players yang memadati area central plaza. Dalam hitungan detik, perangkat Android low-end mengalami hard-crash karena Out-Of-Memory (OOM) exceptions, iOS Jetsam secara agresif mematikan aplikasi Anda, dan CPU dedicated server melonjak hingga 100% saat mencoba menghitung network replication untuk ribuan entity yang saling tumpang tindih.

Saat membangun mobile MMO atau open world skala besar yang dirancang untuk mendukung jutaan user aktif, Anda tidak bisa mengandalkan engine defaults standar. Hardware mobile memiliki limitasi thermal throttling yang ketat dan batas memori yang keras (seringkali membatasi game Anda di bawah 2GB RAM yang bisa digunakan pada perangkat mid-range). Secara bersamaan, server Anda harus menangani player cluster yang padat tanpa mengalami kegagalan sistem.

Mencapai optimasi scaling mobile game yang sesungguhnya membutuhkan pendekatan tiga pilar: spatial partitioning yang agresif di server, manajemen memori yang ketat di client, dan arsitektur distributed backend untuk menangani volume koneksi yang masif. Dalam tutorial step-by-step ini, kita akan membedah cara merancang arsitektur kota skala besar untuk platform mobile.

Step 1: Server-Side Spatial Partitioning

Musuh utama performa server dalam massive multiplayer games adalah masalah O(N²). Jika server Anda melakukan loop melalui setiap player untuk memeriksa jarak mereka terhadap setiap player lainnya guna menentukan siapa yang butuh network updates, kalkulasinya akan membengkak secara katastropik. 100 player membutuhkan 10.000 pemeriksaan jarak per tick. 1.000 player membutuhkan 1.000.000 pemeriksaan jarak. Pada server tick rate 30Hz, itu berarti 30 juta pemeriksaan per detik.

Untuk mengatasi ini, kita harus mengimplementasikan Spatial Hashing (atau sistem Grid/Quadtree). Dengan membagi kota ke dalam logical grid, player hanya perlu memeriksa network relevance terhadap entity di cell mereka saat ini dan cell di sekitarnya. Ini mengubah mimpi buruk O(N²) menjadi grid lookup O(1) yang diikuti oleh pemeriksaan lokal yang sangat terbatas.

Mengimplementasikan Spatial Hash Grid (Contoh C#)

Berikut adalah implementasi Spatial Hash Grid 2D yang sangat efisien dalam C# yang dapat Anda adaptasi untuk Unity, Godot (via C#), atau custom backend server untuk mengelola kedekatan entity tanpa melakukan loop ke seluruh world state.

using System.Collections.Generic;
using UnityEngine;

public class SpatialHashGrid
{
    private readonly float _cellSize;
    private readonly Dictionary<Vector2Int, HashSet<uint>> _grid;

    public SpatialHashGrid(float cellSize = 50f)
    {
        _cellSize = cellSize;
        _grid = new Dictionary<Vector2Int, HashSet<uint>>();
    }

    // Convert a world position to a grid coordinate
    private Vector2Int GetCellCoordinate(Vector3 position)
    {
        return new Vector2Int(
            Mathf.FloorToInt(position.x / _cellSize),
            Mathf.FloorToInt(position.z / _cellSize)
        );
    }

    // Add or update a player's position in the grid
    public void UpdateEntityPosition(uint entityId, Vector3 oldPosition, Vector3 newPosition)
    {
        Vector2Int oldCell = GetCellCoordinate(oldPosition);
        Vector2Int newCell = GetCellCoordinate(newPosition);

        if (oldCell != newCell)
        {
            if (_grid.ContainsKey(oldCell))
            {
                _grid[oldCell].Remove(entityId);
            }
            
            if (!_grid.ContainsKey(newCell))
            {
                _grid[newCell] = new HashSet<uint>();
            }
            _grid[newCell].Add(entityId);
        }
    }

    // Retrieve all entities in the immediate vicinity (9 cells)
    public List<uint> GetEntitiesInProximity(Vector3 position)
    {
        List<uint> nearbyEntities = new List<uint>();
        Vector2Int centerCell = GetCellCoordinate(position);

        // Loop through the 3x3 grid around the player
        for (int x = -1; x <= 1; x++)
        {
            for (int y = -1; y <= 1; y++)
            {
                Vector2Int cellToCheck = new Vector2Int(centerCell.x + x, centerCell.y + y);
                if (_grid.TryGetValue(cellToCheck, out HashSet<uint> entitiesInCell))
                {
                    nearbyEntities.AddRange(entitiesInCell);
                }
            }
        }

        return nearbyEntities;
    }
}

Dengan mengarahkan logika network replication Anda melalui GetEntitiesInProximity, server Anda hanya menghitung jarak persis untuk beberapa puluh player yang aktif berdekatan, secara drastis mengurangi beban CPU dan memungkinkan server Anda menangani ribuan concurrents dalam instance yang sama dengan stabil.

Step 2: Network Interest Management

Bahkan dengan spatial hashing yang menyelesaikan bottleneck CPU di server, Anda masih memiliki masalah bandwidth. Jaringan mobile (4G/5G) secara inheren tidak stabil, rentan terhadap jitter yang tinggi, dan memiliki limitasi bandwidth yang ketat. Mengirim data untuk 50 player terdekat setiap tick akan membanjiri socket buffer pada mobile client, yang menyebabkan desync parah.

Interest Management (atau Network Relevancy) adalah praktik memprioritaskan data apa yang dikirim melalui jaringan. Player yang berjarak 2 meter dan sedang terlibat baku tembak membutuhkan 30 updates per detik. Player yang berjarak 40 meter dan sedang berjalan di jalan yang berbeda hanya butuh 2 updates per detik.

Overriding Network Relevancy (Contoh Unreal Engine C++)

Di Unreal Engine, Anda bisa mengontrol hal ini dengan melakukan override pada fungsi IsNetRelevantFor. Ini memungkinkan Anda memangkas network traffic secara agresif berdasarkan line-of-sight dan tingkatan jarak.

bool ACityPlayerCharacter::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
    // 1. Always relevant to ourselves
    if (RealViewer == this || ViewTarget == this)
    {
        return true;
    }

    // 2. Calculate squared distance (faster than exact distance)
    const float DistanceSquared = FVector::DistSquared(SrcLocation, GetActorLocation());

    // 3. Absolute Cull Distance (e.g., 10,000 units = 100 meters)
    const float MaxRelevancyDistSq = 100000000.0f; 
    if (DistanceSquared > MaxRelevancyDistSq)
    {
        return false;
    }

    // 4. Dynamic Network Update Frequency based on distance
    // If they are far away, we lower how often we send data
    if (DistanceSquared > 25000000.0f) // 50 meters
    {
        NetUpdateFrequency = 2.0f; // 2 updates a second
    }
    else
    {
        NetUpdateFrequency = 30.0f; // 30 updates a second
    }

    return Super::IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
}

Dengan melakukan scaling pada NetUpdateFrequency secara dinamis berdasarkan jarak, Anda dapat mengurangi outbound bandwidth server hingga lebih dari 70%, menghemat paket data mobile player dan mencegah latency spikes.

Step 3: Client-Side Memory Limits dan Asset Streaming

Server memiliki RAM yang melimpah; smartphone tidak. iPhone 13 memiliki 4GB unified memory. Sistem operasi iOS biasanya memesan sekitar 1.5GB hingga 2GB dari total tersebut. Game Anda harus muat sepenuhnya di dalam sisa footprint 2GB tersebut. Jika Anda memuat seluruh kota skala besar ke dalam memori sekaligus, OS akan langsung mematikan aplikasi (terminate).

Untuk bertahan di environment ini, kota Anda harus dibagi menjadi chunk dan di-stream secara asynchronous.

  • Hierarchical Level of Detail (HLODs): Alih-alih merender 50 bangunan individual di blok kota yang jauh (yang setara dengan 3.000 draw calls), Anda harus melakukan bake pada seluruh blok kota tersebut menjadi satu static mesh dengan unified texture atlas. Ini mengurangi draw calls untuk geometri yang jauh dari ribuan menjadi tepat satu saja.
  • Addressable Asset Systems: Jangan pernah menggunakan hard references pada data assets utama Anda. Jika player spawn di District A, client harus menggunakan asynchronous loading (misalnya Unity Addressables atau Unreal PrimaryAssetLabels) untuk men-download atau memuat hanya texture dan mesh yang dibutuhkan untuk District A. District B harus dibersihkan (purge) secara ketat dari RAM.
  • Texture Compression: Andalkan secara eksklusif pada ASTC (Adaptive Scalable Texture Compression) untuk mobile. Format ini memungkinkan footprint blok yang sangat bervariasi, memberi Anda kontrol granular atas memori vs kualitas visual pada basis per-texture.

Step 4: Distributed Backend Architecture dan Server Sharding

Metropolis masif tidak bisa berjalan di satu mesin fisik saja. Saat merancang kota skala MMO, dunia harus dibagi secara fisik ke dalam beberapa server instance (shards atau nodes). Ketika player menyeberangi jembatan dari Downtown Node ke Slums Node, koneksi client dan world state mereka harus berpindah tangan (hand-off) secara mulus di antara dua proses server yang berbeda.

Membangun ini sendiri membutuhkan setup Kubernetes clusters yang diorkestrasi oleh sistem seperti Agones, database sharding dengan Redis untuk memindahkan player state antar server nodes, dan custom UDP load balancers untuk hand-off koneksi yang mulus. Merancang ini secara kokoh agar player tidak kehilangan item selama transisi adalah tugas besar—bisa memakan waktu 4-6 bulan kerja DevOps khusus untuk tim engineering senior.

Jika Anda tidak menangani RPC queues dan database writes dengan benar selama hand-off ini, Anda pasti akan mengalami state corruption. Kami sebelumnya telah membahas mekanika fixing the Unreal Engine RPC replication issue breaking your states, dan prinsip-prinsip yang sama berlaku langsung pada spatial handoffs di seluruh server nodes.

Di sinilah platform solutions bersinar. Dengan horizOn, layanan backend high-concurrency, real-time database sync, dan orkestrasi dedicated server ini sudah dikonfigurasi sebelumnya. Alih-alih menghabiskan runway Anda untuk merancang dan men-debug Kubernetes networking rules, Anda bisa fokus sepenuhnya membangun gameplay loops kota Anda dan optimasi client.

Best Practices untuk Mobile City Worldbuilding

Untuk memastikan kota Anda melakukan scaling dengan sempurna bagi jutaan user sambil mempertahankan frame rate tinggi di perangkat budget, patuhi aturan arsitektur berikut secara ketat:

  1. Aggressive Instance Pooling: Jangan pernah menggunakan Instantiate() atau SpawnActor untuk objek transien seperti kendaraan, pejalan kaki, atau projectile selama gameplay. Mobile CPU sangat terbebani oleh memory allocation dan garbage collection. Lakukan pre-warm pada object pools selama loading screen dan gunakan kembali secara terus-menerus.
  2. Texture Atlasing untuk Blok Kota: Draw calls adalah pembunuh utama mobile GPU (yang mengandalkan Tile-Based Deferred Rendering). Gabungkan texture dari semua generic street props (tempat sampah, bangku, lampu jalan) ke dalam satu texture atlas besar. Ini memungkinkan engine untuk melakukan batch rendering ratusan prop ke dalam satu draw call saja.
  3. Budget Polycount yang Ketat per Chunk: Terapkan batasan keras. Satu chunk kota mobile (misal area 100x100 meter) idealnya tetap di bawah 300.000 visible triangles. Andalkan normal maps daripada geometri mentah untuk mensimulasikan detail arsitektur.
  4. Implementasikan Server-Side Hibernation: Menjalankan dedicated server untuk kota masif di mana 80% map-nya sedang kosong adalah cara cepat untuk membangkrutkan studio Anda. Anda butuh manajemen instance yang agresif, mengambil inspirasi dari Fortnite server optimization hibernation proposal untuk mematikan koordinat grid yang idle dan membangunkan mereka seketika saat player mendekat.
  5. Pisahkan Collision dari Visual Mesh: Jangan pernah menggunakan visual mesh yang kompleks untuk kalkulasi collision di sisi server. Server hanya boleh memahami kota sebagai rangkaian bentuk primitif low-poly (boxes, capsules, spheres). Ini menjaga footprint memori server tetap minimal dan kalkulasi physics di bawah satu milidetik.

Common Pitfalls yang Harus Dihindari

  • Jebakan RPC Flooding: Developer sering memicu Remote Procedure Calls (RPCs) dari server-ke-client untuk efek visual (seperti percikan api dari tabrakan mobil). Jangan lakukan ini. Server hanya boleh melakukan replikasi pada state mobil (misal bIsCrashed = true). Client harus secara independen mengamati perubahan state ini melalui OnRep/property hook dan memicu VFX percikan api secara lokal. Ini menghemat bandwidth jaringan dalam jumlah besar.
  • Kebocoran Memori pada Zone Transitions: Saat melakukan stream out pada chunk kota di mobile, pastikan Anda secara aktif memaksa garbage collection atau melakukan unload pada asset bundles secara manual. Jika Anda membiarkan bahkan beberapa Megabyte texture yatim piatu tertinggal di memori setiap kali player berpindah zona, game mereka pasti akan crash setelah 20 menit bermain.

Kesimpulan

Mencapai optimasi scaling mobile game yang sesungguhnya adalah sebuah aksi penyeimbangan. Ini membutuhkan perjuangan untuk setiap megabyte RAM di client, regulasi ketat pada network relevancy, dan mendistribusikan beban server ke seluruh scalable backend nodes. Dengan mengimplementasikan spatial hashing, dynamic update frequencies, dan asynchronous asset streaming, Anda dapat membangun kota yang masif dan hidup yang berjalan lancar bahkan pada hardware mobile yang sudah berusia beberapa tahun.

Namun, membangun infrastruktur yang scalable untuk mengarahkan ribuan koneksi konkuren dan mengelola server hand-off yang mulus seringkali lebih sulit daripada membangun game itu sendiri. Siap untuk melakukan scaling pada multiplayer backend Anda tanpa mimpi buruk devops? Coba horizOn secara gratis atau pelajari API docs untuk melihat bagaimana kami menangani arsitektur high-concurrency secara instan.


Sumber: Designing Large-Scale Mobile Game Cities: Production, Optimization, & Worldbuilding Expertise