mercredi 7 novembre 2012

Création de jeux sous MSX : Affichage de sprites.





Je vous sentais pressé d'afficher quelque chose sur l'écran ... héhéhéhé. Et bien, affichons des sprites !

Implémentation :

Pour rappel , un sprite est une image qui possède une transparence et qui peut être déplacée dans l'écran.
Nous allons, pour faire apparaitre ces sprites, implémenter les fonctions suivantes :

void setup_sprites(char spritesize, char zoom); : Va initialiser le type de sprite que l'on veut : de taille 8*8 ou 16*16, zoomé ou non ...

Hé oui, nous avons quasiment déja tout ce qu'il faut pour afficher des sprites !! Etonnant non ?!

Ouvrez le fichier video.c et ajouter la méthode suivante :


/* Définit les attribut général des sprites */
/* spritesize : SPRITE8X8
                SPRITE16X16
   zoom :       SPRITE_NO_ZOOM
                SPRITE_ZOOM    1            */
/**/
void setup_sprites(char spritesize, char zoom){
  __asm
 
  ld b,#0x00
  ld a,4(ix)
  and #0x0f
  cp #0x08
  jr z,$1
  set 1,b ; --- si 16x16 allume bit 1
$1:
  ld a,5(ix)
  cp #0x00
  jr z, $2
  set 0,b ; --- si sprite zoomé => allume bit 0
$2:
  ld hl,#0xf3e0 ;
  ld a,(hl)
  and #0xfc
  or b
  ld (hl),a
  call 0x007e ; --- Change le registre
 
  ld a,#0x01
  ld hl,#0xfcaf
  ld (hl),a
  call 0x0069 ; --- reset la table des attributs de sprites

  __endasm;
}


Alors sans entrer dans les détails, on va mettre certains bit de registre vidéo à 1 ou à 0 pour dire que les sprites font 8*8 ou 16*16 ou si ils sont zoomé ou non.

On modifie le video.h ainsi :

#ifndef ___MSXVIDEO_H___
#define ___MSXVIDEO_H___

#define SPRITE8X8      8
#define SPRITE16X16   32

#define SPRITE_NO_ZOOM 0
#define SPRITE_ZOOM    1

void screen_mode_2();
void put_vram(unsigned char* block,int vramaddr,int size);
void setup_sprites(char spritesize, char zoom);

#endif


Et on le compile avec :

sdcc -mz80 -c video.c

Les sprites : la théorie

Les sprites sur Coleco/Msx, c'est 2 zone mémoires.

 1 Zone mémoire image, qui va contenir toute les représentations des sprites. Le début de l'adresse de cette zone est à peek_word(0xF3CF)
 1 Zone mémoire attribut qui va contenir pour chaque sprite : sa position x,sa position y,le n° de son image,sa couleur (0 à 15). Le début de l'adresse de cette zone est à peek_word(0xF3CD).

 Comme vous pouvez de suite le voir, un sprite = 1 seule couleur.

Les sprites : la pratique

Pour affiche un sprite à l'écran, on va donc les initialiser. Ici on va utiliser le cas le plus complexe, les sprites de 16*16. On décidera de ne pas zoomer.

setup_sprites(SPRITE16X16, SPRITE_NO_ZOOM);

Pourquoi le plus compliqué ? Pour calculer le n° d'image d'un sprite de 8*8 pixels c'est simple, si on veut l'image 0, c'est 0, si on veut l'image 1, c'est 1 ...
Un sprite de 16*16 est composé de 4 images de 8*8. Et donc le début de l'image 0 = 0, le début de l'image 1 = 1*4 = 4 et le début de l'image 2 = 2*4 etc etc ...

Dans ce tutorial, je vais vous donner le code de 3 images, un carré, un triangle et un rond. On apprendra à créer ces images dans un autre tutorial, à la main, et avec des utilitaires qui vont bien :)

// Size = 3 sprites * 4 caractères
//      = 3 sprites * (4*8) octets = 96 octets
//
// pattern de début Carré = image 0 * 4 = 0
// pattern de début Triangle = image 1 * 4 = 4
// pattern de début Rond = image 2 * 4 = 8
const unsigned char SPATTERN[] = {
  0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00,    // Carré
  0x00, 0x00, 0x00, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00,   // Triangle
  0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   // Rond
  0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};


 Voilà, on va maintenant charger ces images de sprites à l'adresse mémoire des images de sprites :)

 int addr_sprite_pattern;

 addr_sprite_pattern = peek_word(0xF3CF);

 put_vram(SPATTERN,addr_sprite_pattern,96);


 Charger les images ne suffit pas ... Il faut pour afficher des sprites leur définir une image,une couleur et une position. (A savoir qu'une position de 204 en y par exemple positionne le sprite en dehors de l'écran et le rend donc invisible). ATTENTION, si par exemple le sprite 2 à une position y de 208, les sprites après le sprite 2 ne seront pas affiché !! La valeur 208 est une valeur spéciale.

 On sa créer une structure en C, et un tableau de 32 "sprites".

 typedef struct
{
 unsigned char y;
 unsigned char x;
 unsigned char pattern;
 unsigned char colour;
} sprite_t;

sprite_t sprites[32];


Et maintenant on va définir nos sprites en mémoire RAM :

// Carré
sprites[0].x=10;
sprites[0].y=10;
sprites[0].pattern=0; // Image 0*4
sprites[0].colour=2;
// Triangle
sprites[1].x=42;
sprites[1].y=42;
sprites[1].pattern=4; // Image 1*4
sprites[1].colour=3;
// Rond
sprites[2].x=42+32;
sprites[2].y=42+32;
sprites[2].pattern=8; // Image 2*4
sprites[2].colour=4;


Mais cela ne suffit pas ... Il faut maintenant copier cette table d'attribut en VRAM pour activer l'affichage.

addr_sprite_attr = peek_word(0xF3CD);
put_vram(sprites,addr_sprite_attr,4*32);


Compilez avec :

sdcc -mz80 --std-c99 --data-loc 0xc000 --code-loc 0x4020 --no-std-crt0 crt0.rel -main.ihx tools.rel video.rel main.c

et

objcopy --input-target=ihex --output-target=binary main.ihx result.rom

Voici le code source complet pour ceux qui sont un peu perdu :

#include "tools.h"
#include "video.h"

// Size = 3 sprites * 4 caractères
//      = 3 sprites * (4*8) octets = 96 octets
//
// pattern de début Carré = image 0 * 4 = 0
// pattern de début Triangle = image 1 * 4 = 4
// pattern de début Rond = image 2 * 4 = 8
const unsigned char SPATTERN[] = {
  0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00,    // Carré
  0x00, 0x00, 0x00, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00,   // Triangle
  0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   // Rond
  0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

typedef struct
{
  char y;
  char x;
  char pattern;
  char colour;
} sprite_t;

sprite_t bsprites[32];

void main(void)
{
    int addr_sprite_pattern;
    int addr_sprite_attr;   
   
    screen_mode_2();
    setup_sprites(SPRITE16X16, SPRITE_NO_ZOOM);
   
    addr_sprite_pattern = peek_word(0xF3CF);
    put_vram(SPATTERN,addr_sprite_pattern,96);
       
    // Carré
    bsprites[0].x=10;
    bsprites[0].y=10;
    bsprites[0].pattern=0; // Image 0*4
    bsprites[0].colour=2;
    // Triangle
    bsprites[1].x=60;
    bsprites[1].y=60;
    bsprites[1].pattern=4; // Image 1*4
    bsprites[1].colour=2;
    // Rond
    bsprites[2].x=42+32;
    bsprites[2].y=42+32;
    bsprites[2].pattern=8; // Image 2*4
    bsprites[2].colour=2;

    addr_sprite_attr = peek_word(0xF3CD);
    put_vram(bsprites,addr_sprite_attr,4*32);

   
    while(1){};
}


Lancez la rom dans un émulateur et ... admirez !!!!

Dans le prochain tuto, on va faire bouger tout ça... A la manette !!

Aucun commentaire:

Enregistrer un commentaire