Subject: Algorithmes pour traiter les fonds dans les jeux (voir p From: hpfool@bigfoot.com (Clément PILLIAS (HpFool)) Newsgroups: hpcalc.dev Organization: unavailable Date: Feb 06 2001 18:48:12 User-Agent: Microsoft Outlook Express 4.72.3110.5 Bon puisqu'on me l'avait demandé, voici un rappel des différentes méthodes existantes pour traiter les images de fond dans les jeux, ainsi qu'un algo de ma composition que j'appelle "algo de l'écran cyclique", particulièrement adapté aux scrollings différentiels... Avant tout j'aimerai préciser deux choses : - Ce message va être assez gros, en fait ca pourrait presque être un article a part entière, aussi je trouverai dommage qu'il ne profite qu'aux gens (peu nombreux) qui lisent ce newsgroup. Avis a ceux qui ont un site web ;) - Ca fait longtemps que je me suis intéressé au problème, et comme j'ai déjà eu l'occasion de le montrer, ma mémoire n'est pas éternelle, aussi il est possible que je fasse quelques erreurs que je n'aurais pas faites quelques années plus tôt, j'espère que vous me pardonnerez et que vous me corrigerez si vous n trouvez ;) Bon on s'y met ? Allez c'est partit ! 1/ De la structure d'un programme utilisant un écran de fond. La structure générale d'un tel programme est la boucle suivante (tout au long de ce message je supposerai qu'on utilise du double buffering et que vous savez ce que c'est !) : 'Initialisations diverses { 'Initialisation de l'image que l'on va construire : affichage du fond 'Affichage des plans superposés au fond avec transparence 'Affichage des sprites 'Autres affichages divers (nombre de vies, compteur, etc...) 'Divers (gestion des touches, des déplacements et collisions, IA, réseau, café (euh non !), etc...) UP } On peut en fait voir l'image comme une superposition de plusieurs plans successifs transparents (des calques !) sur une image de fond (qui n'a pas de transparence et qui peut éventuellement être un plan de couleur unie). Les sprites représentent un plan particulier puisqu'il contient des éléments mobiles les uns par rapport aux autres, alors que les plans de décors sont "figés". Remarquez qu'il peut y avoir plusieurs plans de sprites (par exemple quand mario saute sur une plate-forme mobile, il passe devant sans la toucher, pourtant il s'agit d'un sprite). Ici je ne parlerais pas des sprites : ni de leur affichage, ni de leur gestion, ni de leurs collisions... On se concentre sur les décors. Les décors peuvent avoir diverses caractéristiques de taille et de scrolling (différentes pour chaque plan), mais ils ont tous quelque chose en commun : une relation d'ordre. Certains plans sont en effet DEVANT d'autres. Cette relation définit l'ordre dans lesquels les différents plans doivent être affichés : Les plans les plus éloignés (c'est-à-dire les plus "derrière") sont affichés en premiers. Ainsi le fond est la première chose affichée. Le nombre et la forme des plans de décors et du fond nécessite différents traitements et permet différentes optimisations que nous allons voir maintenant en commençant par le cas le plus simple : 2/ Fond uni et pas d'autres plans de décors : Bon je sais, ce chapitre aurait pu être sauté tellement ca semble évident, mais il pose des bases importantes ! C'est vraiment le plus simple : Il suffit de remplir l'image avec une couleur unie. On a tout de même plusieurs méthodes suivant le niveau d'optimisation souhaité et le codage des images. Vous n'êtes en effet pas sans ignorer qu'il existe deux manières fondamentales de stocker des images en niveaux de gris (ah vous l'ignoriez ? désolé !) : - Le format vertical (ou "double hauteur" pour des images en 4 nivs de gris) : L'image est divisée en différentes couches successives en mémoire, ce qui fait un format "GROB largeur x (hauteur * nombre de couches)". C'est le format utilisé par la plupart des éditeurs et viewers de grobs et par les GREYs du MétaKernel. Historiquement c'est le premier apparu, car le plus simple à mettre en place (quand on a pas l'habitude !). Son principal inconvénient est de nécessiter autant de boucles qu'il n'y a de couches dans l'image. - Le format horizontal (le vrai nom est "entrelacé", mais on dit aussi "double largeur" pour les images en 4 nivs de gris) : Ici chaque ligne d'une couche est suivie immédiatement des lignes correspondantes dans les autres couches, avant de passer à la ligne suivante de la première couche. Il en résulte un format du style "GROB (largeur * nombre de couches) x hauteur. Ce format est principalement utilisé avec les sprites (merde j'avais dit que j'en parlerais pas !) ou dès qu'il y a de la transparence à gérer, tout simplement parce qu'avec la transparence on doit en plus gérer un masque qui s'applique à toutes les couches de l'image, autant alors avoir toutes ces couches le plus près possible ! De plus ce format donne des algos plus courts et plus rapides puisqu'il ne nécessite pas de faire plusieurs passages (une seule boucle, mais un peu plus grosse !). Notez qu'il existe deux variantes de ce format suivant qu'on utilise une largeur ajustée au quartet ou à l'octet près, mais ça n'a pas d'importance pour la suite. - Le format spirale (ou double zéro 7 dans le cas d'images avec 7 niveaux de gris) : Cherchez pas, c'est une connerie ;) Revenons à notre fond unis : Ces histoires de format n'ont aucune influence si le fond est entièrement blanc ou entièrement noir, mais en ont sinon, puisque les différentes couches de l'image sont de couleur différentes. On a donc deux algos : %% ALGO 1 : a utiliser pour des images blanches ou noir ou dans le cas d'images au format vertical %% (l'algo est alors à multiplier autant de fois qu'il n'y a de couches dans l'image) %Initialisation : D0 pointe sur le début de l'image A=0 W % insérer ici A= -A-1 W si l'image (ou la couche) est noire LC ((largeur de l'image en quartets)*(hauteur de l'image))/16 - 1 { DAT0=A 16 D0+16 C-1.B % adapter au champ correspondant suivant la taille de l'image... UPNC } % ici il faut éventuellement rajouter : DAT0=A (((largeur de l'image en quartets)*(hauteur de l'image)) modulo 16) % si le produit largeur*hauteur n'est pas un multiple de 16. Remarques sur cet algo : - Il écrit toute l'image d'un coup, sans tenir compte du découpage de celle-ci en lignes, c'est pourquoi il ne peut être utilisé pour des images au format entrelacé. - Il peut être optimisé en vitesse : Il suffit pour cela de "dérouler la boucle" (c'est une technique classique en informatique qui consiste à écrire n fois le corps de la boucle (sans le test de fin, donc ici : DAT0=A 16 D0+16) sans oublier de divisé le nombre d'itérations de la boucle par n. Cela a l'avantage de diviser par n le nombre de tests effectués sur le compteur (et donc d'accélérer), mais à l'inconvénient de multiplier la taille de la routine par n (environ). - Dans la suite nous verrons d'autres algos dérivés de celui ci, les remarques précédentes s'y appliqueront aussi ! %% ALGO 2 : A n'utiliser que pour des images au format entrelacé non blanches et non noires. %Initialisation : D0 pointe sur le début de l'image LC (hauteur de l'image)-1 B=C.B % adapter au champ nécessaire suivant la hauteur de l'image A=0 W C=0 W C= -C-1 W { DAT0=A ou C 16 D0+16 % utiliser A ou C suivant la couleur de la couche ... % Recopier la ligne précédente suffisamment de fois pour remplir toute une ligne ... % par exemple pour une image 131*64, il faut faire : ... % DAT0=C.16 D0+16 DAT0=C.16 D0+16 DAT0=C.B D0+2 ... % recommencer tout ce qu'il y a avant dans la boucle autant de fois qu'il y a de couches. B-1.B % adapter au bon champ UPNC } Remarques sur cet algo : - Il est à peine plus rapide que l'algo 1 (sans déroulage de boucle), mais plus gros ! - On peut aussi dérouler la boucle, mais l'algo est déjà assez gros donc il faut vraiment que ce soit nécessaire ! ;) - Dans la suite nous verrons d'autres algos dérivés de celui ci, la remarque précédente s'y appliqueront aussi (mais pas la première car celui-ci sera alors bien plus rapide !). Attaquons nous maintenant au problème des fonds non unis (images !) 3/ Fond non uni sans autres plans de décors. Maintenant qu'on doit gérer des images, cela pose d'autres problèmes qu'on n'aurait pas supposé tout d'abord : En effet dans le cas d'une image on pourrait se dire qu'il suffit de reprendre les deux algos précédents et de les adapter un petit peu, en utilisant les deux pointeurs : D0 pointant sur l'image à créer et D1 pointant sur l'image de fond enregistrée quelquepart (on ne travailles bien sur pas directement sur celle-ci pour ne pas l'abîmer !). On n'aurait alors qu'à lire quelque chose avant de l'écrire et incrémenter les pointeurs (en gros on remplacerait les séquences "DAT0=A 16 D0+16" par "A=DAT1 16 DAT0=A 16 D0+16 D1+16". C'est en effet une bonne idée mais elle pose deux problèmes : - Comment gérer les scrollings ? - Comment stoker l'image ? En fait ces deux problèmes ne se posent que si l'image de fond est de dimension importante, et essentiellement pour des questions de mémoire. En effet dans ces conditions on ne peut pas utiliser le scrolling "hard" (utilisation de la marge à gauche et de la marge à droite) car l'image à scroller est trop grosse pour qu'on puisse raisonnablement l'utiliser ainsi (en plus son temps de traitement serait prohibitif), il faut donc utiliser une technique de fenêtrage que nous allons voir tout de suite. Quant au stockage, il existe deux solutions pour l'éviter : La compression et le découpage en éléments de base (dont les tuiles !) a) Techniques de fenêtrage pour le scrolling La technique est super simple : il suffit de faire la même chose que dans l'algo 2 en se limitant à écrire 32 quartets et non la ligne entière. Après les 32 quartets il faut donc sauter (largeur de la ligne en quartets)-32 quartets et c'est bon. Ce saut peut se faire avec des D0=D0+... si l'image ne dépasse pas les 362 pixels de large, sinon ca se fait avec une méthode du style CD0EX C+D.A CD0EX où Da contient la valeur de l'incrément... Je ne m'étends pas plus longtemps la dessus, j'en ai déjà longuement parlé dans un autre message, avec les études de temps de cycles détaillées. b) Techniques de stockage Il y a tout d'abord la compression. En fait c'est une fausse technique de stockage car la décompression est trop lente pour être faite en temps réel, il faut donc passer par une image temporaire décompréssée du fond et on revient au problème initial (ca permet juste de stocker plus d'images). Ensuite il y a le découpage en éléments élémentaires. Cette méthode consiste à ne composer que des fonds utilisants des éléments de base (par exemple un arbre, un nuage, etc...) Avec suffisamment d'éléments de base on arrive à produire des fonds relativement corrects du point de vue graphique. L'avantage c'est qu'après on a juste à stocker les éléments de base quelque part, et le fond en lui même consiste en une suite de couples (Identifieur d'élément de base, position de l'élément dans le fond). Ce format en lui même prends déjà beaucoup moins de place en mémoire, et surtout il n'en demande pas de supplémentaire pour l'affichage, puisqu'il suffit de tester pour chaque élément de base composant le décors s'il est visible (au moins en partie) et de l'afficher comme un sprite si c'est le cas. Comme j'ai dit que je ne parlais pas des sprites, je ne vous expliquerait donc pas comment l'afficher ;p Toutefois cette méthode pose un problème fondamental : Pour un niveau assez grand, il peut y avoir beaucoup d'éléments de base utilisés, et il faut pour chacun vérifier s'il est visible au moins en partie, ce qui peut être assez long, et qui a l'inconvénient de créer des ralentissements pour les niveaux graphiquement chargés. Pour remédier à cela, il existe diverses méthodes. Je ne vais pas rentrer dans le milliard d'optimisations possibles dans ce cas et je ne vais en citer qu'une, particulièrement facile à implanter lorsqu'on a un scrolling unidirectionnel (par exemple de gauche à droite, et pas en hauteur, comme dans cyclo) : Il suffit de trier la liste des éléments de base composant le fond par abscisse croissante. Ceci ne se fait bien sur pas en temps réel, le fond doit être stocké ainsi (pré-traitement). Ensuite il suffit de garder un pointeur dans la liste triée vers le premier élément visible à gauche. Alors ceux qui sont situés après dans la liste seront visibles, jusqu'à ce qu'il y en ait un invisible. Cela réduit considérablement le nombre d'éléments de base à tester. Mais cette technique a encore quelques inconvénients : - Comme elle utilise une routine de sprites, les éléments de base peuvent être à n'importe quelle abscisse, et il faut donc faire des décalages en conséquence, ce qui coûte du temps. Alors bien sûr, on peut se limiter à des éléments de base bien alignés tous les 4 quartets mais c'est assez vexant ;) - Comme elle utilise une routine de sprites, il faut initialiser le fond avant, et hop, retour aux algos 1 et 2, gourmands en temps !!! - J'avais dit que je parlerais pas des sprites :( - En fait il ne s'agit pas d'un fond mais d'un plan transparent !!! Heureusement, Zorro est arrivé ! Euh non, je voulais dire : Il existe une solution sympathique : Les tuiles ! Les tuiles fonctionnent exactement selon le même principe, sauf que tous les éléments de base (qu'on appelle maintenant "tuiles") ont la même taille (généralement un multiple de 4, au moins en largeur pour résoudre le problème des décalages), et qu'il existe une tuile vierge, correspondant à l'absence de dessin (généralement numérotée 0). De plus, toutes les tuiles sont alignées dans un tableau, de façon à ce qu'elles recouvrent tout l'écran, sans trous. Comme sur un toit quoi, sauf que sur un toit les lignes de tuiles sont décalées ce qui n'est pas le cas ici, elles sont les unes au dessus des autres. Du coup la structure du fond devient un bête tableau contenant le type de la tuile correspondant à chaque case. Inconvénients : Ca prends généralement plus de place, et ca oblige les dessins à être alignés verticalement et horizontalement ce qui est un peu moins beau. Avantages : Pour savoir quelles tuiles sont affichées, il suffit de savoir quelle partie du tableau est visible et y'a rien de plus simple, puisque toutes les tuiles ont la même taille ! De plus comme ca couvre tout l'écran il n'y a pas besoin d'initialiser le fond avant ! Et pour finir (oh joie suprême !) si la largeur des tuiles est un multiple de 4, il n'y a pas de décalages au pixel près à faire !!! (Ce décalage est déjà fait pour les sprites, on ne va pas le faire ici puisqu'on peut utiliser la marge à gauche pour décaler toutes les tuiles d'un coup !!!) Voila, bien évidement tout cela va être réutilisé dans le chapitre suivant sur les plans transparents : 4/ Les plans transparents Ce que j'apprécie dans ce chapitre, c'est qu'il n'y a pas de surprise ;) Les plans transparents c'est EXACTEMENT le même problème que pour les fonds images, avec de la transparence en plus. Et cette transparence ce n'est qu'un problème d'affichage et non de stockage ou autres comme précédemment. Je vais donc supposer que vous savez traiter la transparence car elle se fait de la même façon que pour les sprites (avec des masques), et je vais passer au chapitre suivant sans remords ! (En fait j'exagère, car il y a quand même d'autres problèmes, comme les décalages qui étaient gérés par la marge à gauche dans le cas du fond et qui ne peuvent plus l'être dans le cas d'un scrolling différentiel puisque les différents plans ne scrollent pas à la même vitesse. Mais je passe car je ne connais pas de solution miracle à ce problème : Il faut se taper les décalages :( ) 5/ Fini les généralités, place aux optimisations ! Bon on a vu toutes les techniques classiques. Maintenant il faut vous avouer la triste vérité : Ca rame !!!! Si vous voulez faire un jeu de plates-formes, avec juste un fond image, quelques plates-formes et quelques sprites, même avec une intelligence artificielle de très bas niveau, en tenant compte de tous les affichages à faire et des tests de collisions et autres choses qu'on peut trouver dans un tel jeu, et bien vous avez intérêt à savoir programmer correctement car sinon ca devient vite trop lent ! On ne s'étonne pas alors qu'il y ait si peu de jeux de plate-formes sur HP !!! On ne s'étonne pas non plus que le seul jeu de plate-formes jouable à disposer d'un scrolling différentiel n'ait qu'un seul sprite !!! A quoi est due cette lenteur ? Tout simplement a un problème de mémoire : Tous ces affichages représentent des tas et des tas d'écritures en mémoire (cela se compte en Ko pour les images de fond !) et généralement sur des petits champs, et vous savez sûrement que l'écriture en mémoire est l'instruction la plus lente du Saturn, et que plus le champ est petit plus elle est lente (relativement) ! Il n'y a malheureusement rien à faire pour la plupart des parties du programme, à part en réduisant les possibilités de ce dernier (par exemple en limitant le nombre de sprites ou la taille des niveaux, ou les niveaux de gris, etc...). On peut toute-fois faire plusieurs choses pour l'affichage du fonds et des plans transparents. On l'as déjà vu, le principal problème posé par le fond et les plans transparents est lié à la mémoire : Si on avait assez de mémoire pour stocker toute l'image sans être obligé de passer par des tuiles ou autres ca serait beaucoup plus rapide : on n'aurait que le fenêtrage à gérer ! Seulement c'est pas possible :( On peut toutefois imaginer des solutions intermédiaires, qui auraient l'avantage de la vitesse de l'image complète, et les avantages de mémoire des tuiles et autres, et ces solutions sont liées au scrolling. En effet, pourquoi reconstruit-on l'image à chaque fois ? Parce qu'avec les scrollings cette image peut changer ! Mais bon c'est un peu exagéré non ? y'a une bonne partie de l'image qui reste encore valide en cas de scrolling, elle est juste décalée ! Et puis on ne scrolle pas à chaque image non plus !!! Ces remarques conduisent à une nouvelle optimisation : On construit chaque plan dans une image tampon de la taille de l'écran qu'on a alors plus qu'à recopier avec des algos ressemblants à l'algo 1 ou à l'algo 2. Cette recopie est beaucoup plus rapide que la reconstruction à partir des tuiles ou des éléments de base !!! On y gagne alors considérablement ! Il reste toutefois un problème : En cas de scrolling l'écran tampon n'est plus valide ! On ne va pas le reconstruire, ca n'aurait aucun sens ! On va simplement le décaler d'une colonne ou deux et puis reconstruire les colonnes qui sont apparues (tuiles forever !) Et c'est effectivement un peu plus rapide, le décalage se faisant sur de grands champs (W), et la construction sur de petits (généralement B, X ou A), donc plus lents. Mais un décalage ca reste quand même très bourin !!! En terme de rapidité on peut faire mieux ! C'est là que vient l'optimisation dite "de la fenêtre coulissante" ! Malheureusement cette optimisation ne marche que pour des scrollings unidirectionnels, mais elle est intéressante quand même. Elle nécessite cette fois ci un écran tampon de taille double (dans le sens du scroling). Prenons par exemple le scrolling de cyclo, vers la droite : Il faut une image tampon de largeur double par rapport à l'écran. Initialement elle est remplie de deux copies (une à gauche (pixels 0 à 130), une à droite) de l'image qui doit être affichée. Imaginons qu'on se déplace vers la droite : On va alors mettre à jour la première colonne à droite de l'image de gauche (la colonne du 132eme pixel). On a alors une image valide correspondant à ce qu'on doit à afficher entre les pixels 1 et 132, qui pourra être recopiée sans remords dans l'écran en cours de construction. On va quand même fignoler un peu et recopier la colonne qu'on vient de mettre à jour dans la colonne située à gauche de la nouvelle image (la colonne du pixel 0 donc !). Pourquoi faire cela ? Et bien continuez encore 130 fois ! Vous aurez atteint la limite droite de l'écran tampon (colonnes 131 à 262), mais vous aurez grâce à ces copies de colonnes une copie de l'image dans la partie gauche (colonnes 0 à 131). Et hop vous pourrez continuer indéfiniment en repartant à gauche !!!!! En fait pour bien être comprise cette technique nécessiterai un dessin. En voici un simpliste en ASCII-art : Image du fond : 0123456789ABCDEF _X_X_X_X_X_X_X_X __XX__XX__XX__XX ____XXXX____XXXX ________ XXXXXXXX Ecran tampon initial (l'écran affiché fait 4 caractères de large) : 01230123 _X_X_X_X __XX__XX ________ ________ Ecran tampon après un décalage : 41234123 _X_X_X_X __XX__XX X___X___ ________ Ecran tampon après 4 décalages : 45674567 _X_X_X_X __XX__XX XXXXXXXX ________ Bon je sais que c'est pas très évident en ASCII art mais vus pourrez constater que les deux images sont toujours là mais décalées (comme une télé mal réglée). C'est ce décalage qui évite justement de décaler l'image à la main ! On à donc transféré la phase de décalage lors de la copie de l'écran tampon plutôt que lors de sa mise à jour !!! Le principe des pointeurs appliqué dans toute sa beauté ! ;) Bon bien sur, j'ai parlé de décalages au pixel près, en pratique on ne fait pas ça, on met à jour directement des colonnes de quartets, voire de tuiles ! Notez aussi que ca marche quelque soit le format des images (vertical ou entrelacé), et également pour des scrollings (uniquement) verticaux. C'est pas beau ça ? Si hein ? Le problème c'est que ca demande deux fois plus de mémoire même si ça reste raisonnable, et que ca ne marche pas avec des scrollings multidirectionnels ! En plus lors de la recopie, il faut passer de lignes de 262 pixels de large à des lignes de 131 pixels de large, on se retrouve alors avec le problème du format entrelacé... ... Et ca vous dirais que je vous donne une solution encore plus rapide et qui n'ait aucun de ces problèmes ??? 6/ Je suis un génie (sisi j'vous jure !!!) Mon algo (enfin l'algo que je m'attribue puisque je l'ai trouvé par moi même et que je n'en ai jamais entendu parlé par quelqu'un d'autre) est basé sur la même idée de décalage, pour reporter la phase de décalage au moment de la recopie plutôt qu'au moment de la mise à jour... Seulement ici j'utilise une image de taille normale (et non double), que je rends CYCLIQUE ! Qu'est-ce que ca veux dire ? Dans le cas précédant l'image était déjà cyclique (comme une télé mal réglée : ce qui disparaît à un bord réapparaît de l'autre coté), mais c'était une cyclicité sur les lignes, alors que chez moi il s'agit d'une cyclicité sur toute l'image, c'est à dire sur la zone mémoire occupée par celle-ci. En gros dans le cas précédant vous saviez dans quelle colonne de votre tampon débute l'image à recopier. Vous aviez donc un OFFSET sur cette colonne. Chez moi c'est pareil, sauf que l'offset ne désigne plus une colonne mais un quartet de l'image (celui qui contient le pixel en haut à gauche de l'image telle qu'elle doit être affichée). Encore de l'ASCII art ? allez c'est partit (bien que je me demande si on puisse vraiment parler d'ART dans ce cas ;) ) Voici une image : _X_X_X_X __XX__XX ____XXXX Et sa représentation mémoire (les lignes les unes après les autres) : _X_X_X_X__XX__XX____XXXX Ce qui nous donne, avec un offset de 15 caractères (les 15 derniers caractères sont passés au début) : _XX__XX____XXXX_X_X_X_X_ Ce qui correspond à l'image (mais ça on s'en fout, car cette image là on ne la regarde pas, c'est juste pour vous donner une idée) : _XX__XX_ ___XXXX_ X_X_X_X_ Plus grand chose à voir avec l'original non ? ;) Mais bon maintenant qu'on a bien rigolé, rentrons plus en détail dans l'algo... Comment dois-je faire pour recopier une telle image dans l'écran que je suis en train de construire ? Bah c'est simple, je part de mon offset (ici le 15ème caractère), et je commence à recopier l'image. Je recopie 24-15 = 9 caractères pour arriver jusqu'à la fin du tampon, et je continue en retournant au début du tampon pour recopier les 15 caractères qui restent. Facile en fait ! Et rapide ! Et pour la mise à jour du tampon ? Imaginons que je me déplace vers la droite : Je vais remplacer la colonne qui commence à l'offset par la colonne que je vois apparaître à droite, en partant de l'offset plus une ligne, puis en descendant. quand je sort du tampon, je remonte a la première ligne du tampon et je continue ! Et puis j'incrémente l'offset de 1 et le tour est joué !!! Facile et rapide !!! Enfin facile... pas tant que ça, la routine de mise à jour de la colonne est un peu plus rusée que la routine habituelle pour tenir compte de la cyclicité de l'écran, mais ça reste supportable, il faut juste bien s'organiser ! Encore un exemple ? Si je devais faire apparaître la colonne : O L E dans l'image précédente j'obtiendrais : _XX__XXL ___XXXXE X_X_X_XO dont la représentation physique est : _XX__XXL___XXXXEX_X_X_XO En recopiant ca avec la méthode que j'ai donné et un offset de 15+1=16 j'obtiens : X_X_X_XO_XX__XXL___XXXXE Ce qui corresponds à l'image : X_X_X_XO _XX__XXL ___XXXXE Ce qui est bien ce que je voulais, non ? Simplicité, efficacité, JLD ;) Bon quels sont les inconvénients ? => C'est un peu plus dur à mettre en place => Ne règle pas le problème des décalages au bit près :( (promis dès que je trouve une méthode d'enfer à ce sujet je vous prévient ;) ) => Nécessite un écran supplémentaire Et les avantages ? => Presque aussi rapide que si on avait une vrai image plutôt que des tuiles => Règle le problème des décalages au quartet prés ;) => Ne nécessite QU'UN écran supplémentaire ;) => Marche quelque soit le format d'image utilisé (vertical ou entrelacé) => Marche aussi bien pour les scrollings multidirectionnels que pour les scrollings unidirectionnels. => C'est moi qui l'ai trouvé !!! (sisi ca compte ! ;) ) Bon, je crois avoir fait le tour, je sais que j'aurais pu rentrer plus dans les détails sur certains points, mais vous êtes gentils j'ai pas que ça à faire ;) C'est déjà pas mal je trouve. Mais je suppose qu'à cause de ça y'a des trucs que vous ne comprendrez pas alors n'hésitez pas à poser des questions... A+