Le cahier des charges
Pour déterminer l'architecture de notre projet : il a fallu tout d'abord le décomposer en plusieurs étapes permettant à la fin de chacune de fournir une sorte de résultat intermédiaire fonctionnel mais ne répondant pas à la totalité de la problématique.
Pour chaque étapes nous avons aussi chercher à découper le problème en plein de petites questions permettant de mieux cerner le code que nous devrons produire après. Cela permet aussi de mieux se répartir le travail en fonction du niveau de chacun, Ludovic n'ayant jamais de programmation alors que Loïc si.
Pour chaque fonction utilisée nous vous invitons à regarder en parallèle la documentation de Processing et ce afin de limiter la teille de cette page.
Etape 1 : La 3D
A la fin de cette étape, on obtient un programme affichant plusieurs objets simples en 3D qui pourront être déplacés par les touches du clavier et dont certaine caractéristiques sont affichées à l'écran,
Ludovic
Comment configurer l'application pour qu'elle puisse gérer la 3D ?
La fonction size() modifie la taille de la fenêtre grâce à ses 2 premiers arguments mais peut aussi, grâce à son 3e argument optionnel, changer le système de coordonnées pour passer en 3D. Ainsi, size(800, 600, P3D); configure la fenêtre pour qu'elle ait 800 pixel de largeur, 600 de hauteur et qu'elle gère la 3D. Cependant ce système ne marche que sur des systèmes supportant OpenGL. La fonction perspetive() est aussi très importante : elle permet de définir le type de projection utilisé : il faut ici indiquer les distance d'affichage, l'angle de vue et la rapport hauteur/largeur de la fenêtre.
Comment afficher des objets ?
La fonction box() affiche un pavé droit, sphere() affiche une sphere, rect(), un carré etc... Ces fonctions ont surtout servis à créer les différents personnages et leurs animations.
Comment gérer la caméra ?
La fonction camera permet de changer le point de vue en indiquant où elle se situe, où elle regarde et quel est l'axe vertical.
Comment faire bouger la caméra ?
Grace aux angles d'Euler (enregistrant la rotation autour des 3 axes sous forme d'angle, mais ici seulement 2 nous intéressent) converti en vecteur Eye pour la caméra grâce au système des coordonnées polaires il est possible de changer l'orientation de la camera en temps réel et ce de manière plutôt simple puisqu'il suffit de rentrer les valeur des angles dans une formule toute prête. Elle peut même prendre en compte du Zoom. Changer la position se fait simplement en changeant les coordonnées du vecteur position. Une fois les modification effectuées il suffit de rappeler la fonction camera et l'affichage change.
Comment déplacer le cuber ?
La fonction translate(x,y,z) change la position du cube lorsque qu'elle est appelée avant box(). Les fonction rotateX(a), rotateY(a) ainsi que rotateZ(a), permettent de le faire tourner d'un certain angle autour d'un angle. Ces 4 fonctions peuvent être combinées mêmes si leur ordre est important. La fonction resetMatrix() permet de remettre les transformations à 0.
Comment le faire grâce aux touches du clavier ?
Il est possible d'enregistrer la position du cube et son orientation sous forme de vecteurs comme la caméra. Les variables key et keyCode permettent de savoir si les flèches du clavier sont touchées et donc s'il faut bouger ou non le cube à chaque frame.
Loïc
Comment gérer des animations ?
Les animations correspondent à des changements de position par rapport au temps. Il faut donc, grâce à la fonction millis(), calculer la durée passée entre le début de l'animation et le temps actuel de programme pour ensuite en déduire la position des objets 3D à afficher.
Comment gérer une scène plus complexe en 3D, avec plusieurs animations en même temps ?
Pour cela, on utilise le polymorphisme : on créer un objet Acteur, détaillé ci-dessous dont tous les objets qui seront affiché dérivent. Il suffi alors de stocker la liste des objets à afficher sous forme de tableau (voire de collection) puis d'appeler à chaque frame la fonction de rendu de chaque objet. La classe acteur possède une fonction pour le rendu, une fonction pour indiquer une nouvelle animation, un entier qui référence le type d'animationen cours, un entier qui enregistre le temps depuis le début de l'animation et la position et l'orientation de l'objet sous forme de 2 vecteurs.
Comment afficher par-dessus la 3D des éléments d'interface graphique comme les statisques des autres joueurs ?
Il faut afficher tout ce qui doit l'être en 3D puis utiliser les fonction suivantes : camera(); hint(DISABLE_DEPTH_TEST); textMode(MODEL); Effectuer tout ce qui doit l'être en 2D puis hint(ENABLE_DEPTH_TEST);
Etape 2 : l'utilisation du réseau
Ludovic
Comment créer un réseau entre 2 ordinateurs ?
La bibliothèque network de Processing contient 2 objets simples d'utilsation que sont client et server. Il suffit d'indiquer l'adresse IP et le port du serveur sur lequel on le connecte pour que la connexion soit faite et qu'il soit possible d'envoyer des données. Pour plus d'infos se référer à la référence.
En utilisant ce réseau, comment échanger des informations ?
Il n'est possible de ne s'échanger que des nombres cepdant beaucoup de choses sont enregistrées comme des nombres ne informatiques y compris les chaines de caractères... La position d'un objet 3D peut être envoyée apr le réseau, son nom... Tout est nombre en informatique donc tout peut être envoyé. grâce à la fonction write(nb) du client ou serveur.
Comment le mouvement d'un cube sur un ordinateur peut être transmis à un autre ?
Si l'on envoie régulièrement au serveur la position actuelle du cube, l'application du serveur peut afficher un cube à cette position. Une modification sur le client changeant quasi-immédiatement celle du cube du serveur, on peut considérer que le mouvement est suivi simultanément sur els 2 ordinateurs.
Loïc
Comment envoyer des tableaux ou des données complexes ?
Le serveur ne connait pas forcemment la taille du tableau de nombres qu'il va recevoir : il faut envoyer la taille du tableau puis les nombres composants le tableau un par un. Il est possible de voir des structures plus complexes telles que des objets comme des tableaux d'octets (bytes).
Comment utiliser ces échanges de données pour les relier à des événéments concret dans notre jeu ?
Nous avons créé un protocole permettant de s'y retrouver. Un nombre est envoyé indiquant de quelle requête il s'agit (envoie des position d'un objet 3D, nom d'un joueur etc...) puis des infos complémentaires sont envoyées si nécessaire. Pour plus d'informations se référer à la section Architecture du programme.
Comment mettre d'accord les clients et le serveur pour qu'ils se calent sur un temps commun ?
Les actions sont sujettes à la latence puisque c'est le serveur qui effectue tous les calculs importants comme indiqués dans le protocole cependant il peut être intéressant de bien gérer le temps pour les animations (un temps serveur/client trop décalé empêcherait l'affichage des animations). Quand la partie démarre, tous les clients ainsi que le serveur enregistrement un entier appelé debut et égale à la valeur de retour de millis(). Toutes les autres valeurs de temps sont calculés à partir de celle-ci, ainsi les temps ne sont plus relatif au début de chaque programme mais au début de la partie qui lui est universel.
Etape 3 : La partie en elle-même
Loïc
Comment implémenter un perso plus complexe ?
Il suffit de créer une classe perso (dérivant elle-même de Acteur) et contenant une fonction par attaque, des entiers pour la masse, la rapidité, le score de frappe, le nombre de points, la hauteur des sauts, et un vecteur pour savoir vers où il regarde.
Comment implémenter plusieurs persos différents ?
Le polymorphisme permet justement cela (on est sûr que tous les persos ont des attaques mais elles sont spécifiques à chacun). Il faut cependant préciser que le client et le serveur doivent avoir la même version du jeu pour être sûr de bien fonctionner ensembles.
Ludovic
Quand un perso meure-t-il ?
Il y a deux solutions possibles et nous n'avons pas encore tranché. On peut considérer que le perso est mort lorsqu'il s'éloigne trop de la carte ou lorsqu'il est arrivé trop haut ou trop bas (sachant que s'il est hors de la carte il va falloir forcement qu'il tombe à un moment ou un autre).
Etape 4 : l'arrivée de la physique
Quelle sont les règles physiques que le jeu doit supporter ?
Il doit principalement supporter la seconde loi de newton et la gravité (qui n'est pas un loi en soit mais qu'il faut quand même prendre en compte). Le reste est de toute façon plus artificiel puisque le score de frappe entre en jeu est multiplie la puissance des coups : plus on s'est fait tapé et plus le prochain nous mènera loin.
Comment calculer la physique d'une scène ?
Il faut effectuer régulièrement les calculs, de préférence à chaque frame et de manière à ce que leur fréquence n'interfère pas ou très peu avec le résultat. Si les calculs sont effectués assez rapidement, il n'y a plus besoin d'enregistrer la trajectoire des joueurs mais seulement leur position, qui sera mise à jour plus tôt. Il existe toujours la possibilité d'un projectile ratant un perso mais cela optimise grandement le code et simplifie beaucoup.
Comment calculer la collision entre 2 objets ?
Les joueurs se traversent pour simplifier les calculs mais la scène peut contenir beaucoup de projectiles et il faudra savoir quel joueurs ils touchent et quand. On peut voir le projectile comme un point se baladant sur la droite d'équation paramétrique de paramètre k
x = ak + d
y = bk + e
z = ck + f
et les personnages comme des pavés droits alignés sur les axes (pour simplifier les calculs).
x0 < x < x1
y0 < y < y1
z0 < z < z1
Lors de la frame le projectile va d'une position à une autre. Le tout est de savoir s'il est passé par un des cube en faisant ce chemin. Par exemple, si avant la frame le projectile était possédait une abscisse plus petite que x0 puis une plus grande que x0 après la frame et de même pour y avec y0, z avec z0, alors on peut dire que le projectile a traversé le cube.
On peut aussi voir qu'il y aune approximation : les projectiles ne sont pas sujets ici à la gravité.
Si projectile s'éloigne trop du centre du jeu, il est supprimé.
Pendant une durée donnée, la trajectoire d'un projectile peut traverser 2 joueurs et 1 joueurs peut se recevoir plusieurs projectiles comment gérer ça ?
Il faut être sûr que l'on fait en premier le calcul de la collision entre le premier joueur et le premier projectile. Pour savoir si c'est le premier joueur qu'un projectile touche, il utilise le paramètre correspondant au point de collision avec chaque joueur (si collision il y a) et prend le plus petits qui correspond au premier joueur touché. On a besoin de savoir quel est le premier projectile pour mettre à jour le score de frappe, pour cela il faut s'arranger pour que le paramètre de l'équation soit le temps, ainsi en calculant les collisions entre tous les projectile et un joueur, le plus petit paramètre correspond au premier projectile qui touche le joueur.
On peut aussi procéder d'une autre façon, on calcule toutes les collisions puis on trie en fonction du paramètre et on les applique une par une en virant celle concernées par des projectiles déjà utilisés.
Quelle interface peut-il exister entre les attaques des joueurs et le moteur physique ?
Les attaques créent des objets Projectiles, dérivant d'Acteur, enregistrant leur trajectoire (vecteur vitesse + un point d'origine) et une fonction pour appliquer des effets au personnage en cas de collision. Les personnages enregistrent la taille du cube qui les délimite, une fonction pour les éjecter dans une direction, un vecteur vitesse initiale (depuis le dernier changement par un projectile, ce temps étant enregistré dans un entier) ainsi qu'un booléen qui les empêche de se déplacer s'ils sont en l'air et tant qu'ils n'ont pas retouché le sol.