Entwickler-Ecke

Delphi Tutorials - DelphiX einfache Bewegung einer Figur + PixelKollision


F34r0fTh3D4rk - Mo 27.06.05 12:01
Titel: DelphiX einfache Bewegung einer Figur + PixelKollision
DelphiX einfache Bewegung einer Figur + PixelKollision

So Leute, das ist mein 2. Tutorial, welches sich wiedermal um DelphiX dreht.
Jemand hat sich dieses Tutorial gewünscht und soll es auch bekommen :D

Fangen wir dann mal an.

(1). Komponenten platzieren und einstellen

- Platziert ein DXDraw Objekt auf eurem Formular.

- Stellt dort Align auf alClient.

- Jetzt noch eine DXImageList.

- Dort muss noch per doppelklick das DXDraw ausgewählt werden (Bilder fügen wir später hinzu).

- Jetzt noch eine DXSpriteEngine, dort das gleiche mit dem DXDraw.

- Einen DXTimer, mit interval 0 (man kann auch andere Werte nehmen, um die FPS einzudämmen, aber so kommt man eben auf die höchste Framezahl und das ist für Kollisionen ganz nützlich).

- Als letztes noch ein DXImput

(2). Die richtigen Animationen

- Für dieses Tutorial solltet ihr die beiligenden (Anhang) Animationen verwenden.

- Doppelklick auf das DXImageList Objekt und wählt die Bilder aus dem anhang aus.

- Ordnet die Bilder in dieser Reihenfolge:

-das 2. Bild der Animation ist das Bild indem keine Bewegung stattfindet, also die Figur steht, von dem Bild gehen dann alle Bewegungen aus.

- TransparentColor der Bilder muss auf clFuchsia und Transparent auf true stehen

- Patternwidthund Patternheight stellen wir für unser Beispiel auf 19, das sind Breite un Höhe der einzelnen Bilder.

(2). Die Animation

- So, zu allererst eine neue Klasse:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
type
  TPlayer = class(TImageSprite)
  public
    procedure DoMove(MoveCount: Integer); override;
    procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
  end;

- DoMove wird unsere Bewegung.

- DoCollision ist später für die Kollision wichtig.

- Nun zur eigentlichen Bewegung (sieht wilder aus, als es ist):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
procedure TPlayer.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  AnimStart := 1;
  AnimCount := 0;
  If (isup in GameForm.DXInput1.States) and not (isdown in GameForm.DXInput1.States) and not (isleft in GameForm.DXInput1.States) and not (isright in GameForm.DXInput1.States) then
    with Player1 do
      begin
        image := GameForm.DXImageList1.Items[0];
        AnimStart := 0;
        AnimCount := image.patterncount;
        y := y - 1;
      end;
  If (isdown in GameForm.DXInput1.States) and not (isup in GameForm.DXInput1.States) and not (isleft in GameForm.DXInput1.States) and not (isright in GameForm.DXInput1.States) then
    with Player1 do
      begin
        image := GameForm.DXImageList1.Items[1];
        AnimStart := 0;
        AnimCount := image.patterncount;
        y := y + 1;
      end;      
  If (isleft in GameForm.DXInput1.States) and not (isright in GameForm.DXInput1.States) and not (isup in GameForm.DXInput1.States) and not (isdown in GameForm.DXInput1.States) then
    with Player1 do
      begin
        image := GameForm.DXImageList1.Items[2];
        AnimStart := 0;
        AnimCount := image.patterncount;
        x := x - 1;
      end;
  If (isright in GameForm.DXInput1.States) and not (isleft in GameForm.DXInput1.States) and not (isup in GameForm.DXInput1.States) and not (isdown in GameForm.DXInput1.States)  then
    with Player1 do
      begin
        image := GameForm.DXImageList1.Items[3];
        AnimStart := 0;
        AnimCount := image.patterncount;
        x := x + 1;
      end;
end;

- Also nehmen wir das gewurschtel mal auseindander:

Delphi-Quelltext
1:
2:
3:
  inherited DoMove(MoveCount);  //altes DoMove aufrufen
  AnimStart := 1;  //Das "Standbild"
  AnimCount := 0;  //Animation steht still

- dann taucht 4 mal sowas hier auf,

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
  If (isup in GameForm.DXInput1.States) and not (isdown in GameForm.DXInput1.States) and not (isleft in GameForm.DXInput1.States) and not (isright in GameForm.DXInput1.States) then
    with Player1 do
      begin
        image := GameForm.DXImageList1.Items[0];
        AnimStart := 0;
        AnimCount := image.patterncount;
        y := y - 1;
      end;

- erstmal wird abgefragt, ob nicht schon eine andere Richtungstaste gedrückt wurde, weil dann soll garnischt passieren, es soll nur eine gedrückt sein.

- Die Animation für die Bewegung nach Norden wird geladen (Bild nr 0).

- Dann wird das erste bild der aninmation auf 0 gesetzt.

- und die Animation auf ihre Maximale Länge.

- und das bild wird um die y achse verschoben (es bewegt sich nach oben).

- Aber wir haben noch etwas vergessen ! ja richtig, das Objekt zu erstellen.

- Wir brauchen also eine Globale Variable mit dem Namen Player1, das Erstellen ist nicht so wild:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure TGameForm.FormCreate(Sender: TObject);
begin
  Player1 := TPlayer.Create(DXSpriteEngine1.Engine);  //Erstellen und Engine zuweisen
  with Player1 do
    begin
      image := dximagelist1.items[1];  //StartBild auswählen 
      AnimStart := 1;
      AnimCount := 0;
      AnimLooped := true;  //Animation als Schleife
      AnimSpeed := 1/150;  //Geschwindigkeit
      width := image.width;  //Breite
      height := image.height;  //Höhe
      x := GameForm.ClientWidth div 2 - width div 2;  //X Position
      y := GameForm.ClientHeight div 2 - width div 2;  //Y Position
      z := 1;
    end;
end;

- Aber was hat das Z zu bedeuten, wir arbeiten doch nicht in 3d ?

- Nein, das Z zeigt an, in welcher reihenfolge die Bilder gezeichnet werden.

- Das mit dem kleinsten Z als erstes und das mit dem größten als letztes (befindet sich also oben).

- jetzt kommt unser Timer dran:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TGameForm.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
  DXInput1.Update;  //Input auflisten
  DXDraw1.Surface.Fill(clBlack);  //Bild mit schwarz füllen
  DXSpriteEngine1.Draw;  //Unsere Sprites zeichnen
  DXSpriteEngine1.Move(20);  //Bewegung
  DXDraw1.Flip;  //Rendern
end;


- Jetzt können wir unsere Figur schon wunderbar bewegen :D
(wenn man DoCollision auskommentiert oder die Prozedur schon hinzufügt)

(3). Kollisionsabfrage

- Jetzt kommt das schwerste von allem die KOLLISIONSABFRAGE

- Keine Angst, es ist einfacher als es klingt, denn es ist schon fast mit nur einem Befehl getan:

Delphi-Quelltext
1:
Collision;                    

- Diese kommt ans Ende unserer DoMove Procedure.

- Warum so wenig Code ?

- Weil DelphiX die Pixelkollision für uns erledigt.

- Nur Brauchen wir erstmal etwas zum Kollidieren:

Delphi-Quelltext
1:
2:
3:
4:
5:
type
  TBall = class(TImageSprite)
  public
    procedure Contact;
  end;

- Es reicht uns, wenn der Ball statisch ist, glaubt mir einfach, dass es auch funktioniert, wenn sich der Ball bewegt :D

- Als Bild könnt ihr euch schnell einen Ball malen, mit der hintergrundfarbe clfuchsia (R: 255, G: 0, B: 255);

- Der kommt dann in die ImageList an letzter Stelle, wird als Globale Variable Ball deklariert und wir erweitern unser Form.Create:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
procedure TGameForm.FormCreate(Sender: TObject);
begin
  Player1 := TPlayer.Create(DXSpriteEngine1.Engine);  
  with Player1 do
    begin
      image := dximagelist1.items[1];  
      AnimStart := 1;
      AnimCount := 0;
      AnimLooped := true;  
      AnimSpeed := 1/150;  
      width := image.width;  
      height := image.height;  
      x := GameForm.ClientWidth div 2 - width div 2;  
      y := GameForm.ClientHeight div 2 - width div 2;  
      z := 1;
    end;
  //Unser Ball
  Ball := TBall.Create(DXSpriteEngine1.Engine);  
  with Ball do
    begin
      image := dximagelist1.items[4];   
      x := 50
      y := 50;       
      width := image.width;
      height := image.height;
    end;
end;

- und nun zur Kollision:

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TPlayer.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
  if Sprite is TBall then
    TBall(Sprite).Contact;
end;

- Das wars schon, es wird geprüft, ob das objekt ein Ball ist und dann die Kollision auf Seiten des Balls aufgerufen und zwar hier:

Delphi-Quelltext
1:
2:
3:
4:
procedure TBall.Contact;
begin
  beep;
end;

- Das beep ist die Reaktion des Balls auf die Kollision.

- Natürlich können da jetzt alle möglichen sachen rein, zB der Ball stirbt, bzw er verschwindet, das sähe so aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TBall.Contact;
begin
  beep;
  dead;
end;

- dazu müssen wir unseren Timer aber etwas erweitern:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TGameForm.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
  DXInput1.Update; 
  DXDraw1.Surface.Fill(clBlack);  
  DXSpriteEngine1.Dead;
  DXSpriteEngine1.Draw;  
  DXSpriteEngine1.Move(20);  
  DXDraw1.Flip;  
end;

- Dead übernimmt das freigeben der gestorbenen Sprites

- so jetzt sind wir schon am ende angelangt, ich hoffe, es hat bei euch allen funktioniert, freue mich auf feedback, auch wenn manches etwas durcheinander geraten sein mag :lol:


Larus - Di 28.06.05 15:12

Thx erstmal das du ein Tut auf meine Bitte hin geschriebn hast 8)


retnyg - Di 28.06.05 15:24

hübsches progrämmlein, warum ist der source ned dabei ?
übrigens tritt keine kollision auf wenn man den rand des bildes überquert :twisted:


F34r0fTh3D4rk - Di 28.06.05 15:25

meinst du, das man nicht ausm bild rauslaufen darf ? das ist ja nun echt zu einfach :lol:


retnyg - Di 28.06.05 15:30

rück mal den source raus


F34r0fTh3D4rk - Di 28.06.05 15:31

den hab ich eben mal so dahin geschrieben steht auch hier im topic oben, aber wenn de willst dann schreib ich dir schnell nen neuen 8) (mit wandkollisionen)


F34r0fTh3D4rk - Di 28.06.05 15:56

so retnyg, hab jetzt ein SourceCode Beispiel hochgeladen :!:

Nochmal eine kleine Anmerkung:

wenn man auch diagonale Bewegungen haben will, dann muss man den Beweungsquellcode folgendermaßen abändern:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
  If (isup in MainForm.DXInput1.States) and not (isdown in MainForm.DXInput1.States) then
    with Player1 do
      begin
        image := MainForm.DXImageList1.Items[0];
        AnimStart := 0;
        AnimCount := image.patterncount;
        y := y - 1;
      end;
  If (isdown in MainForm.DXInput1.States) and not (isup in MainForm.DXInput1.States) then
    with Player1 do
      begin
        image := MainForm.DXImageList1.Items[1];
        AnimStart := 0;
        AnimCount := image.patterncount;
        y := y + 1;
      end;
  If (isleft in MainForm.DXInput1.States) and not (isright in MainForm.DXInput1.States) then
    with Player1 do
      begin
        image := MainForm.DXImageList1.Items[2];
        AnimStart := 0;
        AnimCount := image.patterncount;
        x := x - 1;
      end;
  If (isright in MainForm.DXInput1.States) and not (isleft in MainForm.DXInput1.States) then
    with Player1 do
      begin
        image := MainForm.DXImageList1.Items[3];
        AnimStart := 0;
        AnimCount := image.patterncount;
        x := x + 1;
      end;

zwar fehlt uns dafür die animation, sieht aber trotzdem nicht alzu schlecht aus :D


retnyg - Mi 29.06.05 17:59

thx
wo kriege ich die komponenten, die ich brauche um den source zu verwenden ?user defined image


F34r0fTh3D4rk - Mi 29.06.05 18:16

welche delphi version hast du ?


retnyg - Mi 29.06.05 18:18

5 und 7, würd mir aber für die 7ner reichen user defined image


F34r0fTh3D4rk - Mi 29.06.05 18:19

für delphi 5 sollte es kein problem sein, ich werde editieren wenn ich den link hab :D

hier ist der link:

http://www.torry.net/vcl/packs/middle/hhdelphix.zip

funktioniert mit delphi 3 - 5 :wink:


retnyg - Mi 29.06.05 18:22

user defined image ich meine diese Delphi-X units: DXSprite, DXInput, DXDraws, DXClass


F34r0fTh3D4rk - Mi 29.06.05 18:23

ja der link ist oben :D
(ist glaube ich sogar aktueller als meine version, sollte aber gehen :wink: )


retnyg - Mi 29.06.05 18:28

wahnsinn das zeuch ist ja uralt user defined image, von anno 2000... gibts da nix neueres ?


F34r0fTh3D4rk - Mi 29.06.05 18:46

undelphix, musst du mal googlen, der erste link ist dann die unoffizielle delphix hp, da bekommst du undelphix auch für neue delphi versionen, ich weiß aber nicht ob mein tutorial damit 100% kompatibel ist, man kanns ja mal probieren :P


mimi - So 03.07.05 10:28

müste unDelphiX datet delphiX ja "nur" ab und bringt es von dx 7 auf 8


Darkscream - So 03.07.05 14:17

Also ich hab da mal ne frage undzwar, wenn ich das so mache wie im Tutorial steht (hmm oder wenn ich das so mache wie ich denke das es im Tutorial steht :D ) dann sieht das bei mir folgendermaßen aus http://free.pages.at/warnant/Kollision.bmp xD und auch nur wenn ich mich bewege, denn wenn ich stehenbleibe ist das bomberman-mänchen komplett schwarz.
Ich verstehe halt nich wieso oO wäre für hilfe dankbar :)

MfG,
Darkscream


F34r0fTh3D4rk - So 03.07.05 14:57

du musst auf imagelist1 gehen dort die bilder auswählen und patternwidthund patternheight auf 19 stellen.

Hab ich das vergessen zu schreiben ? :lol:

Das ist die Breite und Höhe der einzelnen Bilder, die Anzahl der Bilder wird daraus automatisch berechnet :wink:


Darkscream - So 03.07.05 14:59

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
du musst auf imagelist1 gehen dort die bilder auswählen und patternwidthund patternheight auf 19 stellen.

Hab ich das vergessen zu schreiben ? :lol:


Sieht so aus ^_^ mal abgesehen davon das du vergessen hast zu sagen wo Collision; hingehört ^^


F34r0fTh3D4rk - So 03.07.05 15:15

danke für die hinweise, habs oben editiert :wink:


Maedhros - Do 21.07.05 08:51
Titel: höchst merkwürdig...
Hm.... komischerweise tut sich bewegungstechnisch gar nichts, wenn ich den Code so schreib wie oben im Tutorial. Ich hab es jetzt so weit nachgeschrieben, dass man die Figur bewegen kann, wenn man DoCollision auskommentiert. Fehlermeldungen bekomm ich keine.
Ich habe mir dann mal Deinen Sourcecode geladen, um mal zu vergleichen, ob da irgendwo Abweichungen sind, konnte aber nichts finden. Dein Source allerdings funktioniert bei mir.
Mit anderen Worten:
Der Source aus dem Totorial -> in neues Projekt kopiert -> keine Bewegung.
Der Source aus dem Totorial -> im eigenen Projekt ("as is") -> alles funktioniert


Woran kann das liegen, dass bei mir die Figur sich partout nicht bewegen will?

(Nachtrag: Mir fiel gerade auf, dass das Tutorial für DelphiX geschrieben ist. Ich habe bei mir UnDelphiX Version 6 installiert. Ist bei der Version etwas besonderes zu beachten?)


F34r0fTh3D4rk - Fr 22.07.05 13:38

wenn mein beispiel funktioniert, dann nein, ansonsten kA :wink:

vielleicht hast du eine einstellung bei den komponenten nicht übernommen ?


Maedhros - Sa 23.07.05 01:27

Die Komponenten auf dem Formular? Die sind exakt so eingestellt, wie es im Totorial geschrieben ist und auch in Deinem Sourcecode sind.
Vielleicht habe ich auch bei der Installation von UnDelphiX etwas verkehrt gemacht... gibt es da noch mehr zu tun, als nur die Packages zu installieren (sprich: Noch an anderen Stellen Einstellungen tätigen)?

Nachtrag:
Ich habe den Fehler jetzt gefunden: Der Code, der eigentlich dem Event "DXTimer1 OnTimer" hätte zugeordnet werden sollen, ist bei mir dem OnActivate zugewiesen gewesen. Klar dass es dann nicht funktioniert :oops:
Wie kam es zu dem Fehler? Aus Gewohnheit hab ich per Doppelclick auf die Komponente den Code für das Event hinzugefügt - bei einem normalen Timer haut das dann ja auch hin, nur der DXTimer weist dann automatisch "OnActivate" zu.
Nun hab ich schon eine Brille... :lol:

Noch mal ein Nachtrag:
Nun wo es funktioniert... bei der Animation fiel mir auf, dass die Figur doch tatsächlich flackert beim laufen. Ich habe dann die folgende Zeile auskommentiert (bei einer der vier Bewegungsrichtungen):


Quelltext
1:
2:
3:
4:
procedure TPlayer.DoMove(MoveCount: Integer);
...

        //AnimCount := image.patterncount;


Man hat dann eine sich bewegende Figur ohne Animation, die aber dafür nicht flackert. Ich habe die Zeile dann mal modifiziert:

Quelltext
1:
AnimCount := image.patterncount - 1;                    

Kein Flackern mehr.
Also irgendwie scheint Delphi hier 5 Frames zu zählen statt der vier vorhandenen, wobei der fünfte dann einfach leer ist.


F34r0fTh3D4rk - Mo 25.07.05 12:56

das mit dem doppelklick kenne ich, das mit dem patterncount kann angehen, hab momentan kein delphi installiert wo ich delphix nutze, also kann ichs nicht testen, aber das flackern hatte ich auch, das -1 ist dann wohl richtig (vielleicht n rundungsfehler oder so)


mimi - Sa 30.07.05 17:10

eine ursache für das problem währe:
du setzt ständig den animation count bei jedem schritt, aber dies sollte nur bei der erstellung passieren damit ist es auch kein wunder das es bei deiner mod zeile kein fehler auftritt *G*

du solltes nur noch prüfen wenn -1 ereicht wurde sonst könnte es zu fehlern kommen !