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!
Voici un video de ce jeu en action:
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:
- Son lorsque la tête frappe la ballon.
- Son lorsqu'un point est marqué.
- 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.)
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!