10 Ekim 2026 Cumartesi

Google Play Games Achievements i mobilspel: Butikspåverkan, 2 000 XP-taket och MonoGame-arkitektur




Google Play Games Achievements i mobilspel: Butikspåverkan, 2 000 XP-taket och MonoGame-arkitektur

När du väl har utvecklat och publicerat ett mobilspel i butiken börjar den verkliga utmaningen: att förvandla spelarens första minuter av nyfikenhet till långsiktigt engagemang. När spelare lämnas med en enkel loop av typen "klara nivån, starta nästa" uppstår förr eller senare trötthet – eller så stänger de appen utan att någonsin upptäcka hälften av de djupare spelmekaniker du har byggt.

Vid en första anblick kan systemet för Google Play Games Services (GPGS) Achievements (Prestationer) verka som inget annat än en samling små märken. Men när det utformas genomtänkt blir det en kraftfull motor för spelarbehållning (retention) som guidar spelaren genom varje lager av spelets mekanik.

I den här tekniska artikeln går vi igenom vad prestationer faktiskt åstadkommer i Google Play-ekosystemet, hur vi designade 79 distinkta prestationer under Googles strikta XP-regler för Blocked: Pixel Panzer, och hur vi konstruerade en asynkron MonoGame / C#-batchningspipeline som förhindrar FPS-tapp och Android ANR (Application Not Responding) under intensiva arkadstrider.


1. Varför spelar Google Play Games Achievements roll?

Att integrera Google Play Games-prestationer ger fyra konkreta fördelar gällande både spelarpsykologi och mätvärden för butiksprestanda:

  1. Alternativa mål och spelarbehållning (Day-7 / Day-30 Retention):
    Alla spelare kan inte nå förstaplatsen på den globala topplistan (leaderboard), men varje spelare kan arbeta mot "Förstör 100 försvarstorn" eller "Klara en nivå utan att ta skada." Stegvisa prestationer (incremental achievements) ger en påtaglig framstegsmätare som rör sig framåt under varje spelsession.
  2. Naturlig inlärning av spelmekanik (Implicit Onboarding):
    Spelare håller sig ofta till sitt standardvapen och tvekar att experimentera. I Blocked: Pixel Panzer uppmuntras de att upptäcka och bemästra varje elementärt läge genom att belönas för att smälta block samtidigt med grön syraammunition på ColorWheel eller få in kritiska träffar med svart ammunition.
  3. Omedelbar feedback och Google Play Gamer XP:
    Att se den officiella Google Play Games-bannern glida ner från toppen av skärmen mitt i en intensiv strid – och tjäna XP mot sin globala Google Play Gamer-nivå – ger spelaren en permanent känsla av framsteg kopplad till sin OS-profil.
  4. Google Play-ekosystemet och kvalitetssignaler:
    Google Play Games Services V2 (PGS V2)-integrationen ger automatisk inloggning utan tryck (Zero-Tap sign-in), sömlös kompatibilitet med Google Play Games on PC, och starkare engagemangssignaler under fliken "Spel" i Google Play Butik.

Blocked: Pixel Panzer In-Game Achievement Unlock Toast
Den officiella Google Play Games "Turret Buster I"-aviseringen visas högst upp på skärmen precis när det 10:e tornet förstörs under en strid på nivå 103 i Blocked: Pixel Panzer.


2. Google Play Console-regler: Gränsen på 2 000 XP och 10 000-stegsgränsen

När du konfigurerar prestationer i Google Play Console måste du ta hänsyn till tre strikta plattformsbegränsningar:

  • Det hårda taket på 2 000 XP: Summan av alla prestationspoäng i ett enskilt spel får inte överstiga 2 000 XP. Varje enskild prestation måste ge mellan 5 och 200 XP, i steg om 5.
  • Gränsen på 10 000 steg för stegvisa prestationer: Ackumulerande räknarprestationer stöder maximalt 10 000 steg i Play Console.
  • Krav på Play Games Services V2: Google fasar ut det äldre V1-SDK:t. Moderna Android-spel måste byggas på Xamarin.GooglePlayServices.Games.V2 (PlayGamesSdk och PlayGames.GetAchievementsClient).

Att designa en katalog med 79 prestationer i Blocked: Pixel Panzer

Genom att analysera våra centrala gameplay-entiteter (TurretBlock, FighterJet, EnemyRocket, ColorWheel, HydraRegenerationSkill, Season) strukturerade vi 79 prestationer på totalt 1 995 / 2 000 XP fördelade över 6 huvudkategorier:

Kategori Exempel på prestationer Typ Mål / Mekanik
Enemy Hunters Jet Hunter I–III, Sky Dominator, Turret Buster I–III, Siege Master Incremental Förstör 10, 100, 1 000 och 10 000 jaktplan, försvarstorn och målsökande raketer.
Block Destruction & Explosives Pioneer Breaker, Demolition Crew, Crusher Supreme, Pixel Obliterator, Chain Reaction Incremental / Standard Krossa 1 000 till 10 000 block och detonera 5/25/50 mål i en enda dynamitexplosion.
ColorWheel & Weapon Mastery Prismatic Pilot, Acid Dissolver, Critical Striker, Power Blaster, Speedy Sprinter, Beam Vaporizer Incremental Smält block med grön syraammunition, gör kritiska träffar med svart ammunition, avfyra röda kraftskott, lasrar och målsökande missiler.
Stage & Boss Progression First Blood, Giant Slayer, Decimator, Speedrunner I–III Incremental / Standard Första fiendedräpet, första bosssegern och att klara nivåer på under 4 minuter.
Purist Challenges Untouchable, Clean Victory I–III, Purist Gladiator, Mono Specialist Incremental / Standard Besegra bossar och klara högnivåbanor utan att ta skada eller utan att använda färdigheter.
Atmosphere, Supplies & Meta Storm Fighter, Retro Connoisseur, Gem Tycoon, Quad Spectrum, Audiophile, Loyal Supporter Incremental / Hidden Förstör 100 torn i regnigt väder, samla CRT-skärmar och lyssna på hela låtar i musikspelaren.

För varje prestation skapade vi anpassade 512x512 pixel-art-märkesikoner vars ramar utvecklas från brons till guld och prismatiska nivåer allt eftersom svårighetsgraden ökar:

Blocked: Pixel Panzer Custom Achievement Badges
Sex exempel på 512x512-märken från vår katalog med 79 prestationer: Sky Dominator, Turret Buster III, Pixel Obliterator, Prismatic Pilot IV, Acid Dissolver II och Power Blaster II.

Proffstips (Massimport via ZIP): Att manuellt mata in 79 prestationer i Google Play Consoles webbgränssnitt tar timmar. Använd istället Play Consoles funktion "Import achievements" genom att paketera AchievementsMetadata.csv, AchievementsIconsMappings.csv och dina 512x512 .jpg-ikoner i ett enda .zip-arkiv för att importera hela katalogen på några sekunder.

Google Play Console Published Achievements Table
Vår tabell över publicerade prestationer och XP-fördelning i Google Play Console.


3. Ren arkitektur: IGamesService och AchievementDirector i delad kod

Blocked: Pixel Panzer är ett plattformsoberoende MonoGame-projekt som körs på både Windows PC (Microsoft Store) och Android (Google Play). För att hålla vårt delade spellogikprojekt (Blocked.Shared) helt fritt från Android SDK-beroenden tillämpar vi Dependency Inversion Principle (SOLID).

Först definierar vi ett plattformsoberoende IGamesService-kontrakt i det delade lagret:

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();
    }
}

Istället för att sprida ut prestationskontroller över CollisionManager, GameplayScreen och Player, centraliserar vi alla 79 regler i en enda koordinatorklass, AchievementDirector:

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; // Körs säkert som no-op på Desktop PC-byggen

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

            // Torn som förstörs under regnigt väder avancerar "Storm Fighter"
            if (Season.Current == SeasonKind.Rainy)
            {
                gamesService.IncrementAchievement(AchievementIds.StormFighter, 1);
            }
        }

        TrackWeaponKill(gamesService, killingBullet, killedByLaser);
    }
}

4. Kritiskt ingenjörsarbete: Eliminera JNI / Binder IPC-overhead för att förhindra ANR

I ett snabbt arkadspel innebär en direkt koppling mellan prestationsutlösare och Google Play SDK en allvarlig prestandafälla:

När en spelare detonerar ett Explosivt block (ExplosiveBlock) eller avfyrar färdigheten Laser Beam, kan 20 till 30 block och fiender förstöras inom en enda bildruta (16,6 millisekunder). När varje förstörelse uppdaterar 5 eller 6 stegvisa nivåer kan en enda bildruta utlösa mer än 150 anrop till IncrementAchievement!

Om du skickar 150 synkrona JNI- och Binder IPC-anrop till Google Play Services på spelets huvudtråd:

  1. Drabbas instegsenheter med Android (Android Go-mobiler med 1–2 GB RAM) omedelbart av kraftiga bildrutehack (frame drops).
  2. Sväller tabellen för JNI Global Reference (GREF), vilket utlöser ANR-rapporter (Input dispatching timed out) i Google Play Vitals.

Vår lösning: 1,5 sekunders minnesbatchning och sessionsdeduplicering

Vår implementering i GooglePlayGamesService.cs isolerar MonoGame-uppdateringsloopen fullständigt från Google Play IPC-anrop:

  1. Sessionsdeduplicering (_unlockedInSession):
    När en standardprestation har låsts upp under en session läggs dess ID till i ett HashSet<string>. Upprepade triggers i samma eller efterföljande bildrutor returnerar omedelbart i C# utan någon JNI-kostnad alls.
  2. 1,5 sekunders bakgrundsbatchning (_pendingIncrements):
    Att anropa IncrementAchievement ökar bara ett heltal i ett minnesbaserat Dictionary<string, int> på huvudtråden (noll JNI-anrop). En bakgrundsarbetare med Task.Run vaknar efter 1,5 sekunder, tar en ögonblicksbild av de ackumulerade summorna och skickar dem till Google Play Services i ett enda kombinerat anrop per prestation.
  3. Hantering av 26502: CLIENT_RECONNECT_REQUIRED och offlinelagring:
    Om en spelare lägger spelet i bakgrunden i flera timmar och återvänder, kan Google Play-sessionstoken löpa ut och kasta felet 26502 (CLIENT_RECONNECT_REQUIRED). Vår tjänst fångar detta, försöker en tyst SignIn() i bakgrunden med ett cooldown-skydd, och faller tillbaka på GPGS lokala offline-cache (client.Unlock) om enheten är offline – vilket säkerställer att inga framsteg någonsin går förlorade.
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)
    {
        // ZERO JNI cost on the game loop thread — pure in-memory aggregation!
        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)
        {
            // 150 frame events become 1 combined step call to GPGS
            client.Increment(kvp.Key, kvp.Value);
        }
    }
}

5. Hur följer spelare sina prestationer i spelet?

Aviseringar under strid är fantastiska, men spelare behöver också en tydlig plats för att bläddra bland låsta märken, kontrollera procentuell utveckling på stegvisa nivåer och välja sitt nästa mål.

För att stödja detta på ett stilrent sätt förvandlade vi vår statistikskärm på mobilen till en enhetlig hubb för STATS & RANKS. Längst upp på skärmen pulserar två gyllene knappar – LEADERBOARD och ACHIEVEMENTS – med ett mjukt 2-sekunders sinusvågsglöd (Math.Sin). På Windows Desktop-byggen döljs dessa mobilspecifika knappar automatiskt, vilket bevarar den klassiska layouten för STATISTICS.

När spelaren trycker på ACHIEVEMENTS:

  1. Körs FlushPendingIncrementsNow() omedelbart så att eventuella framsteg från de senaste 1,5 sekunderna skickas in innan gränssnittet öppnas.
  2. GetAchievementsIntent() öppnar Google Play Games inbyggda Achievements-överlagring direkt ovanpå spelet:

Native Google Play Games Achievements Overlay in Blocked: Pixel Panzer
Den inbyggda Google Play Games Achievements-överlagringen öppnad från STATS & RANKS-skärmen, som visar totalt slutförande (2 / 79), rekommenderade mål, upplåsta märken (Loyal Supporter, Audiophile) och låsta procentringar.

Spelare kan inspektera sina framsteg på 2 / 79, se anpassade rekommendationer och hålla koll på procentringarna utan att någonsin behöva lämna spelet.


6. Sammanfattning och våra spel

När det byggs med en balanserad 2 000 XP-budget och en icke-blockerande bakgrundsbatchningspipeline, är Google Play Games Achievements ett av de mest effektiva sätten att lägga till djup och långsiktigt omspelningsvärde till ett arkadspel utan att kompromissa med bildfrekvensen.

Om du vill uppleva vårt progressionssystem med 79 prestationer, stridsmekaniken med ColorWheel och globala topplistor med egna ögon – eller kolla in våra andra titlar – hittar du dem i butikerna nedan:

🎮 Våra spel:

📢 Följ våra devlogs och community:

Följ oss för fler tekniska artiklar, klipp bakom kulisserna och speluppdateringar:


Taggar och ämnen

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




Hiç yorum yok:

Yorum Gönder