10 Ekim 2026 Cumartesi

Mobil Oyunlarda Google Play Games Başarım (Achievements) Sistemi: Mağaza Etkisi, 2.000 XP Kuralı ve MonoGame Mimarisi




Mobil Oyunlarda Google Play Games Başarım (Achievements) Sistemi: Mağaza Etkisi, 2.000 XP Kuralı ve MonoGame Mimarisi

Bir mobil oyunu geliştirip mağazaya yükledikten sonra asıl zorluk, oyuncuyu ilk birkaç dakikalık meraktan alıp uzun vadeli bir oyun döngüsünün içine çekebilmektir. Oyuncu sadece "bölümü geç, bir sonraki bölüme başla" döngüsüyle baş başa kaldığında bir süre sonra yorulur veya oyundaki derin mekaniklerin yarısını bile fark etmeden uygulamayı kapatır.

Google Play Games Services (GPGS) Başarım (Achievements) sistemi, dışarıdan bakıldığında sadece ekranda beliren küçük bir rozet gibi görünse de; doğru kurgulandığında hem oyuncu bağlılığını (Retention) artıran hem de oyuncuya oyunun tüm mekaniklerini öğreten güçlü bir tasarım aracıdır.

Bu yazıda, Google Play ekosisteminde başarımların ne işe yaradığını, Blocked: Pixel Panzer oyunumuzda 79 farklı başarımı Google'ın katı kurallarına göre nasıl yapılandırdığımızı ve yüksek tempolu bir MonoGame / C# oyununda FPS düşüşü veya ANR (Application Not Responding) yaratmadan bu sistemi nasıl çalıştırdığımızı tüm teknik detaylarıyla paylaşıyoruz.


1. Google Play'de Başarım (Achievements) Sistemi Ne İşe Yarar?

Google Play Games başarımlarını oyuna entegre etmenin hem oyuncu psikolojisi hem de mağaza metrikleri tarafında dört somut karşılığı vardır:

  1. Alternatif Hedefler ve Oyuncu Bağlılığı (Day-7 / Day-30 Retention):
    Her oyuncu liderlik tablosunda (Leaderboard) dünya birincisi olamaz; ancak her oyuncu "100 Taret İmha Et" veya "Bir bölümü hiç hasar almadan tamamla" hedefini başarabilir. Kademeli (Incremental) başarımlar oyuncuya her oyun oturumunda ilerleyen somut bir hedef çubuğu sunar.
  2. Oyun Mekaniklerini Doğal Yoldan Öğretme (Gizli Onboarding):
    Oyuncular çoğu zaman oyundaki alternatif silahları veya stratejileri denemeye çekinir. Örneğin Blocked: Pixel Panzer'da Renk Çarkı (ColorWheel) üzerindeki yeşil asit mermisinin blokları erittiğini veya siyah merminin kritik hasar verdiğini başarımlar üzerinden ödüllendirdiğimizde, oyuncu bu mekanikleri bizzat deneyimleyerek öğrenir.
  3. Anlık Geri Bildirim ve Google Play XP Profili:
    Savaşın en hararetli anında ekranın üstünden kayarak inen resmi Google Play Games bildirimi ve kazanılan XP (Deneyim Puanı), oyuncunun küresel Google Play profil seviyesini yükseltir. Bu, oyuncuya oyunun işletim sistemiyle bütünleşik, kalıcı bir ilerleme sunduğu hissini verir.
  4. Google Play Ekosistemi ve Kalite Sinyalleri:
    Google Play Games Services V2 (PGS V2) entegrasyonu; otomatik sessiz giriş (Zero-Tap Sign-In), Google Play Games on PC uyumluluğu ve Play Store'un "Oyunlar" sekmesindeki etkileşim sinyalleri açısından oyunu standart uygulamaların önüne taşır.

Blocked: Pixel Panzer Oyun İçi Başarım Bildirimi
Blocked: Pixel Panzer'da Seviye 103 savaşının ortasında 10. taret imha edildiği anda ekranın üstünde beliren resmi Google Play Games "Turret Buster I" bildirimi.


2. Google Play Console Kuralları: 2.000 XP Sınırı ve 10.000 Adım Kuralı

Google Play Console'da başarım tanımlarken dikkat edilmesi gereken en önemli teknik kısıtlamalar şunlardır:

  • 2.000 XP Tavan Sınırı (Hard Limit): Bir oyundaki tüm başarımların puan toplamı en fazla 2.000 XP olabilir. Her bir başarım 5 ile 200 XP arasında olmalı ve 5'in katı şeklinde puanlanmalıdır.
  • Kademeli (Incremental) Başarımlarda 10.000 Adım Sınırı: Kümülatif sayaçlı başarımlarda Google Play en fazla 10.000 adım (steps) tanımlanmasına izin verir.
  • Play Games Services V2 Zorunluluğu: Google, eski V1 SDK'sını kullanımdan kaldırmaktadır. Bu nedenle entegrasyonun doğrudan Xamarin.GooglePlayServices.Games.V2 (PlayGamesSdk ve PlayGames.GetAchievementsClient) üzerine kurulması gerekir.

Blocked: Pixel Panzer'da 79 Başarımlık Katalog Mimarisi

Oyunumuzdaki gerçek sınıfları (TurretBlock, FighterJet, EnemyRocket, ColorWheel, HydraRegenerationSkill, Season) analiz ederek toplam 1.995 / 2.000 XP bütçesini kullanan 79 başarımı 6 ana kategoride topladık:

Kategori Örnek Başarımlar Tür Hedef / Mekanik
Düşman Avcıları Jet Hunter I–III, Sky Dominator, Turret Buster I–III, Siege Master Kademeli 10, 100, 1.000 ve 10.000 adet savaş uçağı, taret ve güdümlü roket imhası.
Blok Yıkımı & Patlayıcılar Pioneer Breaker, Demolition Crew, Crusher Supreme, Pixel Obliterator, Chain Reaction Kademeli / Standart 1.000'den 10.000'e kadar blok kırma ve tek dinamitle 5/25/50 hedef patlatma.
ColorWheel & Silah Ustalığı Prismatic Pilot, Acid Dissolver, Critical Striker, Power Blaster, Speedy Sprinter, Beam Vaporizer Kademeli Yeşil asit mermisiyle blok eritme, siyah kritik mermi, kırmızı güç mermisi, lazer ve güdümlü roket serileri.
Bölüm & Boss İlerlemesi First Blood, Giant Slayer, Decimator, Speedrunner I–III Kademeli / Standart İlk düşman, ilk Boss zaferi, 4 dakikanın altında tamamlanan hızlı bölümler.
Purist (Yeteneksiz / Hasarsız) Görevler Untouchable, Clean Victory I–III, Purist Gladiator, Mono Specialist Kademeli / Standart Hiç hasar almadan veya hiç yetenek (skill) kullanmadan Boss ve bölüm geçme.
Atmosfer, İkmal & Meta Storm Fighter, Retro Connoisseur, Gem Tycoon, Quad Spectrum, Audiophile, Loyal Supporter Kademeli / Gizli Yağmurlu havada 100 taret yıkma, CRT monitör toplama, müzik çalarda parça dinleme.

Her başarım için oyunun retro-piksel atmosferine uygun, kademe arttıkça bronzdan altına ve prizmatik çerçevelere evrilen 512x512 piksel özel rozet ikonları hazırladık:

Blocked: Pixel Panzer Başarım Rozetlerinden Örnekler
79 başarımlık katalogdan 6 örnek rozet: Sky Dominator, Turret Buster III, Pixel Obliterator, Prismatic Pilot IV, Acid Dissolver II ve Power Blaster II.

Pratik İpucu (Toplu ZIP Yükleme): 79 başarımı Google Play Console arayüzünden tek tek elle girmek saatler sürer. Bunun yerine Play Console'un "Import achievements" özelliğini kullanarak AchievementsMetadata.csv, AchievementsIconsMappings.csv ve 512x512 .jpg ikonlarını tek bir .zip paketinde birleştirip saniyeler içinde hatasız olarak yükleyebilirsiniz.

Google Play Console Yayınlanmış Başarımlar Tablosu
Google Play Console üzerinde yayınlanmış (Published) başarımlarımız ve puan dağılımları.


3. Clean Architecture: Ortak Kod Tabanında IGamesService ve AchievementDirector

Blocked: Pixel Panzer hem Windows PC (Microsoft Store) hem de Android (Google Play) üzerinde çalışan çapraz platformlu bir MonoGame projesidir. Oyun mantığının bulunduğu Blocked.Shared katmanının Android SDK paketlerine doğrudan bağımlı olmaması için Dependency Inversion (SOLID) prensibini uyguluyoruz.

Önce ortak katmanda platformdan bağımsız IGamesService arayüzümüzü tanımlıyoruz:

namespace Blocked.Shared.Services
{
    public interface IGamesService
    {
        void Initialize();
        Task<bool> SignIn();
        Task<bool> IsAuthenticated();
        
        // Leaderboards
        void SubmitScore(string leaderboardId, long score);
        void ShowLeaderboards();
        void ShowLeaderboard(string leaderboardId);

        // Achievements
        void UnlockAchievement(string achievementId);
        void IncrementAchievement(string achievementId, int steps = 1);
        void ShowAchievements();
    }
}

Oyun içindeki çarpışma veya seviye sınıflarının (CollisionManager, GameplayScreen, Player) içine spagetti kod yazmak yerine, tüm başarım kurallarını AchievementDirector adında tek bir merkezi koordinatörde topluyoruz:

public static class AchievementDirector
{
    public static void OnEnemyKilled(
        bool isJet = false,
        bool isTurret = false,
        bool isRocket = false,
        bool isBoss = false,
        Bullet? killingBullet = null,
        bool killedByLaser = false)
    {
        var gamesService = ScreenManager.Instance?.GamesService;
        if (gamesService == null) return; // Desktop'ta null-safe olarak sessizce geçer

        if (isTurret)
        {
            gamesService.IncrementAchievement(AchievementIds.TurretBuster1, 1);
            gamesService.IncrementAchievement(AchievementIds.TurretBuster2, 1);
            gamesService.IncrementAchievement(AchievementIds.TurretBuster3, 1);
            gamesService.IncrementAchievement(AchievementIds.SiegeMaster, 1);

            // Yağmurlu havada imha edilen taretler "Storm Fighter" sayacını ilerletir
            if (Season.Current == SeasonKind.Rainy)
            {
                gamesService.IncrementAchievement(AchievementIds.StormFighter, 1);
            }
        }

        TrackWeaponKill(gamesService, killingBullet, killedByLaser);
    }
}

Bu yapı sayesinde masaüstü sürümünde hiçbir Google Play kodu çalışmazken, Android sürümünde tüm tetikleyiciler GooglePlayGamesService üzerinden işlenir.


4. Kritik Mühendislik Detayı: JNI / Binder IPC Yükünü Sıfırlamak ve ANR Önleme

Yüksek tempolu bir arcade oyunda başarımları doğrudan Google Play SDK'sına bağladığınızda çok tehlikeli bir performans tuzağı ortaya çıkar:

Oyuncu sahadaki bir Dinamit Bloğunu (ExplosiveBlock) patlattığında veya Lazer yeteneğini ateşlediğinde, tek bir karede (16.6 milisaniye içinde) 20 ila 30 blok ve düşman aynı anda yok olabilir. Her yıkımda 5-6 farklı kademeli başarım tetiklendiğinde, tek bir karede 150'den fazla IncrementAchievement çağrısı oluşur!

Eğer bu 150 çağrıyı ana oyun döngüsü (Main Thread) üzerinde doğrudan Android JNI ve Binder IPC üzerinden Google Play Services sürecine gönderirseniz:

  1. Düşük donanımlı (1–2 GB RAM'li Android Go) cihazlarda oyun anında takılır (FPS drop).
  2. JNI referans tablosu (GREF) şişer ve Google Play Console'da ANR (Input dispatching timed out) hataları patlak verir.

Çözümümüz: Bellek İçi Birleştirme (1.5s Batching) ve Oturum Tekilleştirme

GooglePlayGamesService.cs içinde geliştirdiğimiz mimari, oyun döngüsünü Google Play IPC çağrılarından tamamen izole eder:

  1. Oturum İçi Tekilleştirme (_unlockedInSession):
    Bir standart başarım o oturumda bir kez tetiklendiğinde HashSet<string> içine kaydedilir. Aynı karede veya sonraki karelerde koşul tekrar sağlansa bile JNI katmanına hiç inilmez.
  2. 1.5 Saniyelik Arka Plan Birleştirme (_pendingIncrements):
    IncrementAchievement çağrıldığında ana thread üzerinde sıfır JNI maliyetiyle sadece C# Dictionary<string, int> içindeki sayaç artırılır. Arka planda çalışan bir Task.Run işçisi, 1.5 saniye boyunca biriken tüm artışları toplar ve Google Play Services'e tek bir toplu (batched) çağrı olarak iletir.
  3. 26502: CLIENT_RECONNECT_REQUIRED ve Çevrimdışı Önbellek (Offline Cache):
    Oyuncu oyunu arka plana atıp saatler sonra döndüğünde Google Play oturumu bayatlayabilir ve 26502 (CLIENT_RECONNECT_REQUIRED) hatası dönebilir. Kodumuz bu durumu yakalayıp arka planda 1 kez sessiz SignIn() dener; eğer internet yoksa veya oturum yenilenemezse oyuncunun başarımını kaybetmemek için yerel GPGS önbelleğine (client.Unlock) devreder.
private static readonly HashSet<string> _unlockedInSession = new(StringComparer.Ordinal);
private static readonly Dictionary<string, int> _pendingIncrements = new(StringComparer.Ordinal);
private static readonly object _achievementBatchLock = new();
private static bool _incrementFlushScheduled = false;

public void IncrementAchievement(string achievementId, int steps = 1)
{
    if (!_isInitialized || string.IsNullOrEmpty(achievementId) || steps <= 0)
        return;

    bool shouldSchedule = false;
    lock (_achievementBatchLock)
    {
        // Oyun döngüsünde (Main Thread) SIFIR JNI çağrısı — sadece RAM'de toplama!
        if (_pendingIncrements.TryGetValue(achievementId, out int current))
            _pendingIncrements[achievementId] = current + steps;
        else
            _pendingIncrements[achievementId] = steps;

        if (!_incrementFlushScheduled)
        {
            _incrementFlushScheduled = true;
            shouldSchedule = true;
        }
    }

    if (shouldSchedule)
    {
        Task.Run(async () =>
        {
            await Task.Delay(1500).ConfigureAwait(false);
            FlushPendingIncrementsNow();
        });
    }
}

private void FlushPendingIncrementsNow()
{
    List<KeyValuePair<string, int>>? snapshot = null;

    lock (_achievementBatchLock)
    {
        _incrementFlushScheduled = false;
        if (_pendingIncrements.Count == 0) return;

        snapshot = new List<KeyValuePair<string, int>>(_pendingIncrements);
        _pendingIncrements.Clear();
    }

    var act = _activity;
    if (act == null || act.IsFinishing || act.IsDestroyed) return;

    var client = PlayGames.GetAchievementsClient(act);
    foreach (var kvp in snapshot)
    {
        if (kvp.Value > 0)
        {
            // Biriken 150 çağrı, tek bir birleştirilmiş adım olarak GPGS'e iletilir
            client.Increment(kvp.Key, kvp.Value);
        }
    }
}

5. Oyuncu Başarımları Nereden Takip Ediyor?

Başarımların sadece oyun sırasında bildirim olarak çıkması yetmez; oyuncunun hangi başarımları açtığını, hangilerinde yüzde kaç ilerlediğini ve bir sonraki hedefini istediği an inceleyebilmesi gerekir.

Bunun için ana menümüzdeki istatistik ekranını Android tarafında STATS & RANKS merkezi haline getirdik. Ekranın en üstünde yer alan LEADERBOARD ve ACHIEVEMENTS butonları, oyuncunun dikkatini çekmek için 2 saniyelik yumuşak bir sinüs dalgasıyla (Math.Sin) parlar. Masaüstü (PC) sürümünde ise bu butonlar otomatik olarak gizlenir ve ekran klasik STATISTICS görünümünü korur.

Oyuncu ACHIEVEMENTS butonuna dokunduğunda:

  1. Önce FlushPendingIncrementsNow() çağrılarak bellekte bekleyen son 1.5 saniyelik artışlar anında Google Play'e yazılır.
  2. Ardından GetAchievementsIntent() ile Google Play Games'in kendi yerel (native) Başarılar arayüzü doğrudan oyunun üzerinde açılır:

Oyun İçinden Açılan Google Play Games Başarılar Ekranı
STATS & RANKS menüsündeki ACHIEVEMENTS butonuna basıldığında açılan yerel Google Play Games penceresi: Önerilen hedefler, kilidi açılmış rozetler (Loyal Supporter, Audiophile) ve kilitli kademeler.

Bu pencerede oyuncu toplam ilerlemesini (2 / 79), Google'ın ona özel öne çıkardığı hedefleri ve kademeli başarımlardaki yüzdelik ilerleme halkalarını uygulamadan hiç çıkmadan takip edebilir.


6. Sonuç ve Oyunlarımız

Google Play Games Başarım sistemi, doğru puan bütçesi ve ana iş parçacığını (Main Thread) yormayan bir arka plan mimarisiyle kurulduğunda oyununuzun tekrar oynanabilirliğini doğrudan yukarı taşıyan en değerli ücretsiz araçlardan biridir.

Bu yazıda anlattığımız 79 başarımlık sistemi, ColorWheel mekaniklerini ve liderlik tablolarını canlı olarak deneyimlemek veya diğer oyunlarımıza göz atmak isterseniz aşağıdaki mağaza bağlantılarından ulaşabilirsiniz:

🎮 Oyunlarımız:

📢 Sosyal Medya ve Geliştirici Günlüklerimiz:

Yeni teknik makalelerimizi ve oyun güncellemelerimizi takip etmek için:


Etiketler & Konular

#GooglePlayGames #Achievements #MonoGame #CSharp #MobileGameDev #AndroidDev #IndieDev #GameArchitecture #BlockedPixelPanzer #PaintTrek #ArarGames




Hiç yorum yok:

Yorum Gönder