Delphi custom animation - collision detection

后端 未结 4 375
半阙折子戏
半阙折子戏 2021-01-12 00:35

I\'m working with custom drawing / 2D animation and I\'m trying to figure out how to detect when the moving object collides with a wall in the map. User holds arrow keys on

4条回答
  •  情话喂你
    2021-01-12 01:02

    this unit found on the web (can't remember where, no author mentioned, perhaps someone can provide a link) would give you the ability of calculating collisions and reflection angles.

    unit Vector;
    
    interface
    
    type
      TPoint = record
        X, Y: Double;
      end;
    
      TVector = record
        X, Y: Double;
      end;
    
      TLine = record
        P1, P2: TPoint;
      end;
    
    function Dist(P1, P2: TPoint): Double; overload;
    function ScalarProd(P1, P2: TVector): Double;
    function ScalarMult(P: TVector; V: Double): TVector;
    function Subtract(V1, V2: TVector): TVector; overload;
    function Subtract(V1, V2: TPoint): TVector; overload;
    function MinDistPoint(Point: TPoint; Line: TLine): TPoint;
    function Mirror(W, V: TVector): TVector;
    function Dist(Point: TPoint; Line: TLine): Double; overload;
    
    implementation
    
    function Dist(P1, P2: TPoint): Double; overload;
    begin
      Result := Sqrt(Sqr(P1.X - P2.X) + Sqr(P1.Y - P2.Y));
    end;
    
    function ScalarProd(P1, P2: TVector): Double;
    begin
      Result := P1.X * P2.X + P1.Y * P2.Y;
    end;
    
    function ScalarMult(P: TVector; V: Double): TVector;
    begin
      Result.X := P.X * V;
      Result.Y := P.Y * V;
    end;
    
    function Subtract(V1, V2: TVector): TVector; overload;
    begin
      Result.X := V2.X - V1.X;
      Result.Y := V2.Y - V1.Y;
    end;
    
    function Subtract(V1, V2: TPoint): TVector; overload;
    begin
      Result.X := V2.X - V1.X;
      Result.Y := V2.Y - V1.Y;
    end;
    
    function MinDistPoint(Point: TPoint; Line: TLine): TPoint;
    var
      U: Double;
      P: TPoint;
    begin
      U := ((Point.X - Line.P1.X) * (Line.P2.X - Line.P1.X) +
            (Point.Y - Line.P1.Y) * (Line.P2.Y - Line.P1.Y)) /
        (Sqr(Line.P1.X - Line.P2.X) + Sqr(Line.P1.Y - Line.P2.Y));
      if U <= 0 then
        Exit(Line.P1);
      if U >= 1 then
        Exit(Line.P2);
      P.X := Line.P1.X + U * (Line.P2.X - Line.P1.X);
      P.Y := Line.P1.Y + U * (Line.P2.Y - Line.P1.Y);
      Exit(P);
    end;
    
    function Mirror(W, V: TVector): TVector;
    begin
      Result := Subtract(ScalarMult(V, 2*ScalarProd(v,w)/ScalarProd(v,v)), W);
    end;
    
    function Dist(Point: TPoint; Line: TLine): Double; overload;
    begin
      Result := Dist(Point, MinDistPoint(Point, Line));
    end;
    
    end.
    

    An example implementation would be

    unit BSP;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, Vector, ExtCtrls;
    
    type
      TForm2 = class(TForm)
        Timer1: TTimer;
        procedure FormPaint(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure Timer1Timer(Sender: TObject);
      private
        { Private-Deklarationen }
        FLines: array of TLine;
        FP: TPoint;
        FV: TVector;
        FBallRadius: Integer;
        FBallTopLeft: Windows.TPoint;
      public
        { Public-Deklarationen }
      end;
    
    var
      Form2: TForm2;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm2.FormCreate(Sender: TObject);
    const
      N = 5;
    
    var
      I: Integer;
    begin
      Randomize;
    
      SetLength(FLines, 4 + N);
      FBallRadius := 15;
      // Walls
      FLines[0].P1.X := 0;
      FLines[0].P1.Y := 0;
      FLines[0].P2.X := Width - 1;
      FLines[0].P2.Y := 0;
    
      FLines[1].P1.X := Width - 1;
      FLines[1].P1.Y := 0;
      FLines[1].P2.X := Width - 1;
      FLines[1].P2.Y := Height - 1;
    
      FLines[2].P1.X := Width - 1;
      FLines[2].P1.Y := Height - 1;
      FLines[2].P2.X := 0;
      FLines[2].P2.Y := Height - 1;
    
      FLines[3].P1.X := 0;
      FLines[3].P1.Y := 0;
      FLines[3].P2.X := 0;
      FLines[3].P2.Y := Height - 1;
      for I := 0 to N - 1 do
      begin
        FLines[I + 4].P1.X := 50 + Random(Width - 100);
        FLines[I + 4].P1.Y := 50 + Random(Height - 100);
        FLines[(I + 1) mod N + 4].P2 := FLines[I + 4].P1;
      end;
    
      FP.X := 50;
      FP.Y := 50;
    
      FV.X := 10;
      FV.Y := 10;
    end;
    
    procedure TForm2.FormPaint(Sender: TObject);
    const
      Iterations = 100;
    var
      I, MinIndex, J: Integer;
      MinDist, DP, DH: Double;
      MP: TPoint;
      H: TPoint;
    begin
    
    
      for I := 0 to Length(FLines) - 1 do
      begin
        Canvas.MoveTo(Round(FLines[I].P1.X), Round(FLines[I].P1.Y));
        Canvas.LineTo(Round(FLines[I].P2.X), Round(FLines[I].P2.Y));
      end;
    
      for I := 0 to Iterations do
      begin
        H := FP;
        FP.X := FP.X + FV.X / Iterations;
        FP.Y := FP.Y + FV.Y / Iterations;
        MinDist := Infinite;
        MinIndex := -1;
        for J := 0 to Length(FLines) - 1 do
        begin
          DP := Dist(FP, FLines[J]);
          DH := Dist(H, FLines[J]);
          if (DP < MinDist) and (DP < DH) then
          begin
            MinDist := DP;
            MinIndex := J;
          end;
        end;
    
        if MinIndex >= 0 then
          if Sqr(MinDist) < 2*Sqr(FBallRadius * 0.7 / 2)
             then
          begin
            MP := MinDistPoint(FP, FLines[MinIndex]);
            FV := Mirror(FV, Subtract(MP, FP));
          end;
      end;
    
      FBallTopLeft.X := Round(FP.X - FBallRadius);
      FBallTopLeft.Y := Round(FP.Y - FBallRadius);
      Canvas.Brush.Color := clBlue;
      Canvas.Ellipse(FBallTopLeft.X, FBallTopLeft.Y,
        FBallTopLeft.X + FBallRadius * 2, FBallTopLeft.Y + FBallRadius * 2);
    
    end;
    
    procedure TForm2.Timer1Timer(Sender: TObject);
    begin
      invalidate;
    end;
    
    end.
    

提交回复
热议问题