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
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):
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); AnimStart := 1; AnimCount := 0; |
- 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); 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; 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; DXDraw1.Surface.Fill(clBlack); DXSpriteEngine1.Draw; DXSpriteEngine1.Move(20); DXDraw1.Flip; end; |
- Jetzt können wir unsere Figur schon wunderbar bewegen
(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
- 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
- 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; 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