Connecter deux plates-formes mobiles en un seul code sur Unity





Les développeurs Unity sont déjà familiarisés avec la gestion des flux de jeux et des services sur des plates-formes telles qu'iOS et Android. Cependant, après l'apparition des services mobiles Huawei dans l'écosystème, vous devez désormais prendre en charge une autre version du jeu si vous souhaitez toucher les joueurs disposant d'appareils Huawei.



Cet article explique comment gérer les dépendances entre plusieurs services mobiles Android et Huawei dans une seule base de code avec un minimum d'effort.







image

Figure 1. Différence entre la prise en charge de différentes plates-formes et des services mobiles



Comme vous pouvez le voir dans la figure ci-dessus, les téléphones mobiles Huawei utilisent le système d'exploitation Android, votre projet Unity doit donc l'utiliser lors de la création d'appareils de cette société. Cependant, vous devez maintenant développer 2 APK différents pour Android ou un package séparé pour Huawei AppGallery.





Quelle est la différence entre ces APK?



La première et la plus importante différence réside dans les services mobiles. Il s'agit de services tels que les achats intégrés, la publicité, les services de jeux, les analyses, etc. Vous ne pouvez pas utiliser le GMS sur les appareils mobiles Huawei. Pour cette raison, il devient nécessaire de créer deux APK: pour la publication dans Huawei AppGallery et dans Google Play.



La deuxième différence tout aussi importante est le nom du package. Étant donné que les deux écosystèmes fonctionnent sur Android, afin d'éviter les incohérences et les remplacements, Huawei App Gallery a une règle selon laquelle le nom de votre package doit se terminer par .huawei ou .HUAWEI. Cette approche est utilisée pour séparer les versions Huawei de tous les autres appareils Android.



Mais ne vous inquiétez pas: nous pouvons gérer ces différences dans une seule base de code.



Voici deux petites astuces pour vous aider à résoudre ces problèmes.



1. Avez-vous déjà entendu parler de #defines?



Grâce aux définitions, nous pouvons contrôler nos threads au moment de la construction, et grâce à un environnement de développement unique - et pendant le codage.



De quels flux parlons-nous?



Imaginez que vous deviez exploiter deux types de services de jeu: Google Play et Huawei. Pour créer une application pour eux dans un code, vous pouvez la séparer à l'aide de définitions. Regardons un petit exemple:



internal static class GameServiceFactory
{
    public static IGameServiceProvider CreateGameServiceProvider()
    {
        #if HMS_BUILD
            return new HMSGameServiceProvider();
        #else
            return new GooglePlayGameServiceProvider();                        
        #endif
    }
}

      
      





Si vous ajoutez le mot-clé "HMS_BUILD" à votre liste de définitions, le jeu appellera HMSGameServiceProvider. De cette façon, nous pouvons gérer nos threads en un seul code.



Vous pouvez utiliser le script ci-dessous pour gérer les définitions avant la création. Après avoir modifié et enregistré les DefineKeywords, l'EDI met à jour le flux de code en fonction des mots-clés que vous avez spécifiés.



public class ManageDefines : Editor
 {
     /// <summary>
     /// Symbols that will be added to the editor
     /// </summary>
     public static readonly string [] DefineKeywords = new string[] {
        //"TEST_VERSION",
        "HMS_BUILD",
        //"GMS_BUILD",
     };
  
     /// <summary>
     /// Add define symbols as soon as Unity gets done compiling.
     /// </summary>
     static AddDefineSymbols ()
     {
         List<string> allDefines = new List<string>();
         allDefines.AddRange ( DefineKeywords.Except ( allDefines ) );
         PlayerSettings.SetScriptingDefineSymbolsForGroup (
             EditorUserBuildSettings.selectedBuildTargetGroup,
             string.Join ( ";", allDefines.ToArray () ) );
     }
 }

      
      





2. Scripts avant et après la construction (pré-build et post-build)



Ainsi, comme mentionné précédemment, nous devons changer le nom du package de notre jeu pour la version Huawei AppGallery.



Cependant, si vous utilisez les services Google Play en même temps, toutes les configurations seront liées au nom de votre package existant et sa modification affectera la configuration. De plus, l'éditeur Unity dans une fenêtre contextuelle vous avertira de temps en temps de corriger le nom du package d'application, car désormais le nom du package et la configuration du service mobile sont différents. Fermer ce popup encore et encore est très fastidieux.



Pour résoudre ce problème et gérer deux noms de package différents en même temps, une solution de contournement peut être utilisée avec des scripts pré-build et post-build avec des définitions.



Jetez un œil à ce code:



class MyCustomBuildProcessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
    public int callbackOrder { get { return 0; } }

    public void OnPostprocessBuild(BuildReport report)
    {
        PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, "your.package.name");
    }

    public void OnPreprocessBuild(BuildReport report)
    {
        #if HMS_BUILD
            PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, "your.package.name.huawei");
        #elif GMS_BUILD
            PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, "your.package.name");
        #endif
    }
}

      
      





Comme vous pouvez le voir, tout est simple. À l'aide de définitions, nous pouvons modifier le nom du package lors de la pré-construction pour HMS_BUILD uniquement. Ensuite, après la construction, vous pouvez renvoyer le nom du package à celui d'origine, puis Unity ne nous avertira plus de l'incohérence du nom du package.



C'est tout. Nous sommes maintenant prêts à développer un jeu en un seul code pour Huawei et Google Play en même temps.





Exemple de développement d'application



Créons une application dans la même base de code pour Android et Huawei, qui comprend des services de jeux, des achats en jeu et de la publicité.



Nous n'implémenterons pas toutes les fonctionnalités pour chaque service car il ne s'agit que d'une démonstration. Vous pouvez étendre chaque fonctionnalité et fonctionnalité vous-même.





Structure du module de service





image

Figure 2. Schéma de fonctionnement des modules de service



Pour chaque service, nous aurons le nôtre;



  • Manager : cette classe inclut la logique de jeu pour les services et gère les fonctionnalités générales. Différents jeux peuvent devoir changer cette classe pour répondre à vos besoins.
  • Classe d'usine (Factory) : cette classe inclut la logique de choix d'un fournisseur. Dans notre approche, nous utiliserons des définitions, mais vous pouvez modifier le mécanisme de sélection des fournisseurs à votre guise.
  • Fournisseur : pour chaque service, nous devons créer son propre fournisseur. Toutes les dépendances entre votre projet et les services mobiles doivent rester dans cette classe.
  • Interface fournisseur : pour unifier l'utilisation de divers services mobiles, nous avons besoin de fournisseurs communs, et à cette fin, des interfaces fournisseur sont créées. Selon les exigences de votre jeu, vous devez définir toutes les méthodes que vous utiliserez dans votre jeu à partir des services mobiles.


Si nécessaire, vous pouvez également activer:



  • Entités génériques : vous pouvez avoir besoin d'entités génériques pour extraire certains services de votre jeu. Pour conserver la dépendance uniquement sur les classes de fournisseur, vous pouvez utiliser des entités génériques selon vos besoins.
  • Écouteurs génériques : similaires aux entités génériques, si vous voulez des écouteurs génériques, vous pouvez également les créer.


image

Figure 3. Un exemple de structure pour un module de service de jeu



Regardons maintenant des exemples et essayons de comprendre ce que nous pouvons faire en utilisant la méthode décrite dans l'article.



Pour extraire les services mobiles de la logique du jeu, nous utiliserons des gestionnaires. Les managers communiquent avec les fournisseurs via des tissus. De cette façon, nous pouvons utiliser des fournisseurs comme des plugins. Ils doivent implémenter une interface commune contenant les méthodes auxquelles nous voulons accéder.



public interface IGameServiceProvider
{
    void Init();
    bool IsAuthenticated();
    void SignOut();
    void AuthenticateUser(Action<bool> callback = null);
    void SendScore(int score, string boardId);
    void ShowLeaderBoard(string boardId = "");
    void ShowAchievements();
    void UnlockAchievement(string key);
    CommonAuthUser GetUserInfo();
}

      
      





Ensuite, nous avons besoin d'au moins un fournisseur qui implémente l'interface de service. Comme mentionné précédemment, toutes les dépendances entre le jeu et les services mobiles tiers doivent rester dans cette classe. Créons deux fournisseurs: pour Huawei et Google Play.



Voici quelques exemples de code. Vous pouvez les retrouver dans le projet sur GitHub à la fin de l'article.





Fournisseur de services de jeux Huawei



public class HMSGameServiceProvider : IGameServiceProvider
 {
      private static string TAG = "HMSGameServiceProvider";

      private HuaweiIdAuthService _authService;
      private IRankingsClient _rankingClient;
      private IAchievementsClient _achievementClient;

      public AuthHuaweiId HuaweiId;

      public CommonAuthUser commonAuthUser = null;

      public void Init()
      {
          InitHuaweiAuthService();
      }

      ....
        
}
      
      







Fournisseur de services de jeux Google



public class GooglePlayGameServiceProvider : IGameServiceProvider
{
    private static string TAG = "GooglePlayServiceProvider";

    private PlayGamesPlatform _platform;

    public CommonAuthUser commonAuthUser = null;

    public void Init()
    {
        InitPlayGamesPlatform();
    }

    ....
}

      
      





Nous devons maintenant créer un gestionnaire et une classe d'usine. Ensuite, nous obtenons le fournisseur selon notre définition.



public class GameServiceManager : Singleton<GameServiceManager>
{
        public IGameServiceProvider provider;

        protected override void Awake()
        {
            base.Awake();
            provider = GameServiceFactory.CreateGameServiceProvider();
        }
        
        .....
        
}

      
      





Voici à quoi ressemble notre classe d'usine:



internal static class GameServiceFactory
{
    public static IGameServiceProvider CreateGameServiceProvider()
    {
        #if HMS_BUILD
            return new HMSGameServiceProvider();
        #else
            return new GooglePlayGameServiceProvider();                        
        #endif
    }
}

      
      





Voilà: nous avons maintenant une classe GameManager qui peut gérer les fonctionnalités d'un service de jeu multi-fournisseurs.



Pour initialiser GameServices, utilisez les lignes de code suivantes, le cas échéant:



public class MainSceneManager : MonoBehaviour
{
    void Start()
    {
        GameServiceManager.Instance.Init();
        
        ....
        
    }
    
    ....
    
    private void OnClickedScoreBoardButton()
    {
        GameServiceManager.Instance.provider.ShowLeaderBoard();
    }

    private void OnClickedAchievementButton()
    {
        GameServiceManager.Instance.provider.ShowAchievements();
    }
    
    ....
}


      
      





Nous n'entrerons pas dans le travail de chaque module de service, car ils ont tous la même logique et la même structure. Vous pouvez les voir en action en copiant le code du projet vers GitHub.



De plus, si vous avez besoin de conseils sur la configuration du plugin Huawei dans Unity, cela est décrit dans cet article .



Passons aux conclusions.



En modifiant les définitions entre HMS_BUILD et GMS_BUILD dans DefineConfig.cs:



  1. nous pouvons obtenir deux APK ou packages différents pour Huawei AppGallery et Google Play;
  2. entre HMS et GMS, les fonctions de connexion et de déconnexion, les classements, les réalisations, les achats en jeu changent.


Vous trouverez ci-dessous de courtes vidéos des deux assemblages.



Dans le cas de "HMS_BUILD":



image



Dans le cas de "GMS_BUILD": le



image



projet de démonstration et les fichiers APK préparés pour HMS et GMS sont disponibles sur le lien sur GitHub .



All Articles