![]() |
Space Invaders in Delphi5
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo
wir haben die Aufgabe im Informatikunterricht ein Projekt unserer Wahl zu programmieren. Ein Freund und ich haben uns für einen kleinen Space Invaders - Klon entschieden. (Die Invader sollen einfach nur nach unten kommen und dabei abgeschossen werden, also nicht zurückschießen). Wir sind auch schon recht gut vorangekommen, hängen nun aber am Abschießen der Invader. Die Kollisionsabfrage passt soweit eigentlich, jedoch haben wir ein Problem mit dem "entfernen" des Schusses und der Invader. Um den Schuss löschen zu können, welcher sich bei uns in einem dynamischen Array befindet, dachten wir den gerade betrachteten Schuss zu löschen indem wir alle folgenden Schüsse "eins weiter nach vorn verschieben" (ich hoffe man weiß was gemeint ist ^^). Jedenfalls bringt Delphi uns eine Fehlermeldung. Ich hänge das Projekt einfach mal als Anhang in einer .txt Datei an. (es ist vom Aufbau noch nicht optimal und wird, wenn alles funktioniert noch optimiert) Danke schon einmal ~N4r0 |
Re: Space Invaders in Delphi5
Hallo N4r0,
Willkommen in der DP :dp: Euer Ansatz ist schon richtig. Du hast leider nicht verraten, welche Fehlermeldung angezeigt wird. Ich vermute allerdings mal, sie kommt daher, dass durch die vorwärtslaufende Schleife auf Elemente zugegriffen werden, die nicht mehr vorhanden sind. Versucht mal, die Schleife in Zeile 236 andersherum laufen zu lassen. Sprich:
Delphi-Quellcode:
Eine Sache allerdings noch: Ihr gebt die erzeugten Schuss-Images nirgens frei, was zu einem Memory-Leak führt! Ruft Schuss1[j].Free auf, bevor ihr es aus dem Array entfernt.
for j := High(Schuss1) downto 0 do
... |
Re: Space Invaders in Delphi5
Liste der Anhänge anzeigen (Anzahl: 1)
Hey,
danke für deine schnelle Antwort :) ich hab die Schleife mal abgeändert und vor der Schleife, die den Schuss aus dem Array löschen soll Schuss1[j].Free geschreiben. ich hab nochmal die Fehlermeldung in den Anhang gepackt. (also an sich ne Zugriffsverletzung) |
Re: Space Invaders in Delphi5
Die Richtung stimmte schon.
Delphi-Quellcode:
Außer daß du an der falschen Stelle angefangen hast.
Schuss1[j].Free;
for x:=j to high(Schuss1)-1 do Schuss1[x] := Schuss1[x+1]; SetLength(Schuss1,length(Schuss1)-1); j-1 ist ja der Schuß vor dem zu Löschenden ... du hattest also den Falschen entfernt und wenn j der 1. Schuß ist (j=0), dann hast du den Grund für die Berreichsüberschreitung. |
Re: Space Invaders in Delphi5
Zitat:
|
Re: Space Invaders in Delphi5
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
so siehts jetzt aus:
Delphi-Quellcode:
procedure TForm1.SchussTimerTimer(Sender: TObject);
var j,i,x : integer; collide : boolean; begin collide := false; for j := High(Schuss1) downto 0 do begin for i := 1 to 21 do begin if (kollision(Schuss1[j],i) = true) and (Invader[i].Visible = true) then begin collide := true; Invader[i].visible := false; Schuss1[j].Free; for x:=j to high(Schuss1)-1 do Schuss1[x] := Schuss1[x+1]; SetLength(Schuss1,length(Schuss1)-1); end end; if collide = false then Schuss1[j].top := Schuss1[j].top - 10; end; end; |
Re: Space Invaders in Delphi5
Habe das Projekt mal im Debugger durchgesteppt: Das Problem ist, dass die Schleife, in der die Kollision einer Kugel mit einem Invader geprüft wird, beim Auftreten einer Kollision nicht abgebrochen wird. D.h. wenn die Kugel schon entfernt wurde, wird sie trotzdem noch auf eine Kollision mit den übrigen Invadern geprüft, was zu einer Zugriffsverletzung führt.
Beheben kannst du das, indem du nach dem Entfernen der Kugel aus dem Array die innere Schleife mit dem Befehl break; abbrichst. Es wäre übrigens sinnvoller, wenn du die Variable collide in der äußersten Schleife auf False setzen würdest, statt nur am Anfang der Funktion. |
Re: Space Invaders in Delphi5
Super, danke dir für die schnelle und kompetente Hilfe :D
|
Re: Space Invaders in Delphi5
Hallo ihr,
Ich hätte da ein neues Problem. Undzwar bin ich gerade dabei meine programmierten functions in in neue Klassen, also in andere Units zu packen, um sie aus dem Hauptprogramm zubekommen. Nun versuche ich das die ganze Zeit mit meiner funktion kollision:
Delphi-Quellcode:
Ich scheine die neue Klasse auch richtig in die Unit1 implementiert zu haben, denn mein Programm startet ohne Probleme. function kollision(Schuss : TImage ; i : integer) : boolean; var xposition, yposition: integer; begin kollision := false; xposition := Schuss.left; yposition := Schuss.top; if (xposition >= Invader[i].left) and (xposition <= (Invader[i].left + Invader[i].width)) then begin if (yposition <= (Invader[i].top + Invader[i].height)) and (yposition >= Invader[i].Top) then kollision := true else kollision := false end else kollision := false; end; Jedoch wenn ich versuche einen Schuss auszusenden bekommen ich wieder eine Zugriffsverletzungs-Fehlermeldung und Delphi markiert mir diese Zeile:
Delphi-Quellcode:
Ich nehme also an, dass es damit zu tun hat, dass meine zweite Unit nicht auf die TImages zugreifen kann.
if (xposition >= Invader[i].left) and (xposition <= (Invader[i].left + Invader[i].width)) then
Wie ist es möglich dieses Problem zu beheben? |
Re: Space Invaders in Delphi5
Zitat:
diese Sachen prüfen: - Invader <> nil (bzw. bei einem Array z.B. Length > 0) - (i >= 0) and (i < Length(Invader)) - Invader[i] <> nil - ist Invader[i] wirklich ein gültiges Objekt und nicht bereits an anderer Stelle freigegeben |
Re: Space Invaders in Delphi5
Liste der Anhänge anzeigen (Anzahl: 1)
Tut mir leid, aber ich verstehe grad nicht worauf du hinaus willst, bzw. was das bringen soll ^^
hier nochmal mein komplettes Projekt, vielleicht erkennt man ja da genaueres |
Re: Space Invaders in Delphi5
weiß keiner woran es liegt? ^^
|
Re: Space Invaders in Delphi5
Du hast UKol nirgens initialisiert. Schriebe am Anfang der Funktion
Delphi-Quellcode:
und am Ende
UKol := TKollision.Create;
Delphi-Quellcode:
Allerdings ist es nicht sonderlich sinnvoll, Code so in Klassen auszulagern wie du es hier getan hast. Eine Klasse soll dazu dienen, einen Programmteil so abzukapseln, dass er flexibel und leicht wiederverwendbar ist. Dazu ist es nötig, dass möglichst wenig Abhängigkeiten zu Elementen außerhalb der Klasse bestehen.
UKol.Free
Deine Klasse ist aber immer noch fest mit den Elementen von Form1 verdrahtet, sodass der Wartungsaufwand eher steigt, als dass er abnimmt, denn bei jeder Änderung musst du jetzt gleich an zwei Stellen Anpassungen vornehmen. OOP bedeutet nicht, dass man einfach nur seinen Code in mehrere Klassen packt, sondern dass man sein Programm in voneinander unabhängige Bausteine zerlegt. Wenn nämlich hinterher sich alle Klassen gegenseitig kennen, ist nichts gewonnen. Die Kunst besteht darin, zu erkennen, wo und wie man Bestandteile sinnvoll extrahieren könnte, und sein Programm entsprechend zu strukturieren. |
Re: Space Invaders in Delphi5
hm ja, ich versteh worauf zu hinaus willst. An sich wollte ich das ganze jetzt nur in Klassen packen, weil es auch in der Aufgabenstellung erwähnt ist, dass wir Klassen benutzen sollen.
Aber du hast schon recht, einen wirklkichen sinnvollen Zweck hat das ganze auch nicht, wenn man alles nur "zerreißt". Hast du vielleicht ne Idee, wie ich das mit dem Klassen besser umsetzen kann, bzw. was sinnvoll wär in Klassen zu packen? |
Re: Space Invaders in Delphi5
Zitat:
Und schon hat man eine eigene kleine Spriteengine. Die Kollisionsabfrage könnte man so ähnlich lösen. |
Re: Space Invaders in Delphi5
Zitat:
|
Re: Space Invaders in Delphi5
Ok, ich glaub ich hab verstanden wie du das meinst, klingt logisch.
Ich werds nachher mal versuchen zu realisieren. |
Re: Space Invaders in Delphi5
Gut, ich habe jetzt mal was zusammengebastelt und mich würde interessieren, ob ich das soweit richtig verstanden habe:
Ich habe eine Basisklasse "TBasis" erstellt:
Delphi-Quellcode:
Vor dieser Klasse habe ich nun drei weitere Klassen abgeleitet:
unit UnitBasis;
interface uses extctrls; type TBasis = class protected Invader : array[1..21] of TImage; Spielfeld, Raumschiff, Schuss, Invader1, Invader2, Invader3, Invader4, Invader6, Invader7, Invader5, Invader8, Invader9, Invader10, Invader11, Invader12, Invader13, Invader14, Invader15, Invader16, Invader17, Invader18, Invader19, Invader20, Invader21 : TImage; procedure MoveSpaceShip ; virtual; abstract; function MoveInvader(level: integer): boolean; virtual; abstract; procedure ShotCreate ; virtual; abstract; procedure MoveShot ; virtual; abstract; function Collision(Schuss : TImage ; i : integer) : boolean; virtual; abstract; end; implementation end. "TSpaceShip":
Delphi-Quellcode:
unit UnitSpaceShip;
interface uses UnitBasis, Dialogs, Windows; type TSpaceShip = class (TBasis) protected procedure MoveSpaceShip ; end; implementation procedure TSpaceShip.MoveSpaceShip ; begin if getasynckeystate(VK_RIGHT)<>0 then Raumschiff.left := Raumschiff.left + 8 else if getasynckeystate(VK_LEFT) <> 0 then Raumschiff.left := Raumschiff.left - 8; if Raumschiff.left < Spielfeld.left then Raumschiff.left := Spielfeld.left + 2 else if Raumschiff.left + Raumschiff.width > Spielfeld.left + Spielfeld.width then Raumschiff.left := Spielfeld.left + Spielfeld.width - Raumschiff.width - 2; end; end. "TInvader":
Delphi-Quellcode:
unit UnitInvader;
interface uses UnitBasis, Dialogs; type TInvader = class (TBasis) protected Bewegungsrichtung : integer; function MoveInvader( level: integer): boolean; end; implementation function TInvader.MoveInvader(level: integer): boolean; var i,x,y : integer; begin for i := 1 to 21 do begin Invader[i].left := Invader[i].left + (Bewegungsrichtung * 10); end; if Invader7.left + Invader7.width > Spielfeld.left + Spielfeld.width then begin i := 1; for x := 1 to 3 do for y := 1 to 7 do begin Invader[i].top := Invader[i].top + 25; Invader[i].left := Spielfeld.left + Spielfeld.width - ((Invader[i].width + 7) * (8 - y)) - 2; i := i + 1; end; Bewegungsrichtung := -1 end else if Invader1.left < Spielfeld.left then begin i := 1; for x := 1 to 3 do for y := 1 to 7 do begin Invader[i].top := Invader[i].top + 25; Invader[i].left := Spielfeld.left + ((Invader[i].width + 7) * (y - 1)) + 9; i := i + 1; end; Bewegungsrichtung := 1 end; for i := 21 downto 1 do if (Invader[i].visible = true) and (Invader[i].top + Invader[i].height >= Raumschiff.top - 10) then begin showmessage('Game Over'); break; end; end; end. und "TBullet":
Delphi-Quellcode:
unit UnitBullet;
interface uses UnitBasis, extctrls, MMSystem, Unit1; type TBullet = class (TBasis) protected Schuss1 : array of TImage; procedure ShotCreate; procedure MoveShot; function Collision(Schuss : TImage ; i : integer) : boolean; end; implementation procedure TBullet.ShotCreate ; var Schusstemp : TImage; begin SndPlaySound( '.\pew.wav', SND_ASYNC ); Schusstemp := TImage.Create(FormStarship); Schusstemp.Parent := FormStarship; // this is important Schusstemp.Left := Raumschiff.left + (RaumSchiff.width div 2); // X coordinate Schusstemp.Top := RaumSchiff.top - 20; // Y coordinate Schusstemp.Picture := Schuss.Picture; SetLength(Schuss1, High(Schuss1) + 2); Schuss1[High(Schuss1)] := Schusstemp; end; function TBullet.Collision(Schuss : TImage ; i : integer) : boolean; var xposition, yposition: integer; begin Collision := false; xposition := Schuss.left; yposition := Schuss.top; if (xposition >= Invader[i].left) and (xposition <= (Invader[i].left + Invader[i].width)) then begin if (yposition <= (Invader[i].top + Invader[i].height)) and (yposition >= Invader[i].Top) then Collision := true else Collision := false end else Collision := false; end; procedure TBullet.MoveShot; var j,i,x : integer; collide : boolean; begin for j := High(Schuss1) downto 0 do begin collide := false; for i := 1 to 21 do begin if (Collision(Schuss1[j],i) = true) and (Invader[i].Visible = true) then begin collide := true; Invader[i].visible := false; SndPlaySound( '.\boom1.wav', SND_ASYNC ); Schuss1[j].Free; for x:=j to high(Schuss1)-1 do Schuss1[x] := Schuss1[x+1]; SetLength(Schuss1,length(Schuss1)-1); break; end end; if collide = false then if Schuss1[j].top > Spielfeld.top + 5 then Schuss1[j].top := Schuss1[j].top - 10 else begin Schuss1[j].Free; for x:=j to high(Schuss1)-1 do Schuss1[x] := Schuss1[x+1]; SetLength(Schuss1,length(Schuss1)-1); end; end; end; end. Sieht das soweit verständlich und logisch aus oder was sollte ich ändern? Wenn ich jetzt die Procedure "TSpaceShip.MoveSpaceShip" in meiner Hauptprogramm aufrufen möchte sagt mir Delphi: "[Fehler] Unit1.pas(247): Undefinierter Bezeichner: 'MoveSpaceShip'". Ich habe aber im Hauptprogramm die "UnitSpaceShip" unter "uses" bei "implemantation" bekannt gemacht und die Procedure wiefolgt aufgerufen:
Delphi-Quellcode:
Woran kann es liegen, dass ich trotzdem diese Fehlermeldung bekomme?
procedure TFormStarship.SchiffTimerTimer(Sender: TObject);
var x : TSpaceShip; begin x.MoveSpaceShip; end; Danke schonmal, hier wird einem wenigstens verständlich geholfen :P |
Re: Space Invaders in Delphi5
Ich meinte eher sowas:
Delphi-Quellcode:
type
TSprite = class protected FImage: TImage; FParent: TForm; procedure InitImage; virtual; abstract; public constructor Create(Parent: TForm; Position: TPoint); destructor Destroy; override; procedure Move; virtual; abstract; function CollidesWith(AObject: TSprite): boolean; end; TInvader = class(TSprite) protected procedure InitImage; override; public procedure Move; override; end; TBullet = class(TSprite) protected procedure InitImage; override; public procedure Move; override; end; implementation constructor TSprite.Create(Parent: TForm; Position: TPoint); begin inherited Create; FParent := Parent; FImage := TImage.Create(nil); FImage.Parent := Parent; InitImage; FImage.Left := Position.X; FImage.Top := Position.Y; end; destructor TSprite.Destroy; begin FImage.Free; inherited; end; function TSprite.CollidesWith(AObject: TSprite): boolean; begin Result := (FImage.Left+FImage.Width > AObject.Left) and (FImage.Top +FImage.Height > AObject.Top ) and ...; end; procedure TInvader.InitImage; begin FImage.LoadFromFile('invader.bmp'); // z.B... end; procedure TInvader.Move; begin FImage.Left := FImage.Left+1; // z.B... end; procedure TBullet.InitImage; begin FImage.LoadFromFile('bullet.bmp'); // z.B... end; procedure TInvader.Move; begin FImage.Top := FImage.Top-10; // z.B... end;
Delphi-Quellcode:
TForm1 = class(TForm)
... private FSprites: TObjectList; ... end; implementation procedure TForm1.FormCreate(Sender: TObject); var i,j: integer; begin FSprites := TObjectList.Create(True); for i := 0 to 8-1 do for j:= 0 to 3-1 do FSprites.Add(TInvader.Create(self, Point(i*30, j*20))); end; procedure TForm1.Timer1Timer(Sender: TObject); var i,j: integer; begin for i := 0 to FSprites.Count-1 do TSprite(FSprites[i]).Move; ... end; |
Re: Space Invaders in Delphi5
Delphi-Quellcode:
Das verstehe ich nicht ganz. Wo kommt das TBasis her? Die Klasse, welche du erstellt hast heißt doch TSprite, oder?
function CollidesWith(AObject: TBasis): boolean;
[...] function TSprite.CollidesWith(AObject: TBasis): boolean; begin Result := (FImage.Left+FImage.Width > AObject.Left) and (FImage.Top +FImage.Height > AObject.Top ) and ...; end; |
Re: Space Invaders in Delphi5
Zitat:
|
Re: Space Invaders in Delphi5
(nicht nachgedacht, bevor ich geschreiben habe)
|
Re: Space Invaders in Delphi5
Entschuldigung, daß ich doof frage, aber arbeitet ihr in der Schule wirklich mit Delphi 5 (1999)? Das wäre ja eine Zumutung, habe mir neulich mal mein Delphi 4 angesehen, keine Ahnung wie ich damit Programme entwickeln konnte.
Die Schulen sollten etwas halbwegs Aktuelles nutzen. |
Re: Space Invaders in Delphi5
Ja, das hab ich schon von vielen gehört, aber das liegt leider nicht an uns Schülern :/
|
Re: Space Invaders in Delphi5
Zitat:
|
Re: Space Invaders in Delphi5
Ich habe die Klassen mal nachgbaut und wollte nun die Prozeduren und Funktionen reinschreiben.
Nur leider blick ich jetzt nicht mehr wirklich durch. Hiermit:
Delphi-Quellcode:
werden die Sprite Images ja irgendwie wieder als eine Art Array betrachtet.
procedure TForm1.FormCreate(Sender: TObject);
var i,j: integer; begin FSprites := TObjectList.Create(True); for i := 0 to 8-1 do for j:= 0 to 3-1 do FSprites.Add(TInvader.Create(self, Point(i*30, j*20))); end; procedure TForm1.Timer1Timer(Sender: TObject); var i,j: integer; begin for i := 0 to FSprites.Count-1 do TSprite(FSprites[i]).Move; ... end; Kannst du mir das vielleicht nochmal erklären? |
Re: Space Invaders in Delphi5
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:44 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz