Autor Beitrag
r@cer
Hält's aus hier
Beiträge: 13

Win 7, Ubuntu 10.10
Delphi 5 Prof., Lazarus
BeitragVerfasst: Do 07.04.11 01:26 
Hallo ...

ich bin noch ein Anfänger was es das Programmieren mit Delphi (und auch allgemein) betrifft. Wir haben in der Schule angefangen das Spiel Minesweeper zu programmieren und ich beschäftige mich gerade zuhause ein wenig damit.
Das heißt ich versuche in dem Programm weiterzukommen, hänge aber gerade an einer gewissen Stelle.
Mein Problem ist Folgendes:

Für die verschiedenen Felder in dem Spiel verwenden wir Images. Und ich versuche gerade die ImageClick-Procedure zu programmieren.
Das heißt, wenn ein Bild angeklickt wird muss das Programm überpüfen, ob eines oder mehrere der angrenzenden Felder eine Mine ist.
ich habe einfach angefangen und lasse das Programm erst einmal überprüfen ob überhaupt eines der Felder eine Mine ist (also ob es vielleicht auch mehrere gibt, interessiert mich erst mal nicht).
Wir haben in der Schule dafür einen sehr schreibaufwendigen aber vom Denken her einfachen Weg gewählt. Nämlich haben wir eine Variable namens "umbebung" vom typ array deklariert:
ausblenden Delphi-Quelltext
1:
var umgebung: array[1..25,1..8of byte;					
wie ihr seht gibt es in meinem Spiel auch nur 25 Felder und die maximale Anzahl an benachbarten Feldern ist 8.
in einer butten-procedure habe ich dann folgendes abgetippt (deswegen auch "schreibaufwendig"):
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
umgebung[1,1]:=Image2.tag;
umgebung[1,2]:=Image7.tag;
umgebung[1,3]:=Image6.tag;
umgebung[2,1]:=Image3.tag;
umgebung[2,2]:=Image8.tag;
umgebung[2,3]:=Image7.tag;
umgebung[2,4]:=Image6.tag;
//.....
das heißt ich kann die Nachbarfelder jedes geklickten Feldes mit diesen Variablen angeben.

mein Problem liegt nun in der ImageClick-procedure:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.ImageClick(Sender: TObject);
var i,a:byte;
begin
  for i:=1 to 8 do begin
  if umgebung[tag,i]=bombenposition[1then begin a:=1; showmessage(inttostr(a)); end
  else begin a:=0; showmessage(inttostr(a)); end;
  end;
end;

das Problem ist, dass ich nicht so richtig weiß wie es mit dem "tag" funktioniert. Also ich weiß nicht genau wie ich dem Programm hier vermitteln soll, welches Bild jetzt genau geklickt wurde. Ich hab mir hier gedacht, dass ich ja eine Schleife verwenden kann, in der für jedes Nachbarfeld (des geklickten Feldes) überprüft wird ob es sich um eine Mine handelt. Und wenn eine Mine existiert, dann lasse ich mir eine showmessage anzeigen, damit ich sehe ob's geklappt hat. also es wird entweder a=1 oder a=0 gezeigt.
Wenn ich das Programm starte, erhalte ich also insgesamt 8 Meldungen (wegen der Schleife) die entweder eine 1 oder eine 0 enthalten. Aber ich bekomme nach jedem Programmstart, bei jedem Bild auf das ich klicke, in der fünften Meldung eine 1 angezeigt und in den restlichen Nullen. Also habe ich es mal an einem konkreten Beispiel probiert für den Fall das ich das zwölfte Bild anklicke. Das heißt ich habe im Code oben das tag durch eine 12 ersetzt: ... umgebung[12,i]=bombenposition[1] ...
übrigens bekomme ich auch bei jedem Programmstart vier meldungen die mir die Positionen der jeweiligen vier minen mitteilen (da ich nur vier Minen verwende). Ich muss in diesem beispiel mit der zwölf also so lange neustarten bis die erste Mine (da ich bombenposition[1] verwende) auf ein Nachbarfeld der 12 fällt. Als dies der Fall war habe ich die zwölf angeklickt und ich bekam wieder meine 8 Meldungen. Diesmal hat es aber funkioniert. Ich bekam eine 1 in der zweiten Meldung, da auch die Mine genau auf dem zweiten Nachbarfeld plaziert war.
das heißt schließlich, dass es mit diesem tag nicht so einfach geht wie ich mir das gedacht habe. ich könnte mir natürlich für jedes der 25 Bilder eine eigene ImageClick-Procedure anlegen und es komplett so programmieren wie ich es auch mit der 12 gemacht hatte, aber das wäre mir dann doch zu aufwändig. Ich muss es also irgendwie schaffen, dem Programm mitzuteilen, welches der Felder gerade angeklickt wurde,
und da wäre ich froh wenn mir hoffentlich jemand aus dem Forum hier ein paar Tipps geben könnte

.... und sorry falls ich etwas zu viel Text für dieses kleine Problem verschwendet habe :oops:

lg r@cer

Moderiert von user profile iconNarses: Delphi-Tags repariert/hinzugefügt
Moderiert von user profile iconNarses: Topic aus Multimedia / Grafik verschoben am Do 07.04.2011 um 01:49
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Do 07.04.11 02:39 
Moin und :welcome: im Forum!

Dein Ansatz hat ein paar grundsätzliche "Probleme", deshalb möchte ich dir mal einen anderen vorstellen. :idea: ;)

Erstmal ein paar Definitionen, damit man das später besser anpassen kann (unter die uses-Klausel setzen):
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
const
  sx = 25;   // Anzahl Felder X
  sy = 25;   // Anzahl Felder Y
  dx = 12;   // Feldbreite X in Pixeln
  dy = 12;   // Feldbreite Y in Pixeln

  mines = 4// Anzahl Minen

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure ImageClick(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    Field: array[0..sx*sy-1of TImage; // Die Felder als TImage
    procedure SetImageColor(const i: Integer; const AColor: TColor);
  end;
Und hinterher gleich die Klassendeklaration des Formulars. Du brauchst in der IDE keine Komponenten drauf zu legen, die Images werden alle beim Programmstart dynamisch auf die Form gebracht. :)
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
// Beim Programmstart: Images dynamisch anlegen
procedure TForm1.FormCreate(Sender: TObject);
  var
    i: Integer;
begin
  for i := 0 to sx*sy-1 do begin // alle Felder bearbeiten
    Field[i] := TImage.Create(Self); // Control erzeugen
    Field[i].Parent := Self; // sichtbar machen
    Field[i].Tag := i; // Feld-Nr. mitgeben, damit man das Feld beim Klicken wiederfindet
    Field[i].Left := (i mod sx) *dx; // X-Position
    Field[i].Top := (i div sx) *dy; // Y-Position
    Field[i].Width := dx; // Größe des Controls
    Field[i].Height := dy;
    Field[i].Picture.Bitmap.Width := dx; // Größe des Bildes im Control
    Field[i].Picture.Bitmap.Height := dy;
    Field[i].Picture.Bitmap.PixelFormat := pf24bit; // Farbtiefe
    SetImageColor(i, clWhite); // färben
    Field[i].OnClick := ImageClick; // Click-Handler zuweisen
  end;
end;

Wenn man auf ein Image geklickt hat, landet man hier. Dabei wird in dem Parameter Sender das Image übergeben, das den Klick ausgelöst hat. :idea:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
// Es wurde auf ein Image geklickt
procedure TForm1.ImageClick(Sender: TObject);
  var
    ThisImage: TImage;
    x, y, ix, iy: Integer;
begin
  ThisImage := Sender as TImage; // in Sender wird das Image übergeben, auf das geklickt wurde
  x := ThisImage.Tag mod sx; // X-Position aus der Feld-Nr. berechnen
  y := ThisImage.Tag div sx; // Y auch
  SetImageColor(ThisImage.Tag, clRed);
  for ix := -1 to 1 do // je 1 Feld horizontal drumrum
    for iy := -1 to 1 do // und 1 Feld vertikal drumrum
      if (ix <> 0or (iy <> 0then // aber nicht das in der Mitte
        if ( (x+ix) in [0..sx-1] ) and ( (y+iy) in [0..sy-1] ) then // Ränder weglassen
          SetImageColor(x+ix +(y+iy)*sx, clBlue);
  ShowMessageFmt('Feld-Nr.: %d, x:%d, y:%d',[ThisImage.Tag, x, y]);
end;

Noch ein bischen Verwaltungskram:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
// Farbe eines Feldes setzen
procedure TForm1.SetImageColor(const i: Integer; const AColor: TColor);
begin
  if (i >= 0and (i <= High(Field)) then begin // Feld-Nr. gültig?
    Field[i].Picture.Bitmap.Canvas.Brush.Color := AColor; // Hintergrund
    Field[i].Picture.Bitmap.Canvas.Pen.Color := clGray; // Rand
    Field[i].Picture.Bitmap.Canvas.Rectangle(0,0,dx-1,dy-1); // zeichnen
  end;
end;
Schau mal drüber, ob dir klar ist, wie das funktioniert. :les: Wenn noch Fragen sind, immer her damit. ;)


cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.

Für diesen Beitrag haben gedankt: r@cer
r@cer Threadstarter
Hält's aus hier
Beiträge: 13

Win 7, Ubuntu 10.10
Delphi 5 Prof., Lazarus
BeitragVerfasst: Sa 16.04.11 19:23 
ok vielen Dank für die antwort

Zwar habe ich nicht sofort deinen kompletten Ansatz übernommen, aber es hat mich trotzdem weitergebracht. Z.B. hab ich das mit dem Tag, dank deines Codes jetzt verstanden und habe es folgendermaßen angewandt:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure TForm1.ImageClick(Sender: TObject);
var i,j:byte;
image_geklickt: TImage;
t: integer;
begin

image_geklickt := Sender as TImage;
t:= image_geklickt.tag;
  for j:=1 to 4 do begin
      for i:=1 to 8 do begin
      if umgebung[t,i]=bombenposition[j] then begin z[j]:=j; end;
      end;
  end;
end;


damit hat es funktionniert und ich konnte den groben ablauf des Programms soweit fertig schreiben. Natürlich fehlen noch gewisse Feinheiten wie die Flagge, die bei einem Rechts-Klick erscheint usw.

Nachdem wir es aber in der Schule mit dem Lehrer nochmal etwas aufpoliert haben, sieht diese Prozedur schließlich so aus:

ausblenden volle Höhe 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:
procedure TForm1.ImageClick(Sender: TObject);
var nr,i,j,bombenzahl:byte;
bombe_getroffen:boolean;
begin
bombenzahl:=0;
bombe_getroffen:=false;
with sender as timage do begin
nr:=tag;

  // Bombe getroffenß?
  for i:=1 to 4 do if bombenposition[i]=nr then begin
  picture.LoadFromFile('bombe.bmp');
  bombe_getroffen:=true;
  end;

if not bombe_getroffen then begin
  for i:=1 to 4 do begin
      for j:=1 to 8 do begin
      if umgebung[nr,j]=bombenposition[i] then begin inc(bombenzahl) end;
      end;
  end;
  //showmessage(inttostr(bombenzahl));

case bombenzahl of
 0:picture.LoadFromFile('leer.bmp');
 1:picture.LoadFromFile('1.bmp');
 2:picture.LoadFromFile('2.bmp');
 3:picture.LoadFromFile('3.bmp');
 4:picture.LoadFromFile('4.bmp');
end;
end;
end;
end;


Da alles funktionniert, und wir den Ablauf des Programms soweit verstanden haben, hat unser Lehrer das Projekt mit uns abgeschlossen. Er hat aber gemeint, dass natürlich jeder für sich dem Programm noch einen Feinschliff verpassen kann.
Also hab ich mir gedacht, dass ich z.B. ein Hauptmenü erstellen kann und dann in dem Hauptmenü (genau wie bei dem Minesweeper von Microsoft Windows) einen Unterpunkt "Spiel starten" und einen mit "Optionen". Ich habe mir das mit den "Optionen" so vorgestellt, dass sich ein neues Fenster öffnet in dem man den Schwierigkeitsgrad auswählen kann und den Sound ein oder ausschalten kann.
Ich bin jetzt auch schon so weit, dass ich einen neuen Frame mit einer Unit2 hinzugefügt habe. Und programmieren wollte ich das folgendermaßen: Ich möchte eine Prozedur für den Reiter "Optionen" erstellen in der durch einen Befehl wie z.B. TForm2.visible:=true; das dazugehörige Fenster erscheint.
Jetzt ist mir aber schon klar, das man das mit so einem Befehl sehr wahrscheinlich nicht zum laufen bringt, da sich die erwähnte Prozedur in Unit1 befindet und man mit dem "Visible-Befehl", soweit ich weiß, nur Objekte (wie Buttons, TImages, Panels usw), die sich auf eben diesem TForm1 befinden, unsichtbar machen kann.

Also mein Problem ist ganz einfach dieses, dass ich eine Unit1 habe in der man das eine Fenster programmiert und eine Unit2 die für das zweite Fenster zuständig ist. Und ich weiß eben nicht, wie man in Unit1 mit einem Befehl das zweite Fenster "steuern" kann.

hoffe erneut auf etwas Hilfe :)

Gruß r@cer
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 16.04.11 19:47 
user profile iconr@cer hat folgendes geschrieben Zum zitierten Posting springen:
Ich bin jetzt auch schon so weit, dass ich einen neuen Frame mit einer Unit2 hinzugefügt habe.
Frame oder Form? Das ist ein Unterschied, ein neues Formular wäre hier ein neues Fenster, ein Frame ist ein Container für mehrere Objekte. ;-)

user profile iconr@cer hat folgendes geschrieben Zum zitierten Posting springen:
einen Befehl wie z.B. TForm2.visible:=true;
[...] man mit dem "Visible-Befehl", soweit ich weiß, nur Objekte (wie Buttons, TImages, Panels usw), die sich auf eben diesem TForm1 befinden, unsichtbar machen kann.
Doch, das ist schon richtig so.

Wenn du ein zweites Fenster TForm2 hast (vergib besser einen richtigen Namen :zwinker:), kannst du das auch so steuern:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
Form2.Show;
Form2.ShowModal;
Form2.Hide;
...

user profile iconr@cer hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
 0:picture.LoadFromFile('leer.bmp');
 1:picture.LoadFromFile('1.bmp');
Da fehlt die Pfadangabe, die ist sehr wichtig:
www.delphi-library.d...ewtopic.php?p=499701
beastofchaos
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 247
Erhaltene Danke: 4



BeitragVerfasst: Sa 16.04.11 23:34 
Bei Pfad-Angabe empfehle ich dir eher die Var path(String) zu erzeugen und dann im FormCreate folgendermaßen zu ermitteln;

ausblenden Delphi-Quelltext
1:
path := ExtractFilePath(Application.ExeName);  // ermittel den Pfad der Exe-Datei - Am Ende ist ein /					


Dann muss du deine Prozedur so schreiben:

ausblenden Delphi-Quelltext
1:
picture.LoadFromFile(path + 'leer.bmp');  // Dann müsste dein Bild in dem selben ordner wie deine Exe sein					


Gruß, Thomas
r@cer Threadstarter
Hält's aus hier
Beiträge: 13

Win 7, Ubuntu 10.10
Delphi 5 Prof., Lazarus
BeitragVerfasst: So 17.04.11 10:08 
Was eignet sich für mein Vorhaben den besser? - ein Frame oder ein Formular?
Und wo ist der Unterschied?
Aber es hat jetzt mit einem neuen Formular geklappt :). (Ich hatte vorher ein Frame benutzt)

Was die Sache mit dem Pfad betrifft, verstehe ich nicht so richtig für was das gut sein soll, da es ja auch ohne funktionniert. Denn die Bilder liegen alle im selben Ordner, in dem auch die Exe liegt.

Gruß r@cer
beastofchaos
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 247
Erhaltene Danke: 4



BeitragVerfasst: So 17.04.11 10:58 
KANN zu Fehlern führen. Angenommen du hast zwei Images. Normalerweise legt man sich alle BIlder in einen extra rdner, genauso alle Sounds/ Datenbanken, etc. - kensnt du sicher von größeren Spielen/ Programmen:

ausblenden Delphi-Quelltext
1:
2:
3:
Image1.LoadFormFile('Pictures/Bild1.bmp');  // geht in ordner Pictures

Image2.LoadFormFile('Pictures/Bild2.bmp');  // hier käme Fehlermeldung, weil er schon vorher im Ordner Pictures ist und dann in diesem nach einem weitern Sucht (-> /Pictures/Pictures/Bild2.bmp)


Klar känntest du dann bei jedem weitern mal einfach das Pictures am Anfang weglassen, aber um auf Sound etc. zuzugreifen musst du erst wieder in den Exe-ordner (indem du vor den Pfad ein "../" setzt);
Das kann dabei so unübersichtlich werden, dass man am besten von anfang an so arbeitet ;) Wenn du sowieso alles im Exe-Ordner rumliegen hast, ist das egal, aber wäre gut für eine saubere Lösung vll.


Grüße, Thomas
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 17.04.11 11:01 
user profile iconr@cer hat folgendes geschrieben Zum zitierten Posting springen:
Was die Sache mit dem Pfad betrifft, verstehe ich nicht so richtig für was das gut sein soll, da es ja auch ohne funktionniert. Denn die Bilder liegen alle im selben Ordner, in dem auch die Exe liegt.
Lesen magst du wohl nicht oder wie? Ich habe es unter dem Link doch ausführlich erklärt inklusive Beispiel... :roll: :autsch:

Dann pack einfach einmal einen Öffnen-Dialog auf dein Formular, zeige den als allererstes in FormCreate an und wähle in dem einfach eine Datei aus einem anderen Verzeichnis aus. Und schon geht gar nichts mehr.

Denn wenn du kein Verzeichnis angibst, wird nicht im Verzeichnis der Exe gesucht, sondern im aktuellen Arbeitsverzeichnis. Und das hat absolut überhaupt gar nichts mit dem Verzeichnis der Exe zu tun...

user profile iconbeastofchaos hat folgendes geschrieben Zum zitierten Posting springen:
Wenn du sowieso alles im Exe-Ordner rumliegen hast, ist das egal
Nein, eben nicht.
beastofchaos
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 247
Erhaltene Danke: 4



BeitragVerfasst: So 17.04.11 11:10 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

user profile iconbeastofchaos hat folgendes geschrieben Zum zitierten Posting springen:
Wenn du sowieso alles im Exe-Ordner rumliegen hast, ist das egal
Nein, eben nicht.


Solange es noch funktioniert, eben schon :P Bzw. es funktioniert ja noch bei ihm. Aber hast Recht ;)
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: So 17.04.11 14:07 
Moin!

Wir sind jetzt mittlerweile beim dritten Thema, und das sind eindeutig zwei zu viel. ;)

Bitte für jedes Thema einen separaten Thread nutzen, das wird sonst unübersichtlich. :idea: Da das eigentliche Thema des Threads offensichtlich erledigt ist, mache ich mal zu.

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Dieses Thema ist gesperrt, Du kannst keine Beiträge editieren oder beantworten.

Das Thema wurde von einem Team-Mitglied geschlossen. Wenn du mit der Schließung des Themas nicht einverstanden bist, kontaktiere bitte das Team.