Portage du Platformer Starter Kit XNA 3.1 vers XNA 4.0 pour Windows Phone 7

Vous ne le savez peut-être pas encore mais vous pouvez facilement développer des jeux sur Windows Phone 7 à l’aide du framework XNA. J’en ai ainsi profiter pour récupérer le code source du petit jeu de plateforme nommé “Platformer Starter Kit” livré avec XNA Studio 3.1. Je me suis ensuite occupé de l’adapter à XNA 4.0 et aux spécificités d’un téléphone Windows Phone 7. Vous trouverez donc ci-dessous quelques brèves explications sur les modifications à apporter ainsi que le code source à télécharger à la fin de cet article pour en arriver là :

portagexna4

Mais avant toute chose, qu’est ce qu’XNA en quelques mots ?

XNA est un framework prévu principalement pour concevoir facilement des jeux vidéos. C’est une surcouche managée au-dessus de DirectX. C’est un également un framework multi-plateformes. XNA Studio 3.1 visait ainsi le PC, la Xbox 360 et le Zune (HD). XNA Studio 4.0 vise le PC, la Xbox 360 et le téléphone mobile avec Windows Phone 7. L’avantage est que l’on va pouvoir utiliser souvent plus de 90% de code commun aux 3 plateformes. Les 10% restant seront liés aux spécificités de chaque plateforme : résolution, type de périphériques, présence de capteurs, optimisation du gameplay, etc. Avec XNA, vous pouvez facilement faire tant des jeux 2D que 3D. Au final, vous allez donc faire un unique effort de développement vous permettant de viser un parc gigantesque de périphériques susceptibles de faire tourner votre jeu et surtout… d’avoir un nombre de gigantesque de clients susceptibles de l’acheter ! ;-)

Lorsque j’ai entendu parler d’XNA pour la 1ère fois, c’était à une conférence interne Microsoft aux Etats-Unis il y a quelques années. Les équipes de développement étaient parties du constat qu’il y avait peu de développeurs disposant des compétences nécessaires au développement d’un jeu sous DirectX. Selon moi, 2 raisons évidentes en étant à l’origine : DirectX est un modèle COM accessible uniquement en C++ et il y a beaucoup de concepts à maîtriser avant d’afficher le moindre graphisme à l’écran. L’idée était donc de simplifier l’approche à travers un framework de plus haut niveau pour que les jeunes diplômés, passionnés et autres se fassent les dents sur ce framework sans s’embêter sur de la plomberie. Ensuite, ils pourront éventuellement être formés par un éditeur de jeux vidéo à DirectX pour des jeux plus complexes ou gourmand en ressources. Le framework XNA apporte ainsi les bases solides d’un jeu vidéo.

Note : XNA Studio 3.1 comme XNA Studio 4.0 sont gratuits et fonctionnent avec des versions gratuites de Visual Studio Express. Toutes les ressources à télécharger se trouvent ici.

Voici quelques ressources sympathiques en français pour découvrir XNA :
- [Microsoft TechDays 2008] - XNA, Introduction au développement de jeux PC et XBox360 par David Cathue
- [MSTD10] - Le développement de jeux vidéo en XNA par Valentin Billotte et Antoine Emond
- Presentation sur Microsoft XNA Game Studio Express et XNA Framework : un dossier MSDN très complet !
- Introduction au framework XNA par Helmut
- [Tutoriel XNA] – 2D – Introduction au XNA 3.1 par Xavier Quincieux du Labo .NET
- Introduction à XNA Express par Alain Zanchetta
- le site Xna-France.com animé par Pierre-Yves Gardette

Je vous conseille également cette session des Techdays 2010 où vous pourrez voir jusqu’où aller grâce à XNA à travers un retour d’expérience et une présentation d’un produit nommé SimplyEngine :

- [MSTD10] - 3D XNA SOA : SimplyEngine par Bertrand Copigneaux

Allez, regardons maintenant comment porter le jeu sur notre futur téléphone préféré.

Etape 1 : récupérer le code du jeu depuis XNA Studio 3.1 – Visual C# 2008

La 1ère étape consiste simplement à récupérer le code du jeu en créant le projet du “starter kit” depuis Visual C# 2008 Express ou Visual Studio 2008 comme ici :

wp7platformer001

Une fois l’opération effectuée, nous aurons donc dans le répertoire voulu tout le code source du jeu, les ressources (sprites, niveaux, musique, sons, etc.) à disposition.

Je vous propose de partir du projet Zune qui affiche les niveau sur la hauteur plutôt que des projets Xbox 360/PC avec une vision horizontale 16/9.

Bon, cette 1ère étape était drôlement simple ! :)

Etape 2 : adapter le code et la solution à XNA Studio 4.0 et Windows Phone

Il n’y a pas d’assistant (wizard) de migration entre XNA Studio 3.1/VS 2008 et XNA Studio 4.0/VS 2010. Par contre, cela n’est pas forcément très compliqué de migrer le code. Cela consiste d’abord par une méchante opération de copier/coller.

Créons d’abord le squelette de la solution d’un jeu pour Windows Phone 7 :

wp7platformer002

Ensuite, il faut prendre les différents fichiers de source (Player.cs, Level.cs, Tile.cs, etc.) et les importer dans le projet principal. Enfin, il faut récupérer les ressources du jeu et les mettre dans le projet associé Content. En effet, 1ère différence entre XNA 3.1 et XNA 4.0, les ressources (gérées par le Content Pipeline) sont désormais mutualisées entre les différentes versions (Xbox 360, PC et téléphone) là où il y avait une copie des ressources entre chaque projet avant.

Une fois tout importé, vous constaterez malgré tout que cela ne compile pas encore. En effet, certaines parties ont changé entre XNA 3.1 et XNA 4.0. L’unique partie du code posant problème sur cet exemple de code est lié à l’accès aux fichiers. Il y a ainsi 2 zones de code à modifier.

La première dans le fichier “PlatformerGame.cs” dans la méthode LoadNextLevel() : il faut changer ce code :

 // Try to find the next level. They are sequentially numbered txt files.
levelPath = String.Format("Levels/{0}.txt", ++levelIndex);
levelPath = Path.Combine(StorageContainer.TitleLocation, "Content/" + levelPath);
if (File.Exists(levelPath))
    break;

par celui-ci :

 // Try to find the next level. They are sequentially numbered txt files.
levelPath = String.Format("Levels/{0}.txt", ++levelIndex);
levelPath = "Content/" + levelPath;

try
{
    StreamReader sr = new StreamReader(TitleContainer.OpenStream(levelPath));
    fileFound = true;
}
catch
{
    fileFound = false;
}

if (fileFound)
    break;

Après avoir ajouté l’utilisation de ce namespace :

 using System.IO;

La deuxième dans le fichier “Level.cs” dans la méthode LoadTiles() , il faut changer ce code :

 using (StreamReader reader = new StreamReader(path))

par celui-ci :

 using (StreamReader reader = new StreamReader(TitleContainer.OpenStream(path)))

La 1ère méthode s’occupe de vérifier l’existence du prochain niveau et de le faire charger ensuite par la 2ème méthode. Les niveaux sont décrits dans le répertoire “Level” sous la forme de fichiers texte (0.txt, 1.txt, etc.) et ressemblent à cela :

........

........

.X......

###.....

........

......G.

.....###

........

.G......

###.....

........

......G.

.....###

........

1.......

########

Le ‘1’ indique l’emplacement du joueur au début du niveau, les ‘#’ indiquent des blocs de pierre sur lesquels on peut sauter, les ‘G’ des diamants que l’on doit récupérer pour marquer des points et le ‘X’ la sortie du niveau. Toutes les associations sont décrites dans la méthode LoadTile() . On voit ici une description de niveau pour un Zune découpant l’écran en 8 colonnes et 16 lignes.

Pour terminer, pour que cela compile correctement, il faut changer les propriétés de chacun des fichiers de niveaux pour passer la propriété “Build Action” de “Compile” à “None” et la propriété “Copy to Output Directory” de “Do not copy” à “Copy if newer”. Il vous faudra également modifier les propriétés de certains sons dans le projet Content pour les passer de “Song – XNA Framework” à “Sound Effect – XNA Framework” (sauf la musique du jeu bien évidement). Ces petites modifications à faire sont dues aux méchantes opérations de copier/coller.

Si vous lancez le jeu à cette étape, cela donne pour l’instant… n’importe quoi :)

wp7platformer003

Il va donc falloir que l’on modifie un peu le code et les éléments du décor pour qu’ils soient adaptés à notre résolution.

Etape 3 : adapter le jeu à la résolution du Windows Phone

Si vous regardez le code initial provenant de XNA 3.1, vous noterez qu’il y a des directives de compilation pour les différentes plateformes comme :

 #if ZUNE
        private const int TargetFrameRate = 30;        
        private const int BackBufferWidth = 240;
        private const int BackBufferHeight = 320;
        private const Buttons ContinueButton = Buttons.B;        
#else
        private const int TargetFrameRate = 60;
        private const int BackBufferWidth = 1280;
        private const int BackBufferHeight = 720;
        private const Buttons ContinueButton = Buttons.A;
#endif

Il nous faut donc gérer le cas du téléphone. Essayons d’abord de comprendre comment sont dessinés les niveaux.

Sur Xbox 360/PC, nous avons :

- une résolution demandée (largeur X hauteur) de 1280x720 (ou 720p)

- les niveaux sont décrits dans les fichiers textes avec 15 lignes et 20 colonnes

- cela nous fait donc des blocs de 720/15 = 48 pixels de haut et 1280/20 = 64 pixels de large. Cela s’appelle des “Tile” dans notre jeu. Ils feront 64x48 pixels sur PC.

Sur Zune, nous avons :

- une résolution demandée de
240x320
- les niveaux sont décrits dans les fichiers textes avec 16 lignes et 8 colonnes

- cela nous fait donc des blocs de 320/16 = 20 pixels de haut et 240/8 = 30 pixels de large. Nos tiles feront donc 30x20 pixels sur Zune.

D’ailleurs, si vous vous rendez dans le répertoire “Tiles” des parties “HighResolutionContent” (pour PC/Xbox) et “LowResolutionContent” (pour Zune), vous noterez que c’est exactement la taille des images représentant ces tiles. Dans mon cas, j’ai choisi de retenir les valeurs suivantes :

Sur Windows Phone, nous aurons donc :

- une résolution demandée de
480x800
- je souhaite conserver les niveaux du Zune décrits sur 16 lignes et 8 colonnes

- cela nous fait donc des blocs de 800/16 = 50 pixels de haut et 480/8 = 60 pixels de large. Nos tiles feront donc 60x50 pixels.

Reste plus qu’à modifier les graphismes et le code en conséquence. Pour les graphismes, je suis parti du contenu haute résolution du répertoire “HighResolutionContent” et que j’ai redimensionné à l’aide de Paint.NET.

Pour le code, il faut donc remplacer le code précédent de “PlatformerGamer.cs” par celui-ci :

 #if WINDOWS_PHONE
        private const int TargetFrameRate = 30;        
        private const int BackBufferWidth = 480;
        private const int BackBufferHeight = 800;
        private const Buttons ContinueButton = Buttons.B;        
#else
        private const int TargetFrameRate = 60;
        private const int BackBufferWidth = 1280;
        private const int BackBufferHeight = 720;
        private const Buttons ContinueButton = Buttons.A;
#endif

Puis dans “Tile.cs”, remplacer ce bloc :

 #if ZUNE
        public const int Width = 30;
        public const int Height = 20;
#else
        public const int Width = 64;
        public const int Height = 48;

par :

 #if WINDOWS_PHONE
        public const int Width = 60;
        public const int Height = 50;
#else
        public const int Width = 64;
        public const int Height = 48;

Avec les graphismes haute résolution et nos tiles réajustés avec Paint.NET, on a désormais un jeu qui commence à ressembler à quelque chose :

wp7platformer004

On peut même commencer à y jouer au clavier dans l’émulateur. Cependant, vous noterez 2 problèmes :

1 – la vitesse du jeu ne semble pas du tout adaptée au téléphone

2 – sur le téléphone final (le matériel, le vrai quoi !), nous n’aurons pas de clavier.

Il va donc falloir revoir le gameplay pour s’adapter en conséquence.

Etape 4 : adapter le gameplay aux spécificités du Windows Phone

Commençons par le plus simple, adapter la vitesse des personnages. Il y a 2 parties dans le code gérant les déplacements. 1 pour le héro dans “Player.cs” et 1 pour les méchants dans “Enemy.cs”. Dans “Enemy.cs”, remplacez ce code :

 #if ZUNE
        private const float MoveSpeed = 64.0f;
#else
        private const float MoveSpeed = 128.0f;
#endif

Par celui :

 #if WINDOWS_PHONE
        private const float MoveSpeed = 64.0f;
#else
        private const float MoveSpeed = 128.0f;
#endif

Bon, c’était simple. On a ralenti par 2 la vitesse des méchants pour s’adapter à la largeur plus faible de l’écran par rapport à une console de salon ou un PC. Le héro lui a un ensemble de caractéristiques plus détaillées. On y retrouve ainsi la force de l’impulsion à donner lorsqu’il saute, l’accélération de ses déplacements et sa vitesse maximum, à quelle vitesse il tombe, il peut rester en l’air, etc. C'est ce que l’on appelle un moteur physique (même si ici, il est extrêmement basique). Identifiez la dernière zone conditionnelle dans “Player.cs” et remplacez-là par celle-ci :

 #if WINDOWS_PHONE
        // Constants for controling horizontal movement
        private const float MoveAcceleration = 7000.0f;
        private const float MaxMoveSpeed = 1000.0f;
        private const float GroundDragFactor = 0.58f;
        private const float AirDragFactor = 0.65f;

        // Constants for controlling vertical movement
        private const float MaxJumpTime = 0.35f;
        private const float JumpLaunchVelocity = -4000.0f;
        private const float GravityAcceleration = 3500.0f;
        private const float MaxFallSpeed = 600.0f;
        private const float JumpControlPower = 0.16f;

        // Input configuration
        private const float MoveStickScale = 1.0f;
        private const Buttons JumpButton = Buttons.A;  

Ce sont les choix que j’ai fait en testant un peu dans l’émulateur mais libre à vous de changer ces valeurs !

La dernière étape consiste enfin à pouvoir jouer sans clavier ni gamepad. Pour cela, on peut imaginer utiliser l’accéléromètre du téléphone pour faire bouger le personnage de gauche à droite lorsque l’on inclinera notre téléphone et taper sur l’écran avec un doigt pour le faire sauter.

Pour cela, vous aurez besoin de suivre les instructions suivantes prévues à l’origine pour le Zune HD :

- Zune HD XNA Platformer Game : https://www.allaboutcoding.com/tutorials/cs/zunehd.asp

Cependant, la gestion de l’accéléromètre est légèrement différent entre le Zune HD et le Windows Phone. Typiquement, vous n’avez pas (encore ?) la classe AccelerometerState. Pour palier à ce problème, utilisez le code fourni ici : Windows Phone Accelerometer Support in XNA qui propose un wrapper autour de la classe AccelerometerSensor pour pouvoir obtenir AccelerometerState. Une fois ce wrapper inséré dans votre projet, vous pourrez utiliser tel quel le code prévu pour le Zune HD.

Code source à télécharger

Voici le code source à télécharger contenant le fruit de toutes ces étapes :

Je n’ai malheureusement pas pu tester la partie accéléromètre et touch de ce projet vu que je n’ai pas encore de téléphone Windows Phone 7 Series… :-( Mais si vous faites partie de la bande limitée de veinards en ayant déjà un, n’hésitez pas à me dire comment se comporte le code !

Conclusion

Depuis que j’ai commencé à coder, j’ai toujours secrètement rêvé (comme beaucoup je pense !) de participer au développement d’un jeu vidéo. Pour moi, cela a toujours constitué l’un des types de développement le plus complexe et le plus stimulant qui soit. Cependant, jusqu’à présent, j’avais toujours considéré ne pas être à la hauteur de la tâche face à la montage de problèmes qui se présentaient à moi. XNA me donne l’impression que je peux franchir la montage pour la 1ère fois ! J’espère qu’il vous donnera la même impression et que nous retrouverons bientôt vos jeux et votre créativité sur notre téléphone.

David