Search code examples
delphimoveshapes

How to make a shape stop moving


I am trying to make a shape move towards onother using 2 shapes and 2 timer, but i really can't seem. I was thinking:

At the beggining, I will make shape 1 calculate the distance of shape 2 and then move towards it, this is what i have done, i have also added comments to help you understand the code, because it is a little bit confusing:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
timer1.Interval:=100;           //set interval=200
                            //begin
if shape1.Left=shape2.Left then
begin
shape1.Left:=shape1.left         //If shape's 1 coordinates = shape's 2 then
end else                        //shape1.left:=stop moving else do
begin                           //find if shape 2 is right or left from shape 1
if shape1.left>shape2.Left then
begin
shape1.Left:=shape1.Left-5;
end else shape1.Left:=shape1.Left+5;
//Moving to shape2.left until shape1.left:=shape2.left



end;
end;


procedure TForm1.Timer2Timer(Sender: TObject);
begin
timer2.Interval:=100;      //the same method as timer1

if shape1.top=shape2.top then
begin
shape1.top:=shape1.top
end else
begin
if shape1.top>shape2.top then
begin
shape1.top:=shape1.top-5;
end else shape1.top:=shape1.top+5;

end;
end;

end.

What shape1 does now is to move toward shape 2, but it doesn't stop moving, i mean it sticks to shape 2, but it is still moving upside-down, but not left-right from shape 2. I checked timer's 2 code and there is nothing wrong.


Solution

  • Try the following code (assign OnCreate and OnPaint of the form and set the timer to 30 millisecond intervals):

    unit Unit5;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, ExtCtrls;
    
    type
      TVector = record
        X, Y: real;
      end;
    
      TForm5 = class(TForm)
        Timer1: TTimer;
        procedure FormPaint(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure Timer1Timer(Sender: TObject);
      private
        { Private declarations }
        FPosA, FPosB: TVector;
        v: TVector;
      public
        { Public declarations }
      end;
    
    var
      Form5: TForm5;
    
    implementation
    
    uses Math;
    
    {$R *.dfm}
    
    const RADIUS = 16;
    
    function RealPoint(X, Y: real): TVector;
    begin
      result.X := X;
      result.Y := Y;
    end;
    
    function RoundPoint(P: TVector): TPoint;
    begin
      result.X := round(P.X);
      result.Y := round(P.Y);
    end;
    
    procedure TForm5.FormCreate(Sender: TObject);
    var
      DX, DY: real;
    begin
      FPosA := RealPoint(32, 32);
      FPosB := RealPoint(500, 200);
    
      DX := FPosB.X - FPosA.X;
      DY := FPosB.Y - FPosA.Y;
    
      v.X := DX / 100;
      v.Y := DY / 100;
    end;
    
    function EllipseRectFromPoint(P: TVector): TRect;
    var
      ScreenPoint: TPoint;
    begin
      ScreenPoint := RoundPoint(P);
      result.Left := ScreenPoint.X - RADIUS;
      result.Right := ScreenPoint.X + RADIUS;
      result.Top := ScreenPoint.Y - RADIUS;
      result.Bottom := ScreenPoint.Y + RADIUS;
    end;
    
    procedure TForm5.FormPaint(Sender: TObject);
    begin
    
      // Draw ball A
      Canvas.Brush.Color := clSkyBlue;
      Canvas.Ellipse(EllipseRectFromPoint(FPosA));
    
      // Draw ball B
      Canvas.Brush.Color := clMoneyGreen;
      Canvas.Ellipse(EllipseRectFromPoint(FPosB));
    
    end;
    
    procedure TForm5.Timer1Timer(Sender: TObject);
    begin
      FPosA.X := FPosA.X + V.X;
      FPosA.Y := FPosA.Y + V.Y;
      Invalidate;
    
      if Hypot(FPosA.X - FPosB.X, FPosA.Y - FPosB.Y) < 0.1 then
      begin
        Timer1.Enabled := false;
        ShowMessage('We''re there!');
      end;
    end;
    
    end.
    

    Two balls:

    unit Unit5;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, ExtCtrls;
    
    type
      TVector = record
        X, Y: real;
      end;
    
      TForm5 = class(TForm)
        Timer1: TTimer;
        procedure FormPaint(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure Timer1Timer(Sender: TObject);
      private
        { Private declarations }
        AreWeThereYetA, AreWeThereYetB: boolean;
        FPosA, FPosB, FPosC: TVector;
        vA, vB: TVector;
      public
        { Public declarations }
      end;
    
    var
      Form5: TForm5;
    
    implementation
    
    uses Math;
    
    {$R *.dfm}
    
    const RADIUS = 16;
    
    function RealPoint(X, Y: real): TVector;
    begin
      result.X := X;
      result.Y := Y;
    end;
    
    function RoundPoint(P: TVector): TPoint;
    begin
      result.X := round(P.X);
      result.Y := round(P.Y);
    end;
    
    procedure TForm5.FormCreate(Sender: TObject);
    var
      DX, DY: real;
    begin
      FPosA := RealPoint(32, 32);
      FPosB := RealPoint(132, 32);
      FPosC := RealPoint(500, 200);
    
      DX := FPosC.X - FPosA.X;
      DY := FPosC.Y - FPosA.Y;
      vA.X := DX / 100;
      vA.Y := DY / 100;
    
      DX := FPosC.X - FPosB.X;
      DY := FPosC.Y - FPosB.Y;
      vB.X := DX / 200;
      vB.Y := DY / 200;
    
    end;
    
    function EllipseRectFromPoint(P: TVector): TRect;
    var
      ScreenPoint: TPoint;
    begin
      ScreenPoint := RoundPoint(P);
      result.Left := ScreenPoint.X - RADIUS;
      result.Right := ScreenPoint.X + RADIUS;
      result.Top := ScreenPoint.Y - RADIUS;
      result.Bottom := ScreenPoint.Y + RADIUS;
    end;
    
    procedure TForm5.FormPaint(Sender: TObject);
    begin
    
      // Draw ball A
      Canvas.Brush.Color := clSkyBlue;
      Canvas.Ellipse(EllipseRectFromPoint(FPosA));
    
      // Draw ball B
      Canvas.Brush.Color := clMoneyGreen;
      Canvas.Ellipse(EllipseRectFromPoint(FPosB));
    
      // Draw ball C
      Canvas.Brush.Color := clRed;
      Canvas.Ellipse(EllipseRectFromPoint(FPosC));
    
    end;
    
    procedure TForm5.Timer1Timer(Sender: TObject);
    begin
    
      if not AreWeThereYetA then
      begin
        FPosA.X := FPosA.X + VA.X;
        FPosA.Y := FPosA.Y + VA.Y;
      end;
    
      if not AreWeThereYetB then
      begin
        FPosB.X := FPosB.X + VB.X;
        FPosB.Y := FPosB.Y + VB.Y;
      end;
    
      Invalidate;
    
      if Hypot(FPosA.X - FPosC.X, FPosA.Y - FPosC.Y) < 0.1 then
        AreWeThereYetA := true;
    
      if Hypot(FPosB.X - FPosC.X, FPosB.Y - FPosC.Y) < 0.1 then
        AreWeThereYetB := true;
    
      if AreWeThereYetA and AreWeThereYetB then
      begin
        Timer1.Enabled := false;
        ShowMessage('We are there!');
      end;
    end;
    
    end.
    

    Using arrays and records, it would be easily to generalise to N balls with custom properties (colours, radii, etc.), even random ones. It would also be very easy to implement bouncing. In addition, a real vector type would be good here.