Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin) (https://www.delphipraxis.net/171183-bugs-und-zugriffsverletzungen-meinem-spiel-aspirin.html)

Premaider 24. Okt 2012 22:09

Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin)
 
Hey Leute,
vielleicht kennt einer von euch das Spiel Aspirin. Man muss den Spieler durch die sich bewegenden Gegner zum Ziel bringen. Ich habe noch Items hinzugefügt und man verliert erst wenn die 5 Herzen (5,4,3,2 oder 1 je nach Schwierigkeitsgrad). Mein Probleme sind:
- Manchmal erscheind oben-links in der Form einfach ein weißer Shape (vermutl. Item)
- Ab einer bestimmten Anzahl an Gegnern (TGegner=Abgeleitet von TShape) bekomme ich eine Zugriffsverletzung bei Verlieren (wenn keine Herzen mehr da wären)
- Manchmal passiert nichts wenn man ein Item berührt

Vielleicht ein paar mal spielen. Dann werdet ihr damit sicher "bekanntschaft machen"
Bei mir wird die Hauptarbeit von Timer1 übernommen.
Das gesammte Spiel befindet sich hier: http://www.mediafire.com/?edd20mq7c5f2if2 (Auch mit Grafiken usw., daher kein Anhang mehr möglich)
Den Quellcode von Timer1 findet ihr hier:

Delphi-Quellcode:
procedure TForm1.Timer1Timer(Sender: TObject);
var
l,k,m,n,o,AL1,AL2,Zufall: Integer;
begin
//Spieler bewegegen
If Spielerbewegung=True then
  begin
    If ((KeyPressed(VK_Up)) or (KeyPressed($57)) and (Spieler.Top >= Spielflaeche.Top)) Then Spieler.top := Spieler.top - Spieler.Speed;
    If ((KeyPressed(VK_Down)) or (KeyPressed($53)) and (Spieler.Top <= Spielflaeche.height + Spielflaeche.top - Spieler.Height)) Then Spieler.top := Spieler.top + Spieler.Speed;
    If ((KeyPressed(VK_Left)) or (KeyPressed($41)) and (Spieler.Left >= Spielflaeche.left)) Then Spieler.Left := Spieler.Left - Spieler.Speed;
    If ((KeyPressed(VK_Right)) or (KeyPressed($44)) and (Spieler.Left <= Spielflaeche.width + Spielflaeche.left - Spieler.width)) Then Spieler.Left := Spieler.Left + Spieler.Speed;
  end;
//Kollision Spieler und Ziel
if IsCollision(Spieler.BoundsRect, Ziel.BoundsRect) then
  begin
  Punktzahl:=Punktzahl+1;
  PunkteAnzeige.caption:='Punkte: '+IntToStr(Punktzahl);
    If Sounds='Enabled' Then
      begin
        SoundPlayer.FileName:=Pfad+'\Datein\Ziel.wav';
        SoundPlayer.Open;
        SoundPlayer.play;
      end;
    ItemCount:=ItemCount+1;
    Ziel.Top:=random(Spielflaeche.Height - Ziel.Height) + Spielflaeche.top;
    Ziel.Left:=random(Spielflaeche.Width - Ziel.width) + Spielflaeche.left;
    // Gegner hinzufügen
      begin
       setLength(gegner,length(Gegner)+1);
       AL1 := length(Gegner)-1;
       Gegner[AL1]:= TGegner.Create(Self);
         With Gegner[AL1] do
          begin
            speed:=3;
            Parent := self;
            Cooldown := 1000;
            BringToFront;
            If RandomAusgabe(1)=1 Then
               begin
                  Width:=5;
                  Height:=30;
                  top:= random( Spielflaeche.Height - height ) + Spielflaeche.top;
                  Left := random( Spielflaeche.Width - Width ) + Spielflaeche.left;
                end;
            If RandomAusgabe(1)=0 Then
               begin
                  Width:=30;
                  Height:=5;
                  top:= random( Spielflaeche.Height - height ) + Spielflaeche.top;
                  Left := random( Spielflaeche.Width - Width ) + Spielflaeche.left;
                end;
          end;
        end;
    //Item hinzufügen
      begin
        If ItemCount=5 Then
          begin
            ItemCount:=0;
            Zufall:=RandomAusgabe(3);
            setLength(Item,length(Item)+1);
            AL2 := length(Item)-1;
            Item[AL2]:= TItem.Create(Self);
            With Item[AL2] do
              begin
                Picture.LoadFromFile(Pfad+'\Datein\Item.jpg');
                Parent:=Self;
                Height:=16;
                Width:=16;
                Stretch:=True;
                Top:=random(Spielflaeche.Height - Ziel.Height) + Spielflaeche.top;
                Left:=random(Spielflaeche.Width - Ziel.width) + Spielflaeche.left;
                Brush.Color:=clblack;
                BringToFront;
                Enabled:=True;
                If ((Zufall=1) or (Zufall=2)) then
                  begin
                    Typ:=True;
                  end;
                If Zufall=3 then
                  begin
                    Typ:=False;
                  end;
              end;
        end;
    end;
  end;
    //Gegner bewegen
    If length(gegner)>=1 then
      begin
        For m:= 0 to high(gegner) do
          begin
            If Gegner[m].width > Gegner[m].Height Then
              begin
                //Waagrecht
                Gegner[m].left:=Gegner[m].left + Gegner[m].Speed;
              end;
            If Gegner[m].width < Gegner[m].Height Then
              begin
                //Senkrecht
                Gegner[m].Top:=Gegner[m].top + Gegner[m].Speed;
              end;
          end;
      end;

//Gegner Cooldown
For o:= low(Gegner) to high(Gegner) do
  begin
    If Gegner[o].cooldown>=1 Then Gegner[o].cooldown:=Gegner[o].cooldown-50;
  end;

//Kollision Gegner und Wand
For l:= low(gegner) to high(gegner) do
  begin
    if Gegner[l].Left <= Spielflaeche.Left then Gegner[l].speed:=Gegner[l].speed*-1;
    if Gegner[l].Left >= Spielflaeche.Width + Spielflaeche.Left - Gegner[l].Width then Gegner[l].speed:=Gegner[l].speed*-1;
    if Gegner[l].top <= Spielflaeche.Top - Gegner[l].Width then Gegner[l].speed:=Gegner[l].speed*-1;
    if Gegner[l].top >= Spielflaeche.height + Spielflaeche.top - Gegner[l].height then Gegner[l].speed:=Gegner[l].speed*-1;
  end;

//Kollision Spieler und Gegner
For k:= low(gegner) to high(gegner) do
  begin
    if ((IsCollision(Gegner[k].BoundsRect, Spieler.BoundsRect)) and (Gegner[k].Cooldown<=0))Then
          begin
            If Spieler.Verletzbar Then Dec(Herzen);
            If ((Herzen=4) and (Spieler.Verletzbar)) Then
              begin
                Spieler.Verletzbar:=False;
                Abwarten2.Enabled:=True;
                Herz5.picture.loadfromfile(Pfad+'\Datein\Herz_Leer.jpg');
              end
            Else If ((Herzen=3) and (Spieler.Verletzbar)) Then
              begin
                Spieler.Verletzbar:=False;
                Abwarten2.Enabled:=True;
                Herz4.picture.loadfromfile(Pfad+'\Datein\Herz_Leer.jpg');
              end
            Else If ((Herzen=2) and (Spieler.Verletzbar)) Then
              begin
                Spieler.Verletzbar:=False;
                Abwarten2.Enabled:=True;
                Herz3.picture.loadfromfile(Pfad+'\Datein\Herz_Leer.jpg');
              end
            Else If ((Herzen=1) and (Spieler.verletzbar)) Then
              begin
                Spieler.Verletzbar:=False;
                Abwarten2.Enabled:=True;
                Herz2.picture.loadfromfile(Pfad+'\Datein\Herz_Leer.jpg');
              end;
          end
        Else If Herzen<=0 then
          begin
            Verloren;
            Herz1.Picture.LoadFromFile(Pfad+'\Datein\Herz_leer.jpg');
          end;
  end;

//Kollision Spieler und Item
For n := low(item) to high(item) do
  begin
    If ((IsCollision(Spieler.BoundsRect,Item[n].BoundsRect)) and (Item[n].Enabled=True)) Then
      begin
        Spieler.Upgrade(n,Item[n].Typ);
        Item[n].Enabled:=False;
      end;
  end;
end;
Könnt ihr mir vielleicht beantworten warum diese Bugs entstehen ? Ich habe nämlich keine Ahnung :S

himitsu 24. Okt 2012 22:35

AW: Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin)
 
Wie wäre es, wenn du uns erstmal sagst was genau los ist?

- Fehlermeldungen (ja, Strg+C funktioniert an sehr vielen Stellen, vorallem in Fehlerdialogen)
- in welchen Zeilen treten die Fehler auf?

- Und hast du dir schonmal angesehn wofür ein Debugger gut ist?

Premaider 24. Okt 2012 22:44

AW: Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin)
 
Zitat:

Zitat von himitsu (Beitrag 1188232)
Wie wäre es, wenn du uns erstmal sagst was genau los ist?

- Fehlermeldungen (ja, Strg+C funktioniert an sehr vielen Stellen, vorallem in Fehlerdialogen)
- in welchen Zeilen treten die Fehler auf?

- Und hast du dir schonmal angesehn wofür ein Debugger gut ist?

Also ich hab jetzt ca. 1 Jahr Informatik in der Schule aber von Debugger hab ich noch nie was gehört. Sorry :S
(Edit: hab mal nachgeschaut und jetzt weiß ich was du meinst xD. Aber was soll ich denn machen was sollen mir Haltepunkte bringen. Ich weiß ja nicht warum. Das habe ich auch schon mit Haltepunkten usw. nachgeschaut)

Und eine richtige Zeile gibts ja nur für die Zugriffsverletzung.
Die wäre dann bei
Delphi-Quellcode:
if ((IsCollision(Gegner[k].BoundsRect, Spieler.BoundsRect)) and (Gegner[k].Cooldown<=0))Then
bei //Kollision Gegner und Spieler

RWarnecke 25. Okt 2012 07:50

AW: Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin)
 
Ich würde das Spiel über die Delphi IDE starten und spielen. Sobald eine Zugriffsverletzung auftritt, bekommst Du diese Meldung angezeigt und hast unten rechts einen Button Break. Wenn Du diesen dann anklickst, springt die Delphi IDE an die ungefähre Stelle wo die Zugriffsverletzung auftritt. Wenn Du diese Stelle hast, würde ich ein paar Zeilen davor den Haltepunkt setzen. Danach das Spiel wieder neu starten und spielen. Jetzt solltest Du die gleichen Aktionen ausführen wie beim ersten Mal und wenn Du an den Haltepunkt kommst mit Trace Into (F7) oder mit Step Over (F8) die Zeilen Deiner Funktion durchgehen.

Blup 25. Okt 2012 08:41

AW: Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin)
 
Der Fehler tritt vermutlich beim Zugriff auf Gegner[k] auf.
Eigentliche Ursache ist dann die Stelle, an der Gegner freigegeben werden.
Dazu fehlt aber der Quellcode.

himitsu 25. Okt 2012 09:27

AW: Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin)
 
Es kann auch nicht schaden, zu erwähnen welche Delphiversion man nutzt. Du kannst dein D7 ja ganz einfach mal im Forenprofil angeben.

Projekt > Optionen > Compiler > Laufzeitfehler > alle Haken dort reinmachen
Sicherheitshalber (wärend du noch am Programm arbeitest) auch bei Projekt > Optionen > Compiler > Coderzeugung die optimierung aus und die Stackframes an. (dann hat es der Debugger einfacher)

Nein, Debugger ist nicht nur "Haltepunkte".

Da kann man nachsehn warum es knallt, indem man sich den aktuellen Wert von Variablen ansieht und so erkennen könnte, was nicht stimmt.
Die sind dennoch praktisch, vorallem weil man ab da auch leicht mal den Code Zeile für Zeile, aka Befehl für Befehl einzeln/schrittweise ausführen und nachsehn kann, was genau passiert. :roll


Und ich fragte nicht umsonst nach eine "genaueren" Fehlerbeschreibung, denn da kann man leichter erkennen, ob sich darin ein Hinweis verbirgt.

z.B. "Zugriffsverletzung bei Adresse $xxxxxxxx auf Adresse $000000xx" = dort wird vermutlich auf "nil" (z.B. nicht existierendes Array oder Objekt) zugegriffen.
Bei "Zugriffsverletzung bei Adresse $000000xx ..." wurde wohl eine Methode in einem nichtexistierenden Prozedurzeiger angesprungen.
Und bei "Zugriffsverletzung bei Adresse $xxxxxxxx auf Adresse $xxxxxxxx" stimmt irgendwas Anderes nicht, aber 66% der Fälle hat man dennoch sofort erkannt.
Wie gesagt ... Strg+C in der Form drücken und dann hier im Editor Strg+V.

(X = irgendein Wert, meistens keine 0 ... und 0 = eine 0)

Premaider 25. Okt 2012 12:31

AW: Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin)
 
Die genaue Zugriffsverletzung lautet:
"Im Proekt Project1.exe ist eine Exeption der Klasse EAccessViolation aufgetreten. Meldung'Zugriffsverletzung bei Adresse 0046B726 in Modul 'Project1.exe'. Lesen von Adresse 00000008. Prozess wurde angehalten. Mit Einzelne Anweisung oder Start vortsetzen"

DeddyH 25. Okt 2012 12:56

AW: Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin)
 
Dann lies Dir #5 und #6 noch einmal durch. Anscheinend greifst Du ungeprüft auf ein nil-Objekt zu, d.h. es wurde noch gar nicht erzeugt oder bereits freigegeben.

himitsu 25. Okt 2012 12:59

AW: Bugs und Zugriffsverletzungen in meinem Spiel (Aspirin)
 
So, daß heißt also es wird auf etwas zugrgriffen, was es nicht gibt ... und das kann man sehr leicht nachprüfen.

Zitat:

Und eine richtige Zeile gibts ja nur für die Zugriffsverletzung.
Die wäre dann bei
Delphi-Quellcode:
if ((IsCollision(Gegner[k].BoundsRect, Spieler.BoundsRect)) and (Gegner[k].Cooldown<=0))Then bei //Kollision Gegner und Spieler

Ausgehn würde man erstmal davon, daß der Fehler auch in dieser Zeile liegt (das stimmt nicht immer, da sich der Debugger auch manchmal täucht/verläuft).

Also entweder liegt es am IF oder an dem, was in IsCollision liegt.
(falls es an is colision liegt, dann kann man den Inhalt )


Da es ein nicht "direkt" reproduzierbarer Fehler ist, hilft ein Haltepunkt nicht viel, da man nun jede Ausführung dieser Zeile prüfen müßte, bis es knallt (das kann dauern).

Man läßt es nun also erstmal knallen und schaut sich nach dem Knall im Debugger die Variablen an.
Dort suchst du nun nach einem nil.

Leider sind sind nach einer exception "manchmal" keine Variableninhalte lesbar :cry:


Also hilft man sich ganz einfach mit einem Schutzblock, welcher um den code drumrum kommt, wo man den Fehler vermutet.
In dem Except-Block kann man sich entweder via ShowMessage und Co. Variableninhalte anzeigen lassen
oder man kopiert den Code nochmals dort rein (nur sinnvoll wenn mit einem reproduzierbaren Ergebnis zu rechnen ist).

bei dir also
Delphi-Quellcode:
for ... do
  try
    if ... then
    begin
      ...
    end;
  except
    if ... then  // hier der Haltepunkt
    begin
      ...
    end;
  end;
Wenn es knallt, dann landet man im Except und kann den Code nochmals ausführen.
Dieses mal Schritt für Schritt und dabei guckt man sich "vorher" die Variableninhalte an.

Bei dir ist es also speziell der Inhalt von Gegner, an der Position k,
bzw. der Inhalt von IsColition.
Daß "Spieler" verschwindet, schließe ich einfach mal aus, aber prüfen kann man es denoch. (ist ja kein Aufwand)



Und zusätzlich nochmals:
- wie vorher schonmal beschrieben, soltest du gewisse Einstellungen in den Projektoptionen vornehmen, womit bestimmte Prüfungen von Delphi automatisch eingebaut werden
- manchmal kann es nicht schaden, wenn man geziehlt FreeAndNil verwendet, statt nur .Free (aber bei der genannten Fehlermeldung, ist sowas erstmal nicht nötig, da es sowieso schon nil ist)


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:24 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz