Création d'un space invaders sur ESPLGE de Corax

This tuto is destined to french users but i think i’ll translate it later…
Bonjour à tous, je vous écris ici mes premiers essais de création d’un jeu en utilisant le formidable ESPLGE (https://corax89.github.io/esp8266Game/index.html)

Space Invaders スペースインベーダー est un jeu vidéo développé par la société japonaise Taito, sorti en 1978 sur borne d’arcade. Il s’agit d’un shoot 'em up fixe. Tomohiro Nishikado à conçu et programmé le jeu en s’inspirant de plusieurs médias populaires de l’époque tels que Breakout ou La Guerre des mondes.
Considéré comme le premier archétype du shoot them up, il est aussi l’un des titres les plus influents et célèbres de l’histoire du jeu vidéo.
Son principe est de détruire des vagues d’aliens au moyen d’un canon laser situé à l’avant d’un vaisseau spatial se déplaçant horizontalement en bas de l’écran. Dans les airs, des rangées d’aliens se déplacent latéralement tout en se rapprochant progressivement du sol et en lançant des missiles.
L’objectif est de détruire avec le canon laser une vague ennemie, qui se compose de cinq lignes de onze aliens chacune, avant qu’elle n’atteigne le bas de l’écran. Le joueur gagne des points à chaque fois qu’il détruit un envahisseur. Le jeu n’autorise qu’un tir à la fois et permet d’annuler ceux des ennemis en tirant dessus. La vitesse et la musique s’accélèrent au fur et à mesure que le nombre d’aliens diminue. L’élimination totale de ces derniers amène une nouvelle vague ennemie plus difficile, et ce indéfiniment. Le jeu ne se termine que lorsque le joueur perd, ce qui en fait le premier jeu sans fin

Les aliens tentent de détruire le canon en tirant dessus pendant qu’ils s’approchent du bas de l’écran. S’ils l’atteignent ou arrivent jusqu’au sol, ils ont réussi leur invasion et le jeu est fini. De temps en temps, une soucoupe spatiale apparaît tout en haut de l’écran et fait gagner des points bonus si elle est détruite. Des bâtiments destructibles, que nous appellerons “Bunkers” permettent au joueur de se protéger des tirs ennemis. Ces défenses se désintègrent progressivement sous l’effet des projectiles adverses et de ceux du joueur. Le nombre de bâtiments n’est pas le même d’une version à l’autre mais il était de 4 dans la versions d’arcade.

Tout d’abord écrivons le Cahier des charges fonctionnel, c’est à dire la liste des fonctionnalités à implémenter et autres spécificités utiles pour comprendre le fonctionnement attendu du jeux. Nous allons reprendre tout une partie des fonctionnalités de l’original mais allons également faire des modifications pour l’adapter à l’Espboy et le faire correspondre à ce que l’on souhaite.

Éléments fondamentaux

L’interface est composée des éléments suivants:

  • le vaisseau du joueur; (Fait)
  • une rangée de bunker (nous en créerons 3 et non 4 comme dans l’original par rapport à la taille réduite de l’écran); (Fait)
  • les vaisseaux ennemis; (Fait)
  • les missiles; (Fait)
  • les vaisseaux, les bunkers et les missiles sont représentés à l’écran par des sprites. (Fait)
  • le nombre de vies restantes du joueur. (Fait)

Déplacement du joueur :

  • Le vaisseau du joueur se déplace uniquement horizontalement. (Fait)
  • Le vaisseau du joueur ne peut pas sortir de l’écran. (Fait)
  • Le déplacement se fait au moyen des touches flèche gauche et flèche droite et il tire lorsque l’on appuie sur le bouton “A” (Fait)

Bloc d’ennemis :

  • Les ennemis sont organisés en un bloc est constitué de plusieurs lignes d’ennemis. (Fait)
  • Chaque ligne d’ennemis comprend un ou plusieurs vaisseaux du même type. (Fait)
  • Le bloc arrive par le haut de la fenêtre et se déplace alternativement de gauche à droite et de droite à gauche. (Fait)
  • Le bloc change de direction dès qu’un des vaisseau du bloc arrive au bord de l’écran. (Fait)
  • Lorsque le bloc change de direction, il se décale vers le bas. (Fait)
  • Lorsque le bloc change de direction, sa vitesse de déplacement augmente. (non implémenté)

Missiles :

  • Les missiles se déplacent en ligne droite verticale: de haut en bas si tiré par un ennemis et de bas en haut si c’est un missile du joueur. (Fait)
  • Les missiles des ennemis peuvent entrer en collision avec les bunkers, le vaisseau du joueur et le missile du joueur (pas de friendly fire). (Reste à faire: missile / missile)
  • Les missiles du joueur peuvent entrer en collision avec les bunkers, les vaisseaux ennemis et les missiles des ennemis.
  • Les missiles sont détruits si ils sortent de l’écran. (Fait)
  • Les ennemis tirent aléatoirement. (Fait)
  • La fréquence de tire des ennemis augmente lorsqu’ils se rapprochent du bas de l’écran.
  • Un vaisseau ne peut tirer qu’un seul missile à la fois et doit attendre un certain temps avant de pouvoir tirer à nouveau (le temps du refroidissemnt: cooldown). (Fait)

Vie et mort :

  • Le joueur, les ennemis et les missiles possèdent chacun un nombre de vies. (Fait)
  • Un élément dont le nombre de vie est inférieur ou égal à zéro est détruit. (Fait)

Collision missile-vaisseau :

  • Un missile est en collision avec un vaisseau si un pixel de son sprite est sur un pixel du sprite du vaisseau. (Fait)
  • Lorsqu’un missile touche un vaisseau, les nombres de vies du missile et du vaisseau sont décrémentés du nombre de vies du missile et du vaisseau. (Fait)

Collision missile-bunker :

  • Un missile est en collision avec un bunker si un pixel de son sprite est sur un pixel du sprite du vaisseau. (Fait)
  • S’il y a collision, il y a explosion du missile et les pixels de la partie où à eu lieu l’explosion sont retirés du sprite du bunker: ils deviennent transparents et ne peuvent plus participer à des collisions. (Fait)
  • Le nombre de vie du missile est décrémenté. Dans les faits, le missile n’ayant qu’une vie, il est détruit (Fait)

Collision missile-missile :

  • Deux missiles qui entre en collision se détruisent mutuellement (Non implémenté)

Victoire et défaite, la première des conditions suivantes qui survient met fin à la partie ou au niveau selon les cas précisés ci dessous:

  • Le joueur gagne le niveau s’il détruit tous les vaisseaux ennemis. (Fait)
  • Le joueur perd s’il ne lui reste plus de vie. (Fait)
  • Un vaisseau touché par un tir Alien perd une vie. N’ayant plus de vie, il est détruit, s’il reste des vies au joueur, on les décrémente pour faire apparaître un nouveau vaisseau (Fait)
  • Le joueur perd une vie si les ennemis rentrent en collision avec son vaisseau. (Fait)
  • A la fin de la partie, le joueur peut relancer une nouvelle partie (Fait)

Pause :

  • Tant que la partie n’est pas terminée le joueur peut mettre le jeu en pause en appuyant sur “B”: pendant la pause tous les déplacements et donc toute la dynamique du jeu est interrompue. (non implémenté)
  • Lorsque le jeu est en pause, le joueur peut quitter la pause avec le même bouton B.

Addons

Les addons seront pris en compte uniquement si tous les éléments fondamentaux fonctionnent. Les addons ne sont pas complètements spécifiés et vous êtes libres de développer autour des thèmes proposés ou de proposer de nouveaux thèmes.

Affichage avancé :

  • Les sprites des différents éléments sont animés. (Fait)
  • Les actions de collisions génèrent des effets visuels (par exemple, explosion). (Fait)

Son :

  • Le jeu est accompagné d’une musique en fond sonore. (Fait)
  • Les différentes actions produisent l’émission de sons spécifiques. (Fait)

Système de niveaux :

  • Le jeu comporte plusieurs niveaux. (Fait)
  • La difficulté augmente avec les niveau (Fait)

Scoring

  • Le jeu propose un système de high score avec le nom du joueur, son score et le niveau atteint (non implémenté)
  • Les aliens rapportent un certain nombre de points en fonctions de leur type. Le type rapportant le plus de point étant celui situé le plus en haut. (Fait)
  • La soucoupe rapporte 200 points.(non implémenté)
  • Dans la première rangée (celle le plus en bas) les aliens rapportent 10 points, dans la seconde 20 points, 50 points dans la dernière. (Fait)
  • Le joueur gagne une vie tous les 500 points

Etape 1: Créer un alien qui va faire des allers-retours à l’écran

Je me rends sur la page de l’IDE, j’ai déjà un code source de chargé. Comme il n’a pas de titre, je ne sais pas du quel il s’agit et ça nous donne un première leçon: nous commenterons notre code…
En attendant, je fais un [CTRL] + [A] puis [Suppr] pour partir d’une feuille blanche

Je veux commencer par créer le sprite d’un Alien, du coup, je clique sur le bouton Sprite (1) puis je règle la largeur et la hauteur du sprite. J’ai choisi un sprite de 8 de large par 8 de haut. Ce qui est la taille standard.

J’ai fais d’autres petites réflexions sur les dimensions mais on verra ça plus tard. Pour le moment, terminons de dessiner notre sprite, puis copions et collons le code dans notre programme.

{
0x00, 0x05, 0x50, 0x00,
0x00, 0x55, 0x55, 0x00,
0x05, 0x55, 0x55, 0x50,
0x55, 0x05, 0x50, 0x55,
0x55, 0x55, 0x55, 0x55,
0x00, 0x50, 0x05, 0x00,
0x05, 0x05, 0x50, 0x50,
0x50, 0x50, 0x05, 0x05
};

02_sprite

Rajoutons tout de suite un peu de commentaires avant ce code

// Tuto space invaders - Part 1: Add a sprite and make it move on the screen

// Invader 8x8

Oui, je pense que c’est une bonne habitude à prendre, j’écris mes commentaires en anglais mais bon, si vous n’êtes pas à l’aise avec l’anglais, vous pouvez les mettre en français.

Nous avons une entête pour notre programme et pour notre sprite. Maintenant, il faut indiquer au programme comment s’appelle notre sprite afin de pouvoir l’appeler plus tard. Je n’ai pas fais compliqué, je l’ai appelé invader. C’est un tableau de caractères qui va recevoir les données que nous avons récupéré de l’éditeur de sprite.

Nous faisons simple pour le moment, sans structure ni objets, on pourra voir ça plus tard…
Reprenons le tuto disponible ici: http://segamiga.com/esplge/doc/#/samples/game_tutorial

On va initialiser les 3 procédures: la procédure init() que nous appellerons au démarrage du programme pour initialiser les valeurs. la procedure step() qui sera exécuté à chaque boucle du jeu et qui contiendra la logique du jeu. La procedure draw qui s’occupera de l’affichage des différents éléments et enfin la procédure principale main() qui est celle appelé au lancement du jeu.

Le squelette du programme est donc:

// global variables
void init(){
    // the code at this location will be executed only once when the game starts.
}

void step(){
    // code in this place will execute each frame
}

void draw(){
    //  this is where all objects are drawn
    delayredraw(); // waiting for frame drawing
}

void main(){ // required function for any program, execution starts with it
    init();
    while(1){ // infinite loop
        step();
        draw();
    }
}

On va donc initialiser 2 variables invader_x et invader_y avec la position initiale de l’alien (note invader) et à chaque boucle, on va le faire avancer à la vitesse que nous avons défini.
A chaque fois que le sprite atteindra un des bord, on inversera sa vitesse pour qu’il reparte dans l’autre sens.

En le complétant avec notre programme on obtient:

// Tuto space invaders - Part 1: Add a sprite and make it move on the screen


// define constantes
#define invader_w 	8
#define invader_h 	8
#define invader_init_speed 2


// Sprites
// Invader 8x8

char invader[] ={
0x00, 0x05, 0x50, 0x00,
0x00, 0x55, 0x55, 0x00,
0x05, 0x55, 0x55, 0x50,
0x55, 0x05, 0x50, 0x55,
0x55, 0x55, 0x55, 0x55,
0x00, 0x50, 0x05, 0x00,
0x05, 0x05, 0x50, 0x50,
0x50, 0x50, 0x05, 0x05
};


// define variables

// invaders variables
int invader_x;
int invader_y;
int invader_speed;


// initialisation of values
void init(){
	invader_x = 0;
	invader_y = 20;
	invader_speed = invader_init_speed;
	setframerate(30);
	clearscreen();	
	getsprite(1,invader);
	clearscreen();
}


// Step procedure to make the game evolve between each draw
void step(){
    invader_x = invader_x + invader_speed;
    if (invader_x > 120) invader_speed = -invader_init_speed;
    if (invader_x < 0)   invader_speed =  invader_init_speed;
}


// Draw procedure
void draw(){
    putsprite(1, invader_x, invader_y); // Draw the sprite of an invader
    delayredraw(); 						// wait until the frame is drawn. 
}


// Main procedure
void main(){
	init();
	while(1){	
       step();
       draw();		
	}
}

Et voilà, étape 1 atteinte: notre premier sprite se déplace infiniment de gauche à droite.

1 Like

Etape 2: animons notre Alien.

C’est très simple avec LGE, on met les 2 sprites l’un en dessous de l’autre dans notre tableau “invader”
On créé une variable que l’on appelle frame (mais on l’appelle comme on veut, mais toujours un nom qui aidera a retrouver à quoi elle sert). Comme on n’utilise que 2 images pour notre animation, on utilisera juste cette formule:
frame = 1 - frame;
Elle permet de basculer à chaque boucle cette valeur de 1 à 0
Nous modifions également notre procédure de dessin pour afficher cette animation en chargeant dans notre sprtie 1 l’image souhaitée avant de l’afficher.

Cela nous donne ce code:

// Tuto space invaders - Part 2: Anime a sprite


// define constantes
#define invader_w 	8
#define invader_h 	8
#define invader_init_speed 2


// Sprites
// Invader 8x8x2

char invader[] = {
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x05, 0x50, 0x50, 
0x50, 0x50, 0x05, 0x05, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x00, 0x00, 0x50, 
0x00, 0x50, 0x05, 0x00
};

// define variables

// invaders variables
int invader_x;
int invader_y;
int invader_speed;
int frame;      // We'll use 2 frames 0 and 1


// initialisation of values
void init(){
	invader_x = 0;
	invader_y = 20;
	frame = 0;
	invader_speed = invader_init_speed;
	setframerate(30);
	clearscreen();	
	getsprite(1,invader);
	clearscreen();
}


// Step procedure to make the game evolve between each draw
void step(){
    invader_x = invader_x + invader_speed;
	frame = 1 - frame;
    if (invader_x > 120) invader_speed = -invader_init_speed;
    if (invader_x < 0)   invader_speed =  invader_init_speed;
}


// Draw procedure
void draw(){
	getsprite(1, invader + frame * 32);
    putsprite(1, invader_x, invader_y); // Draw the sprite of an invader
    delayredraw(); 						// wait until the frame is drawn. 
}


// Main procedure
void main(){
	init();
	while(1){	
       step();
       draw();		
	}
}

Aller, on compile et on regarde la petite animation

1 Like

Etape 3: Faisons 3 lignes d’ennemies

Bon on a animé un envahisseur, nous ce que l’on voudrait c’est en animer plusieurs. Attention toutefois sur LGE, il y a une limite de 32 sprites. On en réserve un pour le vaisseau mais surtout on en garde pour les tirs (on pourrait faire autrement mais pour le moment on va utiliser les sprites).On va garder 10 sprites pour les tirs et 3 sprites pour les bunkers. Résultat, il ne nous reste “que” 18 sprites pour les envahisseurs. On va donc se restreindre à 3 lignes de 6 envahisseurs. C’est moins que ce que j’avais espéré mais plus tard, on essayeras de ne plus gérer les tirs sous forme de sprites (donc en gérant nous même les collisions mais on gagnera une ligne d’envahiseurs et on pourra en mettre 7 par lignes… (7x4 soit 28 sprites d’envahisseurs, d’ailleurs vous pourrez modifier les valeurs dans le programme ci dessous en mettant 4 lignes et 8 colonnes, mais si vous mettez plus, vous verrez des comportements bizarres du fait que l’on ait plus de 32 sprites).

Bon alors pour ce faire, on va ajouter 6 autres sprites pour en avoir 2 par lignes (je prévois par avance d’avoir 4 lignes)

Comme on va avoir plusieurs sprites sur plusieurs lignes, on doit rajouter une variable qui va garder l’abscisse minimale de nos alien (l’alien le plus à gauche) et l’abscisse maximale (celle de l’envahisseur le plus à droite). On les appelera respectivement max_x et min_x et pour le moment il seront très simple à calculer mais on fera évoluer cette méthode plus tard quand on pourra éliminier des envahisseurs.

On va charger nos sprites via une double boucle qui parcourreront les lignes et les colonnes correspondant à l’emplacement de chacun de nos invaders.

On changera la vitesse pour inverser le sens si le sprite le plus à droite atteint la limite droite de l’écran ou si celui étant le plus à gauche atteint le bord gauche.

Ce n’est pas plus compliqué que ça…

// Tuto space invaders - Part 3: Add 3 lines of invaders and make then move on the screen


// define constantes
#define invader_w 	8
#define invader_h 	8
#define invader_space_w 2	
#define invader_space_h 2	
#define invader_init_speed 2
#define invader_lines 3
#define invader_columns 6
#define sprite_invader 0


// Sprites
// Invader 8 pixels de largex 8 pixels de haut x2 frames x4 lignes

char invader[]= {
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x00, 0x70, 0x07, 0x00, 
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x70, 0x00, 0x00, 0x07, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x05, 0x50, 0x50, 
0x50, 0x50, 0x05, 0x05, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x00, 0x00, 0x50, 
0x00, 0x50, 0x05, 0x00, 
0x00, 0x60, 0x06, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x00, 0x00, 0x60, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66,
0x00, 0x60, 0x06, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x00, 0x00, 0x60, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66
};


// define variables

// invaders variables
int invader_x;
int invader_y;
int invader_speed;
int frame;      // We'll use 2 frames 0 and 1
int i;
int max_x;
int min_x;


// initialisation of values
void init(){
	invader_x = 0;
	invader_y = 20;
	frame = 0;
	invader_speed = invader_init_speed;
	setframerate(30);
	clearscreen();	
	i = sprite_invader;
	for( int row = 0; row < invader_lines; row++ ){
		for( int col = 0; col < invader_columns; col++ ){
			int x = invader_x + col * (invader_w + invader_space_w);
			int y = invader_y + row * (invader_h + invader_space_h);
			getsprite(i, invader + row * 64);
			putsprite(i, x, y);
			i++;
		}
	}
	max_x = invader_columns * (invader_w + invader_space_w);
	min_x = 0;
	getsprite(1,invader);
	clearscreen();
}


// Step procedure to make the game evolve between each draw
void step(){
    max_x = max_x + invader_speed;
    min_x = min_x + invader_speed;
	frame = 1 - frame;
    if (max_x > 130) invader_speed = -invader_init_speed;
    if (min_x < invader_init_speed)   invader_speed =  invader_init_speed;
}


// Draw procedure
void draw(){
		i = sprite_invader;
		for( int row = 0; row < invader_lines; row++ ){
			for( int col = 0; col < invader_columns; col++ ){
				int x = spritegetvalue(i, S_X) + invader_speed;
				int y = spritegetvalue(i, S_Y);
				getsprite(i, invader + row * 64 + frame * 32);
				putsprite(i, x, y);
				i++;
		}
	}
    delayredraw(); 						// wait until the frame is drawn. 
}


// Main procedure
void main(){
	init();
	while(1){	
       step();
       draw();		
	}
}

1 Like

Etape 4: Créons un vaisseau, 3 bunkers et le contrôle du vaisseau par les touches du clavier

Commençons par reprendre le programme de la partie 2 en remplaçant ce que nous avions programmé pour le sprite de l’invader par celui du player.

// Tuto space invaders - Part 4: Add 3 bunkers, ship and let player control it

char player[]= {
0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 
0x00, 0x00, 0x26, 0x62, 0x00, 0x00, 
0x00, 0x00, 0x63, 0x16, 0x00, 0x00, 
0x00, 0x02, 0x63, 0x16, 0x20, 0x00, 
0x02, 0x21, 0x63, 0x36, 0x12, 0x20, 
0x21, 0xc1, 0x26, 0x62, 0x1c, 0x12, 
0x21, 0xc2, 0x21, 0x12, 0x2c, 0x12, 
0x22, 0x20, 0x04, 0x40, 0x02, 0x22
};define variables

// player variables
int player_x;
int player_y;
int player_speed;



// initialisation of values
void init(){
	player_x = 58;
	player_y = 95;
	player_speed = 0;
	setframerate(30);
	clearscreen();	
	spritesetvalue(1,S_WIDTH,12);
	spritesetvalue(1,S_HEIGHT,8);
	getsprite(1,player);
}


// Step procedure to make the game evolve between each draw
void step(){
    player_x = player_x + player_speed;
    if (player_x > 120) player_x = 120;
    if (player_x < 0)   player_x =  0;
}


// Draw procedure
void draw(){
    putsprite(1, player_x, player_y);   // draw the sprite of the ship played
    delayredraw(); 						// wait until the frame is drawn. 
}


// Main procedure
void main(){
	init();
	while(1){	
       step();
       draw();		
	}
}

Voilà, plus qu’à fusionner les 2 et à gérer le clavier. (En dessous de la section: // manage keyboard inputs)

Pour avoir tous les principes des éléments graphiques, j’ai ajouté le début de la gestion des tirs. On voit tout de suite une limite: il faudra empêcher un nouveau tir avant qu’un certain temps ce soit écoulé après le précédent sinon le joueur en déclenche plusieurs en appuyant sur le bouton.

A priori, je ne pense pas que le code ci dessous ait besoin d’autres explications mais si besoin, demander les parties à éclaircir.

C’est un des derniers codes que je poste ici, les suivant seront sans doute sur Github.

// Tuto space invaders - Part 4: Add 3 bunkers, ship and let player control it

// define constantes
#define invader_w 	8
#define invader_h 	8
#define invader_space_w 2	
#define invader_space_h 2	
#define invader_init_speed 2
#define invader_lines 3
#define invader_columns 6


// Sprites
// Invader 8 pixels x 8 pixels x2 frames x4 rows

char invader[]= {
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x00, 0x70, 0x07, 0x00, 
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x70, 0x00, 0x00, 0x07, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x05, 0x50, 0x50, 
0x50, 0x50, 0x05, 0x05, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x00, 0x00, 0x50, 
0x00, 0x50, 0x05, 0x00, 
0x00, 0x60, 0x06, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x00, 0x00, 0x60, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66,
0x00, 0x60, 0x06, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x00, 0x00, 0x60, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66
};


char bunker[]= {
0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 
0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 
0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x50, 0x00, 0x00, 0x05, 0x55, 0x55, 
0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55
};

char player[]= {
0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 
0x00, 0x00, 0x26, 0x62, 0x00, 0x00, 
0x00, 0x00, 0x63, 0x16, 0x00, 0x00, 
0x00, 0x02, 0x63, 0x16, 0x20, 0x00, 
0x02, 0x21, 0x63, 0x36, 0x12, 0x20, 
0x21, 0xc1, 0x26, 0x62, 0x1c, 0x12, 
0x21, 0xc2, 0x21, 0x12, 0x2c, 0x12, 
0x22, 0x20, 0x04, 0x40, 0x02, 0x22
};


char shot{] = {
0x22, 
0x22, 
0x22, 
0x22
};

//define variables

// invaders variables
int invader_x;
int invader_y;
int invader_speed;
int frame;      			// We'll use 2 frames 0 and 1
int i;
int max_x;
int min_x;
int sprite_invader = 0;  	// max sprite invader = 18


// player variables
int player_x;
int player_y;
int player_speed;
int sprite_player = 19;

// shots variables
int max_shot = 3;
int sprite_shot = 20;   	// max sprite shot = 22
int shoots_done;
int next_shot;
int shot_speed = 2;

// bunkers variables
int sprite_bunkers = 24;
int y_bunkers = 88;


// game variables
int key;


// initialisation of values
void init(){
	
	// init player
	player_x = 58;
	player_y = 105;
	player_speed = 0;
	spritesetvalue(sprite_player,S_WIDTH,12);
	spritesetvalue(sprite_player,S_HEIGHT,8);
	getsprite(sprite_player,player);
	
	// init invaders
	invader_x = 0;
	invader_y = 20;
	frame = 0;
	invader_speed = invader_init_speed;
	i = sprite_invader;
	for( int row = 0; row < invader_lines; row++ ){
		for( int col = 0; col < invader_columns; col++ ){
			int x = invader_x + col * (invader_w + invader_space_w);
			int y = invader_y + row * (invader_h + invader_space_h);
			getsprite(i, invader + row * 64);
			putsprite(i, x, y);
			i++;
		}
	}
	max_x = invader_columns * (invader_w + invader_space_w);
	min_x = 0;
	getsprite(1,invader);
	
	// init player shots
	shoots_done = 0;
	next_shot = 20;
	for(int s = 0; s < max_shot; s++) {
		spritesetvalue(s + sprite_shot, S_WIDTH, 2);
		spritesetvalue(s + sprite_shot, S_HEIGHT,4);
		getsprite(s + sprite_shot, shot);
	}
	
	// init bunkers
	for(int b = 0; b < 3; b++) {
		getsprite(b + sprite_bunkers, bunker);
		spritesetvalue(b + sprite_bunkers, S_WIDTH, 16);
		spritesetvalue(b + sprite_bunkers, S_HEIGHT, 12);
	}	
	
	setframerate(30);
	clearscreen();	

}


// Step procedure to make the game evolve between each draw
void step(){
	
	// manage invaders
    max_x = max_x + invader_speed;
    min_x = min_x + invader_speed;
	frame = 1 - frame;
    if (max_x > 130) invader_speed = -invader_init_speed;
    if (min_x < invader_init_speed)   invader_speed =  invader_init_speed;

    // manage shots
	for(int s = 0; s < max_shot; s++) {
		int y;
		if (spritegetvalue(s + sprite_shot, S_LIVES) > 0) {
			y = spritegetvalue(s + sprite_shot, S_Y) - shot_speed;
			if (y > 10) {
				spritesetvalue(s + sprite_shot, S_Y, spritegetvalue(s + sprite_shot, S_Y) - shot_speed);
			} else {
				spritesetvalue(s + sprite_shot, S_LIVES, 0);
				--shoots_done;
			}
		}
	}


	// manage keyboard inputs
	key = getKey();
	if (key & KEY_RIGHT) { 
		player_speed = player_speed + 3 ;
	} else if (key & KEY_LEFT) { 
		player_speed = player_speed - 3 ;
	}  
	
	// manage limits 
    player_x = player_x + player_speed;
    if (player_x > 116) player_x = 116;
    if (player_x < 0)   player_x =  0;

    // test if the player try to shoot
	if (key & KEY_A) {
		if (shoots_done < 3) {
			putsprite(next_shot, player_x + 6, player_y - 5);
			spritesetvalue(next_shot, S_LIVES, 1);
			++shoots_done;
			++next_shot;
			if (next_shot > 22) next_shot = 20;
		}
	} 

	// reset speed (no inertia)
    player_speed = 0;
}


// Draw procedure
void draw(){
	
	// draw shots
	for(int s = 0; s < max_shot; s++) {
		if (spritegetvalue(s + sprite_shot, S_LIVES) > 0) {
			putsprite(s + sprite_shot, spritegetvalue(s + sprite_shot, S_X),spritegetvalue(s + sprite_shot, S_Y));
		}
	}
	
	// draw invaders
	i = sprite_invader;
	for( int row = 0; row < invader_lines; row++ ){
		for( int col = 0; col < invader_columns; col++ ){
			int x = spritegetvalue(i, S_X) + invader_speed;
			int y = spritegetvalue(i, S_Y);
			getsprite(i, invader + row * 64 + frame * 32);
			putsprite(i, x, y);
			i++;
		}
	}
	
	// draw bunkers
	putsprite(sprite_bunkers, 10, y_bunkers );
	putsprite(sprite_bunkers +1, 56, y_bunkers );
	putsprite(sprite_bunkers +2, 102, y_bunkers );
	
	// draw ship of the player
    putsprite(sprite_player, player_x, player_y);   // draw the sprite of the ship played

    delayredraw(); 						// wait until the frame is drawn. 
}


// Main procedure
void main(){
	init();
	while(1){	
       step();
       draw();		
	}
}

Bon ajoutons nos timers pour bloquer les tirs du vaisseau pendant quelques temps et un autre timer pour pouvoir choisir à quelle vitesse vont se déplacer nos invaders.
Profitons en également pour faire descendre les envahisseurs à chaque fois qu’ils atteignent l’une des extrémités de l’écran. On va également rajouter un autre test pour que les aliens réapparaisent en haut de l’écran quand les derniers ont disparu mais ça c’est juste pour le temps de la mise au point. Normalement le joueur aurait perdu une vie avant…

// Tuto Space invader - Part 5: Add timer for shots and invaders move and make them go down

/*settings*{"name":"Space_Invad","author":"Jicehel","image":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,119,7,112,0,112,0,119,7,119,0,0,112,0,7,7,7,7,7,0,7,0,0,0,7,112,7,7,7,7,7,0,7,112,0,0,0,7,7,112,7,119,7,0,7,0,0,0,119,112,7,0,7,7,0,119,7,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,112,7,7,0,7,0,112,7,112,0,0,112,119,7,7,0,7,0,112,7,7,0,0,112,119,119,0,112,112,7,7,7,7,0,0,112,112,119,0,112,112,7,119,7,7,0,0,112,112,7,0,7,0,7,7,7,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}*/


// define constantes
#define invader_w 	8
#define invader_h 	8
#define invader_space_w 2	
#define invader_space_h 2	
#define invader_init_speed 2
#define invader_lines 3
#define invader_columns 6


#define timer_ship_shot 1
#define timer_invaders  2
#define timer_invaders_shot 3

// Sprites
// Invader 8 pixels x 8 pixels x2 frames x4 rows

char invader[]= {
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x00, 0x70, 0x07, 0x00, 
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x70, 0x00, 0x00, 0x07, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x05, 0x50, 0x50, 
0x50, 0x50, 0x05, 0x05, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x00, 0x00, 0x50, 
0x00, 0x50, 0x05, 0x00, 
0x00, 0x60, 0x06, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x00, 0x00, 0x60, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66,
0x00, 0x60, 0x06, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x00, 0x00, 0x60, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66
};


char bunker[]= {
0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 
0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 
0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x50, 0x00, 0x00, 0x05, 0x55, 0x55, 
0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55
};

char player[]= {
0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 
0x00, 0x00, 0x26, 0x62, 0x00, 0x00, 
0x00, 0x00, 0x63, 0x16, 0x00, 0x00, 
0x00, 0x02, 0x63, 0x16, 0x20, 0x00, 
0x02, 0x21, 0x63, 0x36, 0x12, 0x20, 
0x21, 0xc1, 0x26, 0x62, 0x1c, 0x12, 
0x21, 0xc2, 0x21, 0x12, 0x2c, 0x12, 
0x22, 0x20, 0x04, 0x40, 0x02, 0x22
};


char shot{] = {
0x22, 
0x22, 
0x22, 
0x22
};

//define variables

// invaders variables
int invader_x;
int invader_y;
int invader_speed;
int frame;      			// We'll use 2 frames 0 and 1
int i;
int max_x, min_x, max_y, min_y;
int sprite_invader = 0;  	// max sprite invader = 18

// player variables
int player_x;
int player_y;
int player_speed;
int sprite_player = 19;

// shots variables
int max_shot = 3;
int sprite_shot = 20;   	// max sprite shot = 22
int shoots_done;
int next_shot;
int shot_speed = 2;         // speed of the shot move
int timer_cooldown, delay_invader_shot, delay_move_invaders;

// bunkers variables
int sprite_bunkers = 24;
int y_bunkers = 88;


// game variables
int key;


// initialisation of values
void init(){
	
	// init player
	player_x = 58;
	player_y = 105;
	player_speed = 0;
	spritesetvalue(sprite_player,S_WIDTH,12);
	spritesetvalue(sprite_player,S_HEIGHT,8);
	getsprite(sprite_player,player);
	
	// init invaders
	invader_x = 0;
	invader_y = 20;
	frame = 0;
	invader_speed = invader_init_speed;
	i = sprite_invader;
	for( int row = 0; row < invader_lines; row++ ){
		for( int col = 0; col < invader_columns; col++ ){
			int x = invader_x + col * (invader_w + invader_space_w);
			int y = invader_y + row * (invader_h + invader_space_h);
			getsprite(i, invader + row * 64);
			putsprite(i, x, y);
			i++;
		}
	}
	max_x = invader_columns * (invader_w + invader_space_w);
	min_x = 0;
	min_y = invader_y;
	max_y = invader_y + invader_lines * (invader_h + invader_space_h) - invader_space_h;
	getsprite(1,invader);
	delay_move_invaders = 100;
	delay_invader_shot = 500;
	
	// init player shots
	shoots_done = 0;
	next_shot = 20;
	for(int s = 0; s < max_shot; s++) {
		spritesetvalue(s + sprite_shot, S_WIDTH, 2);
		spritesetvalue(s + sprite_shot, S_HEIGHT,4);
		getsprite(s + sprite_shot, shot);
	}
	timer_cooldown = 800;
	
	// init bunkers
	for(int b = 0; b < 3; b++) {
		getsprite(b + sprite_bunkers, bunker);
		spritesetvalue(b + sprite_bunkers, S_WIDTH, 16);
		spritesetvalue(b + sprite_bunkers, S_HEIGHT, 12);
	}	
	
	setframerate(30);
	clearscreen();	
	settimer( timer_ship_shot, timer_cooldown );
	settimer( timer_invaders, delay_move_invaders);
	settimer( timer_invaders_shot, delay_invader_shot);
}


// Ship shot add
void add_ship_shot( int shot, int xshot, int yshot ) {
	putsprite(shot, xshot, yshot);	
	spritesetvalue(shot, S_LIVES, 1);
	++shoots_done;
	++next_shot;
	if (next_shot > 22) next_shot = 20;
}


// Step procedure to make the game evolve between each draw
void step(){
	
	// manage invaders
	
	if (gettimer( timer_invaders  ) <= 0 ) {
		    char change_ligne = 0;
	    	max_x = max_x + invader_speed;
 		   	min_x = min_x + invader_speed;
			frame = 1 - frame;
    		if (max_x > 130) {
				invader_speed = -invader_init_speed;
				change_ligne = 1;
			}
    		if (min_x < invader_init_speed) {
				invader_speed =  invader_init_speed;
				change_ligne = 1;
			}
	
		
			i = sprite_invader;
			for( int row = 0; row < invader_lines; row++ ){
				for( int col = 0; col < invader_columns; col++ ){
					putsprite(i, spritegetvalue(i, S_X) + invader_speed, spritegetvalue(i, S_Y) + change_ligne * (invader_h + invader_space_h));
					if ((change_ligne > 0) && (min_y > 120)) {
						  	int x = invader_x + col * (invader_w + invader_space_w);
							int y = invader_y + row * (invader_h + invader_space_h);
							spritesetvalue(i, S_X, x);
							spritesetvalue(i, S_Y, y);
					}
					++i;
				}		
			}
			
			if (change_ligne > 0) {
				if (min_y > 120) {
						max_x = invader_columns * (invader_w + invader_space_w);
						min_x = 0;
						min_y = invader_y;
						max_y = invader_y + invader_lines * (invader_h + invader_space_h) - invader_space_h;
						invader_speed =  invader_init_speed;
				} else {
						min_y = min_y + (invader_h + invader_space_h);
						max_y = max_y + (invader_h + invader_space_h);
					}
			}
	
		    settimer( timer_invaders, delay_move_invaders);
	}

    // manage shots
	for(int s = 0; s < max_shot; s++) {
		int y;
		if (spritegetvalue(s + sprite_shot, S_LIVES) > 0) {
			y = spritegetvalue(s + sprite_shot, S_Y) - shot_speed;
			if (y > 10) {
				spritesetvalue(s + sprite_shot, S_Y, spritegetvalue(s + sprite_shot, S_Y) - shot_speed);
			} else {
				spritesetvalue(s + sprite_shot, S_LIVES, 0);
				--shoots_done;
			}
		}
	}


	// manage keyboard inputs
	key = getKey();
	if (key & KEY_RIGHT) { 
		player_speed = player_speed + 3 ;
	} else if (key & KEY_LEFT) { 
		player_speed = player_speed - 3 ;
	}  
	
	// manage limits 
    player_x = player_x + player_speed;
    if (player_x > 116) player_x = 116;
    if (player_x < 0)   player_x =  0;

    // test if the player try to shoot
	if (key & KEY_A) {
		if (shoots_done < 3) {
			if( gettimer( timer_ship_shot ) <= 0 ) {
					settimer( timer_ship_shot, timer_cooldown );
					add_ship_shot( next_shot, player_x + 6, player_y - 5 );
				}
		}
	} 

	// reset speed (no inertia)
    player_speed = 0;
}


// Draw procedure
void draw(){
	
	// draw shots
	for(int s = 0; s < max_shot; s++) {
		if (spritegetvalue(s + sprite_shot, S_LIVES) > 0) {
			putsprite(s + sprite_shot, spritegetvalue(s + sprite_shot, S_X),spritegetvalue(s + sprite_shot, S_Y));
		}
	}
	
	// draw invaders
	i = sprite_invader;
	for( int row = 0; row < invader_lines; row++ ){
		for( int col = 0; col < invader_columns; col++ ){
			int x = spritegetvalue(i, S_X);
			int y = spritegetvalue(i, S_Y);
			getsprite(i, invader + row * 64 + frame * 32);
			putsprite(i, x, y);
			i++;
		}
	}
	
	// draw bunkers
	putsprite(sprite_bunkers, 10, y_bunkers );
	putsprite(sprite_bunkers +1, 56, y_bunkers );
	putsprite(sprite_bunkers +2, 102, y_bunkers );
	
	// draw ship of the player
    putsprite(sprite_player, player_x, player_y);   // draw the sprite of the ship played

    delayredraw(); 									// wait until the frame is drawn. 
}


// Main procedure
void main(){
	init();
	while(1){	
       step();
       draw();		
	}
}

Bon prochaine étape: gérer les collisions et ajouter la réponse des envahisseurs pour qu’ils tirent également. Mais je vais vous laisser un peu de temps pour voir si des gens ont envie de proposer leur solution.
Rappel des règles: quand le vaisseau du joueur ou un invader touche un bunker, il est endommagé.(D’ailleurs c’est un point pour lequel je dois réfléchir de mon côté aussi pour refléter les dommages des tirs sur le bunker). Quand un tir du vaisseau touche un invader il le détruit. Quand il n’y a plus d’invader sur une colonne, les aliens avancent plus loin dans un sens ou dans l’autre selon la colonne détruite.
Si le tir d’un invader touche le vaisseau, celui ci est détruit. (On fera une belle animation d’explosion)
C’est une des dernières étapes sur le moteur de jeu, la suivante sera le bruitage, la musique et la gestion des vies et du score.

1 Like

Petite mise à jour hors programme suite à une très bonne suggestion de Corax. On affiche directement les invaders sous forme de colonne d’aliens. Une colonne = 1 sprite que l’on diminuera en taille en fonction du nombre de vie restante. Ça simplifie pas mal de chose en fait.
Le code de l’étape précédente réécrit selon ce principe donne:

// Tuto Space invader - Part 5: Add timer for shots and invaders move and make them go down

/*settings*{"name":"Space_Invad","author":"Jicehel","image":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,119,7,112,0,112,0,119,7,119,0,0,112,0,7,7,7,7,7,0,7,0,0,0,7,112,7,7,7,7,7,0,7,112,0,0,0,7,7,112,7,119,7,0,7,0,0,0,119,112,7,0,7,7,0,119,7,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,112,7,7,0,7,0,112,7,112,0,0,112,119,7,7,0,7,0,112,7,7,0,0,112,119,119,0,112,112,7,7,7,7,0,0,112,112,119,0,112,112,7,119,7,7,0,0,112,112,7,0,7,0,7,7,7,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}*/


// define constantes
#define invader_w 	8
#define invader_h 	8
#define invader_space_w 2	
#define invader_space_h 2	
#define invader_init_speed 2
#define invader_lines 5
#define invader_columns 8


#define timer_ship_shot 1
#define timer_invaders  2
#define timer_invaders_shot 3

// Sprites
// Invader 8 pixels x 8 pixels x 5 rows x 2 frames with 2 separation lines between rows 

char invader[]= {
0x00, 0x70, 0x07, 0x00, 
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x05, 0x50, 0x50, 
0x50, 0x50, 0x05, 0x05, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x00, 0x00, 0x50, 
0x00, 0x50, 0x05, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x60, 0x06, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66, 
0x00, 0x70, 0x07, 0x00, 
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x70, 0x00, 0x00, 0x07, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x00, 0x00, 0x50, 
0x00, 0x50, 0x05, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x05, 0x50, 0x50, 
0x50, 0x50, 0x05, 0x05, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66, 
0x00, 0x60, 0x06, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x00, 0x00, 0x60  
};


char bunker[]= {
0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 
0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 
0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x50, 0x00, 0x00, 0x05, 0x55, 0x55, 
0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55
};

char player[]= {
0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 
0x00, 0x00, 0x26, 0x62, 0x00, 0x00, 
0x00, 0x00, 0x63, 0x16, 0x00, 0x00, 
0x00, 0x02, 0x63, 0x16, 0x20, 0x00, 
0x02, 0x21, 0x63, 0x36, 0x12, 0x20, 
0x21, 0xc1, 0x26, 0x62, 0x1c, 0x12, 
0x21, 0xc2, 0x21, 0x12, 0x2c, 0x12, 
0x22, 0x20, 0x04, 0x40, 0x02, 0x22
};


char shot{] = {
0x22, 
0x22, 
0x22, 
0x22
};

//define variables

// invaders variables
int invader_x;
int invader_y;
int invader_speed;
int frame;      			// We'll use 2 frames 0 and 1
int i;
int max_x, min_x, max_y, min_y;
int sprite_invader = 0;  	// max sprite invader = 18

// player variables
int player_x;
int player_y;
int player_speed;
int sprite_player = 19;

// shots variables
int max_shot = 3;
int sprite_shot = 20;   	// max sprite shot = 22
int shoots_done;
int next_shot;
int shot_speed = 2;         // speed of the shot move
int timer_cooldown, delay_invader_shot, delay_move_invaders;

// bunkers variables
int sprite_bunkers = 24;
int y_bunkers = 88;


// game variables
int key;


// initialisation of values
void init(){
	
	// init player
	player_x = 58;
	player_y = 105;
	player_speed = 0;
	spritesetvalue(sprite_player,S_WIDTH,12);
	spritesetvalue(sprite_player,S_HEIGHT,8);
	getsprite(sprite_player,player);
	
	// init invaders
	invader_x = 0;
	invader_y = 20;
	frame = 0;
	invader_speed = invader_init_speed;
	i = sprite_invader;
	for( int col = 0; col < invader_columns; col++ ){
			int x = invader_x + col * (invader_w + invader_space_w);
			spritesetvalue(i, S_WIDTH, 8);
			spritesetvalue(i, S_HEIGHT,48);
			spritesetvalue(i, S_LIVES,5);
			getsprite(i, invader);
			putsprite(i, x, invader_y);
			i++;
		}
	}
	max_x = invader_columns * (invader_w + invader_space_w);
	min_x = 0;
	min_y = invader_y;
	max_y = invader_y + invader_lines * (invader_h + invader_space_h) - invader_space_h;
	getsprite(1,invader);
	delay_move_invaders = 100;
	delay_invader_shot = 500;
	
	// init player shots
	shoots_done = 0;
	next_shot = 20;
	for(int s = 0; s < max_shot; s++) {
		spritesetvalue(s + sprite_shot, S_WIDTH, 2);
		spritesetvalue(s + sprite_shot, S_HEIGHT,4);
		getsprite(s + sprite_shot, shot);
	}
	timer_cooldown = 800;
	
	// init bunkers
	for(int b = 0; b < 3; b++) {
		getsprite(b + sprite_bunkers, bunker);
		spritesetvalue(b + sprite_bunkers, S_WIDTH, 16);
		spritesetvalue(b + sprite_bunkers, S_HEIGHT, 12);
	}	
	
	setframerate(30);
	clearscreen();	
	settimer( timer_ship_shot, timer_cooldown );
	settimer( timer_invaders, delay_move_invaders);
	settimer( timer_invaders_shot, delay_invader_shot);
}


// Ship shot add
void add_ship_shot( int shot, int xshot, int yshot ) {
	putsprite(shot, xshot, yshot);	
	spritesetvalue(shot, S_LIVES, 1);
	++shoots_done;
	++next_shot;
	if (next_shot > 22) next_shot = 20;
}


// Step procedure to make the game evolve between each draw
void step(){
	
	// manage invaders
	
	if (gettimer( timer_invaders  ) <= 0 ) {
		    char change_ligne = 0;
	    	max_x = max_x + invader_speed;
 		   	min_x = min_x + invader_speed;
			frame = 1 - frame;
    		if (max_x > 130) {
				invader_speed = -invader_init_speed;
				change_ligne = 1;
			}
    		if (min_x < invader_init_speed) {
				invader_speed =  invader_init_speed;
				change_ligne = 1;
			}
	
		
			i = sprite_invader;
			for( int col = 0; col < invader_columns; col++ ){
				putsprite(i, spritegetvalue(i, S_X) + invader_speed, spritegetvalue(i, S_Y) + change_ligne * (invader_h + invader_space_h));
				if ((change_ligne > 0) && (min_y > 98)) {
				  	int x = invader_x + col * (invader_w + invader_space_w);
					int y = invader_y;
					spritesetvalue(i, S_X, x);
					spritesetvalue(i, S_Y, y);
				}
				++i;		
			}
			
			if (change_ligne > 0) {
				if (min_y > 98) {
						max_x = invader_columns * (invader_w + invader_space_w);
						min_x = 0;
						min_y = invader_y;
						max_y = invader_y + invader_lines * (invader_h + invader_space_h) - invader_space_h;
						invader_speed =  invader_init_speed;
				} else {
						min_y = min_y + (invader_h + invader_space_h);
						max_y = max_y + (invader_h + invader_space_h);
					}
			}
	
		    settimer( timer_invaders, delay_move_invaders);
	}

    // manage shots
	for(int s = 0; s < max_shot; s++) {
		int y;
		if (spritegetvalue(s + sprite_shot, S_LIVES) > 0) {
			y = spritegetvalue(s + sprite_shot, S_Y) - shot_speed;
			if (y > 10) {
				spritesetvalue(s + sprite_shot, S_Y, spritegetvalue(s + sprite_shot, S_Y) - shot_speed);
			} else {
				spritesetvalue(s + sprite_shot, S_LIVES, 0);
				--shoots_done;
			}
		}
	}


	// manage keyboard inputs
	key = getKey();
	if (key & KEY_RIGHT) { 
		player_speed = player_speed + 3 ;
	} else if (key & KEY_LEFT) { 
		player_speed = player_speed - 3 ;
	}  
	
	// manage limits 
    player_x = player_x + player_speed;
    if (player_x > 116) player_x = 116;
    if (player_x < 0)   player_x =  0;

    // test if the player try to shoot
	if (key & KEY_A) {
		if (shoots_done < 3) {
			if( gettimer( timer_ship_shot ) <= 0 ) {
					settimer( timer_ship_shot, timer_cooldown );
					add_ship_shot( next_shot, player_x + 6, player_y - 5 );
				}
		}
	} 

	// reset speed (no inertia)
    player_speed = 0;
}


// Draw procedure
void draw(){
	
	// draw shots
	for(int s = 0; s < max_shot; s++) {
		if (spritegetvalue(s + sprite_shot, S_LIVES) > 0) {
			putsprite(s + sprite_shot, spritegetvalue(s + sprite_shot, S_X),spritegetvalue(s + sprite_shot, S_Y));
		}
	}
	
	// draw invaders
	i = sprite_invader;
	for( int col = 0; col < invader_columns; col++ ){
		int x = spritegetvalue(i, S_X);
		int y = spritegetvalue(i, S_Y);
		getsprite(i, invader + frame * 192);
		putsprite(i, x, y);
		i++;
	}
	
	// draw bunkers
	putsprite(sprite_bunkers, 10, y_bunkers );
	putsprite(sprite_bunkers +1, 56, y_bunkers );
	putsprite(sprite_bunkers +2, 102, y_bunkers );
	
	// draw ship of the player
    putsprite(sprite_player, player_x, player_y);   // draw the sprite of the ship played

    delayredraw(); 									// wait until the frame is drawn. 
}


// Main procedure
void main(){
	init();
	while(1){	
       step();
       draw();		
	}
}

Bon et bien j’attends de voir si quelqu’un veut proposer quelque chose pour mon petit challenge avant de continuer l’écriture.

1 Like

OK, bon, je n’ai pas tout compris avec le problème actuellement sur les variables locales mais du coup j’ai réécris un peu le programme pour que ça fonctionne et comme ça ne bougeait pas fort en réponse à mon petit challenge, j’ai rajouté le test de collision entre le tir du vaisseau et les ennemis.

Du coup ça donne ça:

// Tuto Space invader - Part 5: Add timer for shots and invaders move and make them go down

/*settings*{"name":"Space_Invad","author":"Jicehel","image":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,119,7,112,0,112,0,119,7,119,0,0,112,0,7,7,7,7,7,0,7,0,0,0,7,112,7,7,7,7,7,0,7,112,0,0,0,7,7,112,7,119,7,0,7,0,0,0,119,112,7,0,7,7,0,119,7,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,112,7,7,0,7,0,112,7,112,0,0,112,119,7,7,0,7,0,112,7,7,0,0,112,119,119,0,112,112,7,7,7,7,0,0,112,112,119,0,112,112,7,119,7,7,0,0,112,112,7,0,7,0,7,7,7,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}*/


// define constantes
	
#define invader_init_speed 	2
#define invader_lines 		4
#define invader_columns 	6

#define timer_ship_shot 	1
#define timer_invaders  	2
#define timer_invaders_shot 3

#define debug 0

// Sprites
// Invader 8 pixels x 8 pixels x 5 rows x 2 frames with 2 separation lines between rows 

char invader[]= {
0x00, 0x70, 0x07, 0x00, 
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x05, 0x50, 0x50, 
0x50, 0x50, 0x05, 0x05, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x00, 0x00, 0x50, 
0x00, 0x50, 0x05, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x60, 0x06, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66, 
0x00, 0x70, 0x07, 0x00, 
0x00, 0x77, 0x77, 0x00, 
0x07, 0x77, 0x77, 0x70, 
0x07, 0x77, 0x77, 0x70, 
0x77, 0x07, 0x70, 0x77, 
0x77, 0x77, 0x77, 0x77, 
0x07, 0x70, 0x07, 0x70, 
0x07, 0x00, 0x00, 0x70, 
0x70, 0x00, 0x00, 0x07, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x00, 0x00, 0x50, 
0x00, 0x50, 0x05, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x05, 0x50, 0x00, 
0x00, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x50, 
0x55, 0x05, 0x50, 0x55, 
0x55, 0x55, 0x55, 0x55, 
0x00, 0x50, 0x05, 0x00, 
0x05, 0x05, 0x50, 0x50, 
0x50, 0x50, 0x05, 0x05, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x60, 0x60, 0x06, 0x06, 
0x60, 0x06, 0x60, 0x06, 
0x66, 0x66, 0x66, 0x66, 
0x66, 0x06, 0x60, 0x66, 
0x00, 0x66, 0x66, 0x00, 
0x06, 0x60, 0x06, 0x60, 
0x66, 0x00, 0x00, 0x66, 
0x00, 0x60, 0x06, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 
0x00, 0x06, 0x60, 0x00, 
0x06, 0x66, 0x66, 0x60, 
0x06, 0x06, 0x60, 0x60, 
0x60, 0x66, 0x66, 0x06, 
0x66, 0x00, 0x00, 0x66, 
0x60, 0x60, 0x06, 0x06, 
0x06, 0x00, 0x00, 0x60, 
0x06, 0x00, 0x00, 0x60  
};


char bunker[]= {
0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 
0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 
0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 
0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
0x55, 0x55, 0x50, 0x00, 0x00, 0x05, 0x55, 0x55, 
0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 
0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55
};

char player[]= {
0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 
0x00, 0x00, 0x26, 0x62, 0x00, 0x00, 
0x00, 0x00, 0x63, 0x16, 0x00, 0x00, 
0x00, 0x02, 0x63, 0x16, 0x20, 0x00, 
0x02, 0x21, 0x63, 0x36, 0x12, 0x20, 
0x21, 0xc1, 0x26, 0x62, 0x1c, 0x12, 
0x21, 0xc2, 0x21, 0x12, 0x2c, 0x12, 
0x22, 0x20, 0x04, 0x40, 0x02, 0x22
};


char shot_beam{] = {
0x22, 
0x22, 
0x22, 
0x22
};


//define variables

// invaders variables
int invader_x;
int invader_y = 20;
int invader_speed;
int frame;      			// We'll use 2 frames 0 and 1
int i;
int max_x, min_x, max_y, min_y;
int sprite_invader = 0;  	// max sprite invader = 18
int invader_w =	8;
int invader_h =	8;
int invader_space_w = 2;	
int invader_space_h = 2;
int life_invader; // temp variable with noumber of lifes keeping 
int col_hitted;  // temp variable of column hitted
int shot; // temp variable of shot hitting
int first_column, last_column;
int last_row;
int y_endline;
int nb_life = 3;


// player variables
int player_x;
int player_y;
int player_speed;
int sprite_player = 9;


// shots variables
int max_shot = 6;
int sprite_shot = 10;   	// max sprite shot = 22
int shoots_done;
int next_shot;
int shot_speed = 2;         // speed of the shot move
int timer_cooldown, delay_invader_shot, delay_move_invaders;
int shot_live;
int shot_x,shot_y;


// bunkers variables
int sprite_bunkers = 29;
int y_bunkers = 88;


// game variables
int key;
int score;


// Clear the shot then it goes out of the screen
void clear_shot() {
	spritesetvalue(shot, S_LIVES, 0);
	--shoots_done;
};


// The shot of the ship have hit something
void shot_hit() {
	if (life_invader > 0) {
		score = score + 10;
		drawparticle(spritegetvalue(col_hitted, S_X + 0.5 * invader_w ), spritegetvalue(col_hitted, S_Y) + life_invader * (invader_h + invader_space_h), random(3)+1);
		--life_invader;
		spritesetvalue(col_hitted, S_LIVES, life_invader);
		if (life_invader > 0){
			 int height = life_invader * (invader_h + invader_space_h) - invader_space_h
		     spritesetvalue(col_hitted, S_HEIGHT, life_invader * (invader_h + invader_space_h) - invader_space_h);     
	    } else {
			spritesetvalue(col_hitted, S_HEIGHT,0);
		}
	clear_shot();
	}
};


// initialisation of values
void init(){
	
	// init game
	score = 0;
	setbgcolor(0);
	
	// init player
	player_x = 58;
	player_y = 105;
	player_speed = 0;
	spritesetvalue(sprite_player,S_WIDTH,12);
	spritesetvalue(sprite_player,S_HEIGHT,8);
	spritesetvalue(sprite_player, S_LIVES, nb_life);
	getsprite(sprite_player,player);
	
	// init invaders
	invader_x = 0;
	frame = 0;
	invader_speed = invader_init_speed;
	i = sprite_invader;
	for( int col = 0; col < invader_columns; col++ ){
			int x = invader_x + col * (invader_w + invader_space_w);
			spritesetvalue(i, S_WIDTH, invader_w);
			spritesetvalue(i, S_HEIGHT,invader_lines * (invader_h + invader_space_h)-invader_space_h);
			spritesetvalue(i, S_LIVES, invader_lines);
			getsprite(i, invader);
			putsprite(i, x, invader_y);
			i++;
		}
	}
	first_column = 1;
	last_column = invader_columns;
	last_row = invader_lines;
	
	max_x = invader_columns * (invader_w + invader_space_w);
	min_x = 0;
	min_y = invader_y;
	max_y = invader_y + invader_lines * (invader_h + invader_space_h) - invader_space_h;
	y_endline = 128 - invader_h;
	getsprite(1,invader);
	delay_move_invaders = 100;
	// if (debug == 1) delay_move_invaders = 600;
	delay_invader_shot = 500;
	
	// init player shots
	shoots_done = 0;
	for(int s = sprite_shot; s < (sprite_shot + max_shot); s++) {
		spritesetvalue(s, S_WIDTH, 2);
		spritesetvalue(s, S_HEIGHT,4);
		spritesetvalue(s, S_LIVES,0);
		// spritesetvalue(s, S_ON_COLLISION, shot_hit);
		// spritesetvalue(s, S_ON_EXIT_SCREEN, clear_shot);
		getsprite(s, shot_beam);
	}
	timer_cooldown = 600;
	
	// init bunkers
	for(int b = 0; b < 3; b++) {
		getsprite(b + sprite_bunkers, bunker);
		spritesetvalue(b + sprite_bunkers, S_WIDTH, 16);
		spritesetvalue(b + sprite_bunkers, S_HEIGHT, 12);
	}	
	
	setframerate(30);
	settimer( timer_ship_shot, timer_cooldown );
	settimer( timer_invaders, delay_move_invaders);
	settimer( timer_invaders_shot, delay_invader_shot);
}


// Ship shot add

void add_ship_shot() {
	if (next_shot >= sprite_shot) {
		putsprite(next_shot, player_x + 6, player_y - 5);	
		spritesetvalue(next_shot, S_LIVES, 1);
		++shoots_done;
	}
};


// Step procedure to make the game evolve between each draw
void step(){
	
	// manage invaders
	
	if (gettimer( timer_invaders  ) <= 0 ) {
		    char change_ligne = 0;
			frame = 1 - frame;
			max_x = max_x + invader_speed;
 		    min_x = min_x + invader_speed;
    		if (max_x > y_endline + invader_w) {
				invader_speed = -invader_init_speed;
				change_ligne = 1;
				min_x = min_x + invader_speed;
				max_x = max_x + invader_speed;
			}
    		if (min_x < invader_init_speed) {
				invader_speed =  invader_init_speed;
				change_ligne = 1;
				min_x = min_x + invader_speed;
				max_x = max_x + invader_speed;

			}
		
			for( int col = sprite_invader; col < (sprite_invader + invader_columns); col++ ){
				if (spritegetvalue(col, S_LIVES) > 0) {
					putsprite(col, spritegetvalue(col, S_X) + invader_speed, spritegetvalue(col, S_Y) + change_ligne * (invader_h + invader_space_h));
				}
				if ((change_ligne > 0) && (min_y > y_endline)) {
				  	int x = invader_x + col * (invader_w + invader_space_w);
					int y = invader_y;
					spritesetvalue(col, S_X, x);
					spritesetvalue(col, S_Y, y);
				}
			}
			
			if (change_ligne > 0) {
				if (min_y > y_endline) {
						max_x = invader_columns * (invader_w + invader_space_w);
						min_x = 0;
						min_y = invader_y;
						max_y = invader_y + invader_lines * (invader_h + invader_space_h) - invader_space_h;
						invader_speed =  invader_init_speed;
				} else {
						min_y = spritegetvalue(first_column,S_Y);
						max_y = spritegetvalue(first_column,S_Y) + invader_lines * (invader_h + invader_space_h) - invader_space_h;
					}
			}
	        // if (debug == 1) {
			//	gotoxy(0,15); putn(min_y);puts("    ");putn(max_y);
			//	rect(min_x, min_y, max_x, max_y);
			// }	
		    settimer( timer_invaders, delay_move_invaders);
	}

    // manage shots
	for(int s = 0; s < max_shot; s++) {
		shot = s + sprite_shot;
		shot_live = spritegetvalue(shot, S_LIVES);
		if ( shot_live > 0) {
			shot_y = spritegetvalue(shot, S_Y) - shot_speed;
			if (shot_y < 10) {
				clear_shot();
			} else {
				spritesetvalue(shot, S_Y, spritegetvalue(s + sprite_shot, S_Y) - shot_speed);
			}
		
		// test if an invader has been hitted

		if ((shot_y < max_y ) && (shot_y >= min_y)) {
			shot_x = spritegetvalue(shot, S_X);
			
			if ((shot_x < max_x) && (shot_x >= min_x)) {
				col_hitted = sprite_invader + ((shot_x - min_x) / (invader_w + invader_space_w));
				life_invader = spritegetvalue(col_hitted, S_LIVES);
				if (debug = 1) {
					// setcolor(2);
					// gotoxy(15,1); puts("col:");putn(col_hitted);puts("  ");
					// setcolor(6);
					// rect(min_x, min_y, max_x, (min_y + life_invader * (invader_h + invader_space_h)));
				}
				if (life_invader > 0) {
					if (shot_y < (min_y + life_invader * (invader_h + invader_space_h))) shot_hit();
				}
			}	
		}
	}


	// manage keyboard inputs
	key = getKey();
	if (key & KEY_RIGHT) { 
		player_speed = player_speed + 3 ;
	} else if (key & KEY_LEFT) { 
		player_speed = player_speed - 3 ;
	}  
	
	// manage limits 
    player_x = player_x + player_speed;
    if (player_x > 116) player_x = 116;
    if (player_x < 0)   player_x =  0;

    // test if the player try to shoot
	if (key & KEY_A) {
		if (shoots_done < max_shot) {
			if( gettimer( timer_ship_shot ) <= 0 ) {
					settimer( timer_ship_shot, timer_cooldown );
					for(int s = sprite_shot; s < (sprite_shot + max_shot); s++) {
						if (spritegetvalue(s, S_LIVES) == 0) {
			 				next_shot = s;
						}	
					}
					add_ship_shot();
				}
		}
	} 

	// reset speed (no inertia)
    player_speed = 0;
}


// Draw procedure
void draw(){
	
	clearscreen();
	
		if (debug == 1) {
			setcolor(1);
			gotoxy(0,0); putn(min_y); puts(" ");
			gotoxy(10,0); putn(max_y);puts(" ");
			gotoxy(0,1); putn(min_x); puts(" ");
			gotoxy(10,1); putn(max_x);puts(" ");
		} else {
			gotoxy(0,0); puts("score:");putn(score); puts(" ");
			gotoxy(10,0); puts("vies:      ");
			for(int v = 0; v < 3; v++) {
				putimage(player, 88 + v*13, 1, 12, 8);
			}
		}	 
	
	// draw shots
	for(int s = 0; s < max_shot; s++) {
		if (spritegetvalue(s + sprite_shot, S_LIVES) > 0) {
			putsprite(s + sprite_shot, spritegetvalue(s + sprite_shot, S_X),spritegetvalue(s + sprite_shot, S_Y));
		}
	}
	
	// draw invaders
	i = sprite_invader;
	for( int col = 0; col < invader_columns; col++ ){
		int x = spritegetvalue(i, S_X);
		int y = spritegetvalue(i, S_Y);
		getsprite(i, invader + frame * 192);
		putsprite(i, x, y);
		i++;
	}
	
	// draw bunkers
	putsprite(sprite_bunkers, 10, y_bunkers );
	putsprite(sprite_bunkers +1, 56, y_bunkers );
	putsprite(sprite_bunkers +2, 102, y_bunkers );
	
	// draw ship of the player
    putsprite(sprite_player, player_x, player_y);   // draw the sprite of the ship played

    delayredraw(); 									// wait until the frame is drawn. 
}


// Main procedure
void main(){
	init();
	while(1){		
       step();
       draw();		
	}
}

Bon il reste des choses à faire: Ajout des tir des aliens, test de collision des aliens avec le vaisseau, ajustement des valeurs maxi et mini pour gérer les déplacements des aliens et après on s’attaquera aux sons et à la musique… mais ça avance

Bon maintenant pour la prise en main de l’environnement, je pense que l’on a assez fait de tests. Personnellement, ça ma permit de découvrir esplge. Le programme devient un petit peu trop complexe pour faire un bon tuto mais vous avez les exemples donnés par Igor / Corax accessibles ici: http://segamiga.com/esplge/doc/#/samples/game_tutorial

Quand à notre jeu, en rajoutant la musique, le son et les collisions, il est presque finalisé mais un peu long. je vais le mettre sur mon github here: https://github.com/Jicehel/Invaders-for-espLGE

Merci pour ce super tuto !
Je suis le webmaster de segamiga.com, accepteriez-vous que je le mette en tutoriel dans la doc ?

Bien sûr Loopingstar. Avec plaisir, qu’il serve au plus de gens possible. Par contre je l’ai écrit en même temps que mes premiers essais après avoir lu les tutos, la construction est donc un peu brouillon et ressemble plus à un Devlog qu’à un réel tuto. Si tu veux, je peux en réécrire un version qui soit plus un vrai tuto mais si ça te convient, prends le et corrige ou améliore un peu si tu vois des choses qui pourraient être mieux ou améliorer.

Tu en voudrais d’autre sinon ? Je pourrais en faire un n pong si tu veux (pour le Pong, pas de problème, je pourrais en faire un pour un casse-briques mais la gestion correcte des collisions n’ait pas aussi simple qu’il n’y parait).

Avec plaisir pour le tuto Pong. D’ailleurs, peux-tu faire le tuto Pong en premier (comme ça tu pourras le mener a son terme). Ce serait top si tu pouvais le faire en anglais (pour toucher un max de monde), mais ce n’est pas une obligation bien sur :wink:

OK, je vais le faire en français et j’essayerais de le traduire en anglais après mais il faudra sans doute corriger mes approximation en traduction…

1 Like

j’ai contacté Igor et Roman car je souhaite mettre a jour la doc avec les nouvelles fonctions sur le débugger série, etc.
Il faut aussi que je mette a jour la doc de l’éditeur.
… et que je finisse mes jeux aussi lol :wink:

2 Likes

J’ai commencé le tuto de Pong en anglais vite fait. Je repars des bases: la structure et l’affichage à l’écran.
Ca donne ça https://fdoc.fr/B2qW
(https://www.fichier-doc.fr/2021/01/13/tuto-pong-en-1/)
Je continuerais bien sûr prochainement.

C’est prometteur !
C’est bien de partir des bases.

As-tu finalisé la traduction de la doc en français ? Si oui, je pourrai l’intégrer a la doc/wiki

Non, je l’écris directement en anglais par facilité. Comme ça, je dis ce que je peux en anglais. Ce sera plus simple pour moi de le retraduire en français alors que si j’écris en français, je risque d’avoir plus de mal à le retraduire en anglais … :wink:
Mais bon, ça avance bien, ça devrait être fini avant dimanche soir en anglais et sans doute en français.
Je t’envois le lien dès que la première version du tuto en anglais est finie.
On pourra demander à Roman de la traduire en russe. En fait ce serait bien si ce genre de documents était systématiquement en Anglais / Russe / Français (et plus si des volontaires veulent traduire)

lol, je parlais de la traduction en français du ficher PDF Mode d’emploi de shando69, que tu m’avais envoyé l’année dernière, mis non finie

Ah ok, désolé, ça m’avait échappé. Ça m’était complètement sorti de la tête. Je regarderais ce soir. Je ne me souviens plus si j’avais fini après. Je regarde, je relis et je te renvois la dernière version dans tous les cas.

OK, je ne sais plus si c’est ce que je t’avais envoyé mais la dernière version que j’ai c’est celle là: https://fdoc.fr/f7R4
(https://www.fichier-doc.fr/2021/01/14/espboy---presentation/)