[TUTO] Structures et Listes Chainées, fond etoilé (MaJ 5.42)

Informations pour bien débuter en PureBasic
Avatar de l’utilisateur
Crystal Noir
Messages : 891
Inscription : mar. 27/janv./2004 10:07

[TUTO] Structures et Listes Chainées, fond etoilé (MaJ 5.42)

Message par Crystal Noir »

Voici un nouveau tutoriel que je vous ai concocté pour expliquer comment fonctionnent les listes chaînées.




Déjà pourquoi une liste chainée ? Prenons un exemple simple. Vous êtes en train de programmer un shoot'em up. Vous avez votre vaisseau et vous devez gérer les tirs du joueur. Il faut savoir que vous ne pouvez pas savoir combien de fois le joueur va tirer au cours de la partie. De ce fait si vous demandez à Purebasic de vous afficher un sprite de tir alors qu'il n'y en a plus à l'écran, le jeu va planter.

Damned ! Incroyable ! et pourtant...Quelques programmeurs utiliseront des tableaux pour gérer les tirs du joueurs. C'est à dire qu'il vont initialiser un tableau qui comptabilisera le nombre de tir à l'écran.

Seulement comme je vous l'ai dit vous ne pouvez pas prévoir le nombre de tir à l'avance, le joueur peut tirer 500 fois comme 10 000 fois si c'est un mega bourrin. De ce fait il faudra prévoir un tableau énorme (genre 10 000 en taille) par exemple. Résultat des opérations, vous avez un énorme tableau et en plus, il prend énormement de place en ressources. Donc pas très optimisée comme solution...

C'est ici qu'interviennent les Listes Chaînées (ou Linked List). Comme son nom l'indique, on crée une liste qui sera dynamique. C'est à dire que, dans notre exemple, la liste sera aussi grande qu'il n'y a de tirs dans le jeu. D'autres part ces tirs seront détruits de manière dynamiques aussi. Donc, la liste va s'agrandir, pour devenir plus petite et tout cela sans que vous n'ayez presque rien à faire.

Donc en résumé, une liste chaînée, c'est une liste d'éléments (comme les tirs) qui est gérée de manière dynamique et suivant les évènements du jeu. L'avantages, c'est que les listes ne prennent pas autant de essources qu'un énorme tableau et la liste s'adapte selon les évènements.

A quoi peuvent elles servir sinon ? A gérer par ex une liste d'ennemis (Invaders) ou des étoiles etc...

Généralement, même si ce n'est pas obligatoire, on couple les listes chaînées avec les structures. Alors kesako ca encore lol.

Je ne sais pas si vous avez déjà entendu de la programmation orientée objet ? Bon déjà ce n'est pas tout à fait la même chose, mais les objets ont quelques points communs. Un objet que vous créé en programmation, aura ses propres propriétés (par ex la position en x, en y, la vitesse...). Quand vous créez une structure, vous créez en fait un "objet" (entre "" car ce n'est pas le terme exact), auquel vous attribuez des propriétés qui pourront être utilisées par la suite.

Laissons la liste chaînée de côté, nous allons essayé de comprendre les structures. Admettons que vous vouliez faire un carnet d'adresse. Vous allez avoir dedans, le nom, le prénom et le numéro de téléphone par ex. Si vous voulez stocker ses données dans un tableau, vous imaginez le nb de dimensions que devra avoir le tableau pour tout stocker ? Pourquoi ne pas faire alors une structure qui se présenterait comme ceci :

Structure Carnet
nom.s
prenom.s
numero.w
EndStructure

Ensuite vous avez votre "objet" Carnet avec toutes ses propriétés et vous pourrez stocker dans une unique case d'un tableau, une occurence de cette structure. Par exemple, la case 1 du tableau stockera en même temps le nom, prénom et numero de quelqu'un. Tout ca d'un coup. Pratique non ? Dans le cas contraire vous auriez à créer plus dimensions dans votre tableau en disant par ex que la première ligne c'est la nom, la
seconde les prénoms, un vrai foutoir quoi...Vive les structures.

Les structures fonctionnent de la même manière avec les listes chaînées. Bon assez de blabla passons à la pratique. Pour comprendre les structures et les listes je vous propose de créer un simple scrolling paralaxe d'étoiles, un fond étoilé animé en somme.

Un fond à deux niveaux pour simuler la profondeur de la galaxie. C'est plus joli et plus réaliste. Pour cela nous avons besoin de deux sprites : une étoile claire (blanche), et une étoile un peu plus foncée pour mettre au second niveau et simuler la profondeur. Je vous laisse les faires ca se fait en 5 mns avec un soft de dessin.

Nommez l'étoile clair : star.bmp et l'étoile foncée : star2.bmp

ou telechargez celles-ci ;)

http://michel.dobro.fee.fr/Forum_PB/tut ... e/star.bmp
http://michel.dobro.fee.fr/Forum_PB/tut ... /star2.bmp

Ca y est ? vous avez vos deux petites images ? Magnifique, player one shoots again ! sinon c'est GameOver lol

Je le dis tout de suite je ne vais pas m'attarder sur les bases, donc je prend en considération que vous connaissez les bases (intialisation des sprites création de la fenêtre de jeu etc...)

Bien commencez par créer un nouveau projet avec PureBasic et mettez les lignes de code suivantes :

Code : Tout sélectionner

;Tutorial Listes Chaînées

;Initialisation des composants
InitSprite()
InitKeyboard()
InitMouse()
InitSound()


;Constantes
#Star = 1
#Star2 = 2
 
;Création de la fenêtre principale
SetRefreshRate(60)
MaFenetre = OpenScreen(800,600,32,"Test")
If MaFenetre  = 0
    MessageRequester("Erreur", "Impossible d'ouvrir une fenêtre DirectX", #PB_MessageRequester_Ok)
EndIf

Ici rien de bien difficile je ne reviendrai pas sur ces instructions : au départ on initialise les composants obligatoires du programme. La création de la fenêtre de jeu en 800 X 600 avec test si jamais le programme n'arrive pas à l'ouvrir.

Par contre j'ai créé deux constantes qui définiront nos sprites, à savoir : #Star et #Star2 qui identifieront les deux étoiles que vous avez dessinées précédemment.

Parfait continuons :

Code : Tout sélectionner

;Chargement des sprites
LoadSprite(#Star,"star.bmp")
TransparentSpriteColor(#Star,0)
Structure Etoile
    posx.f
    posy.f
    vitesse.f
EndStructure
Global NewList Stars.Etoile()
LoadSprite(#Star2,"star2.bmp")
TransparentSpriteColor(#Star2,0)
Structure Etoile2
    posx.f
    posy.f
    vitesse.f
EndStructure
Global NewList Stars2.Etoile2()
On commence par charger nos étoiles avec la commande LoadSprite, rien de difficile donc, on charge chaque étoile en lui attribuant la constante définie tout à l'heure. Dans mon exemple l'étoile se trouve sur un fond rouge. J'ai donc défini la couleur de transparence avec TransparentSpriteColor.

Pour chaque étoile nous avons créé une structure. Aaaaah voilà que cela devient intéressant. Ainsi une étoile aura une position en X (posx), une position en Y (posy) et une vitesse (vitesse). Vous remarquerez que à côté de chaque variable se trouve ".f" il s'agit en fait du type de variable, dans notre exemple des nombres flotants. Une étoile aura alors chacune de ces propriétés. Enfin pour chaque étoile nous créeons une liste avec l'instruction NewList (une liste chaînée donc). Prenons la première :

NewList Stars.Etoile()

Cette ligne signifie qu'on crée une liste du nom de Stars et utilisant la structure Etoile. Facile n'est ce pas ?

Nous avons effectué cette opération pour nos deux étoiles.

il serait temps maintenant de donner un mouvement à nos étoiles. Dans notre exemple, le scroll étoilé se fera à la verticale. Donc les étoiles vont évoluer suivant une position sur l'axe des Y (et donc verticale).

Apprenons directement à structurer nos programmes. Vu qu'il faut faire le mouvement pour les deux types d'étoiles, nous allons créer une procédure, qui sera appelé dans le programme principale. Ajoutez ces lignes de code :

Code : Tout sélectionner

Procedure DisplayEtoile()
    ResetList(Stars())
    While NextElement(Stars())
        If Stars()\posy > 600
            Stars()\posy = -5
        EndIf
        DisplayTransparentSprite(#Star, Stars()\posx, Stars()\posy)
        Stars()\posy + Stars()\vitesse
    Wend
    
    ResetList(Stars2())
    While NextElement(Stars2())
        If Stars2()\posy > 600
            Stars2()\posy = -5
        EndIf
        DisplayTransparentSprite(#Star2, Stars2()\posx, Stars2()\posy)
        Stars2()\posy + Stars2()\vitesse
    Wend
EndProcedure

Les procédures permettent de classer les instructions pour une action précise. C'est à dire que dans votre programme principale, au lieu d'avoir 5 000 lignes de code, vous appelerez juste le nom de la procédure qui regroupe l'ensemble des instructions nécessaire à l'action voulue. Ici cela sera de déplacer chaque étoile qui sera crée dans notre démo. Nous avons effectué l'opération pour les deux étoiles donc je vais vous expliquer la première partie, la seconde fonctionnant sur le même shémas que la première. Bon prêt ? ha non qu'est ce que je vois là ! on ne code pas sans une bonne bière et une pizza, alors ca c pas vrai ces newbie hein, bon bon on vous attend allez chercher, et viiite hein !

Bon ca y est ? et on parle pas la bouche pleine ! Donc je reprends....Bon les deux au fond la bas !

Répetez ce que je viens dire ? Et voilà yen a 2 qui suivent ! Donc commençons l'explication de ces lignes.

Rappelez vous que notre première liste (de la première étoile), s'appelle Stars. L'instruction ResetList demande à Purebasic de prendre la liste par le début (bah oui hein vous regardez pas votre liste de courses à l'envers !).

Nous avons ensuite une boucle While Wend. Elle signifie "Pour chaque élément Stars faire..." ou même "Tant qu'il y a des éléments Stars faire". En clair tant qu'on a dans la liste des étoiles alors...C pas dur mais que fais t'on ? Très simple on dit que si la position de notre étoile en Y est supérieure à 600 alors on la replace à une coordonnée Y de -5 donc en clair si elle sort de l'écran paf on la remet hors de l'écran, en haut. Ensuite on affiche l'étoile avec la commande DisplayTransparentSprite et on change la coordonnée Y en ajoutant la variable vitesse,que nous définirons tout à l'heure. Et voilà c fini, le mouvement de nos étoiles est programmé.

Au niveau de la syntaxe avec les listes chainées, sachez que pour appeler une variable qui a été définie dans une structure on fait : nomliste()\variable le "\" est important !

Bon c'est bien beau tout ca mais maintenant les étoiles, va bien falloir leur donner une position initiale et puis donner une valeur à la variable vitesse non ? Allons y en dessous ajoutez ceci :

Code : Tout sélectionner


For i  = 1 To 150
    AddElement(Stars())
    AddElement(Stars2())
    Stars()\posx = Random(800)
    Stars()\posy = Random(600)
    Stars()\vitesse = 2
    Stars2()\posx = Random(800)
    Stars2()\posy = Random(600)
    Stars2()\vitesse = 1
Next 

Ici on initialise le tableau. En clair la boucle FOR NEXT va tourner 150 fois et crée à chaque fois deux étoiles (avec les deux sprites) à des coordonnées aléatoires. Ce qui nous fait au total 300 étoiles.

Ensuite nous définissons la vitesse à 2 pour les étoiles claires, et à 1 pour les étoiles foncées, c ce qui va donner l'effet de profondeur. Remarquez que pour définir les variables on utilise toujours la syntaxe des listes chainée (c un coup à prendre mais vous verrez vous vous y ferez très vite).

Parfait, il ne reste plus qu'à programmer la boucle principale :

Code : Tout sélectionner

Repeat
    ClearScreen(RGB(0,0,0))
    DisplayEtoile()
    ExamineKeyboard()
    FlipBuffers()
Until KeyboardPushed(#PB_Key_Escape)
Très simple, on efface l'écran avec ClearScreen puis on appelle notre procédure pour afficher les étoiles, vous savez celle qu'on a créée tout à l'heure DisplayEtoile(). Ensuite on demande au programme d'examiner le clavier (si on appuie sur ECHAP on sort) et évidemment on fait un FlipBuffers() pour rafraichir l'image et là c magique, faites F5 ca marche waaaaaaaaaaaaaaa !

Je reste à votre disposition pour des infos complémentaires sur ce tutoriel, si vous avez des questions !

bon code !



------ EDIT ----- Mise à jour en PB 5.42 LTS en gardant au maximum le code de Crystal Noir
- Mise à jour de certaines commandes
- Création des 2 sprites plutôt qu'un chargement d'images (sans compter que les liens sont morts dans l'exemple ci dessus)
- Utilisation d'une seule structure pour les 2 sprites

J'ai commenté les modifs dans le code

Code : Tout sélectionner

;Tutorial Listes Chaînées
; Original code : Crystal Noir
; MaJ 5.42 : Ar-S

;Initialisation des composants
InitSprite()
InitKeyboard()
InitMouse()
InitSound()

; On déclare les futures procédures pour qu'elles soient repérées en début et/ou fin de code
Declare Creation_Etoile()
Declare DisplayEtoile()

; On va déclarer les 2 constantes étoile en commençant par 1
Enumeration 1
#Star
#Star2
EndEnumeration

Global NombreEtoile = 50

; 1 structure peut servir à plusieurs sprites

Structure Etoile
    posx.f
    posy.f
    vitesse.f
EndStructure

  
Global NewList Stars.Etoile()
Global NewList Stars2.Etoile()


;Création de la fenêtre principale

MaFenetre = OpenScreen(800,600,32,"Test",#PB_Screen_SmartSynchronization,60)

If MaFenetre  = 0
  MessageRequester("Erreur", "Impossible d'ouvrir une fenêtre DirectX", #PB_MessageRequester_Ok)
  End ; on quitte
  
  
Else
  
  Creation_Etoile()

; Boucle principale
  Repeat
    ClearScreen(RGB(0,0,0))
    DisplayEtoile()
    ExamineKeyboard()
    FlipBuffers()
  Until KeyboardPushed(#PB_Key_Escape)
  
EndIf


  
  
  
  
  
  ; /// PROCEDURES ////////////////////
Procedure Creation_Etoile()
  ; // Création des sprites
  ; Etoile 1
  ; - Creation du sprite vide
  CreateSprite(#Star,2,2)
  ; On remplit ce sprite d'une étoile blanche de 2x2 pixels
  StartDrawing(SpriteOutput(#Star))
  Box(0,0,2,2,RGB(255,255,255) )
  StopDrawing()
  
  ; Etoile 2
  ; - Creation du sprite vide
  CreateSprite(#Star2,2,2)
  ; On remplit ce sprite d'une étoile rouge de 2x2 pixels
  StartDrawing(SpriteOutput(#Star2))
  Box(0,0,2,2,RGB(255,0,0) )
  StopDrawing()
EndProcedure





Procedure DisplayEtoile()
    ResetList(Stars())
    
    For i  = 1 To NombreEtoile
      AddElement(Stars())
      AddElement(Stars2())
      Stars()\posx = Random(800)
      Stars()\posy = Random(600)
      Stars()\vitesse = 2
      Stars2()\posx = Random(800)
      Stars2()\posy = Random(600)
      Stars2()\vitesse = 1
    Next 
    
    
    While NextElement(Stars())
        If Stars()\posy > 600
            Stars()\posy = -5
        EndIf
        DisplaySprite(#Star, Stars()\posx, Stars()\posy)
        Stars()\posy + Stars()\vitesse
    Wend
   
    ResetList(Stars2())
    While NextElement(Stars2())
        If Stars2()\posy > 600
            Stars2()\posy = -5
        EndIf
        DisplaySprite(#Star2, Stars2()\posx, Stars2()\posy)
        Stars2()\posy + Stars2()\vitesse
    Wend
  EndProcedure
  
  ; /////////FIN PROCEDURES ////////////////////////////////////
Dernière modification par Crystal Noir le mar. 09/nov./2004 9:52, modifié 1 fois.
Fred
Site Admin
Messages : 2648
Inscription : mer. 21/janv./2004 11:03

Message par Fred »

Tres clair, nickel.
Avatar de l’utilisateur
Crystal Noir
Messages : 891
Inscription : mar. 27/janv./2004 10:07

Message par Crystal Noir »

merci :)
Revan
Messages : 10
Inscription : mer. 18/août/2004 21:09
Localisation : Paris
Contact :

Message par Revan »

Je sais que j'suis en retard, mais je trouve ton tuto très bien fait. C'est le premier que j'ai lu, et il m'a motivé pour apprendre ce langage :D

C'est écrit avec des mots simples et ça rentre plus facilement qu'avec des termes tordus.

Continues comme ça si tu refais d'autres tutos à l'avenir stp :)
Psycode
Messages : 131
Inscription : lun. 23/août/2004 18:47
Localisation : Jupiter ?

Message par Psycode »

Plusieurs choses a dire :
- remet en forme le texte pour supprimer les espacements.
- indique quelle taille a peu pret doit avoir les 2 etoiles a dessiner (car a premiere vu, on pourrais croire que tu parles de simple point.... et dessiner un point en le sauvegardant en BMP, ca fait zarb)
- tu dis que les etoiles sont sur fond rouge. On ne sais pas d'ou viens ce rouge (est ce le dessinateur des etoiles qui DEVAIT les faire sur fond rouge ????)
Avatar de l’utilisateur
Crystal Noir
Messages : 891
Inscription : mar. 27/janv./2004 10:07

Message par Crystal Noir »

En fait la taille n'a pas d'importance. Evidemment si tu fais une étoile énorme de 10 cm de diamètre ca devient un ballon :lol:

Non je ne parle pas forcément de points, mais pour faire un test on peut faire un point si on veut, moi j'ai peaufiné un peu en faisant un chtite étoie avec des branches ou alors un point avec à coté une nance de clair (pour la brillance) c'est à chacun de voir il n'y a pas de règle.

Pour le fond rouge non plus ca n'a pas d'importance. En fait si tu regardes le tuto, j'explique que moi, mes étoiles étaient sur fond rouge c ma manière à moi de travailler. Le fond rouge c'était pour expliquer tout simplement la commande TransparentSpriteColor rien d'autre. C'est pour expliquer à quoi servait cette commande. Bien sur si vous dessinez sous fond noir le paramètre de TransparentSpriteColor va changer :p
Cinabre
Messages : 11
Inscription : dim. 31/oct./2004 15:17
Localisation : Maine et loire
Contact :

Message par Cinabre »

Très bien fait ce tuto. :)
Mais surtout pour l'utilisation simple des listes chainées.
Un simple tableau aurait rempli la même fonction. :wink:
Ce qui m'interesse, c'est surtout comment ajouter et supprimer dynamiquement les éléments de la liste. Et que ce soit "propre" :!:
Avatar de l’utilisateur
Crystal Noir
Messages : 891
Inscription : mar. 27/janv./2004 10:07

Message par Crystal Noir »

merci :)

oui la particularité des listes c bien de pouvoir changer dynamiquement son contenu quelque soit la taille de la liste.

Sur un tableau un changement de dimensionnement fait un reset des données contenues dedans....
garzul
Messages : 683
Inscription : mer. 26/mai/2004 0:33

Message par garzul »

Tu peux allez voir mon jeux aussi qui utilisent les liste chainer à fond ( Aranoîde )
Stef Leflou
Messages : 5
Inscription : ven. 18/juin/2004 17:58

Message par Stef Leflou »

D'une clarté limpide !

Bravo pour ces explications... et merci

(je sais je suis en retard d'un siècle mais bon c'est dit)
Une dernière après j'arrête
Avatar de l’utilisateur
Crystal Noir
Messages : 891
Inscription : mar. 27/janv./2004 10:07

Message par Crystal Noir »

merci :D
bombseb
Messages : 445
Inscription : jeu. 25/août/2005 22:59
Localisation : 974
Contact :

Message par bombseb »

super ton tuto !

maintenant je sais exactement comment utiliser les structures et les listes chainée !
meganet
Messages : 317
Inscription : jeu. 20/janv./2005 22:00

Trop kool!

Message par meganet »

Trop kool se tuto j'en avait grave besoin j'ai enfin bien compris merci beaucoup!
Avatar de l’utilisateur
Dionyzos
Messages : 53
Inscription : jeu. 05/févr./2004 19:57

Message par Dionyzos »

Bonjour,

J'ai une question (peut-être bête) pour les créateurs de PB au sujet des "Structures" :

Pourquoi avoir utiliser l'antislash ("\") pour délimiter les propriétés de leur structure parente, plutôt qu'un point (".") comme dans bcp de langages ? :|
Dr. Dri
Messages : 2527
Inscription : ven. 23/janv./2004 18:10

Message par Dr. Dri »

Parce que le point sert à définir le type des variables.
Pourquoi avoir choisi le point pour le type des variables ?

Dri :?:
Répondre