Entwickler-Ecke

Multimedia / Grafik - Spielbrett nachbauen.


tetris84 - Mo 24.10.11 22:22
Titel: Spielbrett nachbauen.
Hallo,

ich hoffe mal das es sowas noch nicht gibt. Hab zwar bisschen die Suche benutzt aber nicht wirklich was gefunden.
Ich möchte auf ein Scrabble ähnliches Spielbrett verschiedene (auch Scrabble ähnliche) Spielsteine platzieren. Wie mach ich es, dass die Spielsteine das Feld vom Spielbrett genau besetzen? Ich habe mir das so vorgestelle das wenn man einen Spielstein zwischen zwei Felder legt das er sich automatisch auf das dichtere Feld "legt". Es sollte auch leicht zu prüfen sein wo der Stein liegt und ob ein Feld schon belegt ist.

Lg tetris84


Xion - Mo 24.10.11 22:48

:welcome:

Als Spielbrett würde ich sowas machen:


Delphi-Quelltext
1:
Spielbrett: array [0..49of array [0..49of TCell;                    


Als einzelne Zelle könnte man sich dann z.B. Speichern ob da schon was liegt:


Delphi-Quelltext
1:
2:
3:
4:
type TCell=record
  Belegt: boolean;
//BelegtDurch: TSpielstein;
end;


Zur Platzierung:
Angenommen unser Spielbrett soll 500x500 Pixel groß sein. Also ist eine Zelle 10 groß (500/50). Seien weiterhin X,Y die Position des Spielsteins (welches ja 10x10 groß ist).

=> round(X/10)*10, round(Y/10)*10 schnippt das Objekt ins Raster. Ist die Ecke z.B. bei 19/11 dann wird sie verschoben zu 20/10 (nähste Spielfeld-Zelle).


rushifell - Mo 24.10.11 22:49

Das wäre mit einem zweidimensionalen Array möglich.

Delphi-Quelltext
1:
Var Feld : Array[0..19,0..19of TFeld;                    


Die Daten eines Felds kannst Du in einem Record speichern.

Delphi-Quelltext
1:
2:
3:
4:
5:
TFeld=record
Buchstabe:Char;
Besetzt:Boolean;
...
end;


Berechnen kannst Du den index vom Feld mit DIV. Wenn eine Feldbreite und Feldhöhe jeweils 20 Pixel beträgt:

Delphi-Quelltext
1:
2:
Var aa:Char;
aa:=Feld[X DIV 20,Y DIV 20].Buchstabe;


Für das Spielfeld könnstest du ein TImage verwenden.

EDIT: Xion war einen Tick schneller ;-)


tetris84 - Di 25.10.11 18:09

Auf was würde sich x bzw y den Beziehen? Auf SpielBrettImage.Top bzw Left?

Lg tetris84
PS: Das Spielbrett ist 11*11 Steine groß


Yogu - Di 25.10.11 18:14

user profile icontetris84 hat folgendes geschrieben Zum zitierten Posting springen:
Auf was würde sich x bzw y den Beziehen? Auf SpielBrettImage.Top bzw Left?

X und Y sind, soweit ich das vertanden habe, die Mauskoordinaten im OnMouseDown. Die werden relativ zum linken oberen Eck des Spielfeldes angegeben.


Xion - Di 25.10.11 18:24

X und Y sind erstmal nur die Position deines Spielsteins, den du in das Gitter des Spielfeld einpassen willst.

Woher diese Position kommt ist eine andere Frage, das hängt davon ab, wie du die Steine platzieren lassen willst. Falls du sie per Drag&Drop verschieben kannst, dann funktioniert der oben genannte Code, aber X,Y ist nicht die Position der Maus, sondern die des Spielsteins (der natürlich von der Mausposition abhängt)


tetris84 - Di 25.10.11 18:28

Hmmm also wenn ich das da eingebe kommt immer der Fehler: [DCC Fehler] UHauptformLoopit.pas(181): E2014 Anweisung erforderlich, aber Ausdruck vom Typ 'Int64' gefunden




Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure THauptformLoopit.Image2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Begin
  StartPunkt := ClientToScreen(Point(X,Y));           //Drag & Drop 
  MouseDown:= True;                                   //Drag & Drop
round(X/10)*10; round(Y/10)*10;
End;


Edit: Per Drag and Drop

Moderiert von user profile iconMartok: Delphi-Tags hinzugefügt


Xion - Di 25.10.11 19:16

:?
Natürlich geht das so nicht, und wenn es ginge, dann würde es dir nichts bringen (du speicherst ja das Ergebnis nirgends).

Du könntest schreiben:

Delphi-Quelltext
1:
 Spielstein.Left := round(X/10)*10;                    


//PS:
das solltest du in OnMouseMove schreiben ;)


rushifell - Di 25.10.11 19:23

Mit X,Y meine ich die Mauskoordinaten im OnMouseMove oder OnMouseDown des Images. Per Drag und Drop ist das ganze natürlich schöner, aber wesentlich schwieriger zu implementieren. Wesentlich einfacher wäre es, einen Spielstein zu markieren, den Maus-Cursor auf dem Feld zu platzieren auf das Du den Stein setzten möchtest und den Stein durch Druck der linken Taste zu setzen.

Du kannst ja das Drag und Drop auch später noch implementieren.


Xion - Di 25.10.11 19:28

Meiner Erfahrung nach ist Drag&Drop nicht so kompliziert.

OnMouseDown:


Delphi-Quelltext
1:
2:
  DragDelta.X := X-Spielstein.Left; //Y analog
  MouseDown := true;


OnMouseUp:

Delphi-Quelltext
1:
  MouseDown := false;                    


OnMouseMove:

Delphi-Quelltext
1:
2:
3:
4:
  if MouseDown then
     begin
       Spielstein.Left := X-DragDelta.X;  //PS: hier brauchen wir zusätzlich den round-Code von oben für die Rasterung.
     end;


tetris84 - Di 25.10.11 19:44

Ist mir jetzt bisschen unangenehm zu Fragen aber für was steht dann DragDelta?

Also es muss auch defenitiv Drag&Drop sein. Anders sieht es einfach nicht gut aus :(

Lg tetris84


Xion - Di 25.10.11 19:53

DragDelta ist einfach nur vom Typ TPoint.

Die Idee dahinter:
Du speicherst dir zu Beginn, wo du den Spielstein angeklickt hast. Du klickst ja "irgendwo" in die Mitte. Wenn du dann im OnMouseMove den Spielstein abhängig von der Mausposition verschiebst, dann sollte dieses "ungefähr in der Mitte" identisch sein, sonst ruckelt der Spielstein am Anfang. Du kannst es ja einfach mal ohne ausprobieren ;)

//Edit:
Da fällt mir was ein:
Wie du schon in deinem Code richtig zeigst musst du mit "Screen-Koordinaten" arbeiten, weil du sonst in den Funktionen bei X,Y verschiedene Koordinatensysteme hast. Also immer erstmal ClientToScreen() verwenden.


tetris84 - Do 27.10.11 16:37

Also an mählig hab ich das Gefühl ich bin zu doof :/
Ich habs (meiner Meinung nach) alles richtig gemacht und es passiert garnichts :(
Noch irgentwelche Ideen? Oder hab ich einfach was übersehen?




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:
procedure THauptformLoopit.Spielstein1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Begin
  StartPunkt := ClientToScreen(Point(X,Y));
  MouseDown:= True;
End;

procedure THauptformLoopit.Spielstein1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var AktuellerPlatz: TPoint;
Begin
  If MouseDown then
   with Sender as TImage do
   Begin
     AktuellerPlatz := ClientToScreen(Point(X,Y));
     x := round(Y/11)*11;
     Left:= AktuellerPlatz.X - StartPunkt.X;
     y := (round(Y/11)*11);
     Top:= AktuellerPlatz.Y -StartPunkt.Y
   End;
End;

procedure THauptformLoopit.Spielstein1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Begin
    MouseDown:=False;
End;


Xion - Do 27.10.11 17:20

user profile icontetris84 hat folgendes geschrieben Zum zitierten Posting springen:

Ich habs (meiner Meinung nach) alles richtig gemacht und es passiert garnichts :(
Noch irgentwelche Ideen? Oder hab ich einfach was übersehen?

Meiner Meinung nach hast du was übersehen, was sehr zum Übersehen einläd ;) Hat also nichts mit doof zu tun.

Angenommen du hast die Maus auf deinem Spielstein und bewegst die dort weg. Dann ist diese Bewegung ja ruckartig (d.h. mehrere Pixel auf einmal). Du bist also sehr schnell nicht mehr über dem Spielstein, sondern über dem Rest der Form => Es wird das OnMouseMove der Form (oder was auch immer unter der Maus ist) ausgelöst :!:
Du musst also für alle Objekte MouseOver setzen (auf die MouseOver-Funktion von dir). Das Problem dabei ist natürlich, dass du dann von Hand regeln musst, was gedragged wurde (nicht wie du mit Sender). Das kannst du ja beim OnMouseDown speichern.


user profile icontetris84 hat folgendes geschrieben Zum zitierten Posting springen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure THauptformLoopit.Spielstein1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var AktuellerPlatz: TPoint;
Begin
  If MouseDown then
   with Sender as TImage do
   Begin
     AktuellerPlatz := ClientToScreen(Point(X,Y));
     x := round(Y/11)*11
     Left:= AktuellerPlatz.X - StartPunkt.X;
     y := (round(Y/11)*11);
     Top:= AktuellerPlatz.Y -StartPunkt.Y
   End;
End;

Die markierte Stelle sollte wohl X heißen ;)
Fällt dir an dem Code was auf? Du weißt X und Y was zu, benutzt es aber niemehr. Irgendwie sinnlos, oder :mrgreen: Aber egal, das ist ja nur die Berechnung vom Raster.
Ein andrer Punkt ist: "AktuellerPlatz" ist in Screen-Koordinaten (d.h. vom ganzen Screen=Bildschirm). Beim setzen von Left und Top musst du diese erst in Form-Koordinaten umwandeln, falls dein Image auf einer Form liegt (bei Panel analog).


Xion - Do 27.10.11 17:41

Ich habe es mal als Beispielprojekt gemacht, musste mit dem DragDelta jetzt auch erstmal nachdenken :D


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:
34:
35:
36:
37:
38:
var
  Clicked: boolean;
  DragDelta: TPoint;
  DragObject: TPanel;

[...]

procedure TForm1.Panel2MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  Clicked := true;
  DragDelta.X := X;
  DragDelta.Y := Y;
  DragObject :=  Sender as TPanel;
end;

procedure TForm1.Panel2MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  Clicked := false;
end;

procedure TForm1.Panel2MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var Pos: TPoint;
begin
  if Clicked then
    begin
      Pos := (Sender as TWinControl).ClientToScreen( Point(X-DragDelta.X,Y-DragDelta.Y) );
      //Pos.X := round(Pos.X/55)*55;      //RasterungX
      //Pos.Y := round(Pos.Y/15)*15;      //RasterungY

      Pos := Form1.ScreenToClient( Pos );

      DragObject.Left := Pos.X;
      DragObject.Top  := Pos.Y;
    end;
end;


tetris84 - Di 22.11.11 13:35

So ich hoffe mir schreibt jetzt noch jemand nach der langen Zeit. Ich hab das jetzt weiter versucht und bin dann von Drag and Drop weggegangen und habe mir überlegt das ich das ganze mit einem SGField mache.

Ich hab mir vorgestellt das man einmal auf einen Spielstein klickt und dann auf das Feld wo der Stein liegen soll und in die Zelle wird dann das Bild geladen. Nun hab ich aber keinen Anhaltspunkt mehr wie ich weiterkommen soll. Mein Code sieht bisher so aus:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
type
    TZelle=record
      Beleget : boolean;
      BelegtDuch:String;
    end;
    Spielbrett= array [0..10of array [0..10of TZelle;


var MouseDown:boolean;
    StartPunkt: TPoint;
    path : string;
    DragDelta: TPoint;
    DragObject: TPanel;
    Bild:TBitmap;
Begin
   Bild := TBitmap.Create;
   SpielsteinPfad := 'Spielsteine\'+Zufallsstein;
   path := ExtractFilePath(Application.ExeName) + SpielsteinPfad;
End;








Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
procedure THauptformLoopit.SGFieldDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
var Tester:Tzelle;
begin
  if (ACol = 5and (ARow=5then
     BEGIN
       Bild.LoadFromFile(path);
       SGField.Canvas.StretchDraw(Rect, Bild);
     END;
end;

procedure THauptformLoopit.SGFieldMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var col, row : integer;
begin
  SGField.MouseToCell(X, Y, col, row);
  SGField.refresh;
end;


Xion - Do 24.11.11 21:12

Was hast du denn nun vor? ;)

Die Sache mit dem StringGrid find ich irgendwie nicht sehr professionell. Ich hätte eher eine Matrix (2D-Array) mit TImage verwendet.

Im wesentlichen musst du jetzt erstmal eine Reihe von Bildern anlegen mit den Steinen, die der User hat. Und dann musst du, wenn du dort einen anklickst die ID speichern und danach wenn du aufs Feld klickst den dort reinpacken (TZelle) und das Bild laden/kopieren.


tetris84 - Do 01.12.11 10:31

user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:
Was hast du denn nun vor? ;)

Die Sache mit dem StringGrid find ich irgendwie nicht sehr professionell. Ich hätte eher eine Matrix (2D-Array) mit TImage verwendet.

Im wesentlichen musst du jetzt erstmal eine Reihe von Bildern anlegen mit den Steinen, die der User hat. Und dann musst du, wenn du dort einen anklickst die ID speichern und danach wenn du aufs Feld klickst den dort reinpacken (TZelle) und das Bild laden/kopieren.


Danke nochmal. Hab das fast genauso gemacht. Bin jetzt grade dabei eine Schummelfunktion ein zu bauen. Ich möchte nach doppelklick auf einen Spielstein, mit dem Mausrad scrollen und zwischen allen verfügbaren Steinen einen auswählen. Wie gehts das? Mit ScrollBox?

Lg tetris84


bummi - Do 01.12.11 10:43

Häng Dich doch einfach in die MouseWheel-Events des Forms ....


tetris84 - Do 01.12.11 10:47

Wie meinst du das? Ich hab die Steine ja zurzeit in einem TImage


bummi - Do 01.12.11 11:02

macht ja nichts, das Event schlägt trotzdem auf dem Form auf

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm2.FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
    Caption := IntToStr(Wheeldelta div ABS(Wheeldelta));
    Handled := true;
end;


tetris84 - Do 01.12.11 13:29

user profile iconbummi hat folgendes geschrieben Zum zitierten Posting springen:
macht ja nichts, das Event schlägt trotzdem auf dem Form auf

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm2.FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
    Caption := IntToStr(Wheeldelta div ABS(Wheeldelta));
    Handled := true;
end;


Hmm ich versteh aber nicht wo da jetzt aufs TImage zugegriffen wird?


Tilo - Do 01.12.11 13:37

Im Event wird die Mausposition mit übergeben.
Wenn Du weißt welches TImage wo ist kannst Du selbst drauf zugreifen.

Beim vorherigen Doppelklick auf ein TImage merkst du es dir (z.B in einer Variablen "LastClickedStone").
Wenn nun die Mausposition innerhalbder Koordinaten des Steines sind -> gewünschte Aktion ausführen.

MFG
Tilo


tetris84 - Do 01.12.11 13:59


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure THauptformLoopit.FormMouseWheelDown(Sender: TObject;
  Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
var x:byte;
begin
GewaehlterSteinCache := FSpielsteinName[x];    // GewahlterSteinCache bestimmt welcher Stein angezeigt wird 
x := x+1;
end;


FSpielsteinName gibt den Namen eins Spielsteins wieder.

Geht das so?

Bei mir kommt jetzt immer der Fehler:
[DCC Fehler] UHauptform.pas(455): E2035 Nicht genügend wirkliche Parameter


bummi - Do 01.12.11 14:04

ist wie stochern im Nebel, gegf. kannst Du das Projekt mal als Zip einstellen, oder zeigen was wie deklariert ist ...


tetris84 - Do 01.12.11 14:21

Das mit dem hochladen wird nichts :/ Hab ein so schlechtes Internet da würde das ne Stunde dauern.


bummi - Do 01.12.11 14:29

Da ich nicht weiß was Du wie machst, vielleicht kannst Du folgendes so umbauen wie Du es brauchst .....


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
procedure TForm2.FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
   if Assigned(FSelectedImage) then
      begin
        FSelectedImage.Tag := FSelectedImage.Tag + ( Wheeldelta div ABS(Wheeldelta));
        if FSelectedImage.Tag < 0 then FSelectedImage.Tag := 0
        else FSelectedImage.Tag > C_MAX then FSelectedImage.Tag := C_MAX;
        UpdateSelectedImage;

      end;
end;

procedure TForm2.Image1Click(Sender: TObject);
begin
   FSelectedImage := TImage(Sender);
end;


thepaine91 - Do 01.12.11 14:45

user profile icontetris84 hat folgendes geschrieben Zum zitierten Posting springen:
Das mit dem hochladen wird nichts :/ Hab ein so schlechtes Internet da würde das ne Stunde dauern.


:rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl: :rofl:


tetris84 - Do 01.12.11 15:33

Traurig aber war :(


thepaine91 - Do 01.12.11 15:43

Naja also i-wie kann ich das grade nicht ganz glauben denn bei mir kommt ein Delphiprojekt ohne i-welche Bilder etc. nicht über 1 MB daher frage ich mich wie groß dein Projekt ist das es eine Stunde dauert. Denn bei einer Standardgröße von 1MB wäre das eine Uploadgeschwindigkeit von ~291 byte in der Sekunde.


tetris84 - Do 01.12.11 16:01

Es ist aber 18MB groß. Außerdem möchte ich das nur ungerne Hochladen da ich es ja selber verstehen möchte was da im Quelltext steht. Und wenn das jetzt jemand anderes macht find ich das doof. Ich versuchs jetzt erstmal so weiter.


Xion - Do 01.12.11 19:10

Es sieht mir mehr so aus als willst du einfach nicht deinen Code offenlegen. Das kannst du ruhig sagen, das ist nicht unnormal ;)

Im wesentlichen würd ichs so machen, wie bummi schon gesagt hat. Du speicherst die ID des angewählten Steins und beim OnMouseWheel schaltest du durch die verfügbaren Steine durch und lädst das entsprechende Bildchen.


tetris84 - Fr 02.12.11 11:24

So generell hab ichs jetzt fertig.

Delphi-Quelltext
1:
2:
3:
Stein1: TImage;
Bild : TBitmap;
path : string;



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure THauptformLoopit.FormMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
var i:byte; SteinName,SpielsteinPfad:string;
begin
SteinName := FSpielsteinNamenRueckgabe(i);
i := i+1;

Bild := TBitmap.Create;
SteinCache := SteinName;
SpielsteinPfad := 'Spielsteine\'+ SteinCache;
path:= ExtractFilePath(Application.ExeName) + SpielsteinPfad;
Bild.LoadFromFile(path);
Stein1.Canvas.StretchDraw(Stein1.Canvas.ClipRect, Bild);
end;

Das Problem ist wenn ich jetzt zur Laufzeit Scrolle (nach unten) scrollt er nur in dem SgField. Wie verhinderre ich das und ermögliche das hallt Stein1 sich verändert?

Lg tetris84


bummi - Fr 02.12.11 11:35

so ...

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm2.StringGrid1MouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
     Handled := true;
end;

procedure TForm2.StringGrid1MouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
     Handled := true;
end;


tetris84 - Fr 02.12.11 11:44

Er scrollt immer noch in den Zellen von SGField umher :/ Gibt es in dem Feld eine Option die ich dafür auf False setzten muss?

Edit. Ich hab 60 verschiedene Steine. Wie realisiere ich es das jeder nur einmal gesetzt wird?

Lg tetris84


tetris84 - Mo 05.12.11 13:59

Moin,

hab das mit den Spielsteinen jetzt geschafft. Bei dem Scrollen funktioniert es immer noch nicht :/

Edit: Das mit dem Scrollen funktioniert jetzt. Ich musste erstmal mittels .SetFocus Irgentwo anders den Fokus hinlegen.


tetris84 - Do 08.12.11 14:16

Hey,

ich wollte nochmal fragen wie man ein Array in eine Ini Datei Speicher. Bei String,Integer und Boolean gibt es ja die Funktion ini.WriteString, ini.WriteInteger und ini.WriteBool. Wie soll ich also mein Array speichern?
Bis jetzt kann ich mir nur denken das man es so macht:

Delphi-Quelltext
1:
2:
3:
   Repeat  
ini.WriteString('Steine',Zwischenspeicher, Array[x,y]);
   Until (x = 1and (y = 11);

In Zwischenspeicher stehen dann auch nochmal die Koordinaten.
Geht es auch einfacher?

Lg tetris84


Xion - Do 08.12.11 19:41

Bitte bei der nächsten Frage ein neues Topic aufmachen, damit andere User die Lösung auch per Suche finden können :)


Delphi-Quelltext
1:
ini.WriteString('Steine',inttostr(x)+'-'+inttostr(y), Array[x,y]);                    


So würd ich das machen.