Le composant Delphi X

Voilà une suite de composants dont on parle assez dans le monde de la programmation et c'est donc pour cela que je m'y suis mis.

Cette suite sert avant tout à faire des jeux mais personne ne vous y oblige, on peut aussi créer d'autre type de programme comme des animations graphiques ou bien autre chose.

Ces composants sont assez simples d'utilisation ils ne réclament pas un grand niveau Delphi même s'il faut avoir une connaissance du système de classe qui est assez utilisé.

Nous allons donc supposer que vous savez manier cette technique, mais si ce n'est pas le cas voilà une brève explication.

Attention ce composant ne fonctionne qu'avec les versions 3,4 et 5 de Delphi. Pour faire fonctionner ce composant sur des versions plus récentes voici un lien.

Le but de ce tutoriel sera de faire un jeu. Vous le verrez naître à partir de rien si ce n'est une des plus belles suites de composant Delphi (pour moi bien sur).

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. L'installation

Vous pouvez télécharger DelphiX ici.

Pour l'installer, il vous suffit d'exécuter dans le répertoire..\Bin\.. le fichier qui correspond à votre version de Delphi.

Et voilà c'est fait !!

II. La préparation

Nous allons déjà placer nos composants DelphiX et les décrire 1 à 1, je vous conseille de placer vos composants en lisant ce didacticiel.

Notre fiche va devoir contrôler des composants Delphi X, va devoir passer en plein écran, et d'autre action graphique qu'elle n'aurait pas l'habitude de faire. Pour que ceci se fasse plus facilement nous allons devoir changer le type de notre form, allez au début de votre unité et sous la rubrique type vous avez ceci :

 
Sélectionnez
type
TForm1 = class(TForm)

Changez-le-en :

 
Sélectionnez
type
TForm1 = class(TDXForm)//La class DXFORM et similaire à la class form si ce n'est quel enlève des problèmes entre les composants delphiX et la fiche, elle requière aussi l'unité DXclass que vous devrez ajouté dans le groupe uses.

Ceci n'est pas obligatoire mais recommandé.

Maintenant nous allons pouvoir poser nos composants :

  • DXdraw : le seul composant visuel de la suite, c'est la que passera tout ce que vous afficherez à l'aide des autres composants DelphiX.
  • DXimagelist : c'est une bibliothèque où vous mettrez toutes les images que vous utiliserez.
  • DXtimer : c'est un sosie de notre timer habituel sauf qu'il est spécialement dédié à notre suite DelphiX. Il contient plusieurs options en plus et par exemple une option lag count qui permet d'éviter les retards si votre affichage et ralenti pour une raison inconnue, car Il est tout de même beaucoup plus rapide(100 fois +) que notre timer d'origine.
  • DXsprite : c'est lui qui générera tous les sprites de votre programme, les fera bouger, les détruira.
  • DXinput : indique les touches utilisées.

Bon nous allons nous arrêter là et nous décrierons les autres plus tard, car ce ne sont pas les seuls, vous l'avez remarqué et en plus j'ai beaucoup de chose à vous faire faire avec ceux-là.

Nous allons voir la préparation de tous ces composants pour que votre programme soit prêt à fonctionner :

II-A. DXdraw

Dans l'inspecteur d'objet changer ceci :

 
Sélectionnez
align = alclient

Notre DXdraw aura la même taille que notre fiche (c'est le seul composant visuel que nous ayons ).

 
Sélectionnez
Option -- fullscreen = true

Pour afficher en plein écran : il faudra aussi enlever les bordures de notre fiche pour quelles n'apparaissent pas en plein écran (borderstyle = bsnone).

Pour cacher la souris et la remplacer plus tard par une image nous allons utiliser :

 
Sélectionnez
dxdraw1.cursor := crnone;//Signifie que le curseur sera invisible

Que nous ajoutons dans l'événement create de notre fiche.

Pour ne pas avoir un flash de l'écran à l'ouverture modifier l'autoinitialize en false et dans votre form1create ajoutez :

 
Sélectionnez
Dxdraw1.initialize;//procedure qui prépare l’écran

II-B. DXsprite

Pour fonctionner ce composant à besoin d'une chose très importante le nom du composant DXdraw, indiquer le lui avec la propriété dxdraw.

II-C. DXtimer

La vitesse : là mettez ce que vous voulez mais 33 me semble bien, car de toutes manières il est dur d'avoir un rafraîchissement de l'image supérieur à celui-ci.

Votre DXtimer va servir à réinitialiser votre écran et vos sprites ainsi que les touches donc dans sa propriété timer ajoutez :

 
Sélectionnez
DXdraw1.Surface.Fill(0); // Supprime tout ce qui composait l’écran.
dxspriteengine1.Move(lagcount);//execute la procedure onmove de chaque sprite.
dxspriteengine1.dead; /Détruit les sprites d’on la fonction dead à été activé.
dxspriteengine1.Draw; //Dessine les sprites
dxdraw1.Surface.Canvas.Release;//remet à jours le canvas
dxinput1.Update;//remet à jours les touche tapés car ce composant ne le fais pas seul.
dxdraw1.Flip;//Remet à jours l’écran

II-D. DXimagelist

Bibliothèque d'image, vous pourrez y mettre toutes les images que vous voulez .

Vous pourrez rendre ces images en partie invisibles en précisant la couleur des parties invisibles.

Et précisez dxdraw1 dans la propriété dxdraw.

Pour les images, installez plutôt ce fichier image.dxg qui contient les images utilisées par ce tutoriel.(En double cliquant sur le composant et sélectionnant ce fichier vous les aurez).

II-E. DXinput

Rien à signaler si ce n'est que si vous voulez vous servir de touches spécifiques ajoutez les en double cliquant sur le composant.

II-F. Conclusion

Bon si vous exécutez votre programme à ce moment-là vous aurez un écran vide et pour fermer alt + F4 sera la seule solution donc nous allons ajouter une possibilité de fermer avec la touche echap.

Dans le dxinput mettez button1 en echap (double cliquez sur le composant pour le faire) et dans votre timer ajouter:

 
Sélectionnez
if isbutton1 in dxinput1.States then
//dxinput.state comporte la liste des touches qui ont été pressées depuis la dernière mise à jours de celle-ci(avec dxinput.update) close; //fermeture de l'application si echap est pressée.

Et voilà c'est tout.

III. Le jeu

Oui nous allons passer au jeu en lui-même.

Alors quel jeu va-t-on faire ?

Euh je sais pas moi ?

Tiens on va animer des petits animaux, bonne idée, non je déconne !

Ah je sais ! Disons un jeu ou l'on tire sur des bêtes qui sortent de terre.

C'est mieux ça ? Bon alors elles ne sortent par de terre elles voleront.

De toutes manières vous n'avez pas le choix c'est moi qui décide et en plus ce n'est qu'un exemple pour qu'à la fin vous puissiez faire votre propre jeu.

IV. Les sprites

Bien donc commençons.

Les sprites c'est quoi exactement ?

Pour moi, des imaged animées ou non qui se déplacent ou non et qui réagissent ensemble ou non. Par exemple c'est la fumée de la cheminée dans ZELDA, les combattants dans STREET FIGHTER et aussi la balle et les barres dans PONG.

Donc dans notre jeu quels seront les sprites ?

Eh bien il y aura un viseur avec lequel on tirera, un sprite de bête le même pour toutes et il y aura aussi notre fond qui sera un backroundsprite(il sera affiché en mosaïque).

Tout notre jeu sera composé de sprites.

Alors voilà pour créer un sprite il y à plusieurs méthodes, on peut le créer sans rien déclarer mais nous ne pourrons rien en faire, il sera à l'écran et c'est tout aucune collision aucun mouvement, rien. On peut aussi le déclarer là deux autres possibilités entre en compte.

On peut le créer sans garder de référence, mais utiliser une variable pointeur permet d'accéder facilement à ses propriétés.

Dans les deux cas il pourra bouger et réagir aux collisions.

Notre fond sera créé puis plus jamais nous n'y toucherons donc il sera créé sans avoir été déclarer.

Les ennemis seront créés puis eux non plus ne seront plus jamais utilisés mais ils se déplaceront tout de même donc déclarons-les sans leur attribuer de variable.

Le viseur lui bouge et il faudra pouvoir y accéder lorsque la souris bougera donc déclarons-le ainsi qu'une variable de son nom.

V. Le fond

Rien de plus simple pour le créer allons dans l'événement create de notre fiche et ajoutons ces quelques lignes :

 
Sélectionnez
with TBackgroundSprite.Create(DXSpriteEngine1.Engine) do
//nous créons un fond et comme une fois créé nous ne pourrons plus y accéder(puisque nous avons aucun pointeur ou variable pour connaître sa position) nous allons utiliser with pour préciser ses propriétés
begin
SetMapSize(1, 1);//marque la taille du sprite, ici 1 image sur 1.
Image:=Form1.DXImageList1.Items.Find('fond');//l'image du fond sera celle qui se trouve dans DXImageList1 et qui porte le nom fond
Width := Image.Width;
Height := Image.Height;//la hauteur et la largeur du sprite seront egals à celles de l'image.
Z := 0;
{Z correspond à la couche sur laquelle est notre image si l'on fait un sprite sur la couche 1 il sera visible mais si ont fait un sprite sur la couche -1 il sera invisible.}
Tile := true //notre image sera répétée sur tout l’écran.
end;

Voilà vous n'aurez plus rien à y faire le composant spriteengine s'occupe de l'afficher à l'écran si vous avez bien pensé à mettre spritengine.draw dans le timer(ce que nous avons fait au début).

Si vous exécutez vous verrez un joli fond.

Petite précision sur setmapsize si nous avions mis tile := false alors nous n'aurions eu qu'un exemplaire de notre image et si avec ceci il avait eu setmapsize(2,2) ont aurai eu un rectangle de 2 image sur 2 que composera notre fond.

VI. Le viseur

Donc notre viseur devra lui être déclaré, car il a certaines propriétés qui devrons être déclarées sous forme de procédure. Alors dans type ajoutons :

 
Sélectionnez
Tviseur = class(TImageSprite);

Et dans var une variable

 
Sélectionnez
viseur : Tviseur;

Ensuite pour le créer dans l'événement create de notre fiche de la même manière que notre fond nous allons ajouter :

 
Sélectionnez
viseur := Tviseur.Create(DXSpriteEngine1.Engine);
with viseur do
begin
Image:=Form1.DXImageList1.Items.Find('viseur');//indique l'image
Width := Image.Width; //reprend la largeur de l'image
Height := Image.Height; // et sa hauteur ce qui fait correspondre la taille du sprite avec celle de l'image.
Z := 2;
end;

Vous l'avez vue notre Z est 2, car il faut bien que le viseur soit supérieur au fond, sinon on ne le verrait pas.

Pour le reste pas de problème.

Ensuite dans notre dxdraw dans l'événement onmousemove ajoutons :

 
Sélectionnez
viseur.X:= X;
viseur.Y := Y;

Et voilà.

Mais pourquoi avoir déclaré un classe intermédiaire nommée Tviseur ?

Quelle est cette procédure si importante?

Vous comprendrez plus tard.

VII. Nos petites bêtes

Elles seront mouvantes et au départ elles ne seront pas à l'écran.

On les créera dons à l'extérieur ,hihi pas sur votre table de salon mais toujours dans le canvas de l'écran seulement comme c'est une table de dessin virtuelle on peut dessiner à coté chouette non ?

Alors voilà nos Tenemi (nos bébête quoi).

 
Sélectionnez
Tenemi = class(TImageSprite)
public
sens : boolean; //variable qui indiquera plus tard le sens de direction de nos bêtes
procedure Domove (movecount : integer); override; //procédure propre au timagesprite donc utilisons override pour remplacer celle d'origine
end;

Alors vous en dites quoi elles ont un sens de direction et une procédure de déplacement qui sera exécuté à chaque dxspriteengine1.Move(lagcount); donc tous les temps de millisecondes.

Bon comme nos ennemis seront créés régulièrement nous allons créer une procedure de création d'ennemi.

 
Sélectionnez
procedure tform1.creaenemi;//pensez à déclarer procedure creaenemi; dans notre tform
begin
//---------------------UN ENEMI-----------------
with Tenemi.Create(form1.DXSpriteEngine1.Engine) do
begin
Image:=Form1.DXImageList1.Items.Find('enemi');//déclare l'image
Width := Image.Width;
Height := Image.Height;
sens := random(2) = 1 ;//choisi aléatoirement le sens de notre sprite
if sens then X:=screen.width+image.width // choisi sa position en fonction du sens pour être sur qu'il soit cacher au debut
else
X := -(image.width);//en dehors de l’écran
Y:=random(screen.height);
//positionnement aléatoire
Z:=2;
pixelcheck := true; //cette variable spécifie si les collisions doivent être prisent à l'image près (donc s'il y a des transparences elles seront ignorées) ou si cela doit être au pixel près.
end;
//--------------------------FIN--------------------
end;

Ensuite il faut s'occuper du déplacement de nos ennemis.

 
Sélectionnez
procedure Tenemi.Domove (movecount : integer);
begin
inherited Domove(movecount);
 
{les sprites peuvent être animé avec les propriétés anim et Pattern.
dans la source de DelphiX on remarque que le changement d'image (l'animation) se fait dans le domove de timagesprite
Mais pour que cette action d'animation ce fasse il faut appeler la fonction de l’ancêtre de notre Tenemi, Timagesprite.
Car pour ceux qui ne le savent pas inherited appelle les routines de l'ancetre.
Ici on pourrait juste mettre inherited; car cela appelle la procédure du même nom}
//ici il n'y aura pas d'animation de sprite je vous expliquerais plus loin pourquoi.
 
if sens then X:= X-3 else X := X+3;//déplacement du sprite en fonction du sens choisi
if (X =screen.Width) or (X <= 0) then sens := not sens;// si le sprite est arriver en dehors de l&#8217;écran il change de sens
end;

Bien maintenant comment vont être créés nos ennemis ?

Nous allons créer une variable reste :integer.

Elle indiquera le nombre d'ennemi à créer. Au début dans l'événement create de notre fiche donnons lui la valeur 5. puis dans notre Ttimer ajoutons ceci.

 
Sélectionnez
if (reste 0) then
begin
dec(reste);
form1.creaenemi;
end;

Lancez l'application et vous aurez droit à un superbe jeu.

VIII. ?

Bah aH AH je vous ai eu il ne marche pas le jeu mais pourquoi à votre avis.

Oh je vais vous le dire, car vous avez bien dû trouver (en + si cela ce trouve vous avez corrigé le problème).

Bien sur vous n'avez jamais dit quand il faut détruire les ennemis.

Donc nous allons regarder quand cela serai utile.

Eh bien le mieux c'est le viseur, car c'est lorsque l'on clique qu'on vérifiera le problème et si chaque bête devait le faire ce serait trop long.

Donc ajoutons à notre viseur une option de vérification des collisions.

Avant :

 
Sélectionnez
Tviseur = class(TImageSprite);

Et maintenant :

 
Sélectionnez
Tviseur = class(TImageSprite)
public
procedure DoCollision (Sprite : TSprite; var Done : boolean); override;
//verifie la collision entre notre viseur et nos ennemis
end;

Et voilà pourquoi une classe Tviseur.

C'est mieux mais que faut-il faire en cas de collision et bien continuons.

 
Sélectionnez
procedure Tviseur.Docollision(Sprite : TSprite; var Done : boolean);
begin
inherited Docollision(sprite,done);
if (sprite is tenemi) then
begin
tenemi(sprite).dead;//on détruit l&#8217;ennemi
inc(reste)//reste indique le nombre d&#8217;ennemi à créer
 
end;
 
end;

Puis dans l'événement onclick de notre dxdraw on ajoute :

 
Sélectionnez
viseur.Collision;

Et voilà c'est bon là vous pourrez jouer à votre petit jeu.

IX. C'est triste un jeu de ce genre

Bah oui c'est triste, pas de score, d'explosion, d'animation ,de sons. Alors je vais donc vous passer en revue quelques améliorations.

IX-A. Le sens

Il nous faudrait deux images d'ennemi, car s'il vient de la droite ou de la gauche il n'a pas la même tête donc ajoutons une image enemi2(dans notre dximagelist) et dans notre enemy.domove on fait ceci :

IX-A-1. Avant

 
Sélectionnez
procedure Tenemi.Domove (movecount : integer);
begin
inherited Domove(movecount);
if sens then X:= X-3 else X := X+3;
if X >=screen.Width then sens := true;
if X <= 0 then sens := false;
end;

IX-A-2. Après

 
Sélectionnez
procedure Tenemi.Domove (movecount : integer);
begin
inherited Domove(movecount);
if sens then X:= X-3 else X := X+3;
if X =screen.Width then
begin
sens := true;
Image:=Form1.DXImageList1.Items.Find('enemi');//en changeant de sens on change l'image
end;
if X <= 0 then
begin
sens := false;
Image:=Form1.DXImageList1.Items.Find('enemi2');
end;
end;

C'est tout.

IX-B. Le son

Pourquoi pas !!

À chaque clique de souris faire retentir un bruit (coup de feu…)

ajoutons un DXWaveList (base de donné de son) et un DXSound (lecteur).

Le dxwavelist est similaire à un dximagelist sauf que c'est une banque de donnés qui comporte des sons et non pas des images.

Le dxsound lui se compare plus à un dxdraw.

Dans notre DXWaveList on met dxsound1 dans la propriété dxsound et ont met notre son que l'on nommé bang ici (en double-cliquant sur le composant et sélectionnant ce fichier vous l'aurez tout près).

Ensuite il vous reste juste à ajouter cette ligne dans votre l'événement onclick de notre dxdraw1 :

 
Sélectionnez
form1.DXWaveList1.Items.find('bang').Play(false);

La propriété qui accompagne la procédure play signifie qu'il faut attendre la fin du son pour continuer les autres routines.

IX-C. Les Explosions

Ben oui ce n'est pas drôle, la bête elle disparaît sans rien alors on va la faire exploser (enfin un truc dans ce genre hi hi!).

Alors créons une nouvelle classe :

 
Sélectionnez
TBoom = class(TImageSprite)
public
procedure Domove (movecount : integer); override;
end;

Notre Tboom sera une animation de quelques images donc pour le créer on ajoutera une procedure boom(LX, LY :integer) (les variables LX et LY correspondent à la position de notre explosion celle de la bête ).

On décrit cette procédure:

 
Sélectionnez
procedure Tform1.Boom(LX,LY:integer);
begin
with Tboom.Create(form1.DXSpriteEngine1.Engine) do
begin
X:=LX;
Y:=LY;
Z:=2;// même hauteur que les bêtes.
Image:=Form1.DXImageList1.Items.Find('boom');
Width := Image.Width;
Height := Image.Height;
AnimCount:=Image.PatternCount;//nombre d'image dans l'animation
AnimLooped := false; //animation en boucle
AnimPos := 0;//image de départ pour l'animation
AnimSpeed := 1/10; //vitesse d'animation
end;
end;

Alors on va décrire un peu le truc.

On a créé un sprite Tboom avec l'image boom anim count correspond au nombre d'image animlooped indique si oui ou non ce sprite par en boucle.

Animpos l'image sélectionné. Animspeed la vitesse d'animation.

Patterncount correspond au nombre d'image dans notre image boom (car elle est divisé en plusieurs images), pour décrire ces images modifiez la propriété pattern dans votre image(cela correspond au dimension d'une image).

Notre sprite sera créé à chaque destruction d'une bête donc ajoutons dans la collision de notre sprite viseur cette petite ligne :

 
Sélectionnez
Form1.boom(sprite.X,sprite.Y);

Bien sur il faut ajouter une procédure domove pour lui indiquer quand il doit se détruire.

Donc voilà notre domove :

 
Sélectionnez
procedure Tboom.Domove (movecount : integer);
begin
inherited Domove(movecount);
if animpos = animcount-1 then dead;
end;

Ceci fait en sorte que si la position de l'animation est égal à celle de la dernière image du sprite.

IX-D. Le Score

Et oui il faut un score pour savoir où on en est.

Donc commençons par déclarer une variable

 
Sélectionnez
Score : integer;

Ensuite dans l'événement create de notre fiche on met score := 0 et à chaque collision de viseur inc(score).

Mais on a notre score Maintenant il faut l'afficher donc dans l'événement timer du timer avant de dessiner notre écran donc en après avoir déplacé les sprites (au cas ou un d'autre eux soit mort) et dessiné ceux-ci, on met cette ligne qui écrit notre score en haut à gauche de l'écran.

 
Sélectionnez
t := inttostr(score);
dxdraw1.Surface.Canvas.TextOut(1,1,'SCORE : '+t);

Bien sur déclarez t en tant que string.

Vous pouvez bien sur changer la taille ou la police ect (couleur & co…).

Par exemple :

 
Sélectionnez
dxdraw1.Surface.Canvas.Font.Color := clblue;
dxdraw1.Surface.Canvas.Font.Height := 40 ;

Ce qui donne un truc dans ce genre :

SCORE : 0

X. Mais pourquoi ils ne sont pas animés nos ennemis

Eh bien cela aurai été avec plaisir mais malheureusement un bug dans DelphiX nous en empêche.

Je vais donc vous donnez ce que j'ai trouvé au sujet de ce bug.

Lorsque que l'image n'est pas sur sa position 0 de l'animation et que vous avez choisi pixelcheck (ce qui est fait le plus souvent), eh bien la collision est toujours fausse.

Enfin je ne vais pas vous ennuyer avec cela amusez-vous bien avec votre nouveau joujou euh..pardon composant :-).

XI. Petit récapitulatif

Oui, un petit bilan s'impose pour que tout soit bien en ordre.

Pour pouvoir commencer il faut mettre les composants.

Ensuite il faut préparer la fiche (dxform,borderstyle…) et dans notre timer mettre les actions de réactualisation des sprites et de l'écran(enfin le dxdraw).

Pour afficher des sprites il faut les créer.

Pour cela on peut les créer avec ou sans référence et avec ou sans classe.

Dans delphiX il y a plusieurs types de sprite mais le plus utilisé est Timagesprite.

Chaque Timagesprite à entre autre un propriété domove et une propriété docolision, Ces propriétés sont importantes, car se sont-elles qui rendront nos sprites mouvants et réactifs aux autres sprites donc retenez bien ces deux propriétés .

Et pour finir on peut toujours utiliser directement le dxdraw et écrire dessus(avec le canvas) comme nous l'avons fait mais nous pouvons aussi dessiner avec des procédures du dximagelist, draw (dessine) drawadd(dessine avec des effets de transparence(alpha blending)) et quelques autre que vous découvrirez en regardant les exemples fournis avec delphiX vous y découvrirez par exemple un joli shoot'em up (Samples\Sprite\Shoot) ou un exemple dans lequel toutes les manières de dessiner une image (en vague en alpha en agrandissement etc.) sont présentes.

C'est fini il ne vous reste plus qu'à télécharger les exemples de ce tutoriel si vous en avez besoin et si vous avez des problèmes des critiques ou autre chose voici mon mail, .

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Bodman . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.