Portage du jeu Arcade Volleyball vers la SMS

Introduction

Arcade Volleyball est un petit jeu de volleyball auquel je me souviens avoir joué assez jeune sous DOS. Selon son article sur Wikipedia, ce jeu qui aurait été un shareware à l'époque est désormais gratuit.

Toujours grâce à Wikipedia, j'ai appris que ce jeu, qui était à l'origine pour le Commodore 64, a également été fait sur Amiga et a été distribué sur la disquette incluse avec l'édition automne 1989 du magasine Compute!'s Amiga Resource sous forme de programme exécutable et de code source.

Code source? Cela a retenu mon attention! Visionner le code source des jeux de mon enfance, j'adore! Et il se trouve en plus que le jeu a été codé en C, mon langage préféré!

Comme j'ai récemment fait quelques projets pour la Sega Master System, justement en C à l'aide de devkitSMS, j'ai immédiatement pensé, pourquoi ne pas essayer de le porter à la Sega Master System? Excellent petit projet pour un week-end!

Version pour DOS

Version pour DOS

Version Amiga

Version Amiga

Ma version pour SMS

Ma version pour SMS




Voici un video de ce jeu en action:




Téléchargement

Version 1.0
20 avril 2021 (Mardi)
Version initiale:
  • Pour 1 ou 2 joueurs
  • Jouable avec des manettes standard ou des Paddle (HPD-200 ou équivalent)
  • Testé sur une SMS Japonaise et un Megadrive NTSC (US)
Fichier(s):
avb-1.0.zip (11.9 KB)

Ce projet est aussi disponible sur GitHub!
Pour suggérer de nouvelles fonctionnalités, signaler un problème ou contribuer au projet, vous pouvez m'écrire ou utiliser le dépôt GitHub:
https://github.com/raphnet/arcadevolleyball



Le processus de portage

Ceci est un jeu plutôt simple qui n'était pas bien difficile à porter, mais j'ai pensé qu'il serait peut-être intéressant de parler un peu du processus.

1. Obtenir le code source

La première étape était donc de mettre la main sur ce code source. L'article Wikipedia mentionne le magasine Compute!'s Amiga Resource, édition automne 1989... Heureusement, le contenu du disque l'accompagnant est archivé ici:

http://amr.abime.net/issue_1231_coverdisks


2. Parcourir le code source

Le code du jeu était dans un seul fichier ArcadeVolleyball.c d'environ 1500 lignes. Je l'ai parcouru rapidement sans chercher à comprendre le fonctionnement de la logique même du jeu, mais plutôt pour identifier les entrées (clavier/joystick) et sorties (graphismes, sprites, effets sonores).

Conclusion: L'arrière plan est dessiné programatiquement (ce n'est pas une image ou un tilemap), et le jeu fonctionne avec les sprites hardware, qui font 16x26. L'état des boutons des joysticks est obtenu en accédant directement à certaines adresses mémoires, et les sons sont des échantillons.


3. Première compilation

La modification du code source commence ici. J'ai commenté les parties du code qui n'étaient pas applicables (par exemple, chargement des échantillons sonores depuis des fichiers) et mis en place des fonctions avec signatures identiques aux fonctions systèmes (absentes sur SMS) se faisant appeler, ou utilisé des macros les faire disparaître. J'ai aussi dû définir quelques types (par exemple, USHORT qui corresponds à uint16_t).

Il n'y a pas eu d'obstacles majeurs, après peu de temps le jeu pouvait être compilé.


4. Réimplémentation des entrées (clavier, joystick)

Voici le code de gestion du joystick en jeu. Ces accès directs à des adresses mémoires ne fonctionnent certainement pas sur la SMS!
void joystick(pl)
int pl;
{
 USHORT *joy[2] = {
  {(USHORT *)0xDFF00A},
  {(USHORT *)0xDFF00C}
 };
 USHORT *firereg = (USHORT *)0xBFE0FE;

 keymove[pl].right=0;
 keymove[pl].left=0;
 keymove[pl].up=0;
 if ((*joy[pl^jflag]&0x0002)==0) keymove[pl].left=-2;
 if ((*joy[pl^jflag]&0x0200)==0) keymove[pl].right=2;
 if ((*firereg&(0x40<<(pl^jflag)))==0) keymove[pl].up=1;
}
J'ai refait la fonction ci-dessus en utilisant des appels à SMS_getKeysStatus(). J'ai réimplémenté une autre fonction qui vérifie si la touche ESCAPE a été appuyée par le code nécessaire pour gérer le bouton PAUSE de la console.


5. Gérer la différente taille d'écran

Ce jeu est conçu pour un écran faisant 320x200 pixels de taille. Or, sur la SMS, l'écran ne fait que 256x192... Pendant un moment, j'ai considéré modifier le jeu pour qu'il fonctionne dans cet espace plus restreint, mais la fonction docollisions() qui contient des dizaines de comparaisons entre la position des joueurs, du ballons, du filet m'a découragé. Le risque de briser quelque chose ou de modifier le comportement du jeu sans le vouloir était trop grand.

Voici une partie de la fonction docollisions():
    if (i && tby>81 && tbx>127 && tbx<160) {
  if (tby>91) {
   if (tbx<148)
    bvelx=-abs(bvelx);
   else
    bvelx=abs(bvelx);
  }
  else
   if ((tbx>147 && 161-tbx>=polecol[91-tby]) ||
      (tbx<148 && tbx-133>=polecol[91-tby])) {
    if (bvely>0) {
     dx=tbx-145;
     if (dx<-5) bvelx=-abs(bvelx);
     if (dx>5) bvelx=abs(bvelx);
     bvely=-abs(bvely);
    }
    if (abs(bvelx)>32) bvelx=bvelx>>1;
    if (abs(bvely)>32) bvely=bvely>>1;
   }
J'arriverais certainement à tout comprendre si je le voulais vraiment, mais il faut choisir ses défis, et j'avais une solution plus facile: Changer l'échelle des coordonnées.

En effet, la boucle de jeu fait plusieurs calculs pour déplacer les objets (joueurs et ballon), détecter les collisions entre ceux-ci, etc, pour à la fin, simplement demander au matériel d'afficher chaque sprite à de nouvelles coordonnées.

Les coordonnées calculées sont faites pour un écran 320x200 pixels. Pour les adapter à l'écran 256x192 de la SMS, il est possible d'en modifier l'échelle comme ceci:
	X_sms = X - X/4 - X/16;
	Y_sms = Y - Y/8;
Ainsi, le point du coin inférieur droit de l'écran (319,199) correspondra au point 219,174, lequel est bien à l'intérieur de l'écran sur la SMS. Il suffit donc d'appliquer cette transformation avant d'appeler SMS_updateSpritePosition.

Les collisions joueur/ballon fonctionnent donc comme avant, et les collisions ballon/environnement aussi, à condition d'avoir un arrière plan (filet, murs) dessiné au bon endroit.


6. Extraire les sprites

Le jeu utilise un total de 6 SimpleSprite faisant 16x26. Chaque élément (ballon et joueurs) est formé de 2 sprites, et occupent donc 32x26. Les images pour ces sprites sont harcodées dans le code source. Par exemple, le début des données pour la première moitié du joueur de gauche ressemblent à ceci:
USHORT chip AVSpriteData[20][56] = {
 /* lmanOneLeft */
 {0x0000,0x0000,
  0x001A,0x000F,
  0x00D1,0x002F,
  0x0103,0x02FF,
  0x04C7,0x033F,
  ...
Cela continue pour un total de 20 sprites. Il y avait aussi une série d'appels à la fonction SetRGB4 pour configurer la palette de couleurs. J'ai lu un peu de documentation sur le fonctionnement des sprites sur Amiga et ayant compris le format des données ci-dessus, et quel sprite utilise quelles couleurs dans la palette, j'ai créé un petit programme dans lequel j'ai recopié ces données (voir extsprites.c dans les sources) à l'aide duquel j'ai généré ce PNG:



Les sprites du ballon ne sont pas bien rondes n'est-ce pas? Le ballon semble légèrement écrasé. C'est sans doute à cause qu'en 320x200, les pixels n'étaient pas parfaitement carrés (ils étaient un peu plus hauts que large).


7. Adaptation des sprites

Les sprites originales font 32x26. Sur la SMS, il faudrait donc 4 sprites pour chaque joueur. La limite de sprites par ligne sur la SMS étant de 8, certaines sprites ne seraient pas affichées quand le ballon est entre les deux joueurs...

Mais par chance, l'écran étant plus petit, les sprites doivent également subir une petite réduction de taille! J'ai donc redimensionné les sprites d'origine de sorte que 9 sprites SMS soient utilisées par joueur (disposés en 3x3), et 4 sprites SMS pour le ballon (disposées en 2x2). Dans le pire des cas (ballon entre les joueurs) on a exactement 8 sprites par ligne! C'est parfait!

Voici les graphismes auxquels je suis arrivé après quelques ajustement pour corriger le ratio (afin que les ballons soient ronds) et plusieurs retouches (car quand on redimensionne du pixel art, c'est pratiquement toujours un désastre).


Voici une version zoomée avec une grille délimitant les tuiles 8x8 de la SMS.



8. Le son

Il y a trois sons dans ce jeu:
  1. Son lorsque la tête frappe la ballon.
  2. Son lorsqu'un point est marqué.
  3. Son lorsque l'échange est perdu.
J'aurais peut-être pu utiliser pcmenc afin de reproduire les sons Amiga originaux (des échantillons) sur la SMS, mais j'avouerais que les sons originaux ne me plaisent pas beaucoup... (d'après ce que j'ai entendu en visionnant un vidéo du jeu Amiga sur youtube)

C'est peut-être car ayant grandi avec la version DOS, je suis habitué à un son aigu évoquant un sifflet pour un point marqué et un son plus grave, similaire à un beep d'erreur, lorsqu'un échange est perdu. La version DOS n'a pas de son pour la frappe du ballon, mais sur Amiga il y a un effet sonore qui varie en fonction de la vitesse que je ne trouve pas très naturel. Désolé.

Bref, j'ai simplement fait des sons qui me plaisent à l'aide de Deflemask et PSGlib.


9. Ma touche personnelle: Le Paddle!

Selon l'article Wikipedia, le jeu aurait été inspiré par Pong... alors quoi de plus approprié que de supporter le Paddle? J'ai ajouté le support nécessaire, et il est donc possible de jouer à Arcade Volleyball sur Master System avec un ou deux Paddles Sega HPD-200 (ou un équivalent maison.)

Exemple de Paddles supportés

Exemple de Paddles supportés




10. Conclusion

Il y quelques autres changements mineurs, comme le retrait de l'option pour quitter du menu principal, mais sinon, je trouve que le jeu se comporte très bien, exactement comme je m'en souviens. Je suis content du résultat, et très heureux d'avoir eu la chance de porter un jeu, dont j'ai de bons souvenirs vers la SMS!