Autor Beitrag
Pepp3r
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 82



BeitragVerfasst: Fr 18.03.11 01:33 
Hallo liebes Delphi Forum,
ich möchte hier meine Variante zum rotieren von Tetris Klötzen veröffentlichen, hab bisher nichts brauchbares im Netz gefunden und nach Stunden des Denkens und Programmierens habe ich es nun endlich geschafft. Ausgenommen ist hier der seint "I" und "O"; "I" bedarf eine Sonderbehandlung und "O" brauch man nicht zu rotieren.
Übrig bleiben die Klötze "T", "L", "J", "S", "Z" ( de.wikipedia.org/wiki/Tetris ).
Die Klötze sind eine Zusammensetzung aus Steinen. Also Stein1+Stein2+Stein3+Stein4 = Klotz :P
Ein Stein ist eine Zelle eines Zweidemensionalen Arrays.
Jetzt hört sich das mit dem Rotieren vllt relativ einfach an. Ist es auch :D

Im Groben klappt das so:
- Jeder der Klötze ("T", "L", "J", "S", "Z") hat einen "Angelpunk"-Stein der sich zum Zeitpunkt der Drehung nicht weg bewegt.
- Alle anderen Steine des Klotzes bewegen sich bei der Rotation um den "Angelpunkt"-Stein, bis die gewünschte Position erreich ist.

Probleme:
- Ein Klotz befindet sich IRGENDWO im Array, diese Position ändert sich ständig
- Der Klotz muss nach der Rotation diesselbe Position haben wie davor
- Jeder Klotz hat eine Individuelle Form

Lösung:
Wir betrachten einen Klotz in einem 3*3 Array, EGAL wo er sich gerade im x*y Array befindet.
Fassen wir zusammen:
-Jeder der Klötze besteht aus genau 4 Steinen
-Jeder der Klötz besitzt einen (von uns) definierten Angelpunk

Das Beispiel setzt voraus, das die Koordinaten der Steine des fallenden Klotzes bekannt sind.
Also können wir uns jeden Klotz folgend vorstellen (X: Normaler Stein, O: Angelpunk-Stein):

T
___________
|_X_|_X_|_X_|
|___|_O_|___|
|___|___|___|

L
___________
|___|_X_|___|
|___|_O_|___|
|___|_X_|_X_|

J
___________
|___|_X_|___|
|___|_O_|___|
|_X_|_X_|___|

S
___________
|___|_X_|_X_|
|_X_|_O_|___|
|___|___|___|


Z
___________
|_X_|_X_|___|
|___|_O_|_X_|
|___|___|___|

...wobei
___________
|_1_|_2_|_3_|
|_8_|_A_|_4_|
|_7_|_6_|_5_|

...mit Koordinaten

A: (x,y)
1: (x-1,y-1)
2: (x,y-1)
3: (x+1,y-1)
4: (x+1,y)
5: (x+1,y+1)
6: (x,y+1)
7: (x-1,y+1)
8: (x-1,y)

...ist.

Alles was man jetzt machen muss ist, einen Stein nehmen und ihn ihm Urzeigersinn im zwei Stellen entlang des Angelpunktes drehen.

Jetzt geht man so vor:
1.: Ermittle die Koordinaten der Angelpunktes: angelX, angelY.
2.: Ermittle die Koordinaten des zu verschiebenden Steins: altX, altY.
3.: Den Stein in der 3x3 Matrix betrachten:
ausblenden Quelltext
1:
2:
altX = altX - angelX
altY = altY - angelY

Das Ergebnis ist der Abstand des Steines in Bezug zum Angelpunkt. Also zB: (-1,-1) für den Stein an stelle 1.
4.: Errechne die neuen Koordinaten für den zu verschiebenden Stein: neuX, neuY.
ausblenden Quelltext
1:
2:
neuX = altY * (-1)
neuY = altX


5.: Mache 2.-4. für jeden Stein des Klotzes AUßER für den Angelpunkt.

...fertig.

Ich hoffe es war einigermaßen verständlich. Vllt habe ich einigen mit meiner Arbeit geholfen :)
Bei bedarf kann ich noch den "I"-Klotz erläutern.
Hier mein persönlicher Beispielcode:


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:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
procedure TKlotz.RotateKlotz;
var
  zentrumX,
  zentrumY,
  xAlt, yAlt,
  xNeu, yNeu: Integer;
begin

{ Hier werden die Steine um das Zentrum des Klotzes erfasst und dessen
  Koordinaten neu berechnet.}


  if AktKlotz.klotz in ['T''S''L''J''Z'then  //versteht man 
  begin
    zentrumX := AktKlotz.steinAngel.x;
    zentrumY := AktKlotz.steinAngel.y;
   
    {Stein2} //jaja, bei mir fangen die steine bei 2 an X)

    xAlt := AktKlotz.stein2.x - zentrumX; //zur Betrachtung des Klotzes in der 3x3 Matrix ist dieser Schritt nötig
    yAlt := AktKlotz.stein2.y - zentrumY;
    Tetris.Spielbrett[xAlt + zentrumX, yAlt + zentrumY].aktiv := False; // Fallende Klötze bestehen bei mir aus "aktiven" Steinen, die mach ich jetzt kaputt
    xNeu := (yAlt *(-1)) + zentrumX; //neue Koordinaten werden berechnet
    yNeu := xAlt + zentrumY;
    AktKlotz.stein2.x := xNeu; //neue Koordinaten werden übergenen
    AktKlotz.stein2.y := yNeu;
    //usw. ...
    {Stein3}
    xAlt := AktKlotz.stein3.x - zentrumX;
    yAlt := AktKlotz.stein3.y - zentrumY;
    Tetris.Spielbrett[xAlt + zentrumX, yAlt + zentrumY].aktiv := False;
    xNeu := (yAlt *(-1)) + zentrumX;
    yNeu := xAlt + zentrumY;
    AktKlotz.stein3.x := xNeu;
    AktKlotz.stein3.y := yNeu;

    {Stein4}
    xAlt := AktKlotz.stein4.x - zentrumX;
    yAlt := AktKlotz.stein4.y - zentrumY;
    Tetris.Spielbrett[xAlt + zentrumX, yAlt + zentrumY].aktiv := False;
    xNeu := (yAlt *(-1)) + zentrumX;
    yNeu := xAlt + zentrumY;
    AktKlotz.stein4.x := xNeu;
    AktKlotz.stein4.y := yNeu;
  end;
end;


...is schon spät, ich entschuldige mich für jeglichen dummen Rechtschreobfehler :P

Edit:

Ok, klappt auch mit den anderen Figuren :D


Zuletzt bearbeitet von Pepp3r am Fr 18.03.11 04:57, insgesamt 1-mal bearbeitet
Bergmann89
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1742
Erhaltene Danke: 72

Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
BeitragVerfasst: Fr 18.03.11 01:46 
Hey,

von der Idee her hät ich das auch so gemacht, aber ich hätte nicht jeden Block einzelln verschoben. Ich würd ne schleife über alle 8 Blöcke machen und dann mit sin und cos die neuen Variablen berechnen. Kannst du dir ja nochmal vornhemen, wenn du Lust dazu hast ;)

MfG Bergmann.

_________________
Ich weiß nicht viel, lern aber dafür umso schneller^^
Pepp3r Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 82



BeitragVerfasst: Fr 18.03.11 01:57 
Moderiert von user profile iconNarses: Komplett-Zitat des letzten Beitrags entfernt.

Das geht im Moment über meine mathematischen Fähigkeiten hinaus, könntest du deine Idee vielleicht etwas näher erläutern, es würde mich interessieren.
Bergmann89
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1742
Erhaltene Danke: 72

Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
BeitragVerfasst: Fr 18.03.11 03:21 
Hey,

du berechnest den Winkel des Blocks mit Koorinaten: tan(alpha) = x/y. Dann addierst du die 90° und wandelst ales wieder in Koordinaten um: x = Round(cos(alpha+Pi/2)); y = Round(sin(alpha+Pi/2)). Da sin und cos aber Kommazahlen zurückgibt musst du das ganze noch runden. Das gut daran ist, es lässt sich leichter verwalten, es ist weniger Code und man kann zur Not auch größere Arrays drehen. Kleines Bsp-Proggi:
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:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
program Project1;

uses
  Forms, SysUtils, Math;

{$R *.res}
{$APPTYPE CONSOLE}

type
  TArray = array[-1..1, -1..1of Integer;

var
  arr: TArray;
  x, y, i: Integer;

  procedure DisplayArray(arr: TArray);
  var
    x, y: Integer;
    str: String;
  begin
    for y := -1 to 1 do begin
      str := '';
      for x := -1 to 1 do
        str := str + IntToStr(arr[x, y]);
      writeln(str);
    end;
    writeln('');
  end;

  function GetAngle(x, y: Integer): Single;
  begin
    if x <> 0 then
      result := ArcTan2(y, x)
    else
      result := Pi - Pi/2*y;
  end;

  procedure RotateArray(var arr: TArray);
  var
    tmp: TArray;
    x, y, _x, _y: Integer;
    a, b: Single;
  begin
    tmp := arr;
    for y := -1 to 1 do
      for x := -1 to 1 do
        if (x <> 0or (y <> 0then begin
          a := GetAngle(x, y);
          _x := Round(cos(a+Pi/2));
          _y := Round(sin(a+Pi/2));
          arr[_x, _y] := tmp[x, y];
        end;
  end;

begin
  i := 1;
  for y := -1 to 1 do
    for x := -1 to 1 do begin
      arr[x, y] := i;
      inc(i);
    end;

  DisplayArray(arr);
  RotateArray(arr);
  DisplayArray(arr);

  readln;
end.


MfG Bergmann.

_________________
Ich weiß nicht viel, lern aber dafür umso schneller^^
Pepp3r Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 82



BeitragVerfasst: Fr 18.03.11 03:59 
Jau jetzt hab ichs in etwa gerafft. Du lädst den Klotz tatsichlich in ein Array in passender größe und Rotierst direkt das komplette Array.
Auf diese Idee bin ich bisher nicht gekommen. Ich habe nur die Steine innerhalb des Spielbretts, also des finalen Array verschoben.
Deine Idee finde ich allerdings ziemlich gut, die Vorteile hast du ja bereits genannt... ich bin mir bei nur nich sicher ob deine Variante nicht doch mehr Rechenoperationen erfordert.
Ich werde das ganze mal probieren.
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Fr 18.03.11 11:19 
Hallo,

man kann sich die Winkelberechung sparen.
Denn bei 90 Grad im Uhrzeigersinn ist x:= -y und y := x;

Edit etwas geändert, da es nur bei quadratischen Feldern kein Problem gibt.
Es funktiniert aber auch bei ungerader Spaltenzahl.
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:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
program Project1;
// Ein quadratisches array um 90 Grad rotieren

{$APPTYPE CONSOLE}
uses
   SysUtils;


const
  CRLF = #13#10;

  Spalten = 7;
  ofs_mitte = (Spalten-1);

type
  tIndex = 0..Spalten-1;
  TArray = array[tIndex,tIndex] of Integer;

var
  arr,tmpArr: TArray;
  x, y, i: Integer;

  procedure DisplayArray(const arr: TArray);
  var
    x, y: Integer;
    s: AnsiString;
  begin
    s := '';
    for y := low(Tindex) to High(TIndex) do
      begin
      for x := low(Tindex) to High(TIndex) do
        s := s + Format('%3d ',[arr[x, y]]);
      s:= s+CRLF;
      end;
    writeln(s);
  end;


  procedure RotateArrayClockwise(var arr: TArray);
  // x_neu = -y und y_neu = x
  var
    x, y,
    x_neu: Integer;
  begin
    tmpArr := arr;
    for y := low(Tindex) to High(TIndex) do
      begin
      x_neu := -y  + ofs_mitte;
      for x := low(Tindex) to High(TIndex) do
        begin
        arr[x_neu, x{y_neu}] := tmpArr[x,y];
        end;
     end;
  end;

  procedure RotateArrayCounterClockwise(var arr: TArray);
  // x_neu = y und y_neu = -x
  var
    x, y,
    y_neu: Integer;
  begin
    tmpArr := arr;
    for y := low(Tindex) to High(TIndex) do
      for x := low(Tindex) to High(TIndex) do
        begin
          y_neu := -x + ofs_mitte;
          //x_neu := y;
          arr[y,y_neu] := tmpArr[x,y];
        end;

  end;

begin
  i := 1;
  for y  := low(Tindex) to High(TIndex) do
    for x := low(Tindex) to High(TIndex) do begin
      IF y<>x then
        arr[x, y] := i
      else
        arr[x, y] := 0;
      inc(i);
    end;
  writeln('Ausgangsfeld');
  DisplayArray(arr);

  writeln('Im Uhrzeigersinn 90 Grad');
  RotateArrayClockwise(arr);
  DisplayArray(arr);

  RotateArrayCounterClockwise(arr);// Zurueckdrehen
  writeln('Im Gegenuhrzeigersinn 90 Grad');
  RotateArrayCounterClockwise(arr);
  DisplayArray(arr);

  readln;
end.

Als Ausgabe:
ausblenden 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:
  0   2   3   4   5   6   7   8
  9   0  11  12  13  14  15  16
 17  18   0  20  21  22  23  24
 25  26  27   0  29  30  31  32
 33  34  35  36   0  38  39  40
 41  42  43  44  45   0  47  48
 49  50  51  52  53  54   0  56
 57  58  59  60  61  62  63   0

 57  49  41  33  25  17   9   0
 58  50  42  34  26  18   0   2
 59  51  43  35  27   0  11   3
 60  52  44  36   0  20  12   4
 61  53  45   0  29  21  13   5
 62  54   0  38  30  22  14   6
 63   0  47  39  31  23  15   7
  0  56  48  40  32  24  16   8

  0   2   3   4   5   6   7   8
  9   0  11  12  13  14  15  16
 17  18   0  20  21  22  23  24
 25  26  27   0  29  30  31  32
 33  34  35  36   0  38  39  40
 41  42  43  44  45   0  47  48
 49  50  51  52  53  54   0  56
 57  58  59  60  61  62  63   0



ofsX_mitte und ofsY_mitte hätte ich zu einem Wert zusammenfassen sollen, denn bei verschiedenen Werten überschreitet man ja die Feldgrenzen beim drehen.
Edit : geändert.

Gruß Horst
P.S.
Hier: stackoverflow.com/qu...ting-bitmaps-in-code gibt es Überlegungen es richtig schnell zu machen.
Ich habe es mit 2048 Spalten probiert und kam mit byte-Daten auf 190 ms mit einem 2,9 Ghz Athlon 2 und freepascal 2.4.2
Also wesentlich langsamer als die dort erreichten 19 (AMD) bzw 9 ms (Intel)