Drawing on a paintbox - How to keep up with mouse movements without delay?

前端 未结 3 1191
囚心锁ツ
囚心锁ツ 2021-01-24 18:44

I decided to have a little go myself at making a map editor for a simple RPG game. The map will allow drawing tiles at 32x32 into the map, nothing too fancy, but to give an idea

相关标签:
3条回答
  • 2021-01-24 19:02

    Your approach is wrong. The delay you suffer is mainly because the timer.

    Here are some guidances:

    1. Draw on a single (offscreen) bitmap.
    2. On the PaintBox OnPaint event just copy the offscreen bitmap.
    3. Draw on MouseMove, MouseUp directly (if needed)
    4. Preload your tiles as different bitmaps or combine them into a bigger one.
    5. Never create bitmaps in a time when you should draw.
    0 讨论(0)
  • 2021-01-24 19:11

    You should collect the mouse coordinates inside of the OnMouseMove event, otherwise you'll only get a new mouse position when the timer fires.

    In addition to that, use GetMouseMovePointsEx in order to get up to 64 mouse positions you missed.

    0 讨论(0)
  • 2021-01-24 19:22

    Don't use a TTimer to control your drawing. When the mouse moves around the PaintBox, set your flags as needed, and also keep track of the current mouse coordinates, and then call the PaintBox's Invalidate() method to trigger a repaint when flow control returns back to the message queue. Whenever the PaintBox's OnPaint event is triggered for any reason, draw your map and tiles as needed, and if a tile is being dragged around then draw it at the saved mouse coordinates.

    Also, in your DrawTileOnMap() method, you don't need to copy the image to a temp TBitmap, you can copy from your source TImage directly to your target TCanvas.

    Try something more like this:

    const
      FTileHeight = 32;         // height of each tile
      FTileWidth  = 32;         // width of each tile
      FSnapX      = 32;         // size of the X Snap
      FSnapY      = 32;         // size of the Y Snap
    
      FMapHeight  = 1280;       // height of the map 
      FMapWidth   = 1280;       // width of the map 
    
    var
      FTilesetPos: TPoint;      // tile position in tileset
      FMapTilePos: TPoint;      // tile position in map
      FMapTileColumn: Integer;
      FMapTileRow: Integer;
      FIsDrawing: Boolean;      // flag to determine if drawing tile on map.
    
    procedure DrawTileOnMap(const Tileset: TImage; TileX, TileY: Integer;
      MapX, MapY: Integer; OutCanvas: TCanvas);
    begin
      OutCanvas.CopyRect(
        Rect(MapX, MapY, MapX + FTileWidth, MapY + FTileHeight),
        Tileset.Canvas,
        Rect(TileX, TileY, TileX + FTileWidth, TileY + FTileHeight));
    end; 
    
    procedure TMainForm.FormCreate(Sender: TObject);
    begin
      FTilesetPos := Point(-1, -1);
      FMapTilePos := Point(-1, -1);
      FMapTileColumn = -1;
      FMapTileRow := -1;
      FIsDrawing := False;
    end;
    
    procedure TMainForm.MapEditorMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    begin
      if Button = mbMiddle then Exit;
    
      if Button = mbLeft then
        FIsDrawing := True
      end else
        DeleteTileAtPosition(FMapTilePos.X, FMapTilePos.Y, lvwRecords);
    
      MapEditor.Invalidate;
    end;
    
    procedure TMainForm.MapEditorMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    begin
      ConvertToSnapPosition(X, Y, FSnapX, FSnapY, FMapTilePos);
    
      FMapTileColumn := MapTilePositionToColumn(FMapTilePos.X);
      FMapTileRow := MapTilePositionToRow(FMapTilePos.Y);
    
      if (Button = mbLeft) and FDrawing then
        MapEditor.Invalidate;
    end;    
    
    procedure TMainForm.MapEditorMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    begin
      if Button = mbLeft then
      begin
        FIsDrawing := False
        MapEditor.Invalidate;
      end;
    end;
    
    procedure TMainForm.MapEditorPaint(Sender: TObject);
    var
      I, J: Integer;
      TileX, TileY: Integer;
      MapX, MapY: Integer;
    begin
      // draw empty/water tiles << NEEDS OPTIMIZATION AS VERY SLOW >>
      {for I := 0 to GetMapTilesColumnCount(FMapWidth) do
      begin
        for J := 0 to GetMapTilesRowCount(FMapHeight) do
        begin
          DrawTileOnMap(Image1, 0, 0, I * FTileWidth, J * FTileHeight, MapEditor.Canvas);
        end;
      end;}
    
      // draw tiles
      with lvwRecords do
      begin
        for I := 0 to Items.Count -1 do
        begin
          MapX := StrToInt(Items[I].Caption);
          MapY := StrToInt(Items[I].SubItems[0]);
          TileX := StrToInt(Items[I].SubItems[1]);
          TileY := StrToInt(Items[I].SubItems[2]);
          DrawTileOnMap(imgTileset, TileX, TileY, MapX, MapY, MapEditor.Canvas);
        end;
      end;
    
      PaintGrid(MapEditor.Canvas, FMapWidth, FMapHeight, 32, 1, $00543B1B); 
    
      if (FMapTileColumn > -1) and (FMapTileRow > -1) and FDrawing then
        DoDrawTile(FMapTilePos.X, FMapTilePos.Y);
    end; 
    
    0 讨论(0)
提交回复
热议问题