I. Colt McAnlis

Colt McAnlis est développeur chez Google, plus précisément, sur les jeux Chrome et le Native Client. Avant, il a été programmeur système et moteur graphique dans l'industrie du jeu vidéo chez Blizzard, Microsoft (Ensemble) et Petroglyph. Il a aussi passé quatre ans en tant que professeur assistant à l'école SMU Guildhall dispensant des cours de mathématiques, physique, moteur graphique et programmation parallèle. Lorsqu'il ne travaille pas avec des partenaires, Colt occupe son temps en se préparant pour une invasion de fourmis géantes venues de l'espace.

II. Introduction

Lorsque les gens entendent parler de « jeux sur le web », ils s'imaginent souvent une représentation d'un jeu 2D avec de mauvais graphismes et un gameplay simpliste. Cependant, avec la puissance de Chrome et du Native Client, il est tout à fait possible d'atteindre la qualité des jeux consoles dans le navigateur.

Image non disponible
Capture d'écran du jeu From Dust d'Ubisoft fonctionnant dans Chrome avec le Native Client. Magnifique rendu pour un titre originellement conçu pour la XBox 360, non ?

Depuis le lancement officiel du Native Client en décembre 2011, les développeurs ont réalisé plus de deux douzaines de jeux avec cette technologie et plusieurs middlewares supportent maintenant le Native Client. Mon rôle en tant que développeur chez Google m'a permis de travailler directement avec ces développeurs de la première heure afin de les assister dans leurs efforts de portage et aussi de récupérer leurs avis permettant l'amélioration de la technologie Native Client ainsi que de la plateforme de jeux Google. Cet article détaille ce que j'ai appris à travers mon travail afin que vous puissiez bénéficier de ma connaissance, dans un format raccourci pour booster vos efforts de portage.

III. Qu'est-ce que le Native Client ?

Simplement, la technologie Native Client permet d'exécuter du code C++ dans une page Web, aussi sûrement que du JavaScript et cela sans plugin. C'est une aubaine pour les développeurs de jeux, qui s'efforcent d'optimiser le développement pour les tendances mobiles actuelles, consoles et PC mais qui veulent aussi diffuser leurs jeux au plus grand nombre d'utilisateurs possible. L'exécution d'un code C++ dans une page Web permet d'atteindre un tas de nouveaux utilisateurs sur internet. Les développeurs de jeux ainsi que les éditeurs embrassent agressivement cette stratégie.

Avec cette motivation en tête, voici mes conseils pour porter votre jeu sur le Web.

IV. Conseil 1 - Apprendre les bases : comprendre comment le Web, Chrome, Pepper et le Native Client fonctionnent

Cet article suppose que vous êtes un développeur de jeux traditionnels, venant du monde du desktop/mobile/console et que le développement Web est nouveau et vous effraie. Pour démystifier fonctionnement d'Internet (vous savez, cet ensemble de tuyaux), parcourez rapidement le site http://www.20thingsilearned.com/en-US. C'est une bonne introduction.

Chrome étant un navigateur Web moderne, il ne fonctionne pas comme la plupart des navigateurs. Aussi, il est important de comprendre son fonctionnement. Cela est utile pour comprendre les aspects « magiques » d'une part et la gestion du Native Client d'autre part.

L'API du plugin Pepper (Plugin Pepper API : PPAPI), appelé Pepper pour des raisons de simplicité est une API pour les navigateurs open source et multiplateformes. Du point de vue du Native Client, Pepper est un ensemble d'API qui permet à un module Native Client en C ou C++ de communiquer avec le navigateur hôte et d'accéder aux fonctions systèmes de manière fiable et portable. Une des contraintes de sécurité dans le Native Client est que les modules ne peuvent pas faire d'appels au niveau du système d'exploitation. Pepper fournit des API analogues que les modules peuvent cibler à la place. Pour recevoir une courte introduction sur Pepper, regardez cette section de ma récente présentation à Google I/O.

Finalement, cela aide à comprendre pourquoi Google a créé le Native Client, que ce soit du point de vue de la performance ou de celui de la sécurité.

V. Conseil 2 : Comprendre la façon dont le portage affectera la structure du jeu

Comme je le décris dans le conseil numéro 3, ci-dessous, je recommande de réaliser le portage en deux étapes : portez votre jeu sur Pepper, puis sur le Native Client. Avant de préciser les mécanismes de ces étapes, il est important de comprendre quelques considérations basiques qui vont affecter la façon dont votre jeu est structuré.

V-A. Portage sur Pepper

Vous devez considérer le portage sur Pepper similaire à n'importe quel autre portage. Vous allez devoir changer votre API spécifique à la plateforme, peut-être modifier les ressources du jeu, configurer la compilation et améliorer les performances pour la plateforme ciblée.

Il y a trois choses essentielles que vous devez connaître à propos des API Pepper :

- les API Pepper doivent être appelées dans le thread principal ;

- les API Pepper sont asynchrones ;

- votre thread principal ne peut pas effectuer une boucle infinie.

En les combinant, ces trois problèmes peuvent devenir des difficultés pour les développeurs.

Par exemple, les développeurs qui s'attendent à ce que fread() soit bloquant et qui ont écrit leur code en suivant ce principe, seront embêtés de trouver que l'API de Pepper pour les entrées sorties des fichiers est non bloquante. Cela peut être un problème difficile à gérer par lui-même mais si vous le combinez avec le fait que tous les appels à Pepper doivent être effectués dans le thread principal (donc, que tous vos appels à des entrées/sorties de fichiers doivent être dans le thread principal), les choses deviennent rudement compliquées.

Une autre considération est que n'importe quel appel au thread principal de votre application Natice Client ne doit pas être bloquant. Cela signifie que vous ne pouvez pas placer votre boucle logique primaire dans ce thread sinon cela bloquera le navigateur. Pour gérer ce cas, certains programmeurs démarrent immédiatement un thread de travail lorsque leur module Native Client démarre et exécute leur boucle logique primaire dans ce thread tout en ajoutant une communication interthread adéquate afin de passer des commandes au thread principal (quelquefois appelé le « thread Pepper »). Cette configuration vous permet quelques nuances du contrôle logique et a comme avantage second de vous permettre de surcharger fread() afin d'être bloquant comme avant. ;)

Par contre, sachez que les appels OpenGL ES 2 doivent toujours provenir du thread principal, donc si vous mettez en place un thread de travail, tôt ou tard vous allez devoir laisser le contrôle des appels OpenGL au thread Pepper pour effectuer le rendu.

Avant de commencer le portage sur Pepper, il est crucial de faire le point sur les API disponibles dans Pepper, pour que vous puissiez récapituler quelles sont les API que vous utilisez et celles que vous pouvez porter. Par exemple, Pepper ne supporte pas les accès aux sockets TCP/IP bruts. À la place, il supporte les WebSockets, qui fournissent les mêmes fonctionnalités à travers une structure d'API différente.

V-B. Portage sur le Native Client

Le Native Client apporte ses propres restrictions, dont la première est la nécessité d'utiliser un GCC spécial pour générer les bibliothèques statiques finales, les bibliothèques dynamiques et les exécutables. Cela signifie que si votre application utilise des bibliothèques tierces (par exemple, un middleware), vous devrez compiler les bibliothèques avec le même compilateur GCC que celui du SDK Native Client.

Toutefois, ne vous inquiétez pas. Vous pouvez chercher dans la liste des projets open source et fermés qui ont déjà été portés sur le Native Client afin de vérifier si votre logiciel y est déjà. Si cela n'est pas le cas, n'hésitez pas à nous contacter ou à contacter le développeur du middleware pour demander le support du Native Client.

Vous devez aussi savoir que le Native Client possède des restrictions sur certaines API qui sont considérées comme dangereuses/non sécurisées (par exemple, certaines fonctions inline en assembleur).

V-B-1. Exécution sur le Web

Un autre point important qui impactera votre application est les modifications que vous devez effectuer pour distribuer les ressources du jeu sur le Web. Demandez-vous : « Où seront toutes les ressources qui seront utilisées ? ».

Les applications Native Client doivent actuellement être distribuées par le Chrome Web Store (CWS) et peuvent être soit hébergées ou packagées. Les applications hébergées sont souvent des marques pages : la page Internet de votre jeu et le module Native Client sont sauvegardés sur un serveur Web que vous gérez et l'utilisateur visite votre page web pour accéder au contenu. Les applications packagées fonctionnent plus comme un service de distribution digitale: les ressources de votre jeu, les pages web et le module Native Client sont packagés, compressés et sauvegardés dans le Chrome Web Store puis installés sur la machine de l'utilisateur lorsqu'il clique sur le bouton d'installation.

Il y a des pour et des contre que ce soit pour les applications hébergées que pour les applications packagées. Par exemple, avec les applications hébergées, vous distribuez un immense volume de contenu de jeux aux utilisateurs qui peuvent ou pas vous payer et donc, il peut être avantageux d'investir dans une grande capacité en bande passante et dans la segmentation des ressources. Si vous pensez distribuer votre jeu comme une application hébergée, vous devriez longuement regarder le moteur d'application Google. Les applications packagées ont une limite de 100 Mo pour les fichiers de ressources. Si votre jeu dépasse cette taille, vous devez streamer les données à partir d'un serveur Web pendant que l'utilisateur joue.

Que vous distribuiez votre jeu comme une application hébergée ou une application packagée, vous devez avoir une bonne compréhension sur la façon dont le jeu chargera les ressources à partir du Web. Par exemple, si vous allez distribuer votre jeu en tant qu'application hébergée, vous pouvez réduire le nombre de données transférées sur le Web en récupérant les ressources du jeu avec l'API URLLoader de Pepper et en mettant en cache localement les ressources grâce à l'API FileIO de Pepper. Cette configuration est cruciale pour les performances lorsque le joueur rejoue au jeu, donc planifiez des niveaux de mise en cache et de streaming.

Il y a de nombreuses autres choses à considérer mais vous devez surtout prendre en compte que votre jeu s'exécute sur le Web, ce qui inclut l'authentification, la localisation et la monétisation. Pour un résumé de ces problèmes, regardez ma présentation à la GDC 2012.

VI. Porter en premier sur Pepper, puis sur Native Client

VI-A. En premier, le Plugin Pepper

Un aperçu technique sur le fonctionnement de Chrome révèle que Chrome vous permet de construire des DLL externes (plugins) auxquelles l'API Pepper peut directement accéder et de dessiner dans une page Web avec facilité. C'est super, car la plus grande partie des efforts résidera dans le portage du jeu afin d'utiliser les API de Pepper. Utilisez votre EDI actuel pour vous aider dans cette tâche (voir une démonstration durant le Google I/O). Bien que la suite d'outils Native Client soit déjà étoffée, elle n'est pas encore suffisamment exhaustive et finalisée pour être une plateforme de développement solide. Utilisez donc la puissance de votre EDI pour faire le travail à votre place.

Comme annoncé durant la présentation au Google I/O 2012, une extension Visual Studio 2010 sera prochainement disponible et aidera au développement sous Windows. Cette extension vous permettra de créer une configuration pour les plateformes de Pepper et du Native Client et ainsi vous pourrez utiliser votre suite de travail habituelle pour porter votre jeu.

Le développement de votre jeu comme plugin Pepper vous apportera quelques fonctionnalités. En plus de faire du débogage et d'utiliser des points d'arrêt, vous pouvez par exemple mélanger les appels à l'API Pepper avec les appels à l'API spécifique de la plateforme dans le même code.

VI-B. En second, le Native Client

Une fois que vous avez porté votre jeu sur Pepper, il ne reste plus qu'à modifier les appels aux fonctions spécifiques à une plateforme pour les remplacer avec celles du Native Client. Cela peut nécessiter plusieurs étapes selon l'architecture de votre code. Par exemple, si le code utilise des threads, vous allez devoir remplacer tous les appels spécifiques à la plateforme par des appels aux threads POSIX (suivant votre plateforme, les entêtes POSIX peuvent ne pas être disponibles en dehors du SDK Native Client, ce qui signifie que vous n'auriez pas pu utiliser les threads POSIX auparavant). Vous allez aussi devoir supprimer toutes les autres traces de code spécifique à la plateforme de votre jeu.

Un autre souci existe si votre jeu inclut une quelconque technique de chargement dynamique de code. Vous devez utiliser la suite d'outils Native Client et compiler et charger les binaires dynamiquement à l'aide d'un processus spécifique.

VII. Le Web est ce que vous en faites

Ne soyez pas désenchanté à cause de la nature économique du Web : c'est un monde de monétisation en pleine explosion, attirant quelques-unes des plus grosses entreprises, mais enterrant aussi au passage de nombreuses voix créatives dans le brouhaha. Le succès sur le Web n'arrive pas en une nuit et n'est pas gratuit, surtout dans le monde du jeu vidéo. Les succès vidéo ludiques du Web (peu importe la technologie) utilisent pleinement les avantages de l'internet comme une plateforme. Nombreux sont ceux qui travaillent durement pour changer l'ancien modèle de distribution de contenus devenu inadapté aux mobiles et consoles, s'efforçant de toucher tout le monde, partout. Ils prospèrent en connectant les gens et en trouvant des modèles de monétisation qui fonctionnent dans un écosystème capricieux et méfiant. Le vrai défi reste dans le portage de votre jeu C++ sur le web, une fois ceci accompli, le portage vers Native Client est la partie facile du travail.

Prêt à mettre votre jeu en ligne ? Commencez par là : gonacl.com

VIII. Remerciements

Merci Colt McAnlis de nous avoir permis de traduire son article. Merci à rawsrc d'avoir relu la traduction et de l'avoir corrigée et finalement, merci à ClaudeLELOUP pour sa relecture orthographique.