Jeu de plateforme tangible
Sommaire
Cahier des charges
Présentation générale du projet
Il s'agit de réaliser un système tangible qui à l'aide de cubes matérialise un niveau de jeu en 2D de type Mario. Ainsi dès le moment où nous disposons des cubes sur une surface,
ces cubes sont analysés puis ils apparaissent instantanément dans le jeu. Il est ainsi possible de modifier les niveaux pendant que l'utilisateur principal joue. On peut donc crée une infinité de niveaux.
Cahier des charges
Le dispositif devra être capable :
- De détecter des cubes matériels formant un "niveau".
- D'analyser ces cubes et de les convertir en numérique.
- De les intégrer à un jeu fonctionnant sur PC
- D'être suffisamment rapide pour que si une personne change des cubes le niveau du jeu s'actualise.
Choix techniques : matériel et logiciel
Après rendez-vous avec Mr Boé nous sommes partis sur une solution à base d'ARTag. En effet, nous allons devoir réaliser des cubes sur lesquels nous placerons des Tags qu'une webcam
pourra détecter et qu'un ordinateur pourra analyser à l'aide de la bibliothèque ARToolkit qui permet de faire du développement pour de la réalité augmentée. Il y aura plusieurs sortes de cubes
( Un cube de départ et de fin de niveau et des cubes simples de chemins ou de vides ) chacun de ces cubes aura sur lui un tag. Les cubes pourront avoir des tailles différentes selon ce qu'ils sont et ainsi augmenté le réalisme.
Établissement d'une liste de matériel
En ce qui concerne le matériel nous aurons besoin :
- D'une webcam pour faire les acquisitions vidéos.[Fournie 27/01/2015]
- D'une raspberry Pi pour les traiter.[Fournie 27/01/2015]
- D'utiliser l'imprimante 3D afin de confectionner les cubes pour le jeu.
Contraintes attendues
- La position de la webcam par rapport aux cubes : positionner la webcam suffisamment loin pour avoir un nombre de cubes suffisant tout en faisant attention
à la résolution de la caméra.
- Les obstacles entre la caméra et les cubes : par exemple si une personne met sa main entre la webcam et les cubes ceux-ci vont disparaître dans le jeu.
- La détection de la position des cubes les uns par rapport aux autres.
Déroulement des séances
Semaine 1 et 2
Nous avons cette première semaine tenté de comprendre les différents programmes fournis par ArToolkit pour pouvoir l'adapter comme nous le souhaitions mais également d'installer et de configurer la Raspberry. Malheureusement après plusieurs tentatives nous avons constaté que la raspberry n'était pas assez puissante pour pouvoir faire tourner Artoolkit. Nous avons donc abandonné l'idée de la raspberry pour utiliser une webcam.
Semaine 3
A partir de cette semaine nous avons décidé de nous partager le projet, c'est-à-dire l'un de nous s'occupe de la partie jeu et l'autre de la partie Artoolkit.
Partie Artoolkit
Pour cette partie nous avons décidé d'utiliser un programme dont le code était fourni dans les exemples de la bibliothèque en le modifiant afin de le faire convenir à nos besoins. La première difficulté fut de comprendre le code car il utilise beaucoup de fonctions qui ne sont pas utiles à notre jeu de plateforme. Après analyse il a fallu supprimer toutes les fonctions d'animations 3D du programme. Après cela, il était nécessaire de pouvoir déterminer la position de plusieurs patterns de façon simultanée afin de pouvoir effectuer une comparaison de position les uns par rapport aux autres. Pour cela nous avons dû utiliser une variable fournie dans les bibliothèques d'Artoolkit à savoir patt_trans[3][4] qui est une matrice qui nous donne les coordonnées en x,y et en z du pattern par rapport à la caméra. Cela fait, il fallait maintenant comparer les positions des différents patterns par rapport à un cube de "départ" imposé. Pour cela il faut réaliser une boucle qui va vérifier la présence des patterns et si il y a présence, sauvegarder ses coordonnées et regarder par rapport aux coordonnées de la base mais cela ne fonctionne qu'en partie pour le moment.
Partie Jeu
Pour cette partie nous avons décidé d'utiliser le langage de programmation C pour programmer notre jeu avec l'aide de la bibliothèque SDL, qui permet de réaliser des Applications graphiques comme les jeux 2D ou 3D. Cette librairie correspond donc tout à fait avec ce que nous avons besoin dans notre projet. Nous avons donc du apprendre à manipuler cette librairie et ses nombreuses fonctions à l'aide de différents tutoriels trouvés sur internet. Nous avons ensuite choisi d'utiliser la technique de Tile Mapping de cette bibliothèque. Cette technique permet de créer un niveau de jeu à l'aide d'une matrice de nombres comprises à l'intérieur d'un fichier texte. Écrire et lire dans un fichier à partir d'un programme C étant dans nos compétences déjà acquises, nous pourrons donc plus facilement faire la liaison entre nos deux parties grâce à cette technique par l’intermédiaire de fichiers texte. Nous nous concentrons donc dans cette partie plus sur la programmation du jeu, par le traitement des fichiers texte, de la création et l’interaction avec les décors (collisions) puis de l'animation.
Semaine 4
Partie Conception
Nous avons réalisé les cubes sur l'imprimante 3D et nous avons donc du modifier le code car la taille n'était pas exactement la même que celle des cubes en cartons.
Partie Artoolkit
Après avoir rencontré des difficultés avec la détection sur les trois dimensions nous avons décidé d'effectuer des tests pour une dimension afin de simplifier dans un premier temps le code. Après plusieurs manipulations il s'est avéré qu'il fallait prendre en compte que la bibliothèque rendait le programme extrêmement précis ( pour un cube immobile on obtenait une marge d'erreur de 6 pour l'axe des x et 7 pour l'axe des y ). Ainsi il a été nécessaire d'incorporer cette marge d'erreur à notre code. On a ainsi obtenu le résultat escompté :
Les différents chiffres présents dans la console identifient les patterns utilisés de la façon suivant :
1 pour le cube principal A
2 pour le cube B
3 pour le cube C etc..
Cela nous permettra par la suite lors de la programmation du jeu de pouvoir différencier des cubes représentant le sol, ceux qui représentent des tuyaux etc..
Partie Jeu
Certaines fonctions du jeu comme la fonction de création de niveau à partir du fichier texte, la gestion de l'animation du personnage et des collisions ont été réalisé et testé positivement.
Création du niveau à partir d'une matrice comprise dans un fichier texte :
Nous avons ensuite implémenté une méthode de scrolling à notre programme. Des problèmes ont été rencontrés lors de la réunion de toutes ces fonctions. La solution fut finalement trouvée en équipe, nous avions oublié de prendre en compte l'animation du personnage dans le scrolling de l'écran. L'étape suivant est donc la mise en place d'une " gravité" , qui est en fait un forçage du personnage vers le bas , hors saut. Donc ensuite ajout au personnage la possibilité de sauter. Nous en sommes actuellement à ce point dans cette partie car il y a un problème de collision en l'air lors d'un saut.
Semaine 5
Partie Artoolkit
Nous avons rajouté dans le programme une fonction qui écrit la matrice du niveau dans un fichier texte afin de permettre à l'application du jeu de le lire pour pouvoir réaliser le niveau en scrolling.
Partie Jeu
L'installation de la gravité dans le jeu pour le personnage est finie avec résolution des problèmes de collision en l'air. Pour cela il a fallu prendre une décision : quand le joueur appuie sur une direction en y et en x et que le jeu détecte une collision uniquement dans la direction des x, nous avions la possibilité soit d'autoriser uniquement le mouvement du personnage vers l'axe des y, soit d'interdire tout mouvement. Nous avons donc décidé de choisir la deuxième solution , car dans le cas ou le personnage était déjà en l'air lors de cette collision, si on lui autorisait encore à monter sur l'axe des y , on avait une impression que le personnage volait le long des décors.
Semaine 6
Partie Artoolkit
Cette semaine nous nous sommes posés la question de la réunion de nos deux programmes.
Pour cela nous avons décidé d'utiliser des threads pour pouvoir faire tourner les deux programmes de façons simultanées. Nous avons également vu avec Mr Boé la semaine dernière qu'il nous faudrait trouver un moyen pour économiser des ressources du processeur car nous étions dans l'optique d'un scrolling de base à savoir que le jeu devait réactualiser la carte en continu pour éviter de manquer le changement de position d'un cube.
Ainsi nous sommes parvenu à une solution en écrivant une fonction qui somme la totalité de la valeur des cubes.
Ainsi, en cas de changement de la valeur on passe une valeur chgt à 1 qui repasse à 0 le tour suivant. De cette façon, le programme du jeu ne doit recharger la map qu'en cas de modification de la variable changement.
Partie Jeu
Notre niveau était jusqu'à présent constitué de 2 types de tiles, soit un mur, donc l'impossibilité pour le personnage d'y passer à travers, soit du ciel. Nous allons ajouter pour un peu de plus de piquant à notre jeu, un nouveau type : le danger, qui fera perdre à notre personnage une vie lorsqu'il entrera en collision avec ce type de tiles. Pour pouvoir dire à notre programme comment réagir avec ce type de tiles, nous avons modifié la fonction de gestion des collisions ( qui retournait 0 ou 1 selon la détection d'une collision avec un mur ou non) en lui permettant de renvoyer un 2 lorsqu'une collision avec un danger a été détecté.
Voici donc notre nouvelle fonction détectant les collisions :
int CollisionDecor(Map* carte,Sprite* test)
{
int xmin,xmax,ymin,ymax,i,j,indicetile;
xmin = test->x / carte->LARGEUR_TILE;
ymin = test->y / carte->HAUTEUR_TILE;
xmax = (test->x + test->anim->largeurchar -1) / carte->LARGEUR_TILE;
ymax = (test->y + test->anim->hauteurchar -1) / carte->HAUTEUR_TILE;
if (xmin<0 || ymin<0 || xmax>=carte->nbtiles_largeur_monde || ymax>=carte->nbtiles_hauteur_monde)
return 1;
for(i=xmin;i<=xmax;i++)
{
for(j=ymin;j<=ymax;j++)
{
indicetile = carte->schema[i][j];
if (carte->props[indicetile].mur )
return 1;
if (carte->props[indicetile].danger )
return 2;
}
}
return 0;
}
Nous avons donc en parallèle, rajouté un nouveau paramètre à notre petit personnage : ses vies, et donc la possibilité à notre programme de finir en GAME OVER. Pour que le jeu nous affiche un game over lorsque notre personnage n'a plus de vie, nous avons divisé notre programme d'une boucle en deux boucles distinctes. La première étant la boucle faisant tourner le jeu, prenant fin lorsque le joueur appuie sur la touche échap OU que son personnage n'a plus de vie. Le programme arrive alors dans la seconde boucle qui va simplement nous afficher une image de GAME OVER en attente d'une interruption généré par l'appui sur la touche espace, le programme s’arrête alors.
Semaine 7
Rassemblement
Cette semaine, nous avons travaillé sur la réunion de nos programmes. Nous avons dans un premier temps tenté une exécution séparée des deux programmes afin de vérifier que la lecture du fichier de scrolling ainsi que l'actualisation de la carte se faisait correctement. Cela fonctionne mais pour le moment nous actualisons la carte en appuyant sur la touche espace et nous tenterons la semaine prochaine de faire en sorte que cela se fasse en temps réel tout en rajoutant des améliorations du côté du jeu.
Partie Jeu
En parallèle du rassemblement, nous avons améliorer le contenu de notre jeu. Précédemment nous avions rajouté des vies à notre personnage, il nous faut donc maintenant afficher ce nombre de vie pour informer le joueur du nombre de vie qui lui reste. Comme pour de nombreux jeux de plateforme nous avons décidé d'afficher ce nombre de vie sous forme de cœur. Un cœur rouge représente une vie encore présente alors qu'un cœur vide représente une vie perdu. Pour programmer cela nous créons une nouvelle fonction avec une boucle sur le nombre de vie, on colle les cœurs de la gauche vers la droite, tant que nous sommes dans le nombre de vies restants , on colle des cœurs plein, sinon on colle des cœurs vides.
Semaine 8
Rassemblement
Cette semaine nous avons terminé la mise en place des threads. Pour ce faire nous avons créer une fonction qui parcourt la matrice en sommant les valeurs de presence[i][j] et en vérifiant par rapport à la fois précédente afin de voir s'il y a eu changement. Si aucun changement on continue, mais dans le cas contraire, le programme de la caméra bloque le mutex afin de pouvoir écrire dans le fichier texte puis libère le mutex pour laisser le champs libre au programme du jeu.
Partie Jeu
Du côté du jeu, nous avons continuer sur le système de vie, en gérant les collisions avec les décors "dangereux". Notre première amélioration a était de décrémenter le nombre de vie lors de la collision avec un bloc de décor de lave ( que nous spécifions de type "danger" au lieu de type ciel ou de type mur). La deuxième amélioration a était de forcer notre personnage à sauter lors d'une perte de vie , lui permettant de ne pas perdre toutes ses vie si il tombait dans la lave, mais aussi de pouvoir se remettre à l'abri sur une plateforme de type "mur".
Semaine 9 et 10
Rassemblement
Pendant ces deux semaines nous avons travaillé sur amélioration du jeu en incorporant un système de points dégressifs. En effet, ainsi cela améliore le potentiel du jeu en créant la possibilité de comparer son score ( qui en fait correspond au temps mis pour effectuer le parcours ) avec une autre personne. De plus nous sommes en train de démarrer la possibilité d'incorporer des ennemis dans le jeu. Cependant cela est toujours en stade de développement. Nous avons également rajouté un affichage d'un nouvel écran lors d'un game over qui affiche "Game Over" ainsi que le score du joueur.
Partie Jeu
Pour finaliser et améliorer notre jeu, nous avons décider de rajouter des ennemies. L'affichage d'un ennemie se faisant exactement comme pour l'affichage de notre personnage, celle-ci se faisait sous forme de boucle où l'on affichait tout les personnages en parcourant un tableau de sprites ( dessins des personnages ), contenant à la fois notre personnage et ceux contrôlés par l'ordinateur. Nous n'avons pas programmé de réel intelligence artificielle, cela aurait été très compliqué étant donné la possibilité infinie de décors environnant au personnage contrôlé par celle-ci. Le personnage ennemie se charge donc simplement de faire un balayage de gauche à droite de quelque case. Lors d'une collision entre notre personnage et un ennemie, notre personnage se comporte comme avec la rencontre de la lave, c'est à dire un saut vers le haut en perdant une vie.
Semaine 11 et 12
Cette semaine nous avons tourné la vidéo de notre projet ainsi qu'améliorer la partie gameplay en améliorant la gravité dans le jeu trop présente depuis le début ainsi que les quelques bugs qui s'étaient glissés dans notre jeu notamment sur le fait que perdre rapportais plus de point que de gagner.