Nous attachons le multijoueur au jeu mobile "Make words from words" sur iOS et Android, écrit en C ++

Plus tôt, j'ai écrit sur mon expérience dans le développement d'un jeu de mots mobile sur Android et iOS, qui est très populaire, et j'ai décidé d'y associer le mode multijoueur, lorsque deux joueurs s'affrontent, écrivant des mots à leur tour, comme le dernier tour de la télédiffusion de Sergey Suponev heure. "



Il m'a fallu un mois et demi pour étudier et implémenter le multijoueur, dans l'article, je vais essayer de décrire le concept sans exemples de code source, en faisant une pincée sur la quantité de travail effectuée.

Un peu d'histoire


L'application a été écrite en C ++ à l'aide du SDK Marmalade. Depuis lors, le fournisseur a cessé de prendre en charge cette plate-forme, en vendant les types aux Japonais, et l'avenir de cet environnement de développement est devenu très vague.



La question s'est posée de savoir sur quoi porter les projets en cours pour leur soutien supplémentaire.

Pourquoi pas cocos2d-x




Cocos2d-x est l'un des moteurs de développement de jeux mobiles multiplateformes C ++ les plus courants. Apparemment, en raison de son code source gratuit et ouvert. Le moteur est mal documenté. La description couvre la maigre partie du moteur et la plupart du matériel est depuis longtemps dépassé.

Sur la base des résultats d'une période, j'ai quand même réussi à créer un prototype de mon application. Mais les impressions étaient très mauvaises: on dirait que cocos2d-x est assemblé sur le genou. Les niveaux d'abstraction Scene, Sprite, Application Delegate me semblaient très inconfortables, et la nécessité de chercher des réponses aux questions sur le forum coconut vous amène de plus en plus à penser que vous faites quelque chose de mal. Mes mains poussent probablement au mauvais endroit.

Mon choix s'est porté sur SDL




SDL , comme Marmalade SDL, n'est pas un moteur, c'est une plate-forme. Il fournit une API de bas niveau, à partir de laquelle je crée ensuite des niveaux d'abstraction qui me conviennent. Tout cela est écrit en C, le code source est ouvert.

En résumé, le SDL est une bibliothèque multiplateforme gratuite pour travailler avec les graphiques, le son et le traitement des messages du système d'exploitation. Il est très pratique de créer une version win32 et de déboguer la logique du jeu sous Windows, ne laissant aux émulateurs mobiles et aux appareils physiques que le débogage des fonctionnalités spécifiques au système d'exploitation.

Heureusement ou malheureusement, le SDL ne fournit pas d'outils pour une tâche aussi étroite que le développement d'un multijoueur pour iOS et Android, j'ai donc dû m'intégrer aux services correspondants moi-même.

Architecture d'application multithread


La logique de l'application et de tout le travail avec les graphiques est implémentée dans le thread principal, qui est un cycle de traitement des messages et commence dans la fonction principale. Appelez ce flux SDL de flux. D'autres threads, à leur tour, lancent des événements (SDL_PushEvent) pour le traitement dans la file d'attente, et il les lit à partir de celle-ci à l'aide de SDL_WaitEvent et SDL_PollEvent. Il s'agit soit d'événements système lancés par le système et dont la prise en charge est déjà implémentée en SDL, soit d'appels de rappels et d'écouteurs, que nous implémentons déjà au-delà de la fonctionnalité SDL.



Toute la logique du jeu est écrite en C ++. Le répertoire du projet contient un ensemble de fichiers * .cpp qui peuvent être divisés en trois groupes:

  • multiplateforme - les fichiers qui sont inclus dans l'assemblage de toutes les plates-formes (logique de jeu);
  • monoplateforme, c'est-à-dire sont inclus dans l'application de n'importe quelle plate-forme pour implémenter ses fonctionnalités.

En conséquence, il existe trois répertoires distincts pour la conception de chaque plate-forme:

  • proj.win32 - projet VS2017 Community Edition;
  • proj.android - projet Android utilisant Gradle;
  • proj.ios - projet Xcode pour iOS.

Intégration avec des services multijoueurs


Maintenant, nous devons coller un calque séparé, qui sera responsable de fonctionnalités telles que:

  • recherche d'un adversaire, connexion au jeu;
  • messagerie entre rivaux;
  • sortie de la salle de jeux;
  • fixer des points de joueur dans les classements.

Les plates-formes iOS et Android prennent en charge le multijoueur en temps réel (RTMP). Dans le cas d'Android, nous intégrons avec Google Play Services (GPS), dans le cas d'iOS, Game Center. Auparavant, Google soutenait l'intégration avec iOS, mais cette année a décidé de l'abandonner.

Dans cet article, je ne décrirai pas les actions que vous devez effectuer dans la console Google Play et AppStoreConnect pour configurer le multijoueur, je ne décrirai pas la spécification des classes et des méthodes d'intégration - tout cela est décrit sur les sites des fournisseurs.

Ensuite, je décrirai brièvement quelles modifications doivent être apportées au projet pour chacune des plates-formes.

Android


Comment? Je ne l'ai pas encore dit? Android NDK est utilisé pour compiler du code C ++. Cependant, si vous êtes un développeur Android, vous le savez déjà.

Les instructions générales pour intégrer les services Google Play dans un projet Android sont décrites sur le site pour les développeurs Android. Dans mon projet, j'utilise les dépendances suivantes:

implementation 'com.google.android.gms:play-services-games:16.0.0' implementation 'com.google.android.gms:play-services-nearby:16.0.0' implementation 'com.google.android.gms:play-services-auth:16.0.1' 

Initialement, l'idée était d'utiliser une API C ++ , qui se présente sous la forme de bibliothèques statiques compilées sans sources. Étant donné qu'il n'y a pas d'assemblage pour la plate-forme x86_64 dans la liste des bibliothèques, j'ai décidé que les gars de Google ne surveillent pas vraiment la pertinence de ce SDK et j'ai décidé d' inventer leur vélo pour écrire cette couche en Java, en l'enveloppant avec des wrappers JNI . Et puis, pourquoi ai-je besoin d'une dépendance supplémentaire sous la forme de bibliothèques sans codes source qui à l'intérieur de Java tirent toujours Java? En plus de la pertinence des classes Java, vous devrez également surveiller la pertinence de ces bibliothèques.

Comme guide, j'ai utilisé un bon exemple de Google Samples . Merci à Google pour cela. Apple, prends un exemple de Google!

iOS


Pour intégrer avec Game Center, vous devez connecter le framework GameKit. Nous décrivons l'intégralité de la couche d'intégration avec Game Center dans un fichier * .m et lui fournissons l'interface via un fichier * .h distinct. Étant donné que C ++ est un sous-ensemble du langage objective-C, il n'y aura aucun problème avec l'assemblage des fichiers * .cpp et * .m dans un projet.

En plus de la documentation officielle, il a été guidé par ce projet: GameCenterManager . Certes, certaines choses de l'exemple sont déjà obsolètes, Xcode 10 vous le dira et vous remplacerez la fonctionnalité obsolète par la nouvelle.

Le principe de travailler avec la couche multijoueur


Point d'entrée unique


Après avoir étudié les fonctionnalités de travail en multijoueur sur les deux plates-formes, j'ai créé une seule asbstraction C ++ pour mon application et au moment de la compilation, l'implémentation correspondante la "correspond", en fonction de la plate-forme particulière. Autrement dit, mon application ne connaît aucun des services Google Play, Game Center et leurs fonctionnalités. Il ne connaît que l'API C ++ qui lui est fournie, où par exemple, il existe des méthodes telles que:

 SignIn() //     SignOut() //     LeaveRoom() //    SendMessage(...) //    ShowLeaderboards() //    SubmitScore(...) //    ... 

Rechercher un adversaire


Le joueur peut inviter un ami de la liste de ses contacts, ou commencer la partie avec un adversaire aléatoire. Le joueur qui a reçu l'invitation peut l'accepter ou la refuser. Pour tous ces scénarios, j'utilise l'interface standard du service utilisé. Je voudrais noter que les muselières Google sont beaucoup plus belles iOS iOS. Peut-être qu'un jour mes mains y arriveront et j'écrirai mon interface avec des dominos et des demoiselles.

Connexion à la salle de jeux


Lorsque deux joueurs se connectent à la salle de jeu virtuelle, ils reçoivent les rappels correspondants. Vous devez maintenant choisir qui sera l'hôte.

Sélection d'hôtes


Parmi les joueurs, vous devez choisir un hôte, afin qu'il détermine l'état initial du jeu.
Examinez les moyens possibles d'acheminer les messages entre les joueurs dans le cas général. Veuillez noter que dans le deuxième mode de réalisation, l'hôte se voit également attribuer le rôle de routeur.



Comme je n'ai toujours que deux joueurs dans le jeu, il s'avère que j'ai un cas particulier de connexion peer-to-peer. Et donc, seule la définition de l'état initial relève du rôle d'hôte, à savoir le choix du mot à partir duquel les mots seront composés.

Ainsi, après que les joueurs se soient connectés à la salle de jeux, chacun des joueurs connaît la liste des identifiants des participants au jeu qui a commencé. Nous l'appellerons une liste de participantID . participantID est un certain identifiant de chaîne unique du participant au jeu, qui est attribué par le service. Vous devez choisir lequel d'entre eux sera l'hôte, apporter ceci à l'hôte lui-même et dire à l'autre que son adversaire est sélectionné comme hôte. Comment faire

Sélection d'hôte Android

Je n'ai trouvé aucun conseil sur le choix d'un hôte dans google dock. Ils sont silencieux, partisans. Mais les bonnes personnes sur stackoverflow.com ont jeté un lien vers la vidéo , qui explique en détail le principe suivant:

  • chaque participant trie la liste des ID participants (ascendant ou descendant - peu importe, l'essentiel est que tout le monde le fasse dans le même ordre);
  • chaque participant compare son participantID avec le premier participantID de la liste;
  • s'ils correspondent, le joueur actuel a le droit de choisir qui sera l'hôte. Il jette une pièce, tire au hasard (), choisissant ainsi un hôte parmi les participants existants, et dit à tout le monde qui est l'hôte.

Sélection d'hôtes sur iOS

Pour iOS, il existe une méthode chooseBestHostPlayerWithCompletionHandler , qui simplifie considérablement le scénario de sélection d'hôte par rapport à ce que j'ai décrit pour Android. Mais à en juger par les retards notables lors de l'appel à cette méthode, il estime les paramètres de réponse du réseau, mesure le ping et, à partir de ces statistiques, décide qui devrait être l'hôte. Cela est plus probable pour l'architecture client-serveur ci-dessus, où l'hôte agit comme un routeur. Dans ma version d'une connexion peer-to-peer privée, cela n'a pas de sens, et pour gagner du temps, j'utilise un principe similaire à ce que j'ai fait pour Android.

Messagerie entre joueurs


Qu'est-ce qu'un message? Un message est un tableau d'octets.

  • en java, c'est un type:
     byte[] 
  • dans l'objectif-C, c'est:
     NSData * 
  • en C ++, je mappe tout ce qui précède à
     std::vector<Uint8> 

Il existe 2 types d'envoi de messages:

  • Fiable - livraison garantie via la file d'attente. Utilisé pour délivrer des messages critiques.
  • Fiable - livraison non garantie. Messages utilisés dont le succès de livraison peut être négligé.

Unreliable est généralement livré plus rapidement que Reliable. Vous pouvez en savoir plus sur le site Web des fournisseurs:


Comment allons-nous utiliser ce tableau? Très simple:

  • dans le premier octet, nous écrirons le type de message.
  • si le message aura des paramètres, nous les mettrons dans les octets suivants. Pour chaque type de message qui a ajouté. paramètres, nous implémentons notre fonction de sérialisation et de désérialisation.
  • à la fin du message pour vérifier l'intégrité, nous mettrons une somme de contrôle.

Nous définissons donc l' énumération avec les types de messages que les joueurs échangeront entre eux pendant le jeu:

  • Je suis sélectionné par l'hôte. Je transmets l'état initial. Maintenant c'est mon tour (paramètres: numéro de version du protocole de messagerie, mot source);
  • Vous êtes sélectionné par l'hôte. J'ai hâte de vous entendre;
  • J'ouvre (appelle) le mot. Maintenant c'est votre tour (paramètre: mot nommé);
  • J'abandonne. Vous avez gagné;
  • Je n'ai pas pu dire un mot pendant le déménagement. Vous avez gagné;
  • J'accepte de me venger;
  • J'ai quitté le jeu;
  • Erreur d'analyse du message. Déconnecté;
  • Votre version du protocole de messagerie est obsolète. Vérifiez la mise à jour de l'application. Déconnecté;
  • Ma version du protocole de messagerie est obsolète. Besoin de vérifier la mise à jour. Déconnecté;
  • Ping (message système);

Lorsque l'application reçoit un message entrant d'un adversaire, le rappel correspondant est appelé, qui le transmet à son tour au thread SDL principal pour traitement.

Surveillance de connexion


Les services de jeux (celui de Google, celui d'Apple) ont une fonctionnalité d'écoute qui, sous une forme ou une autre, est conçue pour nous informer d'une déconnexion d'un adversaire. Mais, j'ai remarqué que si l'un des joueurs est déconnecté d'Internet, alors le second ne sait pas immédiatement que le premier s'est déconnecté et qu'il n'y a personne avec qui jouer. Les rappels ne sont pas appelés dans de tels cas, ou sont appelés après un temps assez long. Pour que dans ce cas, le deuxième joueur n'attende pas que le cancer siffle sur la montagne, j'ai dû faire mon propre suivi de la connexion, en travaillant sur le principe:

  • Chacun des joueurs envoie un message ping à l'adversaire chaque seconde;
  • Chaque joueur vérifie: s'il n'y a pas eu de message de l'adversaire pendant plus de 5 secondes, alors la connexion est perdue, nous quittons le jeu.

Résultat


Grâce au travail accompli, j'ai eu un jeu auquel je joue moi-même avec mes amis et ma famille. Je joue sur iOS et Android.

Certes, il y a une nuance sur iOS - pour une raison quelconque, les lunettes ne sont pas fixes dans les classements, dont je suis actuellement en correspondance avec le support Apple.

J'espère que cet article sera utile aux membres de mon équipe et à ceux qui souhaitent développer des applications mobiles. Merci de votre attention.

Source: https://habr.com/ru/post/fr433172/


All Articles