Créer un jeu vidéo avec microStudio

Par :
pmgl
lun, 16/12/2019 - 14:24
Niveau :
Facile

microStudio est un environnement de découverte de la programmation et de création de jeux vidéo. Il repose sur le langage microScript, qui tire son inspiration de LUA. microScript se caractérise par la simplicité de sa syntaxe et une certaine tolérance aux erreurs. L’un des objectifs de microStudio est d’intéresser les jeunes à la programmation, en leur fournissant une expérience rapidement valorisante qu’ils seront fiers de partager avec leur entourage.

Ce tutoriel s’adresse à vous si vous voulez accompagner quelqu’un dans la découverte de la programmation, à travers la réalisation d’un jeu vidéo fonctionnel qui pourra être installé sur un smartphone ou une tablette. Le programme complet comporte 70 lignes de code. C’est un jeu de type "runner" : un personnage court sur un muret et doit sauter pour éviter des obstacles.

Pour commencer, rendez-vous sur https://microstudio.dev/fr/ et cliquez sur Créer. Le site vous proposera soit de créer en tant qu’invité, soit de créer votre compte. Choisissez à votre convenance, puis cliquez sur le bouton “Créer un projet”. Choisissez un titre, par exemple "Runner".

La fenêtre de projet comporte une série d’onglets, notamment Code, Sprites, Paramètres etc. Dans l’onglet Code, vous verrez que le code source de notre projet est prérempli avec la définition de trois fonctions : init, update, draw.

init = function()
end

update = function()
end

draw = function()
end

La fonction init est appelée une fois, au lancement du programme. La fonction update est appelée exactement 60 fois par seconde. Cette fonction est spécialement conçue pour gérer tous les aspects d’animation, de physique ou de gameplay sans être dépendant de la fréquence de rafraîchissement de l’écran. Enfin la fonction draw est appelée à chaque fois que l’écran peut être "redessiné". Nous allons donc commencer par ajouter quelques lignes à cette fonction :

draw = function()
  screen.fillRect(0,0,screen.width,screen.height, "#456")
  screen.drawSprite("hero",0,0,20)
end

Nous pouvons immédiatement lancer notre programme en cliquant sur le bouton "play" dans l’espace d’exécution à droite. L’écran d’exécution se remplit de bleu-gris.

La première ligne que nous avons ajoutée dessine un rectangle plein, centré sur les coordonnées 0,0 (le centre de l’écran) et ayant comme largeur et hauteur les dimensions exactes de l’écran (screen.width et screen.height). La couleur est le dernier paramètre, "#456"; cliquez dessus (pour y placer le curseur d’insertion) puis maintenez enfoncée la touche Ctrl du clavier. Une palette de couleur apparaît, qui vous permettra d’essayer d’autres couleurs. Notez que vous pouvez ainsi éditer votre code et voir le résultat de vos modifications apparaître immédiatement dans la fenêtre d’exécution.

La deuxième ligne dessine le sprite intitulé "hero", aux coordonnées (0,0) (toujours le milieu de l’écran), avec une taille de 20. Le problème est que ce sprite n’existe pas, nous ne l’avons pas encore créé ! Ouvrons donc l’onglet Sprites et créons un nouveau sprite, changeons son nom en "hero" et commençons à dessiner. En cours de dessin, si vous revenez vers l’onglet Code, vous verrez votre héros apparaître sur l’écran d’exécution. Comme pour les modifications de code, les modifications de sprites se répercutent immédiatement dans le programme en cours d’exécution.

Lorsque vous revenez dans l’onglet Code, vous pouvez jouer un peu avec les coordonnées d’affichage du sprite et sa taille. Cliquez sur l’une de ces valeurs et maintenez la touche Ctrl enfoncée : un curseur apparaît, qui vous permet d’ajuster votre valeur, et là encore vous constatez le résultat en temps réel.

Avant d’aller plus loin, quelques explications sur le système de coordonnées avec microStudio : le point d’origine (0,0) est le centre de l’écran. La dimension de l’écran la plus petite, que ce soit sa largeur ou sa hauteur, aura pour longueur 200. Donc si la largeur de l’écran est plus petite que la hauteur, l’écran s’étendra exactement de x=-100 à x=+100. Si la hauteur de l’écran est la plus petite, l’écran s’étendra exactement de y=-100 (bas de l’écran) à y=+100. Ce système de coordonnées est conçu pour faciliter la prise en charge d’écrans avec des proportions et des résolutions très diverses.

Décor

Notre personnage doit courir sur un mur. Nous allons donc créer un "bloc" de mur, que nous pourrons répliquer plusieurs fois horizontalement. Pour cela, le mode "Tile" de l’éditeur de sprite nous sera utile. Créons donc un sprite intitulé "wall" (nous suggérons de changer sa taille en 32x32 pixels).

Ajoutons maintenant le code nécessaire pour afficher notre mur :

draw = function()
  screen.fillRect(0,0,screen.width,screen.height,"rgb(19,57,57)")

  for i=-10 to 10
    screen.drawSprite("wall",i*40,-80,40)
  end

  screen.drawSprite("hero",-80,-50,20)
end

Nous avons aussi modifié les coordonnées d’affichage de notre héros, pour qu’il soit bien positionné sur le mur et plutôt à gauche de l’écran. Cela lui donnera de meilleures chances de voir venir les obstacles !

Avançons

Notre personnage doit avancer. Pour caractériser cela, nous allons créer une variable globale position, qui va augmenter au cours du temps. Nous initialisons donc la variable position dans la fonction init. Puis dans la fonction update (appelée 60 fois par seconde), nous l’incrémentons de 2. Puis dans la fonction draw, nous utilisons sa valeur pour décaler l’emplacement où nous dessinons nos morceaux de mur. Cela créera l’illusion de mouvement :

init = function()
  position = 0
end

update = function()
  position += 2
end

draw = function()
  screen.fillRect(0,0,screen.width,screen.height,"rgb(19,57,57)")
  local x = position%40
  for i=-10 to 10 by 1
    screen.drawSprite("wall",i*40-x,-80,40)
  end

  screen.drawSprite("hero",-80,-50,20)
end

Sautons

Notre personnage va sauter dès que le joueur touchera l’écran tactile (ou enfoncera un bouton de la souris). Pour cela, nous allons utiliser une variable hauteur qui indique donc la position verticale de notre personnage par rapport à sa position d’origine. Dans la fonction update, nous allons tester la valeur de touch.touching qui nous indique à tout moment si une action tactile est en cours (un doigt touche l’écran / ou bouton de souris enfoncé). Nous introduisons une autre variable vv qui est la vitesse verticale de notre personnage. Le personnage ne peut sauter que lorsque la hauteur est égale à zéro (il est posé sur le mur). Son impulsion de saut se traduit par une modification de sa vitesse verticale (vv = 7). Lorsque le personnage est en l’air (hauteur>0 ou vv>0), il nous le soumettons à une force de gravité qui affecte sa vitesse verticale (vv = vv - 0.3).

Enfin n’oublions pas de décaler l’affichage du sprite de notre héros en utilisant la variable hauteur qui résulte de tout cela :

init = function()
  position = 0
end

update = function()
  position += 2
     
  if touch.touching and hauteur == 0 then
    vv = 7
  end
 
  if hauteur>0 or vv>0 then
    vv = vv - 0.3
    hauteur = max(0,hauteur+vv)
  end
end

draw = function()
  screen.fillRect(0,0,screen.width,screen.height,"rgb(19,57,57)")
 
  local x = position%40
  for i=-10 to 10 by 1
    screen.drawSprite("wall",i*40-x,-80,40)
  end

  screen.drawSprite("hero",-80,-50+hauteur,20)
end

Notez que nous n’avons pas pris la peine d’initialiser la variable hauteur. En effet, en microScript, toute variable non initialisée a pour valeur par défaut zéro, ce qui nous convient très bien pour notre hauteur.

Obstacles

Notre personnage doit éviter des obstacles. Considérons que ce sont des lames de couteau tranchantes ("blades" en anglais). Puisqu’elles seront nombreuses, nous allons utiliser une liste (ou "tableau") pour les mémoriser. Nous créons donc une liste vide [], assignée à une variable blades dans la fonction init. Dans la fonction update, nous définissons les conditions d’apparition d’une nouvelle lame : lorsque la position est un multiple de 20 (nous avons donc avancé d’une "case") et qu’un nombre tiré aléatoirement entre 0 et 1 (random.next()) est inférieur à 0,1 (donc une chance sur 10). Dans ce cas, nous ajoutons un nombre à notre liste, qui est la position de la nouvelle lame.

Dans la fonction draw, nous faisons une itération sur les lames de notre liste (for b in blades). Pour chacune de ces lames, nous dessinons le sprite "blade" à la position requise. Au fait, nous n’avons pas encore créé le sprite "blade" ! Il est encore temps de le faire.

init = function()
  blades = []
  position = 0
end

update = function()
  position += 2

  if position%20 == 0 and random.next()<.1 then
    blades.push(position+300)
  end
   
  if touch.touching and hauteur == 0 then
    vv = 7
  end
 
  if hauteur>0 or vv>0 then
    vv = vv - 0.3
    hauteur = max(0,hauteur+vv)
  end
end

draw = function()
  screen.fillRect(0,0,screen.width,screen.height,"rgb(19,57,57)")
 
  local x = position%40
  for i=-10 to 10 by 1
    screen.drawSprite("wall",i*40-x,-80,40)
  end

  screen.drawSprite("hero",-80,-50+hauteur,20)
 
  for b in blades
    screen.drawSprite("blade",b-position,-50,20)
  end
end

Phases de jeu

Notre jeu est presque fini ! Faisons le point sur ce qu’il manque encore :

Nous devons détecter si le héros heurte une lame et donc perd la partie. Nous allons faire cela avec une autre itération sur blades dans la fonction update.

C’est toujours plus sympa d’afficher un score ! Créons donc une variable score, qui sera incrémentée à chaque fois que le héros saute par dessus une lame sans la toucher.

Définissons plusieurs phases de jeu : la partie commencera dès que le joueur touchera l’écran une première fois. Utilisons pour cela une variable running qui vaudra 0 si la partie n’est pas démarrée ou 1 si elles l’est. Utilisons une autre variable gameover qui vaut 0 tant que le joueur n’a pas perdu. Dès qu’il touche une lame, gameover passe à 1 puis nous continuerons à l’incrémenter pour compter 5 secondes avant de réinitialiser le jeu.

Ajoutons quelques sons en appelant audio.beep (plus d’informations dans la documentation microStudio)

Affichons quelques informations utiles dans la fonction draw : le score, le texte "GAME OVER" si la partie est perdue, le texte "READY ?" lorsque la partie est en attente de démarrage.

init = function()
  gameover = 0
  running = 0
  blades = []
  score = 0
  position = 0
end

update = function()
  if gameover then                     // la partie est perdue
    gameover += 1                      // incrémentons gameover pour compter
    if gameover>300 then init() end    // 5 secondes avant de redémarrer une partie
  elsif running then
    position += 2
    if position%20 == 0 and random.next()<.1+position/100000 then
      blades.push(position+300)
    end
   
    if touch.touching and hauteur == 0 then
       vv = 7
       audio.beep("square tempo 20000 volume 10 span 100 C4 to C6")  // un son !
    end

    for b in blades
      if abs(b-(position-80))<10 then       // la lame est tout près du héros
        if hauteur<10 then                  // et le héros est près du sol
          running = 0                       // c’est perdu
          gameover = 1
          audio.beep("saw tempo 10000 volume 50 span 50 C2 to C4 to C2 to C4")
        elsif b == position-80 then         // le héros est pile au dessus
          score += 1                        // incrémentons le score
          audio.beep("saw tempo 960 volume 50 span 20 C6")  // un son !
        end
      end
    end
  else                                      // la partie n’est pas encore démarrée
    if touch.touching then running = 1 end  // on touche l’écran => c’est parti
  end
 
  if hauteur>0 or vv>0 then
    vv = vv - 0.3
    hauteur = max(0,hauteur+vv)
  end
end

draw = function()
  screen.fillRect(0,0,screen.width,screen.height,"rgb(19,57,57)")
 
  local x = position%40
  for i=-10 to 10 by 1
    screen.drawSprite("wall",i*40-x,-80,40)
  end

  screen.drawSprite("hero",-80,-50+hauteur,20)
 
  for b in blades
    screen.drawSprite("blade",b-position,-50,20)
  end
 
  screen.drawText(score,120,80,20,"#FFF")      // affichons le score
  if gameover then                             // c’est perdu ?
    screen.fillRect(0,0,screen.width,screen.height,"rgba(255,0,0,.5)")
    screen.drawText("GAME OVER",0,0,50,"#FFF")
  elsif not running then                       // partie pas encore commencée ?
    screen.drawText("READY?",0,30,50,"#FFF")
  end
end

Finalisation et installation

Notre jeu est terminé ! Nous pouvons maintenant l’installer sur un smartphone ou une tablette et y jouer. Notez que nous aurions même pu le faire plus tôt, le jeu se mettra à jour instantanément sur votre smartphone si vous continuez à faire des modifications à votre projet après son installation.

Avant de l’installer, créez une belle icône pour votre jeu. Allez dans l’onglet Sprites et modifiez le sprite par défaut "icon". C’est lui qui sera utilisé pour illustrer votre jeu.

Allez ensuite dans l’onglet Paramètres. Réglez le paramètre Orientation sur "Paysage". Puis notez l’URL de votre projet. Cette URL comporte un code secret que vous pouvez modifier ici. Entrez l’URL du projet dans le navigateur web de votre smartphone. A l’ouverture de la page, votre smartphone vous proposera d’installer un lien sur votre écran d’accueil. Votre jeu n’est pas une simple page web, c’est une PWA (Progressive Web App) qui une fois installée sur votre écran d’accueil se comportera tout comme une vraie application.

Pour aller plus loin

Le site microStudio propose une gamme de tutoriels interactifs en français pour apprendre la programmation. Il inclut aussi une documentation complète du langage microScript et des fonctionnalités de microStudio. Les projets publics disponibles dans la section Explorer sont des exemples qui peuvent être dupliqués, puis modifiés pour votre propre usage.

Ajouter un commentaire

Filtered HTML

Plain text

CAPTCHA
Cette question permet de vérifier que vous n'êtes pas un robot spammeur :-)
 W     W  FFFF   SSS   M   M  U   U 
W W F S MM MM U U
W W W FFF SSS M M M U U
W W W F S M M U U
W W F SSSS M M UUU