Premier développement sur Zune

par Yome NetSan @ 7 avril 2010

Comme un retour aux sources de ce site, voici mes premiers pas dans le développement de jeux sur Zune et plus généralement avec le XNA de Microsoft.

Le site des créateurs XNA propose différents tutoriaux pour la création de jeux sur Windows, Xbox 360, Zune et Windows Mobile. Le framework est le même pour tous ces supports, mise à par pour Windows Mobile qui a la chance d'avoir une nouvelle version exclusive (la 4.0 alors que les autres sont en 3.1).

J'ai trouvé 3 tutoriaux qui m'ont parus intéressants pour commencer mon apprentissage :

Ils sont à destination de Windows et portent sur les collisions entre objets de diverses formes (rectangulaire, par pixel, transformé). Mais qui dit collision, dit affichage de sprite, mouvement et contrôle utilisateur.
Le fait qu'il soit pour Windows n'est pas un problème en soit étant donné que le framework est le même.

 

Je suis donc parti avec le premier exemple et j'ai créé mon projet sur Visual Studio 2005. Pour commencer, j'ai créé un projet pour Windows, histoire de voir au moins le jeu tourner tel qu'il doit le faire à l'origine.

Il s'agit d'un exemple très simple avec un personnage en bas de l'écran et des blocs triangulaires qui tombent d'en haut. Il est possible, avec les touches gauche/droite du clavier, de déplacer le personnage. Le fond passe de la couleur bleu au rouge lorsque d'un bloc entre en contacte avec le personnage (le jeu continu).
Rien de plus, rien de moins. On voit vite que la collision est détectée en fonction des rectangles contenant les sprites (c'est écrit d'ailleurs dans le titre du tutorial). Cela n'est donc pas très précis et l'exemple est donc très basique mais encore une fois, il contient les bases pour commencer : afficher des sprites, les déplacer (automatiquement pour les blocs et avec contrôle utilisateur pour le personnage) et une détection de collisions.

Etudions un peu le projet maintenant.
Le code est regroupé dans un seul fichier "qui fait tout", appelé par un autre qui ne fait que ça : appelé le code principale. Pas de classe propre à chaque élément ou à une fonction particulière.

La structure d'un jeu (en tout cas dans ce framework) est à base d'une grande boucle qui répète les étapes 2 et 3 de cette liste :

  1. L'initialisation (où l'on crée les objets, charge les textures, etc.)
  2. La mise à jour des données (où l'on fait les calculs de score, le déplacement des objets, etc.)
  3. L'affichage (où l'on dessine les objets tels qu'ils ont été calculés à l'étape précédente
  4. La fin

Ceci est très bien expliqué sur cet article de XNA-France : http://xna-france.com/?p=12

L'aspect graphique des sprites est issu de deux fichiers BMP de 32x32 pixels.

Le sprite du joueur d'origine : "la personne"

 

Le sprite des ennemis d'origine : "le bloc"

 

Mon but maintenant est donc de porter ce "jeu" sur Zune et d'en modifier les sprites.

Je crée un nouveau projet que je nomme PizzAttack. Oui, je suis très doué pour donner des noms très cons à mes projets. Le thème de la pizza est venu de la forme triangulaire des blocs dans le jeu d'origine.

 

Portage

Dans un projet de ce type (simple), il n'y a que deux différences entre un jeu pour Windows et pour Zune :

  • la résolution de l'écran
  • les contrôles utilisateur

La résolution d'origine était faite pour passer à la fois sur Windows et sur Xbox 360 : 853 x 480 pixels.

Or, la résolution d'un Zune est de 320 x 240, ce qui est donc plus petit (sans blague ?!) mais aussi d'une proportion différente.
Le jeu était donc base sur un écran de type paysage (horizontal, plus large que haut) alors que le Zune a une résolution de type portait (vertical, plus haut que large).

Il fallait donc modifié la résolution mais pas seulement. Il fallait regarder si cela avait un impacte pour l'affichage des autres éléments. Ben oui, ce serait dommage que le personnage apparaisse au début en dehors de l'écran, ou qu'il soit possible de le faire sortir durant une partie.

La modification de la résolution est rapide. Dès le début du code, il y a deux lignes assez explicites et il suffit donc de modifier les valeurs :

            // Prefer a resolution suitable for Windows and Xbox 360
            graphics.PreferredBackBufferWidth = 853;
            graphics.PreferredBackBufferHeight = 480;

Au niveau des incidences, le code est plutôt bien fait car toute les positions sont basées sur les bords de la fenêtre :

            // Start the player in the center along the bottom of the screen
            personPosition.X = (Window.ClientBounds.Width - personTexture.Width) / 2;
            personPosition.Y = Window.ClientBounds.Height - personTexture.Height;

Le problème qui se pose est que l'élément Windows n'existe pas pour Zune.
Il y a quelques mois, j'avais étudié un peu un autre exemple de jeu fait pour le Zune, Alien Game, toujours issue du Club XNA.
Ce jeu est un peu plus complet avec un menu, des sprites sympas, des animations, des points et un game over. J'ai donc ouvert le code de ce projet pour faire des comparaisons et j'y ai trouvé la notion de Viewport. J'ai donc pu modifier le code d'origine avec ceci :

            // Calculate safe bounds based on current resolution
            Viewport viewport = graphics.GraphicsDevice.Viewport;
            safeBounds = new Rectangle(
                (int)(viewport.Width * SafeAreaPortion),
                (int)(viewport.Height * SafeAreaPortion),
                (int)(viewport.Width * (1 - 2 * SafeAreaPortion)),
                (int)(viewport.Height * (1 - 2 * SafeAreaPortion)));

            // Start the player in the center along the bottom of the screen
            personPosition.X = (safeBounds.Width - personTexture.Width) / 2;
            personPosition.Y = safeBounds.Height - personTexture.Height;

Enfin, j'ai remplacé toutes les occurences de Window.ClientBounds par safeBounds.

Maintenant que le jeu s'affiche, il faut modifier les contrôles. Après une petite recherche, je trouve dans le code les lignes correspondantes :

            // Move the player left and right with arrow keys or d-pad
            if (keyboard.IsKeyDown(Keys.Left) || gamePad.DPad.Left == ButtonState.Pressed)
            {
                personPosition.X -= PersonMoveSpeed;
            }
            if (keyboard.IsKeyDown(Keys.Right) || gamePad.DPad.Right == ButtonState.Pressed)
            {
                personPosition.X += PersonMoveSpeed;
            }

On remarque facilement que l'on change la position de l'objet Person lorsque l'on appuie sur une touche précise du clavier (Keys.Left et Keys.Right) ou sur une direction de la croix de direction d'une manette (gamePad.DPad.Left et gamePad.DPad.Right). Une petite comparaison avec Alien Game me montre que le pad de direction du Zune est considéré comme la croix d'une gamepad ! La modification est donc très rapide : suppression de ce qui a rapport au clavier. Si ça se trouve, le jeu aurait marché tout pareil mais je préfère faire du code propre.

            // Move the player left and right with arrow keys or d-pad
            if (gamePad.DPad.Left == ButtonState.Pressed)
            {
                personPosition.X -= PersonMoveSpeed;
            }
            if (gamePad.DPad.Right == ButtonState.Pressed)
            {
                personPosition.X += PersonMoveSpeed;
            }

Alors le pad du Zune est aussi tactile. Il serait bien de voir aussi comment déplacer le personnage en utilisant cette fonction au lieu (ou en plus) des boutons, mais ce sera pour une prochaine fois, restons simple pour commencer.

Et voilà ! Le jeu se lance sur mon Zune ! Je déplace sans problème le personnage, il ne sort pas de l'écran, les "pizzas" tombent bien d'en haut et l'écran deviens rouge quand on se colisionne !

 

Modification des sprites

On va rigoler un peu maintenant.

 Comme le sprite d'origine m'a fait pensé une pizza, je me suis dit : "Autant y mettre de vrais pizzas !".

 

J'ai aussi voulu changer le "héro" du jeu et, faute de plus d'imagination, j'ai choisi mon Mii.

 

Il est à noté que le code génère le rectangle du sprite en prenant comme dimension celle de l'image d'origine. Le fond magenta sera aussi transparent, visiblement automatiquement (je n'ai pas trouvé où cela se passait...).

            // Get the bounding rectangle of the person
            Rectangle personRectangle =
                new Rectangle((int)personPosition.X, (int)personPosition.Y, 
                              personTexture.Width, personTexture.Height);

J'ai donc essayé de modifié l'image de mon Mii pour qu'elle soit rectangulaire (26 x 32) au lieu de carré (32 x 32). Le comportement s'adapte automatiquement à cette nouvelle taille sans avoir à toucher le code. Les collisions sont bien prisent en compte en fonction de cette taille réduite.

 

J'ai aussi cherché un fond un peu plus funky et j'ai trouvé cette photo de Pizzeria. Je l'ai dupliqué pour en avoir une version "rouge" lorsque le Mii mange une pizza.

Une fois les images créées, il faut les intégrer dans le projet, dans le dossier "Content". J'ai ensuite modifié les références aux fichiers, vu que je les avais renommés.

 

Pour le fond, le code d'origine modifiait la couleur unie utilisé par le processus graphique.

J'ai donc du crée 2 nouveaux objets pour pouvoir charger mes images. Pour faire vite, j'ai copié le principe des sprites existants. Le fond est donc un sprite de 240 x 320 pixels ! Je doute que cela soit la meilleure façon de faire, il faudra que je recherche un peu plus loin pour cela.

Je me suis fait avoir sur un truc : chaque sprite à dessiner doit être ajouter à l'objet spriteBatch après que celui-ci ait été démarré. Dans un copier/coller, j'avais supprimé la ligne correspondant au démarrage sans la voir, le jeu ne démarrait donc pas.

        // The images we will draw
        Texture2D fondTexture;
        Texture2D fondHitTexture;

Dans protected override void LoadContent()

            // Load textures
            fondTexture = Content.Load("Fond");
            fondHitTexture = Content.Load("FondHit");

Dans protected override void Update(GameTime gameTime)

            fondPosition.X = 0;
            fondPosition.Y = 0;
            fondHitPosition.X = 0;
            fondHitPosition.Y = 0;

Dans protected override void Draw(GameTime gameTime)

            spriteBatch.Begin();

            // Change the background to red when the person was hit by a block
            if (personHit)
            {
                GraphicsDevice.Clear(Color.Red);
                spriteBatch.Draw(fondHitTexture, fondHitPosition, Color.White);
            }
            else
            {
                GraphicsDevice.Clear(Color.CornflowerBlue);
                spriteBatch.Draw(fondTexture, fondPosition, Color.White);
            }

 

Test et conclusion

Les modifications sont à présent terminées et le projet est prêt à être lancé sur le Zune.

Et voilà le résultat :

On a maintenant un bon début, mais rien de bien ludique. Les pizzas tombent toujours à la même vitesse et il n'y a pas de challenge (ni game over, ni possibilité de gagner).

Comme le suggère le tutorial, il est possible d'améliorer ça avec certains points plus ou moins faciles :

  • Ajouter un compteur de pizzas touchées
  • Ajouter un compteur de pizzas évitées (plus dur)
  • Ajouter différents types de pizzas (peperoni, calzone, etc.)
  • Augmenter le nombre de pizza et leur rapidité au fur et à mesure
  • Ajouter un bonus d'invincibilité temporaire

C'est ce que je vais m'amuser à faire prochainement. Je vais aussi jeter un oeil au portage sur Xbox 360, mais je crois qu'il faut payer l'abonnement XNA même pour simplement debugger sur sa propre console...

 

Téléchargements :

Comments are closed

Recherche avancée

Now doing...

Now Playing...
Detective Box
Le Tueur Au Tarot - Ep.1

Now Listening...
Roberto Fonseca
Contradanza del espíritu

Now Playing...
Super Famicom
パネルでポン (Panel De Pon)

Now Playing...
Playdate
Under the Castle

Now Playing...
Playdate
Pulse

Now Playing...
Jeu de société
Planet Unknown (Solo)

Now Reading...
Black & White Stories
Black & White Stories #3

Now Playing...
Jeu de société
Wingspan Duo (Art + Océanie)

Now Listening...
Tété
L'air de Rien

Now Watching...
Théatre
Le Diner de Cons

Now Listening...
Tété
Fauthentique : Acte 2

Now Listening...
Tété
Préambule