MSX Village forum

La Place des Développeurs Fusion-C Tutorial. Coder un Casse brique à partir de zéro

ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 15/01/2019 à 14h39
Coder en C n'est pas bien compliqué, et je pense que tous ceux qui connaissent un peut la programmation, même si ce n'est que le Basic MSX, peuvent très bien se mettre au C sans grosses difficultés.
Ce Tutorial est donc dédié aux débutants, mais aussi aux curieux et à tous les experts de l'assembleur qui seront dégouté de voir comment c'est facile et rapide de créer un jeu MSX en C :p


Pour commencer ce tutorial, il est nécessaire d'avoir FUSION-C, d'installé et utilisable sur votre ordinateur.

Tutorial : Coder un Casse Briques. Partie 1
Pour MSX2 en C, avec Fusion-C


Je vous propose de coder un jeu de casse brique. Je vais détailler chaque étape et chaque concept, pour que même un débutant en programmation puisse comprendre et coder facilement ce type de jeu.
Notez, que les principes sont identiques pour n’importe quel langage, et que le code qui est donné en C ici, peut être reproduit quasi à l’identique pour un développement sur un autre ordinateur avec une autre librairie C. Seuls les fonctions graphiques devraient être adaptés à la plateforme sur laquelle vous travailler.


Bien que tout le monde (je pense) connaisse les principes du casse brique, on va quand même revoir les concepts de base de ce jeu.

La théorie du Casse briques !
En haut de l’écran nous avons un mur de brique fixes. En bas de l’écran une raquette, contrôlée par le joueur qui se déplace latéralement de gauche à droite. Au centre de l’écran une balle en mouvement constant qui va détruire les briques qu’elle touche, tout en rebondissant sur celle-ci. Le but étant de rattraper la balle avec la raquette avant qu’elle ne tombe vers le bas de l’écran et disparaisse.
Notons aussi que la balle rebondie sur tout obstacle, notamment les bords de l’écran.



Concentrons-nous sur le mouvement de la balle. Celle-ci est en mouvement constant, elle ne s’arrête jamais.
La balle se situe dans un plan en 2 dimensions, et se déplace sur l’axe X, et sur l’axe Y suivant un vecteur (une direction, une longueur et un angle), que nous pouvons informatiquement parlant représenter par 2 variables, « dir_x » et « dir_y » (Direction X, et Diretion Y)



Si l’on modifie les variables « dir_x » et « dir_y », la balle se déplacera dans différentes directions, à différentes vitesses. Comme nous travaillons sur un ordinateur 8bits nous allons nous restreindre à des valeurs simples et surtout entières.
Nous avons donc à notre disposition, les variables « x » et » y », qui correspondent à l’emplacement de la balle à un instant T, et les variables « dir_x » et « dir_y », qui correspondent à la direction que la balle prend.
Pour déplacer la balle sur notre écran, on va tout simplement, ajouter la valeur de « dir_x » à « x » et « dir_y » à « y », ces opérations se faisant dans une boucle, la balle sera toujours en mouvement.

Boucle :
x = x + dir_y ;
y = y + dir_y ;
Fin de Boucle :

Voyons donc se qui se passe si :
dir_x=1 ;
dir_y=0 ;



La balle va se déplacer d’un pixel vers la droite à chaque cycle.

Maintenant Si :
dir_x= -1 ;
Dir_y= 0 ;



La balle va se déplacer de 1 pixel vers la gauche à chaque cycle. Vous l’avez compris, la même chose est valable sur l’axe Y, si l’on modifie la valeur de « dir_y » entre 1 et -1, la balle se déplacera vers le haut ou le bas.
Si « dir_x » et « dir_y » sont égales à 1, alors la balle se déplacera en diagonale.



Les rebonds.
La balle rebondie sur chaque obstacle qu’elle touche. Nous n’allons pas entreprendre un jeu complexe avec des angles de rebond différents suivant les obstacles ou les matières rencontrées, ce qui dépasserait le cadre de ce tutoriel. Alors, nous allons nous contenter de faire rebondir la balle suivant un angle de 45 degrés.
Ne nous préoccupons pas des briques pour le moment, et voyons ce qui doit se passer quand la balle percute les bords de l’écran.



On suppose que l’on démarre avec la balle qui se déplace vers le haut et vers la droite, « dir_x=1 » et « dir_y=-1 », quand la balle va percuter le bord de l’écran droit, alors sa direction sur l’axe X va s’inverser « dir_x=-1 » et « dir_y=-1 ». Quand la balle va percuter le bord de l’écran du haut, alors c’est la direction de la balle sur l’axe Y qui sera inversée «dir_x=-1 » et « dir_y=1 ». Et ainsi de suite …
Les mêmes rebonds s’appliquent à tout objet rencontrés, que cela soit le bord de l’écran ou une brique, ou même la raquette (à une exception prêt).

On en conclu le théorème suivant, lorsque la balle percute un obstacle vertical alors on inverse la direction X de la balle (dir_x), si la balle percute un obstacle horizontal alors on inverse la direction Y de la balle (dir_y).


Edité par ericb59 Le 15/01/2019 à 14h40


banniere-ericb59e
Site web    
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 15/01/2019 à 14h52
Tutorial : Coder un Casse Briques. Partie 2
Passons à la pratique




Le minimum requis pour
un programme C


La procédure « main » est la procédure principale.
C, commence toujours par lire cette procédure.
Ce programme est vide, et ne fait rien, bien évidemment.
C’est quoi le void ? Void veut dire « vide ». Le premier « void », juste avant le nom « main », correspond à ce qui peut sortir de la procédure. Le second « void », après le nom « main », entre parenthèses, correspond à ce qui peut entrer dans la procédure. Dans les deux cas, ici, rien ne rentre dans la procédure, et rien n’en sort.



De base C ne connaît quasiment aucune instruction. Les instructions que nous allons utiliser dans notre programme sont dans des fichiers de définitions « header files » qui portent l’extension « .h »
Ces fichiers doivent être inclus dans le programme pour pouvoir être utilisées. Cela permet au compilateur d’aller rechercher le code de ces instructions dans les bibliothèques.
Ici, pour l’exemple, nous utilisons uniquement l’instruction Print, pour afficher un message. Cette instruction est définie dans « msx_fusion.h »
Une fois compilé, ce programme affichera le texte « Hello MSX Wolrd » sur l’écran de MSX-DOS.

Pour ce qui nous intéresse aujourd’hui, nous avons besoin de « msx_fusion.h », « VDP_graph2.h », et « vdp_sprites.h »




Puis nous définition les sprites dans des variables globales, ainsi que nos deux premières variables, x, y, dir_x et dir_y.
Les variables en C ont différentes portées. Elles peuvent être globales, c’est à dire accessibles partout depuis n’importe quelle procédure, ou locales, c’est à dire cantonnées à une procédure. Tout dépend de l’endroit où elles sont déclarées. Une variable déclarée en dehors d’une procédure, comme ici, au début de programme est dite globale. Nous en reparleront un peut plus tard.
Nous déclarons ici plusieurs variables, et deux tableaux.
« board_pattern » et « ball_pattern » sont des tableaux, on les reconnais grâce aux [].
Un tableau est un ensemble de données indexées. Un sprite est défini par 8 lignes de 8 bits (0 ou 1). Ils sont définis ici au format binaire, mais on aurait aussi pu les définir par leur équivalent décimal, ou hexadécimal ; mais le binaire permet de se rendre compte de la forme du sprite immédiatement.

« ball_pattern » est donc un tableau de « Unsigned Char ». « Unsigned Char » c’est le type de variable, cela correspond à un nombre entre 0 et 255.
On aurait pu le définir comme un tableau d’entiers, c’est à dire des « Int » un nombre entre - 65535 et +65535, dans ce cas on aurait écrit « static const int ball_pattern[] ».
Le terme « static » (optionel) indique ici que la variable n’est visible que dans le fichier dans lequel il est déclaré, et « const » veut dire que le tableau ne peut pas être modifié, les valeurs sont donc constantes.
Les variables « x » et « y » vont nous servir à déterminer la position du sprite de la balle, tandis que « dir_x » et « dir_y » vont définir les directions X et Y dans laquelle la balle se déplace.

Continuons dans notre procédure Main.



Nous déclarons aussi ici 2 tableaux, « BallColors » et « BoardColors ». Ces tableaux étant déclarés dans « Main » sont dits, « locaux » et ne sont donc accessible que depuis la procédure « main ». Sur MSX2 chaque ligne d’un sprite peut avoir une couleur différente. Chacune des 8 données du tableau est donc la couleur correspondant à une ligne du sprite. Les données sont données ici en décimal.
Avec le compilateur SDCC, les déclarations de variables doivent toujours se faire au début de la procédure.
Aux lignes 44 à 47 nous définissions les valeurs de départ des variables globales.

Puis nous préparons l’écran, on défini les couleurs de base, puis on passe en mode Screen 8 (256 Couleurs), pour lequel ont défini aussi les couleurs d’écran (Couleur d’affichage texte, couleur de fond d’écran, couleur de bord d’écran), Enfin on s’assure que l’écran est vide en l’effaçant d’un Cls(). Ensuite, on désactive le son des touches, et on passe le VDP en mode 60Hrz (Optionnel).
Aux lignes 57 à 61, nous testons si le MSX sur lequel le programme est lancé est un MSX Turbo-R ou pas. Si c’est le cas, on passe son micro-processeur R800 en mode Z80, car sinon, l’animation serait trop rapide.
Vous rencontrez ici, votre première condition. La fonction « ReadMSXtype() » renvoie une valeur 0,1,2 ou 3, cette dernière valeur correspondant à un Turbo-R. Si la condition est vraie alors on exécute la fonction ChangeCPU(0), sinon on passe à la suite.
A partir de la ligne 64, on défini nos sprites. On s’assure qu’aucun sprite ne soit déjà défini, grâce à « SpriteReset ()», et on défini une taille de sprite de 8x8 pixel (SpriteSmall() ).
Enfin, on défini nos « Patterns » de sprite, c’est à dire leur formes, grâce aux variables tableaux définis plus haut.
Le pattern n°0 sera celui de la balle, et le Pattern n°1 sera celui d’un segment de notre raquette. Un seul sprite de 8 pixels de long est trop petit pour une raquette digne de ce nom, nous allons donc utiliser 3 segments de 8 pixels, c’est à dire 3 sprites pour constituer la raquette.
De la même façon que nous avons défini les patterns, on va définir les couleurs des 4 Sprites que nous allons utiliser.
Vous aurez remarqué que chaque fin d’instruction en C se termine par un « ; »
Pour une bonne lecture du programme on n’écrit qu’une seule instruction par ligne, mais on pourrait ajouter des instructions à la suite, séparées par des « ; », mais cette pratique est totalement déconseillée ; la longueur du listing n’a aucune incidence sur la taille finale du programme une fois compilé.

Nous sommes maintenant prêt pour la suite.
Pour réaliser un bon programme, il faut décomposer les taches que celui-ci doit faire. Chaque tache doit alors devenir une routine du programme.
Pour l’instant nous voulons faire rebondir la balle sur les 4 bords de l’écran, nous avons donc besoin d’une routine qui calcule où doit être affiché la balle, et une routine qui affiche la balle à l’écran.



Les routines que nous ajoutons doivent se trouver au dessus de la routine « Main ».
« Main » étant généralement la dernière routine visible dans un listing C. La raison en est simple, on appel les routines depuis « Main », elles doivent donc être connues et déclarées avant d’être appelées
A part la routine « Main » qui ne peut pas changer de nom, vous êtes libre d’appeler les autres routines, comme bon vous semble. Toute fois un peut de méthode ne fait pas de mal.
Pour ma part, toutes mes routines commencent par « FT_ », ceci me permet d’identifier rapidement mes propres routines vis à vis des fonctions, ou des routines que j’aurais pu importer d’ailleurs.
Regardons la routine « void FT_CalcPosition (void) », elle ne renvoie aucune valeur, et aucune variable ne rentre dans la routine, car elle va uniquement utiliser des variables globales.
Lignes 49 à 52, on va tester si la position de la balle est supérieur à 247 ou inférieur à 1.
(Notez que le OU s’écrit avec un double « pipe » || )
Ce qui correspond à la chose suivante : Si la balle touche le bord d’écran droit ou le bord d’écran gauche. On a vu que si cela arrivait alors on devrait inverser la direction X de la balle.
C’est ce que réalise :  dir_x * = -1 
Dans le cas où « dir_ »x est égal à 1, si on le multiplie par -1, « dir_x » vaudra : -1
Dans le cas où « dir_x » est égal à -1, si on le multiplie par -1, « dir_x » vaudra alors : 1
On procède de la même façon avec « dir_y », dans le cas ou la balle touche les bords d’écran haut ou bas. Remarquez que j’ai volontairement fait rebondir la balle non pas tout en bas de l’écran, mais à la coordonnée Y=187, car nous allons placer la raquette du joueur à ce niveau.
Enfin, on incrémente les coordonnées X et Y de la balle avec les valeurs de « dir_x » et « dir_y », ce qui a pour effet de déplacer la balle.
Précisons que : x +=dir_x est une façon d’écrire x=x+dir_x


La fonction void FT_Sprites(void), est pour le moment toute simple, elle se contente d’afficher la balle, le sprite n°0 avec le Pattern n°1, aux coordonnées x et y, avec la couleur n°10.
(La couleur n’est pas utilisée ici, car nous somme sur MSX2. L’instruction « PutSprite » étant la même pour MSX1 et MSX2, il convient donc d’indiquer une couleur même si celle-ci n’est utilisée)

Bien, il nous manque une dernière petite chose pour faire fonctionner tout ceci … Retournons dans « Main »



Nous avons besoin d’une boucle ! Sans une boucle, le programme se terminerai au bout de la dernière ligne. Ici nous créons une boucle « While » infinie.
While (1), signifie dans notre langage humain, tant que 1 est vrai, ou bien, tant que 1 vaut 1. « 1 », n’est pas une variable, c’est un chiffre, sa valeur ne peut jamais changer, donc le test est toujours vrai, don la boucle ne s’arrête jamais, le programme continuera de fonctionner tant qu’une cause extérieure ne vient pas l’arrêter... C’est bien se que nous voulons dans un jeu…
Nous verrons plus tard, comment gérer les bouches While. Pour l’instant contentons nous de voir que nous appelons les routines « FT_CalcPosition() ; » puis « FT_Sprites() ; » en continue… La balle va donc rebondir sur les bords de l’écran jusqu’à la fin des temps  !

Listing complet à ce stade : Breaker_Tutorial-Step-1.c

Je vous laisse digérer ça, et on passera à la suite ... :siffle :D
Edité par ericb59 Le 16/01/2019 à 14h52


banniere-ericb59e
Site web    
Fabf Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++

Inscrit le : 24/08/2010 à 20h55

Messages: 4833

Le 16/01/2019 à 14h46
ericb59 :







J'ai pas encore pris le temps d'installer Fusion-C mais j'ai bien étudier le listing.
Je trouve la partie définition des patterns et couleurs des sprites étranges.

Pourquoi c'est pas comme ça ?
// Creating Sprites
SpriteReset();
SpriteSmall();
SetSpritePattern(0, ball_pattern,8);
SetSpritePattern(1, board_pattern,8);
SetSpritePattern(2, board_pattern,8);
SetSpritePattern(3, board_pattern,8);

SC8SpriteColors(0,BallColors);
SC8SpriteColors(1,BoardColors);
SC8SpriteColors(2,BoardColors);
SC8SpriteColors(3,BoardColors);
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 16/01/2019 à 16h24
Je pense que tu confonds le "sprite" et le "pattern".

Pour le jeu nous avons besoins de 4 Sprites, mais seulement deux patterns (formes).

SetSpritePattern défini en mémoire un numéro de pattern pour une forme donnée. Les sprites peuvent utiliser n'importent quel pattern.

Si tu regarde l'instruction PutSprite
PutSprite ( Sprite_no, pattern_no, x , y , color);
donc
PutSprite ( 1, 1, x , y , color); va afficher le sprite n°1 avec la forme n°1
PutSprite ( 1, 0, x , y , color); va afficher le sprite n°1 avec la forme n°0

Dans l'instruction PutSprite le dernier paramètre est la couleur du sprite, mais ce paramètre n'est pas fonctionnel sur un mode écran MSX2, il ne fonctionne que sur MSX1.
Sur MSX2, chaque ligne d'un sprite peut avoir une couleur différente.
C'est pourquoi on doit définir chaque ligne de chaque sprite avec l'instruction SC8SpriteColor.


banniere-ericb59e
Site web    
Visiteur

Vagabond

Rang

Avatar

Message : 0

Le 16/01/2019 à 17h00
Eric, j'ai une question au sujet de la fonction srand :hum

A priori srand devrait prendre en compte un paramètre comme "graine" pour que rand() ne donne pas la même suite de valeurs à chaque exécution du programme.
Mais si on fournit un paramètre à srand on a "error 101: too many parameters".

Comment fournir la graine du hasard pour rand() ?

D'autre part comment utilise-t-on la macro RAND_MAX pour déterminer la borne supérieure des valeurs fournies par rand() ?




   
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 16/01/2019 à 19h37
Code C :
 
//
// Fusion-C
// Pseudo Random Number
//
#include <stdlib.h>
#include <stdio.h>
#include "fusion-c/header/msx_fusion.h"
char FT_RandomNumber (char a, char b)
{
    char random;
 
    random = rand()%(b-a)+a;  // Random number between a and b-1
    return(random);
}
void main(void) 
{
    char n,i;
    srand(5);
    for(i=0;i < 10; i++)
    {
        n=FT_RandomNumber(1,100);
          printf("%d,",n);
      }
      Exit(0);
}
 
 


Ce code fonctionne parfaitement.
Cela dit, même avec SRAND, on est toujours en pseudo-aléaoire.
A chaque démarrage du programme la même liste de nombres sort.

------------- UPDATE -------------------
Code C :
 
//
// Fusion-C
// Pseudo Random Number
//
#include <stdlib.h>
#include <stdio.h>
#include "fusion-c/header/msx_fusion.h"
TIME tm;
char FT_RandomNumber (char a, char b)
{
    char random;
 
    random = rand()%(b-a)+a;  // Random number between a and b-1
    return(random);
}
void main(void) 
{
    char n,i;
    GetTime(&tm);
    srand(tm.sec);
    for(i=0;i < 10; i++)
    {
        n=FT_RandomNumber(1,100);
          printf("%d,",n);
      }
 
      Exit(0);
}
 
 


Par contre on peut améliorer le random en ajoutant un nombre variable dans le SRAND.
Ici j'utilise l'horloge du MSX-DOS. Je mets le compteur des secondes dans le SRAND, (soit un nombre entre 0 et 59)
Edité par ericb59 Le 16/01/2019 à 20h33


banniere-ericb59e
Site web    
Visiteur

Vagabond

Rang

Avatar

Message : 0

Le 17/01/2019 à 08h07
ericb59 :

Ce code fonctionne parfaitement.

Merci, ça marche :) en fait il me manquait #include <stdlib.h>
Sans stdlib, srand est connu mais n'accepte pas de paramètre.

ericb59 :

Par contre on peut améliorer le random en ajoutant un nombre variable dans le SRAND.
Ici j'utilise l'horloge du MSX-DOS. Je mets le compteur des secondes dans le SRAND, (soit un nombre entre 0 et 59)

Oui, c'est ce que j'ai fait aussi.

Sinon je parlai de la macro RAND_MAX parcequ'elle apparaît dans la documentation Fusion-C.
   
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 17/01/2019 à 08h15
La macro permet de définir la borne supérieure du RAND.
Par défaut elle est définie comme tel : #define RAND_MAX 32767


banniere-ericb59e
Site web    
Visiteur

Vagabond

Rang

Avatar

Message : 0

Le 17/01/2019 à 08h31
ericb59 :
La macro permet de définir la borne supérieure du RAND.
Par défaut elle est définie comme tel : #define RAND_MAX 32767


J'ai essayé de redéfinir avec
Code C :
#define RAND_MAX 100
mais cela n'a pas fonctionné. J'obtiens des valeurs entre 0 et 32767.
Mais ce n'est pas grave, ta fonction RandomNumber par exemple permet de gérer les bornes.
   
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 25/01/2019 à 15h03
Tutorial : Coder un Casse Briques. Partie 3
Interaction niveau CP !


Si vous testez le programme vous constaterez que l’animation est extrêmement rapide, on va donc la ralentir un peu pour y voir quelque chose.



Ajoutez cette routine au dessus des autres. Et voyons comment elle est construite.
Cette fois une nouveauté, il n’y a pas « void » après le nom de la routine, cela veut dire que la routine à besoin d’un paramètre pour fonctionner. Ce paramètre est un entier (int) dont le nom de variable s’appel « cicles ».

Lorsque l’on va appeler cette fonction à partir du reste du programme on écrira donc : « FT_wait(5) ; » par exemple. Ce qui aura pour effet d’attendre 5 cycles processeur avant de continuer le programme.
Dans la routine, il y a une boucle « for » dont vous pouvez aisément comprendre le fonctionnement. La variable « i » qui est définie au tout début de la routine, à une valeur égale à 0, ; elle est incrémentée de 1 à chaque itération, jusqu’à ce que « i » soit égal à la valeur de « cicles », dans notre exemple : 5. Une fois cette valeur atteinte par « i », la boucle s’arrête et on passe à la suite du programme.

Maintenant ajoutons l’appel de cette fonction dans notre boucle principale.



Maintenant si vous recompilez, et relancer le programme, vous devriez constater que la balle bouge beaucoup moins vite.

Ajoutons maintenant une première fonction interactive ! La raquette et son déplacement par le joueur.
Commençons par ajouter les deux variables qui vont nous permettre de contrôler nos sprites de raquette.



Retournons donc tout en haut, là ou nous avons déclarer nos variables globales, et ajoutons, les déclarations de « px », et « py ».

Puis ajoutons la fonction FT_joy() à notre programme.



Nous déclarons ici dans la routine, deux variables locales, « joy » pour récupérer l’état du joystick et « trig » pour récupérer l’état du bouton du joystick.
JoystickRead(0) nous permet de lire les données du joystick 0, c’est à dire des touches fléchées du clavier. JoystickRead, renvois un chiffre entre 0 et 8 en fonction de la direction du joystick.
Pour le bouton, le fonctionnement est identique, TriggerRead renvois 0 ou 1 suivant que le bouton est enclenché ou pas.
Lignes 88 à 92, nous testons si on appui sur le bouton, qui est pour nous la barre espace. Si c’est le cas, on repasse en mode Screen 0, et on sort du programme.
Effectivement, nous n’avons pas besoin du bouton du joystick pour jouer au casse brique, pour ce tutorial, on utilisera donc la Barre Espace pour sortir du programme et retourner sous MSX DOS.
Lignes 94 à 108, nous testons la directions renvoyées par JoystickRead(0) ;
Nous n’utilisons pas pour faire ces tests, des « if » classiques, mais la commande « switch »
Cette commande nous permet d’avoir différents embranchements suivant la valeur d’une variable, ce qui est plus rapide que d’utiliser une série de « if » classiques.
Ici nous utilisons l’instruction « switch » avec la variable « joy ». Nous désirons contrôler uniquement deux directions, « droite » qui correspond la valeur 3, et gauche qui correspond à la valeur 7.

Comme vous pouvez le voir sur le listing, aux   lignes  96 et 103, on utilise « case <valeur> : » qui agit comme un test. Un « case » se termine par un « Break; »
Pour chaque direction nous testons avec un « if » classique si notre coordonnée n’est pas en dehors de l’écran, si on est bon, alors on incrémente ou on décrémente la variable « px » qui correspond à la coordonnée X de notre raquette.

Ajoutons maintenant l’affichage des sprites de notre raquette à la routine « FT_sprites »



Comme je l’ai indiqué plus haut, nous utilisons 3 sprites pour composer la raquette. La longueur de la raquette sera donc de 3x8 pixels de long, soit 24 pixels au total.
Nous affichons donc notre sprite n°1 avec le pattern 0 aux coordonnées « px » et « py ».
Puis notre sprite n°2 avec le pattern 0, aux coordonnées « px+8 » et » py », et enfin le sprite n°3 avec le pattern 0, aux coordonnées  « px+16 » et » py »



Il ne nous reste plus qu’un intégrer le contrôle de la raquette dans notre routine « main ».



D’abord on défini les valeurs par défaut, (au début du jeu) de notre raquette.
On défini « px » à 170, au milieu milieu de l’écran, et « py » à 185



Enfin, on intègre la routine « FT_joy() » dans notre boucle principale.
Si on recompile le programme, on peut maintenant déplacer la raquette de gauche droite, et de droite à gauche sur toute la largeur de l’écran.

Listing complet à ce stade : Breaker_Tutorial-Step-2.c




banniere-ericb59e
Site web    
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 25/01/2019 à 15h25
Tutorial : Coder un Casse Briques. Partie 4
Et le maçon pose les briques


Il est temps maintenant de nous occuper des fameuses briques…
Nos briques feront 16 pixels de long, et 8 pixels de haut. Nous afficherons 4 lignes de briques sur la partie supérieure de l’écran.

L’écran de notre MSX2 a une résolution de 256 pixels de large, sur 192 pixels de haut.
256 divisé par 16 = 16, nous pouvons donc afficher un maximum de 16 briques par lignes.
192 divisé par 8 = 24. Hypothétiquement nous pourrions afficher 24 lignes de briques pour remplir l’écran, mais nous nous contenterons de 4 lignes pour pouvoir jouer !

Il existe plusieurs méthodes pour gérer les briques dans le jeu, ici nous allons créer une représentation en mémoire de notre mur de briques. Pour se faire nous allons utiliser une variable tableau à 2 dimensions. Pour vous représenter la chose, pensez à une feuille de calcul sur Excel, qui est composée de CY lignes et CX colonnes. Dans notre exemple, nous auront donc 24 lignes set 16 colonnes.



Nous allons donc créer une variable tableau en mémoire qui ressemble au schéma ci-dessus.
Appelons cette variable, « playfield » et déclarons-la en haut de notre programme avec les autres variables globales.



Comme vous le constatez, c’est très facile de déclarer notre tableau de 24 lignes et 16 colonnes. Le tableau est de type « unsigned char » car il ne va contenir que des entiers entre 0 et 255. On pourrait déclarer le tableau en « Int » c’est à dire des entier (entre 0 et 65535), mais cela prend plus de place en mémoire, et ça ne sert à rien de gaspiller de la mémoire qui peut servir pour autre chose.

Nous allons maintenant créer deux routines qui vont remplir notre tableau. Nous allons placer dans chaque case, le chiffre « 1 » si on y place une brique et le chiffre « 0 » si on ne place rien dans cette case.



La première routine, « FT_EmprtyField », va remplir le tableau avec des Zéro, histoire d’être certain que le tableau est bien vide, accessoirement, cette routine peut nous servir à vider le tableau entre 2 niveaux de jeu différents par exemple.
Nous utilisons 2 boucles « for » imbriquées, la première nous permet d’aller des lignes 0 à 23, et la seconde nous permet d’aller des colonnes 0 à 15.
Dans chaque case du tableau on met un 0.


La seconde routine « FT_PutBricks », fonctionne sur le même principe, sauf que l’on ne va parcourir le tableau à partir de la ligne 1 jusque 4, et de la colonne 0 à 15. On place le chiffre « 1 » dans chaque case, ce qui au final nous donne la représentation en mémoire du schéma de tableau indiqué plus haut.

Maintenant il nous faut afficher nos briques à l’écran ; nos avons donc besoin d’une routine d’affichage, que nous allons appeler « FT_ShowBricksWall »



Nous avons toujours nos deux boucles imbriquées qui nous permettent de lire chaque case du tableau. Avec un test, nous contrôlons que nous voulons bien une brique (si le contenu de la case du tableau est égal à 1), alors on procède à l’affichage de la brique, sous la forme d’un rectangle.
Ici nous pouvons voir la relation entre le tableau et l’écran. Chaque case du tableau représente 16 * 8 pixels sur l’écran. La coordonnée X du coin supérieur gauche de la brique est égal à i*16, et sa coordonnée Y est égal à j*8.
On trace donc des rectangles plein de 14 pixels de long, sur 6 de haut, que l’on recouvre par un rectangle blanc pour réaliser les bords des briques. Chaque brique est séparée des autres par 1 pixel qui reste noir.
Voici ce que nous devons avoir à l’écran à cette étape :



Voici ce que nous devons avoir à l’écran à cette étape : Listing complet à ce stade : Breaker_Tutorial-Step-3.c




banniere-ericb59e
Site web    
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 25/01/2019 à 15h34
Tutorial : Coder un Casse Briques. Partie 5
Comment casser des briques avec une balle !


Notre tableau « playfield », va aussi nous servir à tester si notre balle touche une brique ou pas, mais avant cela, il nous faut nous pencher sur la façon dont nous allons tester si la balle percute une brique, et comment elle la percute.

Nous l’avons vu au début, pour déterminer dans quelle sens la balle rebondie lorsqu’elle touche quelque chose, nous avons besoin de savoir si la balle percute un mur sur un coté verticale, ou horizontal.
Lorsqu’il s’agit de savoir quel bord de l’écran la balle touche c’est facile, nous l’avons vu plus haut, grâce aux coordonnées de la balle elle même.
Mais quand il s’agit de savoir de quel coté la balle percute une brique, nous ne pouvons pas le savoir grâce aux coordonnées de la balle.



Nous allons donc créer 4 points (virtuels) autour de notre balle, que nous allons tester à tour de rôle pour connaître la façon dont la balle à percuté un objet.

Le dessin si dessous représente le sprite de notre balle. Les coordonnées X,Y que nous utilisons pour placer la balle à l’écran sont les coordonnées (0,0) de notre sprite (la case peinte en noir).



Ce point ne nous permet pas de faire tous nos tests, nous allons donc utiliser 4 autres coordonnées autour de la balle, comme indiqué sur le schéma suivant :



Ces quatre points vont nous permettre de déterminer comment la balle touche un obstacle.
En effet, si un obstacle est détecté sur le point P1, alors on saura que la balle touche un obstacle horizontal, idem si un obstacle est détecté sur le point P3.
De la même façon on pourra déterminer que la balle a touché un obstacle verticale si, les points P2 ou P4 détectent une collision.

Nous avons maintenant besoin de mettre ceci en pratique dans notre programme.
Commençons par ajouter quelques variables globales, dont nous verrons l’utilité par la suite.
Nous avons besoin de : « brx », « bry » et « verif_flag ».



Nous avons déjà une routine « FT_CalcPosition », nous allons l’utiliser en la modifiant comme ceci :



Nous ajoutions quelques tests à la suite de ceux effectués pour savoir si la balle atteint l’un des bords de l’écran.
Nous devons effectuer 4 tests, sur les différents points définis plus haut, autour de la balle. Toute fois nous n’allons pas effectuer ces tests tout le temps. En effets, quand la balle ne se situe pas dans la zone de l’écran où se situent les briques, cela ne sert à rien de tester si la balle rencontre une brique ! C’est pourquoi, nous n’effectuons ces tests que si la balle se situe dans la zone des briques, c’est à dire si « y » est compris entre 0 et 44 pixels, ou plus simplement si y est inférieur à 44.
La première chose que nous faisons c’est de mettre « verif_flag » à 0.
Cette variable nous permet de tester les autre points de tests, que si la balle n’est pas déjà en collision avec une brique.
Nous renvoyons nos tests sur une routine « FT_verif » que nous allons voir par la suite.
« FT_verif » à besoin de 3 paramètres entrant, les coordonnées x et y à tester et le numéro du point de contrôle que nous testons, entre 1 et 4.
Nous réalisons 4 tests, pour les 4 points de contrôle.



Créons maintenant la routine « FT_verif ». Elle ne renvois aucune donnée, mais on y rentre 3 entiers, « x », « y » et « point », ces variables entrantes, sont automatiquement déclarées comme des variables locales de la routine.
Nous avons aussi besoin de 2 variables locales « fx » et « fy », ces 2 variables servent à faire le lien entre les coordonnées à l’écran, et les coordonnées dans le tableau « playfield »
En effet, la position « x » de la balle divisée par 16 nous donne la coordonnée « X » dans tableau « playfield », et la coordonnée « y » de la balle divisée par 8, nous donne la coordonnée « Y » dans le même tableau.
Exemple, si notre balle se trouve aux coordonnée (118,38)
120 / 16 = 7 (arrondi)
38 / 8 = 4 (arrondi)
Pour savoir s’il y a une brique à l’endroit où se trouve la balle, on regarde donc dans le tableau dans la case (7,4)
Et c’est bien ce que fait notre routine. Si une brique est détectée à l’endroit de la balle, on met la variable « verif_flag » à 1, ce qui évite de poursuivre les tests sur les autres points de contrôle (dans FT_CalcPosition), puis on retire la brique du tableau, en mettant 0 dans cette même case, et enfin la routine vérifie quel point de contrôle est à l’origine de la détection de la brique, pour appliquer à la balle la bonne direction de rebond.
Enfin, on met dans les variables « brx » et « bry », les coordonnées trouvées, qui vont nous permettre d’efface la brique de l’écran, dans un second temps.
Il ne reste plus qu’à modifier la routine « FT_sprite » dans le but d’ajouter l’effacement de la brique.



On aurait pu effacer la brique de l’écran dans la routine « FT_Verif », mais il est préférable de rassembler au même endroit toutes les modifications graphiques, cela évite d’avoir à faire appel au processeur video du MSX à de multiples reprises, et donc, de diminuer la vitesse du jeu.
La modification est simple, on ajoute un test, pour savoir si « brx » et « bry » sont différents de 0. Si c’est le cas, c’est que l’on a une brique à effacer. On utilise la commande « Rect » pour dessiner un rectangle noir aux coordonnées indiquées par « brx » et « bry » et ainsi faire disparaître la brique en question. Une fois que cela est fait, on repasse ces deux variables à 0.
A cette instant, le casse brique est presque fonctionnel. Il ne manque plus qu’à gérer les rebonds sur la raquette pour avoir un vrai jeu.

Listing complet à ce stade : Breaker_Tutorial-Step-4.c






banniere-ericb59e
Site web    
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 25/01/2019 à 15h42
Tutorial : Coder un Casse Briques. Partie 6
Variations autours d'une raquette


Nous allons introduire une légère variation aux rebonds. Lorsque la balle touche la raquette en son centre, on va définir un rebond classique, mais quand la balle touche la raquette sur l’une de ses extrémités droite ou gauche, on va renvoyer la balle dans la direction d’où elle vient.





Nous allons une nouvelle fois ajouter quelques lignes de code dans la routine « FT_CalcPosition ».




Nous ajoutons d’abord une variable locale « touch » en haut de notre procédure, puis de la ligne 138 à 153 nous écrivons se qui va servir à contrôler le rebond sur notre raquette.
Le premier test « if » nous permet de faire nos tests que si la balle se situe dans la zone où se trouve la raquette. C’est à dire si la balle et entre 180 et 185 sur l’axe Y, et si la position « x » de la balle est dans le voisinage de la raquette.
Si c’est le cas, on met dans la variable « touch », la différence entre la position « x » de la balle et la position « px » de la raquette. « touch = x – px », ce qui nous donne une position relative de la balle par rapport à la raquette.
Le test de la ligne 143, nous permet d’identifier si la collision à lieu sur les extrémités de la raquette, si c’est le cas, on inverse la direction « dir_x » de la raquette, ce qui aura pour effet de la renvoyer dans la direction d’où elle vient.
Si la balle touche une autre partie de la raquette, et bien on ne fait rien, enfin, rien de plus que d’inverser la direction « dir_y » de la balle, pour réaliser le rebond.
Par la même occasion, on change la couleur des bordures de l’écran, pour créer un « flash » lumineux, et on demande un Beep sonore.

Voilà, cette fois nous avons le prototype d’un Casse Brique totalement fonctionnel.

Listing complet à ce stade : Breaker_tutorial-Step-final.c


Si vous trouver l’action trop lente, il vous suffit modifier la routine « FT_wait » qui se trouve dans la boucle principale, indiquez lui un paramètre à « 0 »
« FT_wait(0); »

Pour avoir un jeu complet, il y en encore un peut de travail, ajouter un score, un nombre de vie, un Game Over, un écran de démarrage plusieurs configurations de murs de briques, de vrais graphismes en bitmap …
Vous avez tout le loisir de continuer à compléter ce tutoriel …

J'ai voulu par ce tutorial montrer la facilité d'usage du C avec FUSION-C pour MSX, et montrer aux débutants qu'il n'est jamais trop tard pour s'y mettre. Programmer c'est aussi amusant que de jouer ... En tout cas c'est mon avis !

Aller ..

Codez bien, et amusez vous bien.
;)



F I N

Edité par ericb59 Le 27/01/2019 à 08h14


banniere-ericb59e
Site web    
Monos Membre non connecté

Touriste

Rang

Avatar

Inscrit le : 28/09/2018 à 17h10

Messages: 101

Le 27/01/2019 à 07h22
Partie 6 pour le dernier ?
Les liens Breaker_tutorial-Step-final.c nous donnes sur un 404 !
Bon tuto pour démarrer le C.


Il faut donner à manger à nos machines !
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5469

Le 27/01/2019 à 08h15
@Monos : Effectivement ! J'ai corrigé !


banniere-ericb59e
Site web    
Répondre
Vous n'êtes pas autorisé à écrire dans cette catégorie