Einzelnen Beitrag anzeigen

Benutzerbild von Diamondback2007
Diamondback2007

Registriert seit: 2. Feb 2007
260 Beiträge
 
Delphi 2007 Professional
 
#6

Re: [Andorra] Kollisionserkennung verbessern

  Alt 17. Jul 2008, 20:31
Ach das ist doch alles blöd
Ich bekomm das nicht hin mit dem "Seperating Axis Theorem"...
Ich poste mal meinen Code:
Die Polygonklasse:
Delphi-Quellcode:
unit uPolygon;

interface

uses
  AdPhysics,
  Vectors,
  Math;

type
  // Array mit den Vertices des Polygons
  TV2fArray = array of TVector2f;
  // Polygon Klasse
  TPolygon = class(TPhysicalBoxSprite)
  private
    fposition: TVector2f; // Position
    fvertices: TV2fArray; // Vertices (Objektkoordinaten)
    function GetVertex(n: integer): TVector2f; // Liefert die Objektkoordinaten
    function GetVertexAbs(n: integer): TVector2f; // Liefert die absoluten Koordinaten
    procedure SetVertex(n: integer; Value: TVector2f); // Setzt die Objektkoordinaten
    function GetCount: integer; // Liefert length(fvertices)
  public
    procedure AddVertex(v: TVector2f); // Fügt ein Vertex hinzu
    procedure AddVertexAbs(v: TVector2f); // Fügt ein Vertex mit Weltkoordinaten hinzu
    procedure RemoveVertex(n: integer); // Entfernt ein Vertex
    property position: TVector2f read fposition write fposition; // Position
    property vertices[n: integer]: TVector2f read GetVertex write SetVertex; // Vertex Koordinaten
    property vertices_abs[n: integer]: TVector2f read GetVertexAbs; // Vertex Weltkoordinaten
    property Count: integer read GetCount; // siehe GetCount
  end;
  TPolygonArray = array of TPolygon;

function BrickBallIntersect(A, B: TPolygon; var MTD: TVector2f): boolean;

implementation

function CreateAxis(P: TPolygon): TV2fArray;
var
  i, l : integer;
  tmp : TVector2f;
begin
  for i := 0 to (P.count - 1) do
    begin
      l := i + 1;
      if l > (P.count - 1) then
        l := 0;
      // Berechnung der Seitenfläche
      tmp := v2f_sub(P.vertices[l], P.vertices[i]);
      // Berechnet die Normale der Seitenfläche
      setlength(result, length(result) + 1);
      result[high(result)] := v2f_normalize(to_v2f(-tmp.y, tmp.x));
    end;
end;

procedure ProjectOntoAxis(P: TPolygon; proj: TVector2f; var pmin, pmax: extended);
var
  i : integer;
  dp : extended;
begin // Projeziert den ersten Wert
  pmin := v2f_dotproduct(P.vertices[0], proj);
  pmax := pmin;
  // Findet den kleinsten und größten projezierten Wert für die Gerade für P
  for i := 1 to (P.count - 1) do
    begin
      // projezieren
      dp := v2f_dotproduct(P.vertices[i], proj);
      if dp < pmin then
        pmin := dp;
      if dp > pmax then
        pmax := dp;
    end;
end;

function CollisionCheck(A, B: TPolygon; var axis: TVector2f; voffset: TVector2f): boolean;
var
  foffset,
    amin, amax,
    bmin, bmax,
    d1, d2, depth : extended;
begin
  ProjectOntoAxis(A, axis, amin, amax);
  ProjectOntoAxis(B, axis, bmin, bmax);
  foffset := v2f_dotproduct(voffset, axis);
  amin := amin + foffset;
  amax := amax + foffset;
  d1 := amin - bmax;
  d2 := bmin - amax;
  // Wenn es keine Überschneidung gibt, abbrechen -> keine Kollision
  if (d1 > 0) or (d2 > 0) then
    begin
      result := false;
      exit;
    end;
  // Ansonsten den Verschiebungsvektor bestimmen
  depth := max(d1, d2);
  axis := v2f_scale(axis, abs(depth));
  result := true;
end;

function BrickBallIntersect(A, B: TPolygon; var MTD: TVector2f): boolean;
var
  axis : TV2fArray;
  voffset : TVector2f;
  i : integer;
begin
  MTD := to_v2f(0, 0);
  // Offset berechnen
  voffset := v2f_sub(A.position, B.position);
  // Alle Achsen für A
  axis := CreateAxis(A);
  // Alle Achsen für B
  axis := CreateAxis(B);
  // Projezieren der Polygone
  for i := 0 to high(axis) do
    if not CollisionCheck(A, B, axis[i], voffset) then
      begin
        result := false;
        exit;
      end;
  // MTD bestimmen
  MTD := axis[0];
  for i := 1 to high(axis) do
    if v2f_length(axis[i]) < v2f_length(MTD) then
      MTD := axis[i];
  if v2f_dotproduct(voffset, MTD) < 0 then
    MTD := v2f_scale(MTD, -1);
  // Kollision
  result := true;
end;

{ TPolygon }

procedure TPolygon.AddVertex(v: TVector2f);
begin
  setlength(fvertices, length(fvertices) + 1);
  fvertices[high(fvertices)] := v2f_sub(v, position);
end;

procedure TPolygon.AddVertexAbs(v: TVector2f);
begin
  setlength(fvertices, length(fvertices) + 1);
  fvertices[high(fvertices)] := v;
end;

function TPolygon.GetCount: integer;
begin
  result := length(fvertices);
end;

function TPolygon.GetVertex(n: integer): TVector2f;
var
  acos, asin : extended;
begin
  sincos(degtorad(angle), asin, acos);
  result.x := fvertices[n].x * acos + fvertices[n].y * -asin;
  result.y := fvertices[n].x * asin + fvertices[n].y * acos;
end;

function TPolygon.GetVertexAbs(n: integer): TVector2f;
begin
  result := v2f_add(fvertices[n], fposition);
end;

procedure TPolygon.RemoveVertex(n: integer);
var
  i : integer;
begin
  for i := n to high(fvertices) - 1 do
    fvertices[i] := fvertices[i + 1];
  setlength(fvertices, length(fvertices) - 1);
end;

procedure TPolygon.SetVertex(n: integer; Value: TVector2f);
begin
  fvertices[n] := Value;
end;

end.
Die ist nur abkopiert, sollte also soweit richtig sein.

Dann noch die Erstellung meiner Objekte:
Delphi-Quellcode:
procedure TGameForm.AddBrick(aX, aY: Double; aColor: Integer);
begin
  with TBrick.Create(AdSpriteEngine) do
    begin
      Image := AdImgLst.Find('brick');
      X := 1 + aX;
      Y := 1 + aY;
      Width := 90;
      Height := 20;
      Position := to_v2f(X, Y);
      AddVertex(to_v2f(90, -20));
      AddVertex(to_v2f(90, 0));
      AddVertex(to_v2f(0, 0));
      AddVertex(to_v2f(0, -20));
     { AddVertex(to_v2f(BoundsRect.Right, BoundsRect.Bottom));
      AddVertex(to_v2f(BoundsRect.Right, BoundsRect.Top));
      AddVertex(to_v2f(BoundsRect.Left, BoundsRect.Top));
      AddVertex(to_v2f(BoundsRect.Left, BoundsRect.Bottom));}

      Color := aColor;
      Typ := ptStatic;
      InitializeShape;
    end;
end;
und
Delphi-Quellcode:
procedure TGameForm.NewBall;
begin
  with TBall.Create(AdSpriteEngine) do
    begin
      Image := AdImgLst.Find('ball');
      X := Bat.X + Bat.Width / 2;
      Y := Bat.Y;
      Width := 10;
      Height := 10;
      Position := to_v2f(X, Y);
      AddVertex(to_v2f(10, -10));
      AddVertex(to_v2f(10, 0));
      AddVertex(to_v2f(0, 0));
      AddVertex(to_v2f(0, -10));
      {AddVertex(to_v2f(BoundsRect.Right, BoundsRect.Bottom));
      AddVertex(to_v2f(BoundsRect.Right, BoundsRect.Top));
      AddVertex(to_v2f(BoundsRect.Left, BoundsRect.Top));
      AddVertex(to_v2f(BoundsRect.Left, BoundsRect.Bottom));  }

    end;
  Bat.IsBallOnbat := true;
end;
Die Überprüfung findet hier statt:
Delphi-Quellcode:
for i := 0 to GameForm.AdSpriteEngine.Items.Count - 1 do
    begin
      if GameForm.AdSpriteEngine.Items[i] is TBrick then
        begin
          if BrickBallIntersect(GameForm.AdSpriteEngine.Items[i] as TBrick, self, MTD) then
            DoBrickCollision(GameForm.AdSpriteEngine.Items[i]);
        end;
    end;
Wenn ich das ganze starte, dann werden alle Bricks gleichzeitig "berührt" und verschwinden dann. Die Kollisionsabfrage klappt also nicht richtig...
Fabian E.
  Mit Zitat antworten Zitat