Accueil > Développement > Intégration > Jouer avec AJAX II/III

Jouer avec AJAX II/III

Une carte avec de vrai morceau d’Ajax dedans...

lundi 25 septembre 2006, par Arckam

Dans le dernier article nous avons discuté de ce qu’Ajax pouvait apporter en terme d’ergonomie pour le joueur. Nous sommes finalement arrivé à la conclusion qu’Ajax est très riche - quel surprise - mais aussi que cette technique peut s’avérer assez complexe à mettre en place.

Notre but cette fois et de parcourir une implémentation (parmi tant d’autres) d’un moteur de gestion de carte. Notre moteur tentera de tirer parti d’Ajax en évitant au joueur de devoir supporter une période de loading.

Notre carte est volontairement simple : pas de support de layers, nous n’afficherons que le sol. Le but ici est de démontrer qu’Ajax peut être un outil fort intéressant dans le cadre du pré-loading d’une carte, rien d’autre. Evidemment si cette structure peut vous donner des idées c’est tant mieux...

Concept

Notre carte est composée de cases, toutes placées fort judicieusement les unes à côté des autres. Jusque là rien de bien nouveau.

GIF - 11.7 ko
Concept de la carte

Nous ne voulons pas de loading, il nous faudra donc :

  1. Pouvoir faire le rendu de la carte de façon modulaire
  2. Pouvoir gérer un ou plusieurs buffers, dont le rendu sera assuré par les méthodes du point 1.

Dans notre cas nous prendrons 4 buffers, en haut, en bas, à droite et à gauche. La dimension des buffers dépends de vous et de vos tests, ici je les prendrai de 1x5 (1 case de large sur 5 cases de long).

Base de données

GIF - 5.6 ko
Structure de la DB

Voici la structure de la DB. Très classiquement nous allons retrouver des zones (les carrés de la carte) qui permettrons de construire la carte (on parle de tiles ou de tuiles).

Notre carte sera découpée en lignes et colonnes, ce qui nous permettra plus facilement de gérer un buffer de cases.

Chaque case dispose d’une image qui lui est propre, cette image peut être NULL. Si ce champ est NULL nous utiliserons l’image par défaut du type de terrain (type_terrain.type_img)

Pour le principe ajoutons une table joueur minimaliste. Elle ne retient qu’une chose : la position du joueur.

Dans la réalité le schéma de votre DB sera nettement plus complexe, on est tous d’accord, mais ça nous suffira pour l’instant.

Ecrire des queries pour cette structure est très simple : prenons un exemple : comment trouver le buffer de gauche de notre carte ?

SELECT C.case_id, C.type_terrain_id, C.case_ligne, C.case_col, C.case_img, T.type_img
FROM         type_terrain T, cases C
WHERE T.type_terrain_id=C.type_terrain_id
AND C.case_col = 4
AND C.case_ligne BETWEEN 11 AND 15

Ce select renvoie donc le buffer gauche de notre carte. C’est à dire une colonne composée de 5 cases. Pour obtenir les autres buffers nous jouerons sur les deux dernières conditions, faisant varier soit les colonnes soit les lignes. Par exemple, pour trouver le buffer du bas

AND C.case_col BETWEEN 5 AND 9
AND C.case_ligne = 16

Structure du code


Nous allons essayer de faire aussi générique que possible. De quoi avons-nous besoin ? Tout d’abord d’un script capable de nous renvoyer le XML proprement dit. Nous appellerons ce script un module.

Notre structure, en sus de la carte, devra être capable d’accueillir bien d’autres modules, pour à peu près n’importe quoi. Puisque nous risquons d’avoir un nombre de modules importants il ne serait pas opportun de lier chaque appel Ajax à un module en particulier, ça nous obligerait à connaître à chaque fois l’URL de ce module, ce qui peut être contraignant (et ennuyeux niveau sécurité).

Pour éviter de coupler module et code côté client nous allons définir un point d’entrée. Ce script particulier sera chargé de dispatcher l’appel Ajax vers le module ad-hoc puis d’en renvoyer le code XML. Avantage supplémentaire, nous pourrons valider ce que le client nous envoi directement au niveau du point d’entrée.

Le code en dessous vous donne un aperçu de l’implémentation du point d’entrée et d’un module.

Le point d’entrée

<?
//A ne pas oublier si vous utilisez des sessions
session_start();
     
/***********************************
             GLOBAL
***********************************/
//Contient les infos génériques (passwords, etc)
     
  include_once("../globals.php");
     
/***********************************
             DATABASE
************************************/
/** Fonctions d'accès à la DB. Notez qu'il peut être utile de les découper aussi en fonction des modules, ce qui évite d'inclure des centaines de lignes inutiles.**/
 
  require_once("../includes/database/db_interface.php");
 
/***********************************
             DEFINITION MODULES
************************************/
/**
Contient la liste des modules devant être supportés. Il est plus simple de rassembler tous les requires des modules dans un seul fichier

Pour plus d'efficacité on peut également faire le require du module nécéssaire juste avant d'apeller son code.
**/
     
  require_once("module_definition.php");
   
/***********************************
             OPERATIONS
************************************/
     
//! Fermer la connexion  *AVANT* de renvoyer le code XML.
  db_open_connection();
     
  $str_xml ="";
     
     if (!isset($_REQUEST['action']) )
     {
             die "action non définie!";
             /**
                     Erreur mal gérée! Idéalement il faut logger les détails de la requête.
                     Il peut également être intéressant de renvoyer un XML contenant les détails de l'erreur
                     Ca simplifiera d'autant le debugging.
             **/
     }
     else
     {                     
             switch($_REQUEST['action'])
             {
                    
                     /** SUPPORT MODULE CARTE **/
                     case $_REQUEST['action']=='map':
                             $str_xml .= get_default_view(/*params*/);
                             if ($SYS_DEBUG==1) raw_log(1, "list_mail ok");
                             break;
                            
                     // Pour créer un nouveau mode il suffira d'ajouter un case ici
                            
                     /** AUTRE / DEFAULT **/
                     default:
                             $str_xml .= "DEFAULT";
                             if ($SYS_DEBUG==1) detailled_log(1, "action vide");
             }
            
     }                     
         
          //Fin de la génération du XML --> fermeture de la connexion devenue inutile
          db_close_connection();
         
          // Renvoie le code XML généré
          echo $str_xml;
?>

Un module

(ici la carte, donc)

<?

/****************************
/*                DEFAULT                                *
/***************************/

//fonction appelée par le mode lié à ce module, c'est à daire par le site "standard", pas par un appel AJAX.
//Elle affiche la version de base du module

function map_mod_default()
{
//Cette fonction renvoie du HTML, pas du XML, ce n'est pas un appel AJAX

 $str_html="";
 
 //scripts necessaires à l'application --> AJAX
 $str_html .= init_map_script();
 $str_html .= /* rendu de base */;
 

 return $str_html;

}         

/****************************
/*                 Script JS                        *
/***************************/

function init_map_script()
{      
   /**
   N'importe quel script dont vous pourriez avoir besoin est à ajouter ici.
   Notez que dans le cas des scripts génériques utilisés régulièrements il sera plus sage de les
   inclure dans votre index.php une fois pour toute.
   **/
   return "";
}


/****************************
/*                Actions                                *
/****************************/
function get_default_view()
{
        $str_xml = ... /* Renvoie vos infos sous forme de XML */
       
        /**
                C'est ici qu'il faudra insérer l'accès à la DB et la création du XML,
                autrement dit c'est le coeur de votre moteur de carte, pas niveau rendu,
                mais bien niveau data
        **/
       
        return $str_xml
}

?>

Côté client

Mais qu’en est-il côté client ? Très simple : du javascript à foison ! Dans le chapitre précédent nous avions un simple « Moteur javascript », et bien c’est lui qui va se charger de tout :

  1. Des appels pour récupérer le code XML
  2. Des buffers
  3. De l’affichage des informations recueillies

La solution la plus propre pour gérer l’affichage est d’utiliser des feuilles de style XSL. Mais ce n’est pas forcément la solution la plus simple. XSL fait appel à des technologies telles que XPath, qui ne sont pas toujours accessibles aux moins expérimentés.

En pseudo-code, voilà ce que donne notre moteur JS :

Vérifier si le buffer contient ce dont on a besoin
Si il ne le contient pas
Recupérer le handler (l'objet XMLHttpRequest)
Construire la requête en fonction de ce qu'on désire obtenir
Envoyer la requête et récupérer la réponse

Tant que la réponse n'est pas reçue entièrement on attend cette réponse

Si la réponse qui est reçue est une erreur (ex: une page 404) on gère l'erreur
Sinon on met à jour le buffer

On sélectionne le nœud que l'on veut afficher

On récupère la feuille de style XSL

Si on a à la fois les données et la feuille de style on effectue le rendu
Sinon on gère une erreur

On identifie la cible dans laquelle il faut insérer le contenu (avec son attribut ID)
On insère le code HTML obtenu grâce à la transformation XSL

Il existe des tas de très bon tutoriaux pour découvrir quelles sont les fonctions qui vont bien en JavaScript. Allez jeter un œil du côté de developpez.com par exemple pour un très bon papier dessus.

Conclusions


Avec ce que nous avons vu ici vous devriez être capable de construire une structure simple de gestion d’appels Ajax. Pour augmenter les possibilités de cette structure il ne vous reste qu’à développer de nouveaux modules, à choisir l’action qui y est liée (mod) et à effectuer l’appel javascript qui va bien.

Dans le cas qui nous intéresse, une carte, il est clair que nous gagnons en ergonomie ce qui nous perdons en complexité. Le joueur ne doit plus reloader la page à chaque mouvement de son personnage, mais seulement quand le besoin d’un scroll se fait sentir.

Implémenter le support du scrolling manuel (le joueur qui déplace la vue) est également un jeu d’enfant : c’est déjà supporté.

Soyons clair, la méthode décrite n’est pas parfaite et peut-être améliorée. Le code présenté n’est pas orienté objet, ce qui est très dommage. Point de vue performance, le switch sur l’action n’est sûrement pas la panacée. Avec de nombreuses actions différentes vous aurez vite un code horrible. Mais cet exemple a le mérite de montrer qu’il n’est pas forcément si complexe de concevoir « from scratch » le support d’une carte en Ajax.

Dans le prochain épisode nous verrons qu’il existe aussi des frameworks qui supportent déjà toutes ces fonctions, vous évitant donc de réinventer la roue à chaque fois. Stay tuned, et rendez-vous dans un petit mois !

Messages

  • Bien vu l’article, on est le 30 octobre, ou est le III/III ? :)
    Juste une remarque, je pense qu’il faut insister encore plus sur la sécurité.
    L’ajax c’est bien, mais c’est surtout un gros trou en Appel direct de données.
    Vu tous les tests que l’on est obligé de faire à chaque appel pour savoir si on a vraiement le droit de récupérer les données en question je me demande si au final c’est une economie de perfs, vu que l’on fait à priori beaucoup plus d’appels C/S.
    Oh fait, il est ou le forum ?
    Bonne continuation.