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
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.