Entwickler-Ecke

Sonstiges (Delphi) - Positionieren von Gegnern auf einem Spielfeld


mvollmer - Di 12.10.10 14:01
Titel: Positionieren von Gegnern auf einem Spielfeld
Guten Tag,

Ich bräuchte mal eure Hilfe, ich weiß grade nicht wie ich mein Problem genau realisieren soll.

Erstmal zum Programm:

Ich bin dabei eine Tower Defense zu programmieren. Bauen von Türmen, Erstellen von Karten, sowie das "Spawnen" von Gegner und das durchlaufen durch die Karte klappen.
Doch leider sind die Gegner nicht wirklich gut Positioniert, wenn sie über das Spielfeld laufen.

Im Anhang zwei Screenshot zur Verbildlichung :) + Compilierte exe


Und hier die Prozedure mit der ich die Gegner zeichne.

Zur Info: Jedes Feld ist 32x32 px groß. Und eine Karte hat 16*16 Felder.


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 TEnemyManager.drawEnemys(level: Integer);
var i: integer;
begin
  bmEnemys.Canvas.Brush.Color := clFuchsia;
  bmEnemys.Canvas.Rectangle(-1,-1,513,513);
  bmEnemys.Canvas.Brush.Color := clBlue;
  for i := 0 to High(enemys[level]) do
  begin
    if enemys[level][i].isRunning then
    begin
      bmEnemys.Canvas.Rectangle(
        enemys[level][i].PosX+10,       //ANHANG 1!
        enemys[level][i].PosY+10,       // Hier läufts flüssig, aber 'schief' :/
        enemys[level][i].PosX+15,
        enemys[level][i].PosY+15);
//      bmEnemys.Canvas.Rectangle(         // ANHANG 2!Zeichnet die Gegner
//        enemys[level][i].FeldX*32,       // Oben links in die ecken der Felder
//        enemys[level][i].FeldY*32,       // Kein flüssiger spielverlauf,
//        enemys[level][i].FeldX*32+10,    // da Gegner NUR in die mitte
//        enemys[level][i].FeldY*32+10);   // des Feldes gezeichnet werden
    end;
  end;
  uFormMain.FormMain.pb.Canvas.Draw(0,0,bmEnemys);
end;



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure TEnemyManager.moveEnemy(enemy: TEnemy; next: TNextField);
begin
  case next of
    nfDown:  enemy.PosY := enemy.PosY+1;
    nfRight: enemy.PosX := enemy.PosX+1;
    nfUp:    enemy.PosY := enemy.PosY-1;
    nfLeft:  enemy.PosX := enemy.PosX-1;
  end;
  if next <> nfStay then
    Inc(enemy.movedPixel);

  enemy.FeldX := enemy.PosX div 32;
  enemy.FeldY := enemy.PosY div 32;
end;



(Anhang 1)Wie man auf den Screenshots sieht sind die Gegner nicht mittig auf den Feldern.
(Anhang 2)Beim auskommentiertem zeichnen, sind sie zwar mittig auf den Feldern, aber wirklich NUR mittig.
Und ecken überspingen sie.

Nun ist die frage wie ich einen flüssigen Spielablauf hinbekomme und die gegner mittig auf den Feldern laufen.

EDIT: Habe noch ein zweites Problem. Die Gegner "flackern". Das liegt wahrscheinlich an der draw methode. Doch Bitblt kann ich leider nicht benutzten, da diese keine transparenz unterstützt. Gibt es noch etwas anderes?


Habs mitlerweile hinbekommen, das es nach RECHTS und nach UNTEN so klappt wie es soll.

Aber wenns nach LINKS oder nach OBEN gehen soll macht es Probleme...

CROSSPOST auf DelphiPraxis [http://www.delphipraxis.net/155178-gegner-auf-spielfeld-richtig-positionieren.html#post1055202]

Ich danke schonmal für jede Antwort ;)

mfg pustekuchen


Dude566 - Di 12.10.10 14:03

Wenn du schon Bilder aus einem anderen Delphi Forum postest, kannst du auch gleich den Crosspost angeben: http://www.delphipraxis.net/155178-gegner-auf-spielfeld-richtig-positionieren.html#post1055202 ;)


mvollmer - Di 12.10.10 14:07

Hab ich auch grade gesehen das die noch drin waren ;) Sind jetzt raus


Dude566 - Di 12.10.10 14:10

user profile iconmvollmer hat folgendes geschrieben Zum zitierten Posting springen:
Hab ich auch grade gesehen das die noch drin waren ;) Sind jetzt raus


Das ist ja nicht schlimm, nur man sollte angeben wenn man die selbe Frage noch in einem anderen Forum gestellt hat, deshalb die Angabe des Crossposts von mir.


mvollmer - Di 12.10.10 14:23

Habs mitlerweile hinbekommen, das es nach RECHTS und nach UNTEN so klappt wie es soll.

Aber wenns nach LINKS oder nach OBEN gehen soll macht es Probleme...


Teekeks - Di 12.10.10 14:27

Wie machst du es denn für die ersten 2 Fälle?


mvollmer - Di 12.10.10 14:29

Dies ist die Procedure mit der ich die Gegner bewege:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure TEnemyManager.moveEnemy(enemy: TEnemy; next: TNextField);
begin
  case next of
    nfDown:  enemy.PosY := enemy.PosY+1;
    nfRight: enemy.PosX := enemy.PosX+1;
    nfUp:    enemy.PosY := enemy.PosY-1;
    nfLeft:  enemy.PosX := enemy.PosX-1;
  end;
  if next <> nfStay then
    Inc(enemy.movedPixel);

  enemy.FeldX := enemy.PosX div 32;
  enemy.FeldY := enemy.PosY div 32;
end;


Und hier wird sie aufgerufen:


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:
procedure TFormMain.moveEnemys(value: TGameStatus);
var k: integer;
  fieldX, fieldY,PosX, PosY: Integer;
begin
  for k := 0 to High(EnemyManager.enemys[FPlayerLevel]) do
  begin
    if EnemyManager.enemys[FPlayerLevel][k].isRunning then
    begin
//      PosX := EnemyManager.enemys[FPlayerLevel][k].PosX div 32;   // 7
//      PosY := EnemyManager.enemys[FPlayerLevel][k].PosY div 32;   // 71
//      if (map.Playground[PosX][PosY].nextField = nfLeft)then
//      begin
//        fieldX := ((EnemyManager.enemys[FPlayerLevel][k].PosX+7) div 32 );
//        fieldY := ((EnemyManager.enemys[FPlayerLevel][k].PosY+7) div 32);
//      end
//      else
//      begin
        fieldX := (EnemyManager.enemys[FPlayerLevel][k].PosX -7div 32;
        fieldY := (EnemyManager.enemys[FPlayerLevel][k].PosY -7div 32;
//      end;
      case map.Playground[fieldX][fieldY].nextField of
        nfDown:  EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][k],nfDown);
        nfRight: EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][k],nfRight);
        nfLeft:  EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][k],nfLeft);
        nfUp:    EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][k],nfUp);
        nfStay:
        begin
          EnemyManager.enemys[FPlayerLevel][k].isRunning := false;
          Dec(FPlayerLives);
          initLabels;
          if FPlayerLives = 0 then
          begin
            SetGameStatus(gsGAMEOVER);
            exit;
          end;
        end;
      end;
    end;
  end;


Teekeks - Di 12.10.10 14:31

Und wo ist da das Problem momentan?
Das sieht soweit ganz gut aus.


mvollmer - Di 12.10.10 14:33

Habs oben im Post editiert.

Diese wiederrum, wird alle 10 ms von einem Timer aufgerufen


mkinzler - Di 12.10.10 14:35

CrossPost DP [http://www.delphipraxis.net/155178-gegner-auf-spielfeld-richtig-positionieren.html]


Mortal-Shadow - Di 12.10.10 14:56

Ich habs mir angeschaut.
Du scheinst den Eckfeldern falsche Werte zugeordnet haben.
Wenn sie nach links laufen und danach nach oben, hast du ja ein Eckfeld.
Das muss noch nfLeft zugeordnet bekommen, da sie ja immer von Field.next abfragen, welche richtung sie gehen müssen.
Wenn nun das Eckfeld schon nach oben zeigt, gehen sie folglich eins vor ihm schon nach oben.
Mfg.


mvollmer - Di 12.10.10 15:09

Zitat:
Wenn sie nach links laufen und danach nach oben, hast du ja ein Eckfeld.
Das muss noch nfLeft zugeordnet bekommen, da sie ja immer von Field.next abfragen, welche richtung sie gehen müssen.


Danke erstmal ;)

Es ist so, das das Aktuelle Feld abgefragt wird auf welches er als nächstes gehen soll. D.h Wenn er aktuell auf dem Eckfeld ist muss .nextField = nfUp sein

Siehe Anhang, dort ist der Weg angezeigt ;)


HAB DIE LÖSUNG! ;)
Ok nur noch ein Problem, wenn er von unten nach oben und dann nach rechts muss, biegt er wieder zu früh ab^^


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:
procedure TFormMain.moveEnemies(value: TGameStatus);
var k: integer;
  fieldX, fieldY,PosX, PosY: Integer;
begin
  for k := 0 to High(EnemyManager.enemys[FPlayerLevel]) do
  begin
    if EnemyManager.enemys[FPlayerLevel][k].isRunning then
    begin
        fieldX := (EnemyManager.enemys[FPlayerLevel][k].PosX -7div 32;
        fieldY := (EnemyManager.enemys[FPlayerLevel][k].PosY -7div 32;
      case map.Playground[fieldX][fieldY].nextField of
        nfDown:  EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][k],nfDown);
        nfRight: EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][k],nfRight);
        nfStay:
        begin
          EnemyManager.enemys[FPlayerLevel][k].isRunning := false;
          Dec(FPlayerLives);
          initLabels;
          if FPlayerLives = 0 then
          begin
            SetGameStatus(gsGAMEOVER);
            exit;
          end;
        end;
        else
        begin
          fieldX := (EnemyManager.enemys[FPlayerLevel][k].PosX +23div 32;
          fieldY := (EnemyManager.enemys[FPlayerLevel][k].PosY +23div 32;
          case map.Playground[fieldX][fieldY].nextField of
            nfLeft:  EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][k],nfLeft);
            nfUp:    EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][k],nfUp);
          end;
        end;

      end;
    end;
  end;
end;


Ok nur noch ein Problem, wenn er von unten nach oben und dann nach rechts muss, biegt er wieder zu früh ab^^


mvollmer - Mi 13.10.10 09:39

Hab jetzt entgültig den Fehler gefunden, wodran es liegt das sie halt manchmal früher abbiegen.

Ich habe die Gegner immer nur einen pixel verschoben. Sobald also der Feldrand erreicht wurde zählte schon das nextField vom neuem Feld, obwohl er noch nicht richtig drauf ist und somit sind sie dann zu früh abgebogen.

Jetzt hab ich aber ein Problem.
Wenn ich die Zeichenprozedur ausführe sobald i-ein gegner bewegt wird, dann läufts so ab:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
Gegner 1 bewegt sich 1 Feld nach vorne...
[ZEICHNEN]
Gegner 1 bewegt sich 1 Feld nach vorne...
[ZEICHNEN]
[..bis 32..]

Gegner 2 bewegt sich 1 Feld nach vorne...
[ZEICHNEN]
Gegner 2 bewegt sich 1 Feld nach vorne...
[ZEICHNEN]
[..bis 32..]

usw..


Also wird jeder gegner nacheinander bewegt. Aber es soll ja so sein als würden, sich alle "gleichzeitig" bewegen.

Hier jetzt nochmal die aktuellen Prozeduren:



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:
procedure TFormMain.moveEnemies(value: TGameStatus);
var
  fieldX, fieldY: Integer;
  i,k,j: Integer;
  tmpField :array of TNextField;
begin
  SetLength(tmpField,High(EnemyManager.enemys[FPlayerLevel])-1);
  for k := 0 to High(EnemyManager.enemys[FPlayerLevel]) do
  begin
    fieldX := (EnemyManager.enemys[FPlayerLevel][k].Pixels.X ) div 32;
    fieldY := (EnemyManager.enemys[FPlayerLevel][k].Pixels.Y ) div 32;
    tmpField[k] := map.Playground[fieldX][fieldY].nextField;
  end;

  for j := 0 to 31 do
  begin
    for i := 0 to High(EnemyManager.enemys[FPlayerLevel]) do
    begin
      if EnemyManager.enemys[FPlayerLevel][i].isRunning then
      begin
        case tmpField[i] of
          nfDown:  EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][i],nfDown);
          nfRight: EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][i],nfRight);
          nfLeft:  EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][i],nfLeft);
          nfUp:    EnemyManager.moveEnemy(EnemyManager.enemys[FPlayerLevel][i],nfUp);
          nfStay:
          begin
            EnemyManager.enemys[FPlayerLevel][i].isRunning := false;
            Dec(FPlayerLives);
            initLabels;
            if FPlayerLives = 0 then
            begin
              SetGameStatus(gsGAMEOVER);
              exit;
            end;
          end;
        end;
      end;
    end;
    Draw;
  end;
end;



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:
procedure TEnemyManager.moveEnemy(enemy: TEnemy; next: TNextField);
var Pos: TPoint;
begin
  case next of
    nfDown:
    begin
      Pos.Y := enemy.Pixels.Y+1;
      Pos.X := enemy.Pixels.X;
    end;
    nfRight:
    begin
      Pos.X := enemy.Pixels.X+1;
      Pos.Y := enemy.Pixels.Y;
    end;
    nfUp:
    begin
      Pos.Y := enemy.Pixels.Y-1;
      Pos.X := enemy.Pixels.X;
    end;
    nfLeft:
    begin
      Pos.X := enemy.Pixels.X-1;
      Pos.Y := enemy.Pixels.Y;
    end;
  end;
  if next <> nfStay then
  begin
    enemy.Pixels := Pos;
    Inc(enemy.movedPixel);
  end;
end;