Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi [Andorra] Kollisionserkennung verbessern (https://www.delphipraxis.net/117393-%5Bandorra%5D-kollisionserkennung-verbessern.html)

Diamondback2007 17. Jul 2008 12:02


[Andorra] Kollisionserkennung verbessern
 
Hallo zusammen,

ich habe im Moment folgenden Code um auf Kollisionen mit meinen Bricks bei meinem Breakoutspiel zu reagieren.
Delphi-Quellcode:
procedure TBall.DoBrickCollision(aBrick: TSprite);
var
  ObenUnten                  : Boolean;
  linksRechts                : Boolean;
const
  Puffer                     = 0.6;
begin
  (aBrick as Tbrick).Hit;
  if Settings.DoBrickCollisions then
    begin
      ObenUnten := false;
      LinksRechts := false;
      if BoundsRect.Top + (GameForm.AdPerCounter.TimeGap * Puffer) >= aBrick.BoundsRect.Bottom then
        begin
          ObenUnten := true;
          Y := aBrick.BoundsRect.Bottom;
        end;
      if BoundsRect.Bottom - (GameForm.AdPerCounter.TimeGap * Puffer) <= aBrick.BoundsRect.Top then
        begin
          ObenUnten := true;
          Y := aBrick.Y - Height;
        end;
      if BoundsRect.Right - (GameForm.AdPerCounter.TimeGap * Puffer) <= aBrick.BoundsRect.Left then
        begin
          LinksRechts := true;
          X := aBrick.X - Width;
        end;
      if BoundsRect.Left + (GameForm.AdPerCounter.TimeGap * Puffer) >= aBrick.BoundsRect.Right then
        begin
          LinksRechts := true;
          X := aBrick.BoundsRect.Right;
        end;

      if ObenUnten then
        ChangeYDir
      else if LinksRechts then
        ChangeXDir;
    end;
end;
Das klappt eigentlich auch schon recht gut, allerdings nicht immer...
Z.B. bei eingeschaltetem VertSync nicht mehr wirklich gut, da die Framerate dann bei 60 statt 1700 liegt. Für diesen Fall hatte ich eigentlich schon den Puffer da eingebaut, der je nach FPS noch ein paar Pixel mehr oder weniger rechnet...
Aber manchmal fliegt der Ball trotzdem durch die Steine durch...

Hat jemand eine besser Idee? Das wäre super :)

igel457 17. Jul 2008 13:11

Re: [Andorra] Kollisionserkennung verbessern
 
Gehe die Sprite-Liste per Hand durch und berechne eine Kollision über das Separating Axis Theorem:
http://www.delphipraxis.net/posting....ets=1216296554

Du kannst über alle Sprites in der Spriteengine über Engine.Items iterieren.

Edit: Andere Möglichkeit: Führe die Bewegungen und Kollisionskontrollen in einem anderen Thread als das Zeichnen aus. Denke jedoch daran, alle Spriteenginezugriffe über eine Criticalsection zu schützen.

Diamondback2007 17. Jul 2008 14:01

Re: [Andorra] Kollisionserkennung verbessern
 
Okay, kannst du vielleicht dann noch dne richtigen Links posten? ;)

igel457 17. Jul 2008 14:49

Re: [Andorra] Kollisionserkennung verbessern
 
:oops:
http://wiki.delphigl.com/index.php/T...g_Axis_Theorem

Diamondback2007 17. Jul 2008 16:04

Re: [Andorra] Kollisionserkennung verbessern
 
Zitat:

Dies liefert ein Quadrat mit den Maßen 100*100 an der Position (200|200). Die Koordinaten der Vertices werden absolut zur Position und entgegen des Uhrzeigersinns angegeben
Argh... Mir raucht der Kopf ;) Was genu heißt das jetzt? Also das mit dem absolut zur Position..
Einfach die jeweiligen Ecken mit ihrer Position angeben?

Diamondback2007 17. Jul 2008 19:31

Re: [Andorra] Kollisionserkennung verbessern
 
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...

Diamondback2007 21. Jul 2008 10:30

Re: [Andorra] Kollisionserkennung verbessern
 
Ja also ich bekomms einfach nicht hin...
Hat jemand schon mal was funktionieredes damit gemacht?
@igel: Warum baust du das eigentlich nicht in deine Engine ein als Erkennung?Oder hast du das schon?


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:51 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz