Search code examples
delphidelphi-7

Delphi - Image move randomly inside desktop coordinates


I want to created a small application, which should move images smoothly into the desktop coordinates. I was wondering how can I limit that the image remains inside the desktop? I have try like that to move the image:

procedure TForm1.Timer1Timer(Sender: TObject);
Var
  X, Y :Integer;
begin
  X:= random(2+1);
   Y:= random(2+1);
    Image1.Left:= Image1.Left + X;
     Image1.Top:= Image1.Top + Y;
      Image1.Refresh;
end; 

Any help is appreciated.

Thanks.


Solution

  • Is you image placed over Windows Desktop - or over you TForm1 ? I guess the latter. So you would have to care about the WINDOW size, not the DESKTOP size.

    type TForm1=class(TForm)
    ....
    private
       ImageMovesLeft, ImageMovesUp: Boolean;
    end;
    
    .....
    
    procedure TForm1.Timer1Timer(Sender: TObject);
    Var
      dX, dY, NewLeft, NewTop :Integer;
      FormSize: TRect;
    begin
      dX := random(2+1); // did you really mean "random(3)" or "1+random(2)" ???
      dY := random(2+1);
    
      FormSize := Self.ClientRect;
      FormSize.Bottom := FormSize.Bottom - Image1.Height - 1;
      FormSize.Right  := FormSize.Right  - Image1.Width - 1;
    
      // now we have the "box" in which the Image's topleft corner must be
    
      If ImageMovesLeft then dX := -dX;
      If ImageMovesUp   then dY := -dY;
    
      NewLeft := Image1.Left + dX;
      NewTop  := Image1.Top + dY;
    
      if ( NewTop >= FormSize.Top ) and ( NewTop <= FormSize.Bottom ) then begin
         Image1.Top := NewTop; // we fit into the allowed box
      end else begin
         ImageMovesUp := not ImageMovesUp; // we did not fit and have to bounce back
      end;
    
      if ( NewLeft >= FormSize.Left ) and ( NewLeft <= FormSize.Right ) then begin
         Image1.Left := NewLeft; // we fit into the allowed box
      end else begin
         ImageMovesLeft := not ImageMovesLeft; // we did not fit and have to bounce back
      end;
    
    end; 
    

    PS. In an unlikely case you really do need the Windows DESKTOP coordinates and not your Form coordinates you can get them at

    http://docwiki.embarcadero.com/Libraries/XE7/en/Vcl.Forms.TScreen.DesktopRect

    But to use that information you would have to solve another problem - how to place your Image1 over desktop and not over the form, which is much more complex for you. So I do not think you really meant Desktop....

    UPD. The code above if very simple and easy to understand, but it makes few implicit assumptions to work correctly. Those assumptions are:

    • The window(form) size is fixed once for all, it would never be resized.
    • The imagebox size is fixed once for all, it would never be resized.
    • The window is larger than an imagebox in both dimensions.
    • Only our procedure can move the imagebox, there is nothing else that can move it.

    Given those assumptions ( natural for fixed screen size computers many many years ago ) there is no need to analyze if the moving object got too left o too right, too above or too below. It only matters if the new coordinate is correct or not - if it is no more correct, then "bouncing" - reversing the direction without looking which one it was - is enough. But if, for example, user can suddenly resize the window and make it so small that the imagebox would fall outside of it - then this method would stuck infinitely switching directions, because the coordinates would always be incorrect given those very small changes "smooth" movement allows to have.

    To adapt to possible sudden and large changes in geometry there can be a number of approaches, but the most simple one would be to make two changes: distinction between two cases of wrong coordinates (too little or too large now would be different cases) and instant jumps of the image into the allowed box when needed, even if the jump would be large and not-smooth.

    procedure TForm1.Timer1Timer(Sender: TObject);
    var
      dX, dY, NewLeft, NewTop :Integer;
      FormSize: TRect;
    begin
      dX := random(2+1); // did you really mean "random(3)" or "1+random(2)" ???
      dY := random(2+1);
    
      FormSize := Self.ClientRect;
      FormSize.Bottom := FormSize.Bottom - Image1.Height - 1;
      FormSize.Right  := FormSize.Right  - Image1.Width - 1;
    
      // now we have the "box" in which the Image's topleft corner must be
    
      If ImageMovesLeft then dX := -dX;
      If ImageMovesUp   then dY := -dY;
    
      NewLeft := Image1.Left + dX;
      NewTop  := Image1.Top + dY;
    
      if NewLeft > FormSize.Right then begin
         ImageMovesLeft := True; 
         NewLeft := FormSize.Right;
      end;
      if NewLeft < FormSize.Left then begin
         ImageMovesLeft := False; 
         NewLeft := FormSize.Left;
      end;
    
      if NewTop > FormSize.Bottom then begin
         ImageMovesUp := True; 
         NewTop := FormSize.Bottom;
      end;
      if NewTop < FormSize.Top then begin
         ImageMovesUp := False; 
         NewTop := FormSize.Top;
      end;
    
      Image1.Top := NewTop;
      Image1.Left := NewLeft; 
    end; 
    

    UPD. Several controls moving.

    type TControlledObject = record
           obj: TControl;
           MovesLeft, MovesUp: Boolean;
    end;
    
    type TForm1=class(TForm)
    ....
    private
       images: array of TControlledObject;
    end;
    
    procedure TForm1.FormShow(....);
    begin
      SetLength(images, 3);
    
      with images[0] do begin
         obj := Self.Image1;
         MovesLeft := random >= 0.5;
         MovesUp   := random >= 0.5;
      end;
      with images[1] do begin
         obj := Self.Image2;
         MovesLeft := random >= 0.5;
         MovesUp   := random >= 0.5;
      end;
      with images[2] do begin
         obj := Self.Image3;
         MovesLeft := random >= 0.5;
         MovesUp   := random >= 0.5;
      end;
    end;
    
    procedure TForm1.Timer1Timer(Sender: TObject);
    var
      i: Integer
    begin
      for i := 0 to Length(images)-1 do 
        MoveImage(images[i]);
    end;
    
    procedure TForm1.MoveImage(var ImgRec: TControlledObject);
    var .....
    begin 
      dX := random(2+1); // did you really mean "random(3)" or "1+random(2)" ???
      dY := random(2+1);
    
      FormSize := Self.ClientRect;
      FormSize.Bottom := FormSize.Bottom - ImgRec.obj.Height - 1;
      FormSize.Right  := FormSize.Right  - ImgRec.obj.Width - 1;
    
      // now we have the "box" in which the Image's topleft corner must be
    
      If ImgRec.MovesLeft then dX := -dX;
      If ImgRec.MovesUp   then dY := -dY;
    

    ....and so on. Finish the conversion from one to many as your home task.