Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi "MineSweeper" - Probleme mit Rundtime-Buttons (https://www.delphipraxis.net/121697-minesweeper-probleme-mit-rundtime-buttons.html)

64Jabor 2. Okt 2008 17:40


"MineSweeper" - Probleme mit Rundtime-Buttons
 
Hallo DP,

ich stehe mal wieder kurz vorm Verzwefeln -.-

Ich sitze atm an einer MineSweeper-Version und habe für die Minenfelder Runtime-Buttons erstellt.
Die werden alle erstellt, sind sichtbar und funktionieren alle sehr gut.
Aber wenn ich dann anhand der Mausposition versuche herauszufinden, welcher der Buttons gedrückt worden ist, bekomme ich seltsame Ergebnisse!

Hier mal der Code:
Delphi-Quellcode:
procedure TFrmMS.MineBtnDadClick(Sender: TObject);
var x, y: integer;
    RealPosX, RealPosY: integer;
    Found: boolean;
begin
 RealPosX := Mouse.CursorPos.X - Left;
 RealPosY := Mouse.CursorPos.Y - Top ;
 ShowMessage(IntToStr(RealPosX)+', '+IntToStr(RealPosY));
 x:=0;
 repeat
  begin
   if ((MineBtn[x, 0].Left < RealPosX) and (MineBtn[x, 0].Left + MineBtn[0, 0].Width > RealPosX)) then
    begin
     y:=0;
     repeat
      begin
       if ((MineBtn[x, y].Top < RealPosY) and (MineBtn[x, y].Top + MineBtn[x, y].Height > RealPosY)) then Found := TRUE;
       y:=y+1;
      end;
     until ((y = RowsForLevel) or Found);
    end;
   x:=x+1;
  end;
 until ((x = ColsForLevel) or Found);
 Aufdecken(x, y);
end;
Das ist die Prozedur, die ausgelöst wird wenn IRGENDEINER meiner Runtime-Buttons gedrückt wird.
(MineBtn ist ein Array aus TSpeedButtons (groß genug!) und Rows/ColsForLevel sind integers, der Name erklärt den Rest)
Misteriöserweise bekomme ich immer ein "y" welches 2 zuviel ist als es eingentlich sein müsste und wenn ich das einfach korrigiere indem ich später sage y ist y-2, dann erhalte ich immer Fehler wenn ich Buttons der rechten unteren Ecke drücke.
Also iwas stimmt da nicht, aber ich kann den Fehler nicht finden...

Wer kann helfen?
Danke schonmal, greetZ DaSebi

mkinzler 2. Okt 2008 17:43

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Warum verwendest du nicht den Sender-Parameter des Click-Events?

Klaus01 2. Okt 2008 17:49

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Guten Abend,

die könntest beim Erstellen der Buttons jedem Button einen Tag-Wert mitgeben.
Anhand dieses Tag kannst Du den Button dann identifizieren.

Im Button Click Ereignis kannst Du dann so darauf zugreifen:

Delphi-Quellcode:
(sender as TButton).tag
Du kannst dann ein Click Ereignis für alle Buttons benutzen, so wie mkinzler schon geschrieben hat.

Grüße
Klaus

64Jabor 2. Okt 2008 18:33

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
@mkinzler

Meinst du damit dass jeder Button als der Sender gilt?
Das heißt ja dass ich mit Sender.XXX auf alles zugreifen könnte und anhand von Werten wie zB. dem Sender.Left ganz einfach Reihe/Spalte des Butons herausbekomme -.-

Na klasse xD :wall: :wall: :wall:
Vielen Dank! Meine Methode war wohl mit Kanonen auf Spatzen geschossen ^^

@klaus01
das mit dem tag wäre für meine Methode dank zweidimensionalen Array aber entwas aufwendiger, trotzdem danke für diesen Ansatz!

greetZ DaSebi

mkinzler 2. Okt 2008 18:43

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Der Parameter Sender ist eine Referenz auf das auslösende Objekt. Der Tag erleichtert die Identifizierung des Buttons.

64Jabor 2. Okt 2008 19:01

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Ja ich weiß was der Sender ist, aber ich dachte dass der Sender immer mein realer Button wäre, denn alle OnClick-Ereignisse hatte ich auf seines umglenkt...

Hier aber mal der funktionierende Code für alle Interessierten:
Delphi-Quellcode:
procedure TFrmMS.MineBtnDadClick(Sender: TObject);
var x, y: integer;
    i, z: integer;
    Found: boolean;
begin
 i:=0;
 Found := FALSE;
 while ((i < ColsForLevel) and not (Found)) do
  begin
   z:=0;
   while ((z < RowsForLevel) and not (Found)) do
    begin
     if(MineBtn[i, z] = Sender) then Found := TRUE else z := z+1;
    end;
   if not (Found) then i := i+1;
  end;
 x := (MineBtn[i, z].Left - MineBtn[0, 0].Left) div MineBtn[0, 0].Width;
 y := (MineBtn[i, z].Top - 50) div MineBtn[0, 0].Height;
 Aufdecken(x, y);
end;
Vielen Dank nochmal!

mkinzler 2. Okt 2008 19:03

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Was meinst du mit realer Button?

64Jabor 2. Okt 2008 19:23

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Als real meine ich einen Button den ich mir real vorstellen kann xD
Also einen den ich per Drag&Drop auf das Formular gezogen hab, der unsichtbar ist und einfach nur dazu dient, alle OnClicks auf sich zu nehmen.
Ich weiß das real ganz sicherlich nicht der richtige Begriff dafür ist, aber ich fand den iwie passend ^^

Allerdings habe ich mit meiner Variante da oben ein kleines Problem!
In einem völlig unersichtlichen Muster bekomme ich Zugriffsverletzungen im Speicher.
Das wird daher rühren, dass ich eine Prozedur hab, die die Buttons löscht wenn man auf sie klickt und es keine Mine ist.
Dazu hier mal wieder die Prozedur (die von oben ist noch aktuell):
Delphi-Quellcode:
procedure TFrmMS.SmileForUser(ACol, ARow: integer);
var TopLeft, BtmRight: TPoint;
begin
 if not (BtnDestroyed[ACol, ARow]) then
  begin
   MineBtn[ACol, ARow].Destroy;
   BtnDestroyed[ACol, ARow] := TRUE;
  end;
 TopLeft.X := MineBtn[0, 0].Left + ACol*MineBtn[0, 0].Width;
 TopLeft.Y := MineBtn[0, 0].Top + ARow*MineBtn[0, 0].Height;
 BtmRight.X := TopLeft.X + MineBtn[0, 0].Width;
 BtmRight.Y := TopLeft.Y + MineBtn[0, 0].Height;
 Canvas.TextRect(rect(TopLeft, BtmRight), TopLeft.X, TopLeft.Y, MinesArround(ACol, ARow));
 BtnFace.Caption := ':-)';
end;
Weder das Rechteck noch die Schrift erscheinen obwohl sie das meiner Meinung nach egtl müssten ^^
Und ab und zu bekomme ich diese Zugriffsverletzungen TROTZ meinem Schutz, dass ein Button nicht zweimal gelöscht werden kann...woran kann denn das jetzt liegen O.o

mkinzler 2. Okt 2008 19:24

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Und die anderen?

64Jabor 2. Okt 2008 19:29

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Na die anderen werden zur Laufzeit erstellt.
Dazu habe ich eine Prozedur, die je nach Schwierigkeitsgrad 17 bis max 40 Buttons (zweidimensional) erstellt.

Hier die Prozedur:
Delphi-Quellcode:
procedure TFrmMS.GenerateBtns;
var BtnIndexX, BtnIndexY: integer;
begin
 For BtnIndexX:=0 to ColsForLevel-1 do
   For BtnIndexY:=0 to RowsForLevel-1 do
    begin
     MineBtn[BtnIndexX, BtnIndexY] := TSpeedButton.Create(FrmMS);
     MineBtn[BtnIndexX, BtnIndexY].Height := 14;
     MineBtn[BtnIndexX, BtnIndexY].Width := MineBtn[BtnIndexX, BtnIndexY].Height;
     MineBtn[BtnIndexX, BtnIndexY].Top := 50 + BtnIndexY * 14;
     MineBtn[BtnIndexX, BtnIndexY].Left := (ClientWidth - (ColsForLevel-1) * MineBtn[BtnIndexX, BtnIndexY].Width) div 2 + BtnIndexX * MineBtn[BtnIndexX, BtnIndexY].Width;
     MineBtn[BtnIndexX, BtnIndexY].Parent := FrmMS;
     MineBtn[BtnIndexX, BtnIndexY].OnClick := MineBtnDad.OnClick;
    end;
end;

mkinzler 2. Okt 2008 19:32

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Warum weist du dann denen nicht einen idividuellen Tag und die Eventmethode zu?

64Jabor 2. Okt 2008 19:38

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Ja ein Tag ist ein String oder wie sehe ich das doch?
Einen String also der für das Array stehen muss um den Button gut zu identifizieren.
Mein Array ist aber zweidimensional, also müsste ich in diesem einen String die beiden Array"variablen" angeben, das stell ich mir mühsam vor...

EDIT: Ah ok sry, ein Integer, aber trotzdem...

Medium 2. Okt 2008 19:41

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Durch eine eindeutige Nummer ist ein Button immer eindeutig identifiziert. Wenn du nun noch geschickt etwas grundlegende Mathematik hinzu ziehst, kannst du auch aus einem fortlaufenden Index von links oben nach rechts unten eine Zeilen- und Spaltennummer erzeugen.

64Jabor 2. Okt 2008 19:47

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
@medium
Das ist richtig!
Könnte ich durchaus machen um die Sache zu vereinfachen, aber dennoch löst das nicht alle Probleme!
Damit wäre das ganze wohl einen Tick schneller, aber die Zugriffsverletzungen werden dadurch nicht aufhören, denn den korrekten Button hatte ich auf meine Weise auch schon vorher an der Angel...

mkinzler 2. Okt 2008 19:51

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Tja manche sind halt resistent gegen Hilfe :gruebel:

64Jabor 2. Okt 2008 19:54

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
So DAS musst du mir jetzt erklären xDD
Wenn ich jedem Button einen eindeutigen Tag zuweise (und das habe ich gemacht), wie kann ich dann mit dem Sender auf den Tag zugreifen?
Sender.Tag geht doch nicht, also muss ich doch wieder denselben Kram machen den ich in meiner Prozedur eh schon hab, oder seh ich da jetzt was net :drunken:

SubData 2. Okt 2008 19:55

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Delphi-Quellcode:
(Sender as TButton).Tag

64Jabor 2. Okt 2008 19:57

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Oh...

DAS meinte mkinzler damit eben *gg*
DANKE xD

64Jabor 2. Okt 2008 20:07

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
So mein Problem besteht weiterhin :(
Ich habe jetzt Tags verwendet und jeder Button ist somit EINDEUTIG zugewiesen, dennoch bekomme ich die Zugriffsverletzungen, auffallend häufig wenn nicht mehr viele Button übrig sind :gruebel:

mkinzler 2. Okt 2008 20:09

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Und bei was kommt die Zugriffsverletzung?

64Jabor 2. Okt 2008 20:13

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Wenn ein Button gedrückt wird - ein Button der keine Mine sein soll/ist.
Dabei passiert folgendes:

Delphi-Quellcode:
procedure TFrmMS.SmileForUser(ACol, ARow: integer);
var TopLeft, BtmRight: TPoint;
begin
 MineBtn[ACol, ARow].Destroy;
 BtnDestroyed[ACol, ARow] := TRUE;

 TopLeft.X := MineBtn[0, 0].Left + ACol*MineBtn[0, 0].Width;
 TopLeft.Y := MineBtn[0, 0].Top + ARow*MineBtn[0, 0].Height;
 BtmRight.X := TopLeft.X + MineBtn[0, 0].Width;
 BtmRight.Y := TopLeft.Y + MineBtn[0, 0].Height;
 Canvas.TextRect(rect(TopLeft, BtmRight), TopLeft.X, TopLeft.Y, MinesArround(ACol, ARow));
 BtnFace.Caption := ':-)';
end;
Also der Button wird gelöscht und vorgemerkt (damit er bei spielende nicht nochmal gelöscht wird, was ja nicht ginge)

mkinzler 2. Okt 2008 20:16

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Warum zerstörst du den Button überhaupt?

64Jabor 2. Okt 2008 20:19

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Das sollte egtl nur zur Anschauung dienen!
Egtl wollte ich die Caption des Btns ändern und ihn Down machen, sodass jeder sieht wieviele Minen um den Button herum sind.
Testweise habe ich ihn der Einfachheit halber erstmal zerstört, geht aber in die Hose deshalb steige ich jetzt mal auf diese Version um...

Es sähe meienr Meinung nach nur einfach besser aus wenn das Feld frei wäre abgesehen von der Zahl, wenn man einen Button anklickt der keine Mine war

mkinzler 2. Okt 2008 20:20

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Delphi-Quellcode:
.Enabled := False
sollte reichen

64Jabor 2. Okt 2008 20:24

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Hm Moment, dann klappt ja nur der Button nicht mehr, ich möchte ja das der Button
1. als caption die Anzahl der Minen um sich herum hat (bereits erledigt)
und 2. GUT SICHTBAR ist, dass er bereits gedrückt worden ist

Das lässt sich mit
Delphi-Quellcode:
 MineBtn[ACol, ARow].Down := TRUE
allerdings auch nicht bewerkstelligen...

Helmi 2. Okt 2008 20:30

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Hallo,

ich glaube die Fehlermeldung kommt daher, dass du im Eventhandler
des gedrückten Buttons Diesen selbst löscht.

Das heisst du löscht den Button in seinem Eventhandler.
Das geht nicht - das muss krachen - du ziehst ja dem Button
den Boden unter ihm weg.

Um dieses Problem zu umgehen, gibt es hier im Forum schon eine Lösung
Im Großen und Ganzen wird anstatt den Button im Eventhandler
zu zerstören, eine Windows-Message an die eigene Application
geschickt in der dann dieser Button zerstört wird.

[edit]
Das Zerstören eines Buttons in seinem eigenen Eventhandler
wurde hier schon mal diskutiert.
Der dort gezeigte Lösungsweg funktioniert - habs selbst
schon eingesetzt.

64Jabor 2. Okt 2008 20:33

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Vielen Dank Helmi,

das macht Sinn, man sägt ja auch nicht an seinem eigenen Ast *zweideutig xD*
Aber wieso krachen dann nur die letzten paar und die ersten 50 klappen ohne Probleme?
Wie auch immer habe ich nun damit aufgehört die Buttons zu zerstören und stattdessen mache ich sie jetzt nun nur kenntlich - was mir aber noch nicht so recht geligen will!

Helmi 2. Okt 2008 20:37

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Zitat:

Zitat von 64Jabor
Aber wieso krachen dann nur die letzten paar und die ersten 50 klappen ohne Probleme?

ich denke du löscht die Buttons um den gedrückten button herum und dann den gedrückten Selbst.
Dann kommt es erst zu einem Crash wenn du zu dem Gedrückten kommst.
Der Eventhandler der Anderen wird ja zu diesem Zeitpunkt nicht aufgerufen.

64Jabor 2. Okt 2008 20:56

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Nein, ich lösche immer nur den Button auf den geklickt wird, oder besser wurde, denn wie gesagt habe ich mich davon distanziert - mit dem freundigen Nachricht dass keine Zugriffsverletzungen mehr erscheinen - dafür ein Stack-Überlauf der sich gewaschen hat xD

Ich mussj a bei jedem Button-Druck prüfen, on benachbarte Felder ebenfalls an kein Minenfeld stoßen, dazu prüfe ich mittels einer Funktion ob ein benachbartes Feld eine Mine hat und lasse mir dann einen String geben der der umgewandelte Integer-Wert ist.
Da ich das wiederrum aber auch für jede weiterhin frei gewordene Feld tun muss - bis iwann ALLE entweder am Rand des Spielfeldes sind oder an eine Mine angrenzen, habe ich das getan (unter Beachtung der Ränder!).
Das scheint aber zu viel zu sein, denn der Stack ist ja der Ort, an dem die Programm-Einstiegs-/Haltepunkte gespeichert werden, nicht wahr?

Helmi 2. Okt 2008 21:00

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
irgendwie reden wir aneinander vorbei...
ich schau dass ich ne Lösung finde für deine Zugriffsverletzung
dann machst du in der Zwischenzeit etwas ganz anderes und
hast jetzt mit dem ganz Anderen ein Problem...

Bleib doch mal auf eine Schiene und bring das zu Laufen.

64Jabor 2. Okt 2008 21:05

Re: "MineSweeper" - Probleme mit Rundtime-Buttons
 
Hmm das ist wahr, aber ich habe die Buttons vorher gelöscht und mkinzler hat mir da geholfen.
Er hat auch gemeint dass Buttons löschen da ncith notwendig ist - siehe obere Posts und ich hatte es sowieso nur zu Testzwecken gemacht.
Die jetzige Methode mag für dich "von der Schiene" sein, aber letzendlich ist sie eine gute Lösung für das Problem und sie ist wesentlich praktikabler und zwecksmäßiger für ein Programm wie MineSweeper!


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:51 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