Lazarus

Programming => General => Topic started by: shs on September 01, 2017, 01:37:04 pm

Title: how to delete the canvas rectangle
Post by: shs on September 01, 2017, 01:37:04 pm
procedure TForm1.FormPaint(Sender: TObject);
begin
  canvas.rectangle(a, b, c, d);
end;                       

a.b.c.d are all random numbers and i want to draw rectangle in random position every second. but one more thing i want to do is to delete the previous rectangle or x seconds previous rectangle every second so that only certain number of rectangles can be shown  on the form.

p.s filling the triangle with default color is not i want. i want to permanently delete the rectangle       
Title: Re: how to delete the canvas rectangle
Post by: Leledumbo on September 01, 2017, 02:24:15 pm
a.b.c.d are all random numbers and i want to draw rectangle in random position every second. but one more thing i want to do is to delete the previous rectangle or x seconds previous rectangle every second so that only certain number of rectangles can be shown  on the form.
You should maintain a data structure representing currently drawn rectangles, perhaps along with the second they're created and modify the data structure accordingly (e.g. move rectangles from one index to another, resize the container, etc.). This data structure will then be a base for you to draw upon the canvas.
p.s filling the triangle with default color is not i want. i want to permanently delete the rectangle     
There's no such a thing, Canvas.Rectangle does NOT create a rectangle object (which then gets drawn), but draw pixels forming a rectangle (hope you understand the difference), as any other Canvas drawing methods do. As such, "deletion" is indeed filling the same space with the canvas' background color.

FormPaint will clear the canvas anyway, so if you don't draw again what was drawn the previous FormPaint, it will be gone.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 01, 2017, 02:44:51 pm
a.b.c.d are all random numbers and i want to draw rectangle in random position every second. but one more thing i want to do is to delete the previous rectangle or x seconds previous rectangle every second so that only certain number of rectangles can be shown  on the form.
You should maintain a data structure representing currently drawn rectangles, perhaps along with the second they're created and modify the data structure accordingly (e.g. move rectangles from one index to another, resize the container, etc.). This data structure will then be a base for you to draw upon the canvas.

how do i maintain a data structure for that?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 01, 2017, 03:56:23 pm
Hello rhong7,
Welcome to the forum.

You can use array, record, TList, TObjectList, etc to maintain a collection of items. My example below use TFPList. If you don't know which one to use, TList is okay for most cases. You can download my test.zip for testing.

Learn more about collection:
http://wiki.freepascal.org/Data_Structures,_Containers,_Collections

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, StdCtrls, ExtCtrls, Spin;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Label1: TLabel;
  17.     SpinEdit1: TSpinEdit;
  18.     Timer1: TTimer;
  19.     procedure Button1Click(Sender: TObject);
  20.     procedure FormCreate(Sender: TObject);
  21.     procedure FormDestroy(Sender: TObject);
  22.     procedure FormPaint(Sender: TObject);
  23.     procedure Timer1Timer(Sender: TObject);
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. type
  32.   TItem = record
  33.     X1, X2, Y1, Y2: Integer;
  34.     Color: TColor;
  35.   end;
  36.   PItem = ^TItem;
  37.  
  38. var
  39.   Items: TFPList; // list of PItem
  40.  
  41. {$R *.lfm}
  42.  
  43. { TForm1 }
  44.  
  45. procedure TForm1.Button1Click(Sender: TObject);
  46. begin
  47.   Timer1.Enabled := not(Timer1.Enabled);
  48.   if Timer1.Enabled then
  49.     Button1.Caption := 'Pause'
  50.   else
  51.     Button1.Caption := 'Start';
  52. end;
  53.  
  54. procedure TForm1.FormCreate(Sender: TObject);
  55. begin
  56.   Items := TFPList.Create;
  57. end;
  58.  
  59. procedure TForm1.FormDestroy(Sender: TObject);
  60. begin
  61.   Timer1.Enabled := False;
  62.   Items.Free;
  63. end;
  64.  
  65. procedure TForm1.FormPaint(Sender: TObject);
  66. var
  67.   Item: TItem;
  68.   i: Integer;
  69. begin
  70.   for i := 0 to (Items.Count-1) do
  71.   begin
  72.     Item := PItem(Items[i])^;
  73.     Canvas.Brush.Color := Item.Color;
  74.     Canvas.Rectangle(Item.X1, Item.Y1, Item.X2, Item.Y2);
  75.   end;
  76. end;
  77.  
  78. procedure TForm1.Timer1Timer(Sender: TObject);
  79. var
  80.   Item: PItem;
  81.   X1, Y1, X2, Y2: Integer;
  82.   Selected: Integer;
  83. begin
  84.   if (Random(10) > 3) then // new rectangle
  85.   begin
  86.     if (Items.Count >= SpinEdit1.Value) then Exit;
  87.     X1 := Random(Width);
  88.     Y1 := Random(Height);
  89.     X2 := X1 + Random(80);
  90.     Y2 := Y1 + Random(80);
  91.     New(Item);
  92.     Item^.X1 := X1;
  93.     Item^.Y1 := Y1;
  94.     Item^.X2 := X2;
  95.     Item^.Y2 := Y2;
  96.     Item^.Color := RGBToColor(Random(255), Random(255), Random(255));
  97.     Items.Add(Item);
  98.   end
  99.   else // remove rectangle
  100.   begin
  101.     if (Items.Count <= 0) then Exit;
  102.     Selected := Random(Items.Count);
  103.     Items.Delete(Selected);
  104.   end;
  105.   Repaint;
  106. end;
  107.  
  108. end.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 02, 2017, 02:32:40 am
Hello rhong7,
Welcome to the forum.

You can use array, record, TList, TObjectList, etc to maintain a collection of items. My example below use TFPList. If you don't know which one to use, TList is okay for most cases. You can download my test.zip for testing.

Learn more about collection:
http://wiki.freepascal.org/Data_Structures,_Containers,_Collections

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, StdCtrls, ExtCtrls, Spin;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Label1: TLabel;
  17.     SpinEdit1: TSpinEdit;
  18.     Timer1: TTimer;
  19.     procedure Button1Click(Sender: TObject);
  20.     procedure FormCreate(Sender: TObject);
  21.     procedure FormDestroy(Sender: TObject);
  22.     procedure FormPaint(Sender: TObject);
  23.     procedure Timer1Timer(Sender: TObject);
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. type
  32.   TItem = record
  33.     X1, X2, Y1, Y2: Integer;
  34.     Color: TColor;
  35.   end;
  36.   PItem = ^TItem;
  37.  
  38. var
  39.   Items: TFPList; // list of PItem
  40.  
  41. {$R *.lfm}
  42.  
  43. { TForm1 }
  44.  
  45. procedure TForm1.Button1Click(Sender: TObject);
  46. begin
  47.   Timer1.Enabled := not(Timer1.Enabled);
  48.   if Timer1.Enabled then
  49.     Button1.Caption := 'Pause'
  50.   else
  51.     Button1.Caption := 'Start';
  52. end;
  53.  
  54. procedure TForm1.FormCreate(Sender: TObject);
  55. begin
  56.   Items := TFPList.Create;
  57. end;
  58.  
  59. procedure TForm1.FormDestroy(Sender: TObject);
  60. begin
  61.   Timer1.Enabled := False;
  62.   Items.Free;
  63. end;
  64.  
  65. procedure TForm1.FormPaint(Sender: TObject);
  66. var
  67.   Item: TItem;
  68.   i: Integer;
  69. begin
  70.   for i := 0 to (Items.Count-1) do
  71.   begin
  72.     Item := PItem(Items[i])^;
  73.     Canvas.Brush.Color := Item.Color;
  74.     Canvas.Rectangle(Item.X1, Item.Y1, Item.X2, Item.Y2);
  75.   end;
  76. end;
  77.  
  78. procedure TForm1.Timer1Timer(Sender: TObject);
  79. var
  80.   Item: PItem;
  81.   X1, Y1, X2, Y2: Integer;
  82.   Selected: Integer;
  83. begin
  84.   if (Random(10) > 3) then // new rectangle
  85.   begin
  86.     if (Items.Count >= SpinEdit1.Value) then Exit;
  87.     X1 := Random(Width);
  88.     Y1 := Random(Height);
  89.     X2 := X1 + Random(80);
  90.     Y2 := Y1 + Random(80);
  91.     New(Item);
  92.     Item^.X1 := X1;
  93.     Item^.Y1 := Y1;
  94.     Item^.X2 := X2;
  95.     Item^.Y2 := Y2;
  96.     Item^.Color := RGBToColor(Random(255), Random(255), Random(255));
  97.     Items.Add(Item);
  98.   end
  99.   else // remove rectangle
  100.   begin
  101.     if (Items.Count <= 0) then Exit;
  102.     Selected := Random(Items.Count);
  103.     Items.Delete(Selected);
  104.   end;
  105.   Repaint;
  106. end;
  107.  
  108. end.

can you make snake game with those ideas?
Title: Re: how to delete the canvas rectangle
Post by: shs on September 02, 2017, 04:16:39 am
Code: [Select]
    if (Items.Count <= 0) then Exit;
    Selected := Random(Items.Count);
    Items.Delete(Selected);
  end;
  Repaint;
end;

end.
[/quote]

instead of deleting random rectangle, can i delete the one that was created a second before?

Title: Re: how to delete the canvas rectangle
Post by: molly on September 02, 2017, 04:31:15 am
instead of deleting random rectangle, can i delete the one that was created a second before?
That depends on what you meant by "second before". If by "second before" you meant position then yes. If you meant "second before" as 1000 milliseconds then you are doing something wrong  ;)
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 02, 2017, 04:38:33 am
can you make snake game with those ideas?

Yes and No.

Yes, because the ability to draw and delete objects on the screen is the very basic skill needed for game creation. No, because snake game requires more programming skills than you thought.

Creating snake game is not hard but you need to able to understand and use these:

- Multidimensional array
http://wiki.freepascal.org/Multidimensional_arrays

- Reading user input
https://www.freepascal.org/docs-html/rtl/crt/keypressed.html
https://www.freepascal.org/docs-html/rtl/crt/readkey.html
http://wiki.freepascal.org/LCL_Key_Handling

- Collision Detection
Read the simplify version "Hit Testing":
https://en.wikipedia.org/wiki/Hit-testing

- Choosing a Graphics Library
http://wiki.freepascal.org/Graphics_libraries

- Able to use game loop properly
http://gameprogrammingpatterns.com/game-loop.html

Snake is a good project for beginners to learn how to create computer games. But if you have problem to understand all the things mentioned above, it may be to challenging for you. Instead you may try to create animations like falling rectangles, bouncing ball, etc.

Similar to snake is pong game. I think it will be easier because it doesn't need to calculate collision with it's own body and it doesn't need to store data of it's body segments.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 02, 2017, 05:36:15 am
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  LCLType, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Timer1: TTimer;
    Timer2: TTimer;
    procedure Button1Click(Sender: TObject);

    procedure FormCreate(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

    procedure Timer1Timer(Sender: TObject);
    procedure Timer2Timer(Sender: TObject);

  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;
  snake: Trect;
  tail: Trect;
  a:integer;
  b:integer;
  c:integer;
  d:integer;
  direction:integer;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin

 a:=(random(24))*20;
 b:=(random(24))*20 ;
 c:=a+20;
 d:=b+20 ;

 snake.left:=a;
 snake.top:=b;
 snake.right:=c;
 snake.bottom:=d;

 tail.left:=a+40;
 tail.top:=b;
 tail.right:=c+40;
 tail.bottom:=d;






end;



procedure TForm1.Button1Click(Sender: TObject);
begin
  canvas.rectangle(snake.left, snake.top, snake.right, snake.bottom);
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  );
begin

  case direction of
  0:if key in [vk_left] then
     begin
      direction:=3;
     end;


  1:if key in [vk_left] then
     begin
      direction:=1 ;
     end;


  2: if key in [vk_left] then
     begin
      direction:=3;
     end;


  3: if key in [vk_left] then
     begin
      direction:=3;
     end;

  4: if key in [vk_left] then
     begin
      direction:=3;
     end;

  end;

  case direction of
  0:if key in [vk_right] then
     begin
      direction:=1;
     end;


  1:if key in [vk_right] then
     begin
      direction:=1 ;
     end;


  2: if key in [vk_right] then
     begin
      direction:=1;
     end;


  3: if key in [vk_right] then
     begin
      direction:=3;
     end;

  4: if key in [vk_right] then
     begin
      direction:=1;
     end;

  end;

  case direction of
  0:if key in [vk_up] then
     begin
      direction:=4;
     end;


  1:if key in [vk_up] then
     begin
      direction:=4 ;
     end;


  2: if key in [vk_up] then
     begin
      direction:=2;
     end;


  3: if key in [vk_up] then
     begin
      direction:=4;
     end;

  4: if key in [vk_up] then
     begin
      direction:=4;
     end;

  end;


  case direction of
    0:if key in [vk_down] then
       begin
        direction:=2;
       end;


    1:if key in [vk_down] then
       begin
        direction:=2 ;
       end;


    2: if key in [vk_down] then
       begin
        direction:=2;
       end;


    3: if key in [vk_down] then
       begin
        direction:=2;
       end;

    4: if key in [vk_down] then
       begin
        direction:=4;
       end;

end;

end;



procedure TForm1.Timer1Timer(Sender: TObject);

begin
  Canvas.Pen.Color := clBlue;
  Canvas.Brush.Color:=clwhite;
  canvas.rectangle(snake.left, snake.top, snake.right, snake.bottom);


end;

procedure TForm1.Timer2Timer(Sender: TObject);
begin
  if direction = 1 then
    begin
      snake.left:=snake.left+20;
      snake.right:=snake.right+20;

    end;                       

this is everything i have done but i do not know what to do next. how can i delete the rectangle that has been created before in this coding?

can you make snake game with those ideas?

Yes and No.

Yes, because the ability to draw and delete objects on the screen is the very basic skill needed

Title: Re: how to delete the canvas rectangle
Post by: shs on September 02, 2017, 05:50:42 am
here is the attachment
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 02, 2017, 06:06:52 am
You've made a good start. You have potential to be a game programmer.

Have you understand how to use array? You need a 2 dimensional array to store the map information. And one more a single dimension array to store to snake body segment information.

Your code can run on my Linux computer, this is screenshot of your code:
Title: Re: how to delete the canvas rectangle
Post by: shs on September 02, 2017, 06:25:09 am
You've made a good start. You have potential to be a game programmer.

Have you understand how to use array? You need a 2 dimensional array to store the map information. And one more a single dimension array to store to snake body segment information.

Your code can run on my Linux computer, this is screenshot of your code:

i understand how to use array but i don't know how to use it in this game.
my code works on my computer as well but i want the rectangles to be deleted every second so the snake can move around with one rectangle.
If you don't mind, can you help me with the coding please? thanks you very much for keep replying to me, i appreciate it a lot!
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 02, 2017, 06:46:11 am
I'm currently not at my home. I'll be back maybe 4 hours later.
Title: Re: how to delete the canvas rectangle
Post by: molly on September 02, 2017, 07:03:12 am
Not wanting to strip this way form you handoko but 4 hours can be long for someone who is eagerly waiting :)

A little tip for TS: when you want to remove a rectangle, then just do not draw it on the next iteration.

Which brings us to: do not draw on the canvas of a form outside the paint event, use the OnPaint  event for that. You can use invalidate to 'update' your canvas drawing.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 02, 2017, 08:14:48 am
I'm currently not at my home. I'll be back maybe 4 hours later.
okay thank you

Not wanting to strip this way form you handoko but 4 hours can be long for someone who is eagerly waiting :)

A little tip for TS: when you want to remove a rectangle, then just do not draw it on the next iteration.

Which brings us to: do not draw on the canvas of a form outside the paint event, use the OnPaint  event for that. You can use invalidate to 'update' your canvas drawing.

i'm okay with waiting and thank you for the tips
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 02, 2017, 12:53:20 pm
Hello, sorry I am late.

Before we start the coding part, you should know that in game programming:
Quote
after you draw something on the screen, you should forget it.

When I was starting to learn game programming, I thought the same as you "How can I delete that thing on the screen?"

You should not think how to manipulate objects in the screen. For many reasons it is better and easier to manipulate your objects in your game world. And then you write a procedure to draw the objects in your game world to the screen.

So the calculations of collisions, movings, special effects, etc are done on the game world. Not on the screen directly.

In most cases you use a 2 dimensional array to represent the game world. In the following examples I will use a 10 x 10 array to represent the world size of 10 x 10.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 02, 2017, 12:58:23 pm
First you define the types of the item that can occupy your game world:
Code: Pascal  [Select]
  1. type
  2.   TItem = (Emtpy, Snake, Fruit);
Programmers like to use a letter 'T' at the beginning of a type name.

You define your game world size:
Code: Pascal  [Select]
  1. const
  2.   WorldWidth  = 10;
  3.   WorldHeight = 10;
  4.   Scale       = 10;
The Scale is used for drawing the screen. The matrix of the array will be multiply with it.

And this is your game world, represented by a 2 dimensional array:
Code: Pascal  [Select]
  1. var
  2.   GameWorld: array[1..WorldWidth, 1..WorldHeight] of TItem;

After you define all those things above, now you write the code for:
Code: Pascal  [Select]
  1.     procedure ClearWorld;
  2.     procedure PutItem(X, Y: Integer; Item: TItem);
  3.     function GetItem(X, Y: Integer): TItem;
  4.     procedure DrawGameWorld;

These are the codes:
Code: Pascal  [Select]
  1. procedure TForm1.ClearWorld;
  2. var
  3.   X, Y: Integer;
  4. begin
  5.   for X := 1 to WorldWidth do
  6.     for Y := 1 to WorldHeight do
  7.       GameWorld[X, Y] := Emtpy;
  8. end;
  9.  
  10. procedure TForm1.PutItem(X, Y: Integer; Item: TItem);
  11. begin
  12.   if (X < 1) or (X > WorldWidth) then Exit;
  13.   if (Y < 1) or (Y > WorldHeight) then Exit;
  14.   GameWorld[X, Y] := Item;
  15. end;
  16.  
  17. function TForm1.GetItem(X, Y: Integer): TItem;
  18. begin
  19.   Result := Emtpy;
  20.   if (X < 1) or (X > WorldWidth) then Exit;
  21.   if (Y < 1) or (Y > WorldHeight) then Exit;
  22.   Result := GameWorld[X, Y];
  23. end;

And this is the drawing procedure for game world to screen:
Code: Pascal  [Select]
  1. procedure TForm1.DrawGameWorld;
  2. const
  3.   Padding = 2;
  4. var
  5.   X, Y: Integer;
  6.   ScreenX, ScreenY: Integer;
  7. begin
  8.   Refresh;
  9.   for X := 1 to WorldWidth do
  10.     for Y := 1 to WorldHeight do
  11.       begin
  12.         ScreenX := X * Scale;
  13.         ScreenY := Y * Scale;
  14.         case GameWorld[X, Y] of
  15.           Emtpy: ; // do nothing
  16.           Snake: begin
  17.                    Canvas.Pen.Color := clBlue;
  18.                    Canvas.Brush.Color := clwhite;
  19.                    Canvas.Rectangle(ScreenX, ScreenY, ScreenX+Scale-Padding, ScreenY+Scale-Padding);
  20.                  end;
  21.           Fruit: begin
  22.                    Canvas.Pen.Color := clBlack;
  23.                    Canvas.Brush.Color := clRed;
  24.                    Canvas.Ellipse(ScreenX, ScreenY, ScreenX+Scale-Padding, ScreenY+Scale-Padding);
  25.                  end;
  26.           end;
  27.       end;
  28. end;
You can try to modify the padding value to see what it is for.

You can download the zip file for the full source code.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 02, 2017, 01:02:41 pm
Now, we test the code we already have. I put 2 buttons for testing.

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, StdCtrls;
  9.  
  10. type
  11.  
  12.   TItem = (Emtpy, Snake, Fruit);
  13.  
  14.   { TForm1 }
  15.  
  16.   TForm1 = class(TForm)
  17.     Button1: TButton;
  18.     Button2: TButton;
  19.     procedure Button1Click(Sender: TObject);
  20.     procedure Button2Click(Sender: TObject);
  21.   private
  22.     procedure ClearWorld;
  23.     procedure PutItem(X, Y: Integer; Item: TItem);
  24.     function GetItem(X, Y: Integer): TItem;
  25.     procedure DrawGameWorld;
  26.   end;
  27.  
  28. var
  29.   Form1: TForm1;
  30.  
  31. implementation
  32.  
  33. const
  34.   WorldWidth  = 10;
  35.   WorldHeight = 10;
  36.   Scale       = 10;
  37.  
  38. var
  39.   GameWorld: array[1..WorldWidth, 1..WorldHeight] of TItem;
  40.  
  41. {$R *.lfm}
  42.  
  43. { TForm1 }
  44.  
  45. procedure TForm1.Button1Click(Sender: TObject);
  46. //   123456789
  47. // 1 ..........
  48. // 2 ..........
  49. // 3 ..........
  50. // 4 ....X.....
  51. // 5 ...X.X....
  52. // 6 ..XXXXX...
  53. // 7 .X.....X..
  54. // 8 ..........
  55. // 9 ..........
  56. //10 ..........
  57. begin
  58.   ClearWorld;
  59.   PutItem(2, 7, Snake);
  60.   PutItem(3, 6, Snake);
  61.   PutItem(4, 5, Snake);
  62.   PutItem(4, 6, Snake);
  63.   PutItem(5, 4, Snake);
  64.   PutItem(5, 6, Snake);
  65.   PutItem(6, 5, Snake);
  66.   PutItem(6, 6, Snake);
  67.   PutItem(7, 6, Snake);
  68.   PutItem(8, 7, Snake);
  69.   DrawGameWorld;
  70. end;
  71.  
  72. procedure TForm1.Button2Click(Sender: TObject);
  73. //   123456789
  74. // 1 ..........
  75. // 2 ..........
  76. // 3 ..oooooo..
  77. // 4 .o......o.
  78. // 5 .o......o.
  79. // 6 .o......o.
  80. // 7 .o......o.
  81. // 8 ..oooooo..
  82. // 9 ..........
  83. //10 ..........
  84. begin
  85.   ClearWorld;
  86.   PutItem(2, 4, Fruit);
  87.   PutItem(2, 5, Fruit);
  88.   PutItem(2, 6, Fruit);
  89.   PutItem(2, 7, Fruit);
  90.   PutItem(3, 3, Fruit);
  91.   PutItem(3, 8, Fruit);
  92.   PutItem(4, 3, Fruit);
  93.   PutItem(4, 8, Fruit);
  94.   PutItem(5, 3, Fruit);
  95.   PutItem(5, 8, Fruit);
  96.   PutItem(6, 3, Fruit);
  97.   PutItem(6, 8, Fruit);
  98.   PutItem(7, 3, Fruit);
  99.   PutItem(7, 8, Fruit);
  100.   PutItem(8, 3, Fruit);
  101.   PutItem(8, 8, Fruit);
  102.   PutItem(9, 4, Fruit);
  103.   PutItem(9, 5, Fruit);
  104.   PutItem(9, 6, Fruit);
  105.   PutItem(9, 7, Fruit);
  106.   DrawGameWorld;
  107. end;
  108.  
  109. procedure TForm1.ClearWorld;
  110. var
  111.   X, Y: Integer;
  112. begin
  113.   for X := 1 to WorldWidth do
  114.     for Y := 1 to WorldHeight do
  115.       GameWorld[X, Y] := Emtpy;
  116. end;
  117.  
  118. procedure TForm1.PutItem(X, Y: Integer; Item: TItem);
  119. begin
  120.   if (X < 1) or (X > WorldWidth) then Exit;
  121.   if (Y < 1) or (Y > WorldHeight) then Exit;
  122.   GameWorld[X, Y] := Item;
  123. end;
  124.  
  125. function TForm1.GetItem(X, Y: Integer): TItem;
  126. begin
  127.   Result := Emtpy;
  128.   if (X < 1) or (X > WorldWidth) then Exit;
  129.   if (Y < 1) or (Y > WorldHeight) then Exit;
  130.   Result := GameWorld[X, Y];
  131. end;
  132.  
  133. procedure TForm1.DrawGameWorld;
  134. const
  135.   Padding = 2;
  136. var
  137.   X, Y: Integer;
  138.   ScreenX, ScreenY: Integer;
  139. begin
  140.   Refresh;
  141.   for X := 1 to WorldWidth do
  142.     for Y := 1 to WorldHeight do
  143.       begin
  144.         ScreenX := X * Scale;
  145.         ScreenY := Y * Scale;
  146.         case GameWorld[X, Y] of
  147.           Emtpy: ; // do nothing
  148.           Snake: begin
  149.                    Canvas.Pen.Color := clBlue;
  150.                    Canvas.Brush.Color := clwhite;
  151.                    Canvas.Rectangle(ScreenX, ScreenY, ScreenX+Scale-Padding, ScreenY+Scale-Padding);
  152.                  end;
  153.           Fruit: begin
  154.                    Canvas.Pen.Color := clBlack;
  155.                    Canvas.Brush.Color := clRed;
  156.                    Canvas.Ellipse(ScreenX, ScreenY, ScreenX+Scale-Padding, ScreenY+Scale-Padding);
  157.                  end;
  158.           end;
  159.       end;
  160. end;
  161.  
  162. end.

You can download snake2.zip for testing.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 02, 2017, 01:12:14 pm
Now, I guess you already have the basic knowledge how to handle the drawing part for snake game.

- You do not manipulate objects on the screen
- You use an array to represent your game world
- You manipulate objects in the game world
- You write a procedure to draw the game world to the screen

You need more steps to make it a complete snake game:
- Game loop
- User input detection
- A new array to store snake's body information
- Collision detection

Not the best but for beginners, TTimer can be used for game loop. I saw your code use 2 TTimers, you should not do so. It will make the code harder to debug and some unpredicted things may happen.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 02, 2017, 02:07:04 pm
Back to your original question:
Quote
How to delete the canvas rectangle?

First you call PutItem(X, Y, Emtpy);
and then call DrawGameWorld;
Title: Re: how to delete the canvas rectangle
Post by: carl_caulkett on September 02, 2017, 11:02:12 pm
That was a really nicely explained series of posts, Handoko. I like the way you started off with the key principle of
Quote
Before we start the coding part, you should know that in game programming:
Quote
after you draw something on the screen, you should forget it.
And then went on to illustrate it with more and more detail.

Good job  :)
Title: Re: how to delete the canvas rectangle
Post by: jamie on September 03, 2017, 12:05:45 am
if you want to clear the area, look at the Canvas.FillRect.

 prior to painting, you can call that to erase background and then draw your
snake. This is cause flicker.

 Another Way is to do this drawing on a Tbitmap  then paint it on to the Form or Paintbox in the
OnPaint event. As you complete each drawing you can directly force a Paint cycle instead of
waiting for the system to refresh.

 If you have more questions come back..

Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 03, 2017, 03:41:28 am
That was a really nicely explained series of posts, Handoko.

Because I ever be a part-time teacher, I know I should carefully plan the lessons and the explanations. But as usual, I often make mistakes with English grammar and typing. I reread my own posts, I found many typos.

prior to painting, you can call that to erase background and then draw your
snake. This is cause flicker.

Yes, you're right. There are many things to do to reduce the flicker. But I think it is too advanced and will be more suitable for beginners who already understand the basic of game programming.
Title: Re: how to delete the canvas rectangle
Post by: jamie on September 04, 2017, 05:04:28 pm
I find if you want layered images you can use a Tlist or TCollection and add bitmaps to it..

each map is its own image. and then have a final drawing destination bitmap to be shown during
the Paint event..

 Thinking of using a TimageList is attractive but last time I remember the images need to be all
the same size. I guess that is ok if you do it with transparency and set them all the same size.
 
 during the master image update you can   have some array of X,Y points per image so you'll
know where to draw them.

 I am sure many here have done this.. Maybe a little much for a newbie but it gives you a good
starting point on using bitmaps.
Title: Re: how to delete the canvas rectangle
Post by: cpicanco on September 04, 2017, 05:09:12 pm
Hi rhong7, what about hosting your game in a github repository?
Title: Re: how to delete the canvas rectangle
Post by: shs on September 16, 2017, 02:54:31 am
Now, I guess you already have the basic knowledge how to handle the drawing part for snake game.

- You do not manipulate objects on the screen
- You use an array to represent your game world
- You manipulate objects in the game world
- You write a procedure to draw the game world to the screen

You need more steps to make it a complete snake game:
- Game loop
- User input detection
- A new array to store snake's body information
- Collision detection

Not the best but for beginners, TTimer can be used for game loop. I saw your code use 2 TTimers, you should not do so. It will make the code harder to debug and some unpredicted things may happen.

how should i code for the snake to move? I tried but i can't do it :(
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 16, 2017, 03:35:13 pm
You already able to make snake move in your "lazarus snake gamee.zip". So, what is the problem? You need to explain it in more detail.

The basic concept does not change much. In explanations I gave you, you just need to move your snake in the game world and call DrawGameWorld to do the painting.

Your code for TForm1.FormKeyDown is still valid. You only need to change your:
Code: Pascal  [Select]
  1.   canvas.rectangle(snake.left, snake.top, snake.right, snake.bottom);

To:
Code: Pascal  [Select]
  1.   PutItem(snakepos.left, snakepos.top, Snake);

And make sure the increment or decrement of the snake body for moving the snake is 1 instead of 20.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 16, 2017, 04:48:29 pm
hi i think it works but is my coding right? is there a better way to code this? and how should i add the tails? thank you
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 16, 2017, 05:57:08 pm
Yes your code works, I've just tested it. As long as it is working correctly now, you do not need to think what is the 'better' way to code it.

Here some advices, which I learned from doing programming for a long period:
1. Don't think about optimization at the beginning of writing a program or module
2. Think first to write to code that works
3. After it works as what you want, make the code beautiful (indentation, format, etc)
4. Testing, goto step 2 if you find any bug
5. Optimize the code and testing again, goto step 2 if you find any bug

Each programmer may write the same program using different ways. So there is no 'better' way. As long as the code is bug free and no security issue, it is a piece of good code.

Now, lets back to the snake story. I think the image below will give the idea what you should do next. You can use an array or a TList or other similar thing to store the snake body information. Using TList is easier, you can type less code. If you use array, then you have to write some code to handle the snake body growing in length.

In my picture below, the first item in the array is the snake's head. If you like, you can use the last item in the array as the snake's head.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 17, 2017, 01:31:50 am
Yes your code works, I've just tested it. As long as it is working correctly now, you do not need to think what is the 'better' way to code it.

Here some advices, which I learned from doing programming for a long period:
1. Don't think about optimization at the beginning of writing a program or module
2. Think first to write to code that works
3. After it works as what you want, make the code beautiful (indentation, format, etc)
4. Testing, goto step 2 if you find any bug
5. Optimize the code and testing again, goto step 2 if you find any bug

Each programmer may write the same program using different ways. So there is no 'better' way. As long as the code is bug free and no security issue, it is a piece of good code.

Now, lets back to the snake story. I think the image below will give the idea what you should do next. You can use an array or a TList or other similar thing to store the snake body information. Using TList is easier, you can type less code. If you use array, then you have to write some code to handle the snake body growing in length.

In my picture below, the first item in the array is the snake's head. If you like, you can use the last item in the array as the snake's head.

how should code it with TList? can you show me an example please? i don't know how to store the body information
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 12:47:37 pm
Lets start with some theory first. See the attached picture, it assumes you use an array to store the snake body information. The smallest (index = 1) is the snake's head and the largest is its tail.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 12:49:40 pm
Now, you now should already understood how to use array to store the snake body information and how to move it. But you still have to solve this issue:

How to make the snake grow longer?

It is not hard. But you will get this problem:

How to declare an array for the snake that can grow longer?

You can set SnakeBody array to a very big size that you sure it won't reach it. You can use 2000 or bigger:
Code: Pascal  [Select]
  1. var
  2.   SnakeBody[1..2000] of TRect; // set the size to a very big value

Alternatively, you can use Dynamic Array (the better solution):
Code: Pascal  [Select]
  1. var
  2.   SnakeBody: array of TRect; // using dynamic array
Read more about Dynamic Array:
http://wiki.freepascal.org/Dynamic_array

Setting an array to a very big size is not a good solution, it wastes computer memory and you cannot be sure to know what is the best value for it. Using Dynamic array is okay, but using TList is easier.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 12:50:50 pm
Remember on the previous post, I explained that you can use For - Do loop for changing snake information when the snake is moving. You need to write similar code again for making the snake grow longer (new item insertion). But if you use TList, inserting a new item is very easy. You can code less and it also means the code is easier to maintain.

You can see picture below for the comparison. In general using array is faster than TList. But if the code is poorly writen by inexperienced programmers, using array can be as slow or slower than using TList.

As a programmer, you can choose for memory efficienty, performance or easy maintaining code. Same problem can be solved using different methods, it depends on what is the programmer's priority. In this case (snake game), TList will be easier. Using array is faster but it won't be noticable on this simple program.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 12:51:38 pm
(I will come back later after I preapred some example codes)
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 12:53:28 pm
Hello! You're back finally :) :)

I'm so glad that you're back.

Thanks for the explanation and i have some question other than those explanatios.

1. How can I set the tails' movement? how can I make the tails to follow the head?
2. How can I use Tlist?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 12:56:02 pm
If you use array (instead of TList), the code of in the snake1.png picture.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 01:08:45 pm
so if i just do that snake can get longer?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 01:12:50 pm
I'm preparing the code. Explanation is hard, programmers talk better using code. :D

Give me some time.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 01:17:10 pm
Okay thank you very much :)
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 02:19:18 pm
1. Declare The SnakeBody Variable

It is easy, just put this code at at declaration area:
Code: Pascal  [Select]
  1. var
  2.   SnakeBody: TList;

2. Initial The SnakeBody When The Program Start

You need to put this on FormCreate:
Code: Pascal  [Select]
  1. var
  2.   SnakeSegment: PRect;
  3. begin  
  4.   SnakeBody := TList.Create;
  5.   // ...
  6.   SnakeSegment^ := snakepos; // Put this line after ...random(10)
  7.   SnakeBody.Add(SnakeSegment); // Put this line after SnakeSegment^ := ...
  8. // ...

3. Free SnakeBody When Not Needed

Put this on FormClose:
Code: Pascal  [Select]
  1. SnakeBody.Free;

4. Add A New Variable "SnakeIsGrowing"

This variable is needed to know if the snake is moving or growing. If it is moving, then the latest segment of the body (tail) will be deleted automatically.

Add this as a global variable:
Code: Pascal  [Select]
  1. var
  2.   SnakeIsGrowing: Boolean = False;

5. Add A New Procedure "MoveSnake"

Create a new procedure, I call it MoveSnake. It should be on private section of TForm1. Here is the code:

Code: Pascal  [Select]
  1. procedure TForm1.MoveSnake(NewHead: TRect);
  2. var
  3.   SnakeSegment: PRect;
  4. begin
  5.   New(SnakeSegment);
  6.   SnakeSegment^ := NewHead;
  7.   SnakeBody.Insert(0, SnakeSegment);
  8.   if not(SnakeIsGrowing) then begin
  9.     SnakeSegment := SnakeBody[SnakeBody.Count-1];
  10.     PutItem(SnakeSegment^.Left, SnakeSegment^.Top, Emtpy);
  11.     Freemem(SnakeSegment);
  12.     SnakeBody.Delete(SnakeBody.Count-1);
  13.   end;
  14.   SnakeIsGrowing := False;
  15. end;

You can know from the code above, if the snake is not growing then the code will remove the tail.

6. Make It Move

Because you use Timer1 as the game main loop, so you should put this code on Timer1Timer:
Code: Pascal  [Select]
  1. MoveSnake(snakepos);

You should put the code above at the end of the procedure.

7. Fix An Error: PutItem

You should remove PutItem(snakepos.left, snakepos.top, snake) on the FormKeyDown because you already have it on Timer1Timer.

8. Fix An Error: ClearWorld

You should not call ClearWorld on Timer1Timer. ClearWorld is needed only when starting a new round of the game. So remove ClearWorld on Timer1Timer.

9. Write Code for Testing

Everything should be okay now. You can write some code to test if it working correctly.

Add the code on your key down testing section:
Code: Pascal  [Select]
  1.   if key=VK_ADD then
  2.   begin
  3.     SnakeIsGrowing := True;
  4.   end;

Now, you can try pressing '+' when moving the snake, the snake should grow longer.

I wrote this explanation on the same time coding it on your code. It works here on my test.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 02:33:36 pm
Novice may find it difficult to use TList because:

- You use pointer to access its items (in your case it is PRect)
- You need to understand how to use pointer
- You have to manually allocate memory for the items
- You have to manually free the items before calling TList.Delete
- It introduces some new functions: TList.Insert, TList.Delete

Some reading materials:
https://www.freepascal.org/docs-html/rtl/classes/tlist.html
https://www.freepascal.org/docs-html/rtl/system/new.html
https://freepascal.org/docs-html/ref/refse15.html
http://wiki.freepascal.org/Pointers
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 03:06:58 pm
Hi i cannot compile it
error messages came up
Code: Pascal  [Select]
  1. SnakeSegment: PRect;  
identifier not found "prect"
Code: Pascal  [Select]
  1. SnakeSegment^:= snakepos;
illegal qualifier
identifiers not found snakepos
Code: Pascal  [Select]
  1.  New(SnakeSegment);      
pointer type expected but got "<erroneous type>"
Code: Pascal  [Select]
  1. SnakeBody.Insert(0, SnakeSegment);  
unit1.pas(86,35) Error: Incompatible type for arg no. 2: Got "<erroneous type>", expected "Pointer"
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 03:14:14 pm
Please send your code to the forum, I need try and inspect it.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 03:29:37 pm
I guess you need to add LCLType in your uses clause section.
Title: Re: how to delete the canvas rectangle
Post by: helmi on September 27, 2017, 03:35:47 pm
Great explanation by Handoko. This kind of game, you must know very well how to using array or link list to implement in a game. Because it will help you to do the AI part (Another snake). For drawing part, you can use back buffer technique.

ps: sorry, long time not do game programming.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 03:43:40 pm
please find the attachment
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 03:54:09 pm
@helmi

You mentioned link list.

Yes, it is a very hard topic. I remember first time I learned it. I got a book of Assembly Programming, on the last chapter there was the topic about Singly linked lists and Doubly linked list. What, I learned linked list using Assembly Language. Yes it is crazy! :o

It took me months to fully understand it. So I wrote the module and tested it properly and saved the pascal code (I learned Assembly but I don't like it). I now still keep the code. Free Pascal is great, it has ready to use TList so I do not use my own version of TList code anymore.

The TS is a novice, back buffer maybe to advanced for him to understand. I am sure he will later ask, how to improve the performance and reduce flicker.

@rhong7

On your previous code, I saw snakepos but I cannot found it anymore. You deleted it accidentally?

Add this:
Code: Pascal  [Select]
  1. var
  2.   snakepos: TRect;

Also, please remove this line:
Code: Pascal  [Select]
  1.   i := SnakeBody.Count;
I used it for testing but I forgot to delete that line.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 04:00:18 pm
Code: Pascal  [Select]
  1. SnakeSegment^:= snakepos;
C:\Users\rhong\Desktop\snake game by hondoko\unit1.pas(61,16) Error: Illegal qualifier

i still get error for this one
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 04:03:35 pm
What is the version of your Lazarus? What is the version of your FPC? What is your OS?

Lazarus main menu > Help > About

It can compile here on my computer.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 04:06:20 pm
FPC 2.6.4

where can i find os?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 04:09:59 pm
I'm not very sure but maybe you can try, change all of this in your code:

Code: Pascal  [Select]
  1. var
  2.   SnakeSegment: PRect;

to

Code: Pascal  [Select]
  1. var
  2.   SnakeSegment: ^TRect;

PRect means ^TRect, but I guess your version does not support PRect.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 04:15:13 pm
I can compile now but when i compile it the error message comes up saying "Project 1 raised exception class 'External:SIGSEGV'.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 04:15:56 pm
also what's the difference between
Code: Pascal  [Select]
  1. Trect

and

Code: Pascal  [Select]
  1. ^Trect
Title: Re: how to delete the canvas rectangle
Post by: rvk on September 27, 2017, 04:18:08 pm
B.T.W. PRect is defined in Types.
So adding this under implementation works too:
Code: Pascal  [Select]
  1. implementation
  2. uses Types;

The exception that follows is probably because rhong7 added this
Code: Pascal  [Select]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2.   var
  3.   SnakeSegment: PRect;
  4. begin
  5.   SnakeBody := TList.Create;
  6.   // ...
  7.   SnakeSegment^:= snakepos; // Put this line after ...random(10)
  8.   SnakeBody.Add(SnakeSegment); // Put this line after SnakeSegment^ := ...
  9. // ...
  10. end;
But where are the random-lines Handoko is speaking of?

I think you are both working with different base-code. Just saying add this won't work in that case  %)

also what's the difference between
Code: Pascal  [Select]
  1. Trect
and
Code: Pascal  [Select]
  1. ^Trect
TRect is a record structure for a rectangle. With Left, Top etc.
PRect is a pointer to a TRect. It is defined in Types.

Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 04:23:49 pm
@rhong7

Of course it caused error. Because you have not fully follow my instruction. Try to read it carefully again and compare it the actual code. I know what they are  :-X

Quote
^Trect

That is pointer type variable. What is pointer? Well it is a loooong story. Try to read the documentation on the link I gave you.

Pointer is a headache topic for beginners. To proper use it, users must be very careful. A small mistake may cause the code hard to debug.

But once after you understand and able to use it properly, your programming skill will improve a lot. You can solve many new things programmatically.

@rhong7 & @rvk

Don't simply copy paste the code. You should compare it with the original code.

This 3 dots
Code: Pascal  [Select]
  1.   // ...
means the original code.

Quote
But where are the random-lines Handoko is speaking of?

The original code (TS's code) call random function.

You can get the original code here:
http://forum.lazarus.freepascal.org/index.php/topic,38136.msg259652.html#msg259652

And the on the IDE, type [CTRL]+[F] and search "random" on the code.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 04:34:43 pm
Can anyone understand what I just talking? Maybe I need to rephrase the words.

This is the original code:
Code: Pascal  [Select]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   PutItem(snakepos.left, snakepos.top, snake);
  4.   snakepos.left:=random(10);
  5.   snakepos.top:=random(10);
  6.   drawgameworld;
  7.  
  8. end;

I said "Put this line after ...random(10)", it means put it on the line between line 5 and 6. But TS remove all the code in the procedure with mine.

Hope, anyone can understand now.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 04:36:30 pm
Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, LCLType, ExtCtrls;
  9.  
  10. type
  11.  
  12.   TItem = (Emtpy, Snake, Fruit);
  13.  
  14.   { TForm1 }
  15.  
  16.   TForm1 = class(TForm)
  17.     Timer1: TTimer;
  18.     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  19.     procedure FormCreate(Sender: TObject);
  20.     procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  21.     procedure Timer1Timer(Sender: TObject);
  22.   private
  23.     procedure ClearWorld;
  24.     procedure PutItem(X, Y: Integer; Item: TItem);
  25.     function GetItem(X, Y: Integer): TItem;
  26.     procedure DrawGameWorld;
  27.     Procedure Movesnake(NewHead: TRect);
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.   snakepos:trect;
  33.   direction:integer;
  34.   SnakeBody: TList;
  35.   SnakeIsGrowing: Boolean = False;
  36.  
  37. implementation
  38. uses
  39.   types;
  40.  
  41. const
  42.   WorldWidth  = 25;
  43.   WorldHeight = 25;
  44.   Scale       = 20;
  45.  
  46. var
  47.   GameWorld: array[1..WorldWidth, 1..WorldHeight] of TItem;
  48.  
  49. {$R *.lfm}
  50.  
  51. { TForm1 }
  52.  
  53. procedure TForm1.FormCreate(Sender: TObject);
  54. var
  55.   SnakeSegment: PRect;
  56. begin
  57.   SnakeBody := TList.Create;
  58.   PutItem(snakepos.left, snakepos.top, snake);
  59.   snakepos.left:=random(10);
  60.   snakepos.top:=random(10);
  61.   SnakeSegment^ := snakepos;
  62.   SnakeBody.Add(SnakeSegment);
  63.  
  64.   drawgameworld;
  65.  
  66. end;
  67.  
  68. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  69. begin
  70.   SnakeBody.Free;
  71. end;
  72.  
  73. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  74.   );
  75. begin
  76.   PutItem(snakepos.left, snakepos.top, snake);
  77.   Drawgameworld;
  78.   if key=vk_left then
  79.   begin
  80.   direction:=1;
  81.   end;
  82.  
  83.   if key=vk_right then
  84.   begin
  85.   direction:=2;
  86.   end;
  87.  
  88.   if key=vk_up then
  89.   begin
  90.   direction:=3;
  91.   end;
  92.  
  93.   if key=vk_down then
  94.   begin
  95.   direction:=4;
  96.   end;
  97.  
  98.      if key=VK_ADD then
  99.   begin
  100.     SnakeIsGrowing := True;
  101.   end;
  102.  
  103. end;
  104.  
  105. procedure TForm1.Timer1Timer(Sender: TObject);
  106. begin
  107.  
  108.   MoveSnake(snakepos);
  109.  
  110.   if direction = 1 then
  111.   begin
  112.   snakepos.left:=snakepos.left-1;
  113.   end;
  114.  
  115.   if direction = 2 then
  116.   begin
  117.   snakepos.left:=snakepos.left+1;
  118.   end;
  119.  
  120.   if direction = 3 then
  121.   begin
  122.   snakepos.top:=snakepos.top-1;
  123.   end;
  124.  
  125.   if direction = 4 then
  126.   begin
  127.   snakepos.top:=snakepos.top+1;
  128.   end;
  129.  
  130.  
  131.  
  132. end;
  133.  
  134. procedure TForm1.ClearWorld;
  135. var
  136.   X, Y: Integer;
  137. begin
  138.   for X := 1 to WorldWidth do
  139.     for Y := 1 to WorldHeight do
  140.       GameWorld[X, Y] := Emtpy;
  141. end;
  142.  
  143. procedure TForm1.PutItem(X, Y: Integer; Item: TItem);
  144. begin
  145.   if (X < 1) or (X > WorldWidth) then Exit;
  146.   if (Y < 1) or (Y > WorldHeight) then Exit;
  147.   GameWorld[X, Y] := Item;
  148. end;
  149.  
  150. function TForm1.GetItem(X, Y: Integer): TItem;
  151. begin
  152.   Result := Emtpy;
  153.   if (X < 1) or (X > WorldWidth) then Exit;
  154.   if (Y < 1) or (Y > WorldHeight) then Exit;
  155.   Result := GameWorld[X, Y];
  156. end;
  157.  
  158. procedure TForm1.DrawGameWorld;
  159. const
  160.   Padding = 2;
  161. var
  162.   X, Y: Integer;
  163.   ScreenX, ScreenY: Integer;
  164. begin
  165.   Refresh;
  166.   for X := 1 to WorldWidth do
  167.     for Y := 1 to WorldHeight do
  168.       begin
  169.         ScreenX := X * Scale;
  170.         ScreenY := Y * Scale;
  171.         case GameWorld[X, Y] of
  172.           Emtpy: ; // do nothing
  173.           Snake: begin
  174.                    Canvas.Pen.Color := clBlue;
  175.                    Canvas.Brush.Color := clwhite;
  176.                    Canvas.Rectangle(ScreenX, ScreenY, ScreenX+Scale-Padding, ScreenY+Scale-Padding);
  177.                  end;
  178.           Fruit: begin
  179.                    Canvas.Pen.Color := clBlack;
  180.                    Canvas.Brush.Color := clRed;
  181.                    Canvas.Ellipse(ScreenX, ScreenY, ScreenX+Scale-Padding, ScreenY+Scale-Padding);
  182.                  end;
  183.           end;
  184.       end;
  185. end;
  186.  
  187. procedure TForm1.MoveSnake(NewHead: TRect);
  188. var
  189.   SnakeSegment: PRect;
  190. begin
  191.   New(SnakeSegment);
  192.   SnakeSegment^ := NewHead;
  193.   SnakeBody.Insert(0, SnakeSegment);
  194.   if not(SnakeIsGrowing) then begin
  195.     SnakeSegment := SnakeBody[SnakeBody.Count-1];
  196.     PutItem(SnakeSegment^.Left, SnakeSegment^.Top, Emtpy);
  197.     Freemem(SnakeSegment);
  198.     SnakeBody.Delete(SnakeBody.Count-1);
  199.   end;
  200.   SnakeIsGrowing := False;
  201. end;
  202.  
  203. end.
  204.                                            
'
hi i still get the same error message when i compile it
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 04:40:36 pm
Okay it was my fault, I forgot to mention New(SnakeSegment), this is the correct version:

Code: Pascal  [Select]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   SnakeSegment: PRect;
  4. begin
  5.   SnakeBody := TList.Create;
  6.   PutItem(snakepos.left, snakepos.top, snake);
  7.   snakepos.left:=random(10);
  8.   snakepos.top:=random(10);
  9.   New(SnakeSegment);
  10.   SnakeSegment^ := snakepos;
  11.   SnakeBody.Add(SnakeSegment);
  12.   drawgameworld;
  13. end;
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 04:45:12 pm
i can now compile but the snake is not moving and when try to move it, it disappears. do you know why it doesn't work?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 04:51:07 pm
I knew, I am testing it. Mine is working correctly, I now trying to check where you did wrong. Give me some time.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 04:57:09 pm
Okay thank you :) :)
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 05:00:03 pm
I told you to remove PutItem(snakepos.left, snakepos.top, snake) on the FormKeyDown, but you removed the one in Timer1Timer.

To fix it:

1. Goto FormKeyDown
2. Remove or comment the PutItem
3. Goto Timer1Timer
4. Put this 2 lines at the beginning of Timer1Timer:
Code: Pascal  [Select]
  1.   PutItem(snakepos.left, snakepos.top, snake);
  2.   Drawgameworld;

Also, move the line MoveSnake(snakepos); to the end of the procedure (in Timer1Timer).

Tested on my computer, it works.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 27, 2017, 05:18:47 pm
wow! it works so well thank you very much :) i have a question
Code: Pascal  [Select]
  1.  TItem = (Emtpy, Snake, Fruit);

what does that do?

and
Code: Pascal  [Select]
  1. var
  2.   Form1: TForm1;
  3.   snakepos:trect;
  4.   direction:integer;
  5.   SnakeBody: TList;
  6.   SnakeIsGrowing: Boolean = False;
  7.  
  8. implementation
  9. uses
  10.   types;
  11.  
  12. const
  13.   WorldWidth  = 25;
  14.   WorldHeight = 25;
  15.   Scale       = 20;
  16.  
  17. var
  18.   GameWorld: array[1..WorldWidth, 1..WorldHeight] of TItem;
  19.  
  20. {$R *.lfm}
  21.  
  22. { TForm1 }                                                    

why are there two different var?
can't you put it together?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on September 27, 2017, 05:39:00 pm
Glad to hear it works.

That bugs happened because you're not careful, or maybe my instruction wasn't clear enough.

Code: Pascal  [Select]
  1.  TItem = (Emtpy, Snake, Fruit);
what does that do?

Snake is to mark the GameWorld that a body segment is occupying the location. Emtpy (I typed it wrong, it should be Empty), is to mark the GameWorld that the location has nothing there.

You can see in TForm1.MoveSnake, it calls PutItem(..., ..., Emtpy) to remove the snake's tail from the GameWorld.

Fruit, or maybe you can rename it with Mouse (because snake doesn't eat fruit) is to mark that the location has food. It is for future use. You have to write some code if the snake's new head location has fruit/mouse then it grow in length by simply setting SnakeIsGrowing := True.

why are there two different var?
can't you put it together?

Yes, you can. Usually there are no different. But if you're writing module that call by others unit, the variables that being put above (the text:) implementation are accessible by other units. The variables that put below the implementation are not accessible by other units.

Good habit is, make all variables private. Only make it accessible by others (put it above implementation) when you really need it.
Title: Re: how to delete the canvas rectangle
Post by: shs on September 28, 2017, 01:51:21 am
Okay thank you for clear explanation

can you explain the procedure movesnake more in detail please?

Code: Pascal  [Select]
  1.  SnakeSegment: PRect;
  2. begin
  3.   New(SnakeSegment);
  4.   SnakeSegment^ := NewHead;
  5.   SnakeBody.Insert(0, SnakeSegment);
  6.   if not(SnakeIsGrowing) then begin
  7.     SnakeSegment := SnakeBody[SnakeBody.Count-1];
  8.     PutItem(SnakeSegment^.Left, SnakeSegment^.Top, Emtpy);
  9.     Freemem(SnakeSegment);
  10.     SnakeBody.Delete(SnakeBody.Count-1);
  11.   end;
  12.   SnakeIsGrowing := False;  

i still don't get how the tails follows the head.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 01, 2017, 05:43:43 am
I like Sunday, I can have more time doing the thing I like. Have you able to understand that code? Do you still need my explanation? If yes, then I will need some time preparing it.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 01, 2017, 08:32:47 am
I like Sunday, I can have more time doing the thing I like. Have you able to understand that code? Do you still need my explanation? If yes, then I will need some time preparing it.


Yes please i still need explanation but you can take your time
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 01, 2017, 01:59:58 pm
The difference between snake body grow longer and snake just moving without growing is the removal of the tail (last segment) only. That's why we introduce a global variable: SnakeIsGrowing to indicate the need to disable the tail removal.

The pseudocode of MoveSnake procedure is like this:

procedure MoveSnake(NewHead)
begin
    Request memory for NewSegment
    Set NewSegment to contain NewHead data   
    Add the NewSegment into SnakeBody at the first position
    If not(SnakeIsGrowing) then begin
        Remove LastSegment from SnakeBody
    end
    Disable snake growing
end

To understand how to transform the pseudocode into real Pascal code, you need to understand several things.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 01, 2017, 02:00:07 pm
1. The Variable Declarations

TList basically works like array but with some differences. We can declare array of Integer, Byte, TRect, etc. But TList always uses Pointer and if we want use pointer to store data we have to request memory first before using it.

Pointer doesn't store the 'actual' data, it points to the location of data. That's why it calls pointer. You can think a pointer is like a shortcut or link in your computer.

If you use Integer (in an array), the variant for its pointer type is ^Integer (for use in TList). If it is Byte then the pointer type of it is ^Byte.

array of Integer  --->  ^Integer
array of Byte     --->  ^Byte
array of TRect    --->  ^TRect (or PRect)


To use TList we need to do the declaration twice: one for the list itself and one for the items. In the snake program, it should be:

Code: Pascal  [Select]
  1. var
  2.   SnakeBody: TList;
  3.  
  4. var
  5.   SnakeSegment: PRect; // or ^TRect

Because SnakeBody need to access all the time when the snake program is running, it must be declared on the global scope. But we only access SnakeSegment when needed so we can declare it on local scope.

So now we can transform part of the psedocode to Pascal code (brown is psedocode):

procedure MoveSnake(NewHead: TRect);
var
    SnakeSegment: PRect;

begin
    Request memory for NewSegment
    Set NewSegment to contain NewHead data   
    Add the NewSegment into SnakeBody at the first position
    If not(SnakeIsGrowing) then begin
        Remove LastSegment from SnakeBody
    end
    Disable snake growing
end;


2. Adding New Item into The List

Usually we insert a new item into a list using this steps:
1. Request memory for the item
2. Set the item to contain the real data
3. Add the item into the list


We use New to request memory. Read more about New:
https://www.freepascal.org/docs-html/rtl/system/new.html

In pointers, we use this symbol: "^" to set the data. So in your snake game it will be:
SnakeSegment^ := NewHead;

We now will insert the SnakeSegment into the list by calling Insert. Read more about Insert:
https://www.freepascal.org/docs-html/rtl/classes/tlist.insert.html

Because we want the NewHead (SnakeSegment) tobe inserted at the beginning of the list and TList uses zero-based-index, the code will be:
SnakeBody.Insert(0, SnakeSegment);

So now the psedocode becomes:

procedure MoveSnake(NewHead: TRect);
var
  SnakeSegment: PRect;
begin
  New(SnakeSegment);
  SnakeSegment^ := NewHead;   
  SnakeBody.Insert(0, SnakeSegment);

  If not(SnakeIsGrowing) then begin
    Remove LastSegment from SnakeBody
  end
  Disable snake growing
end;


3. Check If The Snake Need Tail Removal

It is easy, just use an if-then-begin and an end. Because we use SnakeIsGrowing variable to indicate that tail removal is not needed, so the code will be:

if not(SnakeIsGrowing) then begin
...
end;

4. Removing The Tail

First you need to get the index of the last segment. Because TList uses zero-based-index, the length of the snake needs to subtract with 1. We use TList.Count to get the total number of the items in the list.
Read more: https://www.freepascal.org/docs-html/rtl/classes/tlist.count.html

So the index of the tail will be:
SnakeBody.Count-1

We now need to point the pointer to the tail before we can progress further. To retrieve the pointer in a list we can use TList[index]. So the code become:
SnakeSegment := SnakeBody[SnakeBody.Count-1];

Now the SnakeSegment is pointing to the tail. But before we remove it, you should remember to remove it first from the GameWorld. Remember the GameWorld we talk previously, it can contain Snake, Fruit or Empty. We need to call PutItem(x, y, Empty);

Because SnakeSegment is a pointer, to get the data we need to use this symbol again: "^". So the code becomes:
PutItem(SnakeSegment^.Left, SnakeSegment^.Top, Emtpy)
Note: it uses emtpy because I mistyped it earlier.

After removing from the GameWorld now we remove it from the list. Do you remember my previous explanation about New? Because we requested memory for SnakeSegment, before we remove it from TList we have to free the memory. We use Freemem to free the requested memory of the pointer.
Read more: https://www.freepascal.org/docs-html/rtl/system/freemem.html
The code is simple:
Freemem(SnakeSegment);

And then we continue to remove it from the list using Delete.
Read more: https://www.freepascal.org/docs-html/rtl/classes/tlist.delete.html
Because it is zero-based-index, the code will be:
SnakeBody.Delete(SnakeBody.Count-1);

Here is the pseudocode transformation to real code:

procedure TForm1.MoveSnake(NewHead: TRect);
var
  SnakeSegment: PRect;
begin
  New(SnakeSegment);
  SnakeSegment^ := NewHead;
  SnakeBody.Insert(0, SnakeSegment);
  if not(SnakeIsGrowing) then begin
    SnakeSegment := SnakeBody[SnakeBody.Count-1];
    PutItem(SnakeSegment^.Left, SnakeSegment^.Top, Emtpy);
    Freemem(SnakeSegment);
    SnakeBody.Delete(SnakeBody.Count-1);

  end;
  Disable snake growing
end;


5. Make Sure The Snake Stop Growing

When growing, the snake will only grow 1 segment and stop growing. We need to make sure it won't grow continuously by adding a single line at the end of the procedure:
SnakeIsGrowing := False;

Finally, the code now becomes:

Code: Pascal  [Select]
  1. procedure TForm1.MoveSnake(NewHead: TRect);
  2. var
  3.   SnakeSegment: PRect;
  4. begin
  5.   New(SnakeSegment);
  6.   SnakeSegment^ := NewHead;
  7.   SnakeBody.Insert(0, SnakeSegment);
  8.   if not(SnakeIsGrowing) then begin
  9.     SnakeSegment := SnakeBody[SnakeBody.Count-1];
  10.     PutItem(SnakeSegment^.Left, SnakeSegment^.Top, Emtpy);
  11.     Freemem(SnakeSegment);
  12.     SnakeBody.Delete(SnakeBody.Count-1);
  13.   end;
  14.   SnakeIsGrowing := False;
  15. end;
Title: Re: how to delete the canvas rectangle
Post by: shs on October 02, 2017, 12:26:37 am
Wow thank you clear explanation again i totally understand everything now.
I have one more question though
Code: Pascal  [Select]
  1. Because we want the NewHead (SnakeSegment) tobe inserted at the beginning of the list and TList uses zero-based-index, the code will be:
  2. SnakeBody.Insert(0, SnakeSegment);

so does that mean the head becomes 0 in the list and the previous head becomes 1?
is that same as
Code: Pascal  [Select]
  1. snake[1]:=snake[0];
  2. snake[0]:=newhead;
  3.  

also i added the fruit and i made the snake the grow when it eats the fruit but when you play it, suddenly the the fruit doesn't show up on the screen and snake tail goes missing somtimes and come back.
do i have to do the bringtofront or something?

please find the attachment
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 02, 2017, 05:43:14 am
I forgot an important thing.

Quote
Don't do any screen drawing on TForm.OnCreate event.

TForm.OnCreate is processed before the canvas of the form has been prepared. Putting drawing code in OnCreate event will has unexpected result. It sometimes may work and sometimes it won't.

So, remove (or comment) the DrawGameWorld; in the OnCreate event, it is on line #72.

Code: Pascal  [Select]
  1. Because we want the NewHead (SnakeSegment) tobe inserted at the beginning of the list and TList uses zero-based-index, the code will be:
  2. SnakeBody.Insert(0, SnakeSegment);

so does that mean the head becomes 0 in the list and the previous head becomes 1?

Yes, you're right. You can use array to store data instead of using TList, but you need to manually shift the items' positions.

TList.Add always add the new item at the last position.
TList.Insert allows you to insert the new item at any position you want.
TList.Insert(0, ..., ...) means add the new item at the beginning position.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 02, 2017, 06:09:49 am
Thank you so much :)

 can you check my file please?

i added the highscore but when i open the highscore board the highscore does not show up.

Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 02, 2017, 01:20:38 pm
I noticed in your AddRank procedure, you have not set value for info:

Code: Pascal  [Select]
  1. ...
  2.   rank[x].name:=namee;
  3.   rank[x].score:=score;
  4.   reset(highscorefile);
  5.   seek(highscorefile, filesize(highscorefile));
  6.   write(highscorefile, info);
  7.   closefile(highscorefile);
  8. ...
Title: Re: how to delete the canvas rectangle
Post by: rvk on October 02, 2017, 01:32:04 pm
And like I already explained in the other topic... if you are going to read the 10 ranks in FormCreate and write it on FormClose, you DON'T need to write it during the running of the program.

Just remove ALL the reading and writing of the highscorefile from Unit1.pas (you are already doing it in Unit2.pas).
Really... just remove every line containing highscorefile from Unit1.pas.

But you add the score in the rank-array but I never see you putting the rank-array to your form2-screen again after calling AddRank(). So you might want create a separate procedure (DisplayRank) in Form2 and put the current rank-array on screen with those FindComponent('label'... lines. Call it at the end of AddRank() in Form1.

B.T.W. What are you doing in Button1Click below the AddRank call? You've already added Edit1.Text to the rank in AddRank. So what are you doing after that call?
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 08:51:08 am
Hello thank you it works now :))

i have 2 more questions

how should i collison detection like when the snake head hits its own tail and sometimes the fruit appears on the position where the tail is, how can i deal with that?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 09:57:20 am
Have you forget the GetItem?
Use GetItem(x, y) = snake the check if that location is occupied by snake body.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 10:13:26 am
oh so how should i use that to find all the snake body location?
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 10:20:31 am
Code: Pascal  [Select]
  1.  getitem(snakesegment^.left, snakesegment^.top):=snake;      

it says unit1.pas(515,4) Error: Argument can't be assigned to
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 10:40:47 am
GetItem is a function, you can't set value for a function.

Here is example code for put new fruit:

Code: Pascal  [Select]
  1.   repeat
  2.     FoodPos.Left := Random(WorldWidth+1);
  3.     FoodPos.Top  := Random(WorldHeight+1);
  4.   until (GetItem(FoodPos.Left, FoodPos.Top) = Empty);
  5.   PutItem(Foodpos.Left, FoodPos.Top, Fruit);

I haven't test the code above, but it should work.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 10:47:21 am
yes it works really well :)

how should i do collision?
Code: Pascal  [Select]
  1.      if getitem(snakesegment^.left, snakesegment^.top)= snake then
  2.       begin
  3.        timer1.enabled:=false;
  4.       end;    

i tired this but does not work
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 10:53:18 am
You do not have to check the SnakeBody. You can simple check the GameWorld, which is simpler and easier.

function IsSnakeThere(X, Y: Integer): Boolean;
begin
  result := (GetItem(X, Y) = Snake);
end;
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 11:10:54 am
You do not have to check the SnakeBody. You can simple check the GameWorld, which is simpler and easier.

function IsSnakeThere(X, Y: Integer): Boolean;
begin
  result := (GetItem(X, Y) = Snake);
end;

but how can i check if the head hits its own tail?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 11:33:09 am
Why do you need to check if it is head or tail? But if you want, here the code:

Code: Pascal  [Select]
  1. IsSnakeHeadThere(X, Y: Integer): Boolean;
  2. var
  3.   SnakeSegment: PRect;
  4. begin
  5.   result := False;
  6.   if (SnakeBody.Count <= 0) then Exit;
  7.   SnakeSegment := SnakeBody[0];
  8.   if (SnakeBody^.Top = Y) and (SnakeBody^.Left = X) then result := True;
  9. end;
  10.  
  11. IsSnakeTailThere(X, Y: Integer): Boolean;
  12. var
  13.   SnakeSegment: PRect;
  14. begin
  15.   result := False;
  16.   if (SnakeBody.Count <= 1) then Exit; // if only 1 segment, then there is no tail
  17.   SnakeSegment := SnakeBody[SnakeBody.Count-1];
  18.   if (SnakeBody^.Top = Y) and (SnakeBody^.Left = X) then result := True;
  19. end;

Again, I haven't tested the code above but I believe it should work.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 11:39:31 am
so if the head hits its own tail it's supposed to die right? do i need to check if it is head or tail? can i do it in other way or?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 11:43:25 am
You can simply add some code at the beginning of Timer1Timer:

Code: Pascal  [Select]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. var
  3.   i:integer;
  4. begin
  5.   snakepos.left := snakepos.left + xdirection;
  6.   snakepos.top  := snakepos.top  + ydirection;
  7.   if (GetItem(snakepos.left, snakepos.top) = Snake) then // the snake hits its own body
  8.   begin
  9.     // do something here
  10.   end;
  11.   ...
  12.  
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 11:56:03 am
Code: Pascal  [Select]
  1.  if (GetItem(snakepos.left, snakepos.top) = Snake) then // the snake hits its own body
  2.   begin
  3.     timer1.enabled:=false;
  4.   end;

i did this and the timer stopped as soon i compiled it. i think it's because the snake head is part of 'Snake' item so maybe that's why it doesn't work

how should i do?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 11:58:23 am
Okay, I know what is wrong. Please provide your complete code. I will check where is wrong. And give me some time.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 12:05:54 pm
Code: Pascal  [Select]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   SnakeSegment: PRect;
  4. begin
  5.   SnakeBody := TList.Create;
  6.   snakepos.left:=random(24);
  7.   snakepos.top:=random(24);
  8.  
  9.   foodpos.left:=random(24);
  10.   foodpos.top:=random(24);
  11.  
  12.   New(SnakeSegment);
  13.   SnakeSegment^ := snakepos;
  14.   SnakeBody.Add(SnakeSegment);
  15.  
  16.   drawgameworld;
  17.  
  18.   edit1.clear;
  19.  
  20. end;
  21.  
  22. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  23. begin
  24.   SnakeBody.Free;
  25.  
  26. end;
  27.  
  28.  
  29.  
  30. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  31.   );
  32. begin
  33.  
  34.   if score >= 1 then
  35.     begin
  36.       case direction of
  37.         1,3,4: if key=vk_left then
  38.            begin
  39.            direction:=1;
  40.            end;
  41.         2: if key=vk_left then
  42.            begin
  43.            direction:=2;
  44.            end;
  45.       end;
  46.  
  47.       case direction of
  48.         2,3,4: if key=vk_right then
  49.            begin
  50.            direction:=2;
  51.            end;
  52.         1: if key=vk_right then
  53.            begin
  54.            direction:=1;
  55.            end;
  56.       end;
  57.  
  58.       case direction of
  59.         1,2,3: if key=vk_up then
  60.            begin
  61.            direction:=3;
  62.            end;
  63.         4: if key=vk_up then
  64.            begin
  65.            direction:=4;
  66.            end;
  67.       end;
  68.  
  69.       case direction of
  70.         1,2,4: if key=vk_down then
  71.            begin
  72.            direction:=4;
  73.            end;
  74.         3: if key=vk_down then
  75.            begin
  76.            direction:=3;
  77.            end;
  78.     end;
  79.   end;
  80.  
  81.   if score = 0 then
  82.     begin
  83.       if key=vk_left then
  84.         begin
  85.          direction:=1;
  86.         end;
  87.  
  88.       if key=vk_right then
  89.         begin
  90.          direction:=2;
  91.         end;
  92.  
  93.       if key=vk_up then
  94.         begin
  95.          direction:=3;
  96.         end;
  97.  
  98.       if key=vk_down then
  99.         begin
  100.          direction:=4;
  101.         end;
  102.   end;
  103. end;
  104.  
  105. procedure TForm1.Timer1Timer(Sender: TObject);
  106. var
  107.   i:integer;
  108. begin
  109.  
  110.   case direction of
  111.     1:  begin
  112.           xdirection:=-1;
  113.           ydirection:=0;
  114.         end;
  115.     2:  begin
  116.           xdirection:=1;
  117.           ydirection:=0;
  118.         end;
  119.     3:  begin
  120.           xdirection:=0;
  121.           ydirection:=-1;
  122.         end;
  123.     4:  begin
  124.           xdirection:=0;
  125.           ydirection:=1;
  126.         end;
  127.   end;
  128.  
  129.  
  130.    if (snakepos.left = foodpos.left) and (snakepos.top = foodpos.top)  then
  131.      begin
  132.        foodpos.left:=random(22)+1;
  133.        foodpos.top:=random(22)+1;
  134.        PutItem(foodpos.left, foodpos.top, fruit);
  135.  
  136.        snakeisgrowing:=true;
  137.        score:=score+1;
  138.        count.caption:=inttostr(score);
  139.      end;
  140.  
  141.    if (snakepos.left > 23) or (snakepos.left < 1) or (snakepos.top > 23) or (snakepos.top < 1)  then
  142.      begin
  143.       for i:= 3 to 5 do
  144.         begin
  145.          (FindComponent('l' + IntToStr(i)) as TLabel).visible:=true;
  146.         end;
  147.         timer1.enabled:=false;
  148.         edit1.Visible:=true;
  149.         button1.Visible:=true;
  150.         l4.caption:= count.caption;
  151.         count.visible:=false;
  152.         clearworld;
  153.         putitem(foodpos.left, foodpos.right, emtpy);
  154.      end;
  155.  
  156.    snakepos.left:=snakepos.left+xdirection;
  157.    snakepos.top:=snakepos.top+ydirection;
  158.    MoveSnake(snakepos);
  159. end;
  160.  
  161. procedure TForm1.ClearWorld;
  162. var
  163.   X, Y: Integer;
  164. begin
  165.   for X := 1 to WorldWidth do
  166.     for Y := 1 to WorldHeight do
  167.       GameWorld[X, Y] := Emtpy;
  168. end;
  169.  
  170. procedure TForm1.PutItem(X, Y: Integer; Item: TItem);
  171. begin
  172.   if (X < 1) or (X > WorldWidth) then Exit;
  173.   if (Y < 1) or (Y > WorldHeight) then Exit;
  174.   GameWorld[X, Y] := Item;
  175. end;
  176.  
  177. function TForm1.GetItem(X, Y: Integer): TItem;
  178. begin
  179.   Result := Emtpy;
  180.   if (X < 1) or (X > WorldWidth) then Exit;
  181.   if (Y < 1) or (Y > WorldHeight) then Exit;
  182.   Result := GameWorld[X, Y];
  183. end;
  184.  
  185. procedure TForm1.DrawGameWorld;
  186. const
  187.   Padding = 0;
  188. var
  189.   X, Y: Integer;
  190.   ScreenX, ScreenY: Integer;
  191. begin
  192.   Refresh;
  193.   for X := 1 to WorldWidth do
  194.     for Y := 1 to WorldHeight do
  195.       begin
  196.         ScreenX := X * Scale;
  197.         ScreenY := Y * Scale;
  198.         case GameWorld[X, Y] of
  199.           Emtpy: ; // do nothing
  200.           Snake: begin
  201.                    Canvas.Pen.Color := shape1.brush.color;
  202.                    Canvas.Brush.Color := clwhite;
  203.                    Canvas.Rectangle(ScreenX, ScreenY, ScreenX+Scale, ScreenY+Scale);
  204.                  end;
  205.           Fruit: begin
  206.                    Canvas.Pen.Color := shape1.brush.color;
  207.                    Canvas.Brush.Color := clRed;
  208.                    Canvas.rectangle(ScreenX+padding, ScreenY+padding, ScreenX+Scale-padding, ScreenY+Scale-padding);
  209.                  end;
  210.           end;
  211.       end;
  212. end;
  213.  
  214. procedure TForm1.MoveSnake(NewHead: TRect);
  215. var
  216.   SnakeSegment: PRect;
  217. begin
  218.   New(SnakeSegment);
  219.   SnakeSegment^ := NewHead;
  220.   SnakeBody.Insert(0, SnakeSegment);
  221.   if not(SnakeIsGrowing) then
  222.    begin
  223.     SnakeSegment := SnakeBody[SnakeBody.Count-1];
  224.     PutItem(SnakeSegment^.Left, SnakeSegment^.Top, Emtpy);
  225.     Freemem(SnakeSegment);
  226.     SnakeBody.Delete(SnakeBody.Count-1);
  227.    end;
  228.   SnakeIsGrowing := False;
  229.   PutItem(snakepos.left, snakepos.top, snake);
  230.   PutItem(foodpos.left, foodpos.top, fruit);
  231.  
  232.   if (snakeSegment^.left = snakepos.left) and snakesegment^.top = snakepos.top) then
  233.    begin
  234.     timer1.enabed:=true;
  235.    end;
  236.  
  237.   Drawgameworld;
  238. end;
  239.  
  240. end.
  241.                  
Title: Re: how to delete the canvas rectangle
Post by: bee on October 03, 2017, 12:18:38 pm
@Handoko… well done, bro. You're so patient and helpful. Respect!

And, you should blog all these snake game tutorial. Many newbies could learn from it. :)
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 01:00:04 pm
@bee

I am afraid I do not have time to do blogging. But I am preparing a short game creation tutorial - The Furious Paladin - the game I submitted for Graphics Contest 2017. Newbies can learn many interesting tricks from the tutorial.

@shs

You have not provided the complete code, I can compile it. Please provide them as the zip file.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 01:38:39 pm
Okay, you need a variable to indicate the game have just started but the snake is not moving yet. Reading your code I found that you can use "direction = 0" for this purpose.

So in your Timer1Timer event put this code before MoveSnake(snakepos):
Code: Pascal  [Select]
  1. if (GetItem(snakepos.left, snakepos.top) = Snake) and not(Direction = 0) then
  2.   begin
  3.     timer1.enabled:=false;
  4.   end;
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 01:49:50 pm
it shows up on the screen now but when i press key (when i change direction ), the timer1 stops. so i think
it's not becasue of the direction
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 01:53:45 pm
Really? It works correctly on my computer. Try to replace your Timer1.Timer with this one:

Code: Pascal  [Select]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. var
  3.   i:integer;
  4. begin
  5.   case direction of
  6.     1:  begin
  7.           xdirection:=-1;
  8.           ydirection:=0;
  9.         end;
  10.     2:  begin
  11.           xdirection:=1;
  12.           ydirection:=0;
  13.         end;
  14.     3:  begin
  15.           xdirection:=0;
  16.           ydirection:=-1;
  17.         end;
  18.     4:  begin
  19.           xdirection:=0;
  20.           ydirection:=1;
  21.         end;
  22.   end;
  23.  
  24.  if (snakepos.left = foodpos.left) and (snakepos.top = foodpos.top)  then
  25.    begin
  26.  
  27.     repeat
  28.     FoodPos.Left := Random(22)+1;
  29.     FoodPos.Top  := Random(22)+1;
  30.     until (GetItem(FoodPos.Left, FoodPos.Top) = Emtpy);
  31.      growing:=true;
  32.      score:=score+4;
  33.      count.caption:=inttostr(score);
  34.  
  35.      drawgameworld;
  36.    end;
  37.  
  38.  if (snakepos.left > 23) or (snakepos.left < 1) or (snakepos.top > 23) or (snakepos.top < 1)  then
  39.    begin
  40.     for i:= 3 to 5 do
  41.       begin
  42.        (FindComponent('l' + IntToStr(i)) as TLabel).visible:=true;
  43.       end;
  44.       timer1.enabled:=false;
  45.       edit1.Visible:=true;
  46.       button1.Visible:=true;
  47.       l4.caption:= count.caption;
  48.       count.visible:=false;
  49.       clearworld;
  50.       putitem(foodpos.left, foodpos.right, emtpy);
  51.    end;
  52.  
  53.    snakepos.left:=snakepos.left+xdirection;
  54.    snakepos.top:=snakepos.top+ydirection;
  55.    if (GetItem(snakepos.left, snakepos.top) = Snake) and not(Direction = 0) then
  56.    begin
  57.      timer1.enabled:=false;
  58.    end;
  59.    MoveSnake(snakepos);
  60. end;
Title: Re: how to delete the canvas rectangle
Post by: Thaddy on October 03, 2017, 02:00:51 pm
Could not help this one. Properly deletes a canvas...:
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 02:03:07 pm
Is it your own monitor?
Title: Re: how to delete the canvas rectangle
Post by: Thaddy on October 03, 2017, 02:06:50 pm
 O:-) Canvas.free Set the canvas free... :P
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 02:10:24 pm
@handoko

thank you it works :)
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 02:12:58 pm
@Thaddy

Not just simply freeing the canvas but FreeAndNil(Canvas);
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 02:29:47 pm
@shs

Congratulation, you've managed to create your first game using Lazarus. It is cross platform, it works on my Linux too! ;)


But here one thing I should told you. Do you remember I said you need to free the memory of the snake's tail? So in the form closing you should free each memory of the body segments. Here I provide you the pseudocode:

For each item in SnakeBody do begin
   Set SnakeSegment to point to the memory location
   Free the SnakeSegment
end
Title: Re: how to delete the canvas rectangle
Post by: Thaddy on October 03, 2017, 02:57:44 pm
@Thaddy

Not just simply freeing the canvas but FreeAndNil(Canvas);
https://community.embarcadero.com/blogs/entry/a-case-when-freeandnil-is-your-enemy-38916

If you NEED FreeAndNil there is usually something wrong with either you skills or the architecture....It belongs to "bordercase" programmers or bordercase - as in seldom needed - code.
I respect your skills, so it must be the other  ::) :o case?

Pascal is too easy in this case: a C or C++ programmer would got bitten... That's why they are not likely to make such mistakes...( they crashed too many times..)

FreeAndNil is for experienced programmers. Beginners should just free... It's like driving a Tesla in autodrive and not realizing you will cause an accident.
Or a malfunctioning parachute: guy hanging on it thinks everything ok, still flying....
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 03, 2017, 03:25:06 pm
I was kidding, no need to be serious.  :D

I know the different between Free and FreeAndNil. I know for sure that some cases Free only is not enough, must use FreeAndNil.
Title: Re: how to delete the canvas rectangle
Post by: Mick on October 03, 2017, 04:56:41 pm
https://community.embarcadero.com/blogs/entry/a-case-when-freeandnil-is-your-enemy-38916
Hmm, Thaddy, but did you read carefully that blog post you are referring to?
Quote from the blog post: "You rely on the fact that it is nil to know that you need to allocate it. This may seem like the perfect case where you should use FreeAndNil! That is in-fact the very problem. There are cases where you should FreeAndNil in this scenario."

If you NEED FreeAndNil there is usually something wrong with either you skills or the architecture....It belongs to "bordercase" programmers or bordercase - as in seldom needed - code.
With all due respect, but this is a bold statement, imho too generalized. Then what should you do in order to indicate that the variable or property that you manage does not store the object instance anymore because the instance was already destroyed? Set to nil. So what's the difference and where is the evil of FreeAndNil in this case?

FreeAndNil is for experienced programmers. Beginners should just free...
But the beginners (and I believe the experienced ones as well) should also set the variable that stored the reference to the destroyed instance to nil.
So they can use FreeAndNil, it's designed for that purpose, isn't it?
Title: Re: how to delete the canvas rectangle
Post by: shs on October 03, 2017, 11:35:28 pm
isn't it this?
Code: Pascal  [Select]
  1. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  2. begin
  3.   SnakeBody.Free;
  4.  
  5. end;      
Title: Re: how to delete the canvas rectangle
Post by: Mick on October 04, 2017, 02:02:37 am
It is.

In my post I was only trying to tell that Thaddy's criticism for FreeAndNil went too far (imho).

In this particular case you are probably perfectly fine with using Free only. That's because you do it in the FormClose event handler, which I believe is the main form of your application. So after this handler will be executed and the destructor called on the instance referenced by the SnakeBody variable / property, you will probably never try to reference the SnakeBody variable / property again, because application will be terminated. So it doesn't really matter if the property still stores the reference of already destroyed instance.

However, if you would add FormDestroy handler (which is executed later in the TForm class lifetime), any attempt of referencing the SnakeBody there would give you access violations, obviously. You could try to prevent from that by checking if SnakeBody is nil (to mark that this variable / property is "invalid"). But you did not set the SnakeBody to nil after freeing it, either by simple assignment of nil to your SnakeBody, or calling FreeAndNil (which does exactly as the name suggest, fist Free, then assing nil - that's why it takes the parameter as var, so it can modify it).

My conclusion - as long as you will not try to "reuse" that variable that stored the instance that was destroyed, you are perfectly fine. But if you would need to "reuse" it (e.g. check if it is still valid, make decision if you need create new one, etc) - assign nil to the variable that stored the reference to the destroyed / freed instance, either by assigning nil after calling Free, or by calling FreeAndNil.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 04, 2017, 04:33:41 am
 @shs

Code: Pascal  [Select]
  1. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  2. var
  3.   SnakeSegment: PRect;
  4.   i: Integer;
  5. begin
  6.   For i := 0 to (SnakeBody.Count - 1) do
  7.     begin
  8.       SnakeSegment := SnakeBody[i];
  9.       FreeMem(SnakeSegment);
  10.     end;
  11.   SnakeBody.Free;
  12.   ...
Title: Re: how to delete the canvas rectangle
Post by: shs on October 04, 2017, 07:52:39 am
okay thank you

is there any other of writing this code by the way?

Code: Pascal  [Select]
  1. case direction of
  2.         1,3,4: if key=vk_left then
  3.            begin
  4.            direction:=1;
  5.            end;
  6.         2: if key=vk_left then
  7.            begin
  8.            direction:=2;
  9.            end;
  10.       end;
  11.  
  12.       case direction of
  13.         2,3,4: if key=vk_right then
  14.            begin
  15.            direction:=2;
  16.            end;
  17.         1: if key=vk_right then
  18.            begin
  19.            direction:=1;
  20.            end;
  21.       end;
  22.  
  23.       case direction of
  24.         1,2,3: if key=vk_up then
  25.            begin
  26.            direction:=3;
  27.            end;
  28.         4: if key=vk_up then
  29.            begin
  30.            direction:=4;
  31.            end;
  32.       end;
  33.  
  34.       case direction of
  35.         1,2,4: if key=vk_down then
  36.            begin
  37.            direction:=4;
  38.            end;
  39.         3: if key=vk_down then
  40.            begin
  41.            direction:=3;
  42.            end;
  43.       end;              
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 04, 2017, 08:13:28 am
Try this:

Code: Pascal  [Select]
  1. case key of
  2.   vk_left:  if not(direction = 2) then direction := 1;
  3.   vk_right: if not(direction = 1) then direction := 2;
  4.   vk_up:    if not(direction = 4) then direction := 3;
  5.   vk_down:  if not(direction = 3) then direction := 4;
  6. end;
Title: Re: how to delete the canvas rectangle
Post by: taazz on October 04, 2017, 09:12:10 am
is there any other of writing this code by the way?

Code: Pascal  [Select]
  1. case direction of
  2.         1,3,4: if key=vk_left then
  3.            begin
  4.            direction:=1;
  5.            end;
  6.         2: if key=vk_left then
  7.            begin
  8.            direction:=2;
  9.            end;
  10.       end;
  11.  
  12.       case direction of
  13.         2,3,4: if key=vk_right then
  14.            begin
  15.            direction:=2;
  16.            end;
  17.         1: if key=vk_right then
  18.            begin
  19.            direction:=1;
  20.            end;
  21.       end;
  22.  
  23.       case direction of
  24.         1,2,3: if key=vk_up then
  25.            begin
  26.            direction:=3;
  27.            end;
  28.         4: if key=vk_up then
  29.            begin
  30.            direction:=4;
  31.            end;
  32.       end;
  33.  
  34.       case direction of
  35.         1,2,4: if key=vk_down then
  36.            begin
  37.            direction:=4;
  38.            end;
  39.         3: if key=vk_down then
  40.            begin
  41.            direction:=3;
  42.            end;
  43.       end;              

Code: Pascal  [Select]
  1. Function GetDirection(aKey, aDirection:integer):Integer;inline;
  2.  
  3. const  cDirection : array[VK_LEFT..VK_DOWN, 1..4] of integer = ((1,2,1,1),  //vk_left
  4.                                                                 (3,3,3,4),  //vk_up
  5.                                                                 (1,2,2,2),  //vk_right
  6.                                                                 (4,4,3,4)   //vk_down
  7.                                                                );
  8. begin
  9.   Result := cDirection[aKey,aDirection];
  10. end;
  11.  
  12.  
although I prefare Handoko's code it is easier to understand in a year or two.
Title: Re: how to delete the canvas rectangle
Post by: bytebites on October 04, 2017, 10:00:47 am
Or remove the intermediate variable direction and

Code: Pascal  [Select]
  1.  case key of
  2.    vk_left:
  3.     if xdirection=0 then begin
  4.      xdirection:=-1;
  5.      ydirection:=0;
  6.     end;
  7.  
  8. etc..
  9.            
Title: Re: how to delete the canvas rectangle
Post by: shs on October 04, 2017, 10:22:00 am
thank you guys it works really well.

but when press 2 keys really quickly it doesn't work.

for example, the snake is in direction 1 (left) but if i press key down and then key right very quickly, the direction becomes 2 (right) instead of becoming 4 (down) and then 2(right)
Title: Re: how to delete the canvas rectangle
Post by: shs on October 06, 2017, 09:43:30 am
i am trying to make the restart button when the game is finished but how can i make the snake length to be 1 again? how can i delete all the tails?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 06, 2017, 01:24:10 pm
The pseudocode should be:

For each item in SnakeBody do begin
   Set SnakeSegment to point to the memory location
   Free the SnakeSegment
end
Call SnakeBody Clear

https://www.freepascal.org/docs-html/rtl/classes/tlist.clear.html

And don't forget to call ClearWorld to clear the game's world.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 06, 2017, 01:49:21 pm
oh okay thank you!

thank you guys it works really well.

but when press 2 keys really quickly it doesn't work.

for example, the snake is in direction 1 (left) but if i press key down and then key right very quickly, the direction becomes 2 (right) instead of becoming 4 (down) and then 2(right)

can you help me with this please?
Title: Re: how to delete the canvas rectangle
Post by: bytebites on October 06, 2017, 05:54:14 pm
See previous page.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 06, 2017, 07:23:01 pm
@shs

You misunderstand how the game works. You give the snake 1 move per the time you give, in your case it is 10 moves per second (or 1 move in 1/10 second).

So no matter how quickly the player press the keyboards, s/he only can move 10 tiles per second. If there are several keys in 1/10 second, only the last one counts.

You can modify the logic of the game, but I don't think that would be a good idea. For example, if I quickly and repeatably pressing forward, then the snake will move faster than it should be. If this is REALLY what you want, simple call the Time1Timer after the keypressing event.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 03:01:27 am
yes but you know how direction changes when i press the different keys right?

Code: Pascal  [Select]
  1. case key of
  2.   vk_left:  if not(direction = 2) then direction := 1;
  3.   vk_right: if not(direction = 1) then direction := 2;
  4.   vk_up:    if not(direction = 4) then direction := 3;
  5.   vk_down:  if not(direction = 3) then direction := 4;
  6. end;

and this is the code i am using

so let's say the direction is 1 so it's going left. And if i press right it won't work because it only works when the direction is not 1. But if change directions really quickly bu pressing up key and right key, it just goes left instead of going up and left.

And i think this is because the direction is changed but the snake didn't move because i pressed so quickly

so it's  vk_left --> vk_up --> vk_right

direction 1 --> direction 3 --> direction 2

so the direction was changed without any problems but the snake just went to right from left without going up.
so is there any way i can fix this problem? if my explanation is not clear please compile the game and see what happens if you press two keys really quickly


Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 03:42:46 am
I fully understood what you're saying but you didn't understand what I said.

These are the key points:

- You use Timer as the main game loop and you set the interval = 100.
- Your FormKeyDown event only set the direction without calling update.
- You update the snake movement in the Timer event only.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 03:55:59 am
so what should i do to fix these problems?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 04:00:20 am
I've already provided you the solution, you didn't read it?
http://forum.lazarus.freepascal.org/index.php/topic,38136.msg261825.html#msg261825
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 04:04:00 am
oh i did read it but i don't get it
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 04:37:54 am
Can you do the thing below, or do you need more explanation?

... simple call the Time1Timer after the keypressing event.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 05:47:15 am
umm nope more explanations please?
can you give me an example of the code please?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 06:13:41 am
Replace your KeyDown with this:


procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  );
begin

  case key of
    vk_left:  if not(direction = 2) then direction := 1;
    vk_right: if not(direction = 1) then direction := 2;
    vk_up:    if not(direction = 4) then direction := 3;
    vk_down:  if not(direction = 3) then direction := 4;
  end;

  if score = 0 then
    begin
      if key=vk_left  then direction := 1;
      if key=vk_right then direction := 2;
      if key=vk_up    then direction := 3;
      if key=vk_down  then direction := 4;
    end;

  Timer1Timer(Sender);

end;



As I told you, it will introduce new problem:
Your snake can speed up by pressing forward. :D
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 07:40:22 am
what should i do to solve that problem?
Title: Re: how to delete the canvas rectangle
Post by: rvk on October 07, 2017, 08:30:10 am
As I told you, it will introduce new problem:
Your snake can speed up by pressing forward. :D
Maybe I'm a bit late to the game...  ;) But isn't calling Timer1Timer in FormKeyDown the wrong thing to solve this problem? (it will indeed speed up the snake)

Isn't it better to just NOT accept any direction change (key-input) until the snake has moved?

For example introduce a boolean DontChangeDirection and do this:

Code: Pascal  [Select]
  1. var
  2.   DontChangeDirection: Boolean = true;
  3.  
  4. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  5. begin
  6.   if DontChangeDirection then exit;
  7.   DontChangeDirection := true;
Code: Pascal  [Select]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. begin
  3.   DontChangeDirection := false;

That way the direction can only change when the snake has really moved position.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 09:14:01 am
Code: Pascal  [Select]
  1.   if DontChangeDirection then exit;
  2.   DontChangeDirection := true;

what does that exit do?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 09:41:44 am
https://www.freepascal.org/docs-html/rtl/system/exit.html
Exit is used to skip or ignore all the commands below and go directly to the end of the procedure or function.

@rvk
Nice trick. I use similar trick often on my codes to prevent unwanted events being triggered.

It depends on how the TS wants the game to behave. The ability to fast forward is good too if he wants the feature. Actually there are several solutions, at least I can think 2 solutions for this case:

1. Add a variable to store and use only the first key when several keys are pressed quickly on the given time (so the Timer1Timer won't use the last key but use only the one stored in the variable).

2. Create a buffer but limit its size (maybe 2 or 3 only). The movement happens only inside Timer1Timer and the direction is based on the buffer (not directly on the keypressing). If the buffer is empty then use the previous direction.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 10:06:55 am
As I told you, it will introduce new problem:
Your snake can speed up by pressing forward. :D
Maybe I'm a bit late to the game...  ;) But isn't calling Timer1Timer in FormKeyDown the wrong thing to solve this problem? (it will indeed speed up the snake)

Isn't it better to just NOT accept any direction change (key-input) until the snake has moved?

For example introduce a boolean DontChangeDirection and do this:

Code: Pascal  [Select]
  1. var
  2.   DontChangeDirection: Boolean = true;
  3.  
  4. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  5. begin
  6.   if DontChangeDirection then exit;
  7.   DontChangeDirection := true;
Code: Pascal  [Select]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. begin
  3.   DontChangeDirection := false;

That way the direction can only change when the snake has really moved position.


the direction still changes when the snake has not moved position
Title: Re: how to delete the canvas rectangle
Post by: rvk on October 07, 2017, 10:41:56 am
the direction still changes when the snake has not moved position
That is strange.
According to my changes the direction can only change when the timer has fired. And when the timer fires the snake moves. So i.e. the direction can only change when the snake moved.

I'm working with outdated code from you so I can't be sure what the problem is (you could post your complete code). But how are you determining that the direction has changed without the snake moving?

B.T.W. Do you want the first key AFTER the snake has moved to determine the direction-change (like my suggestion does) or do you want the first key before the snake even moved to be the direction-changer (regardless what is used afterwards)?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 11:01:19 am
I think there is some misunderstanding. You may need to go back to the see originial problem:
http://forum.lazarus.freepascal.org/index.php/topic,38136.msg261549.html#msg261549
Title: Re: how to delete the canvas rectangle
Post by: rvk on October 07, 2017, 11:17:11 am
for example, the snake is in direction 1 (left) but if i press key down and then key right very quickly, the direction becomes 2 (right) instead of becoming 4 (down) and then 2(right)
Yes I understand. So if the snake is going left it may not go directly right again. It needs to go up or down first (so it doesn't traverse back over itself).

That's why my suggestion to only accept a key when the snake has really moved one position should work. You already don't accept the right direction if going left. So when you press up (or down) you shouldn't accept ANY keys anymore until the snake has really moved one up or down. So only the first key determines the eventual move.

The other suggestion from Handoko would also work and that is to use the first key just before the actual move. But in that case you shouldn't change direction directly in FormKeyDown but you should save the first pressed key. And then in the timer you can do the direction change according to that saved key. In that case you would get something like this:

Code: Pascal  [Select]
  1. var
  2.   FirstPressedKey: Word = 0;
  3.  
  4. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  5. begin
  6.   if FirstKeyPressed = 0 then
  7.     FirstPressedKey := Key;
  8. end;
And you would move the "case FirstPressedKey of" with all the direction-changes to the top of Timer1Timer(). Below the case statement the FirstKeyPressed should be set to 0 again so the FormKeyDown can accept keys again.

The other suggestion from Handoko was buffering all the keys (or last few ones) which is also a way to go (but I think a bit annoying in the game, game-wise).
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 11:26:45 am
Was I misunderstand that TS said?

Quote
for example, the snake is in direction 1 (left) but if i press key down and then key right very quickly, the direction becomes 2 (right) instead of becoming 4 (down) and then 2(right)

The behavior works correctly as what TS (and me) experiencing:
1. In less than 1/10 second, press down and then right
2. The snake move right only (ignoring the down)
3. What TS wants, go down and then right.

I said it works correctly, because:
- After the keypressing event, no movement call
- The movement is processed in the Timer1Timer
- That's why I suggest him to call Timer1Timer at the end of keypressing
- The movement only follows the variable "direction"
- The direction (currently) only reads the last key in the interval time given
- That's why the "down" key is ignored.

One possible solution is to use "buffer" to store the keys pressed by user. We should limit the size, I suggested 2 or 3. Using the first-key-only is okay but I don't think that is what TS wants.
Title: Re: how to delete the canvas rectangle
Post by: rvk on October 07, 2017, 11:38:53 am
Was I misunderstand that TS said?
No, I think we all understand it correctly.
As I said:
Quote
So if the snake is going left it may not go directly right again. It needs to go up or down first (so it doesn't traverse back over itself).
But as the last code I saw from TS was, the FormKeyDown is processed more times then the timer. So if you change direction in FormKeyDown, if direction is left, you can do up and right real quick and direction is right before the timer even fires. So the snake goes right over itself.

The suggested solutions so far:

One possible solution is to use "buffer" to store the keys pressed by user. We should limit the size, I suggested 2 or 3. Using the first-key-only is okay but I don't think that is what TS wants.
No, but the LastKeyPressed method might be more intuitive than the FirstKeyPressed method.

Code: Pascal  [Select]
  1. var
  2.   LastPressedKey: Word = 0;
  3.  
  4. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  5. begin
  6.   LastPressedKey := Key;
  7. end;
  8.  
  9. procedure TForm1.Timer1Timer(Sender: TObject);
  10. begin
  11.  case LastKeyPressed of
  12. // ... etc
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 11:43:36 am
the direction still changes when the snake has not moved position
That is strange.
According to my changes the direction can only change when the timer has fired. And when the timer fires the snake moves. So i.e. the direction can only change when the snake moved.

I'm working with outdated code from you so I can't be sure what the problem is (you could post your complete code). But how are you determining that the direction has changed without the snake moving?

B.T.W. Do you want the first key AFTER the snake has moved to determine the direction-change (like my suggestion does) or do you want the first key before the snake even moved to be the direction-changer (regardless what is used afterwards)?


i know that the direction is changed without the snake moving because

Quote
so let's say the direction is 1 so it's going left. And if i press right it won't work because it only works when the direction is not 1. But if change directions really quickly bu pressing up key and right key, it just goes left instead of going up and left.

please find the attachment for the code

Title: Re: how to delete the canvas rectangle
Post by: rvk on October 07, 2017, 11:47:34 am
please find the attachment for the code
Also please read the last few post. It has multiple solutions for your problem.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 12:24:47 pm
i don't know how i should start coding for those ideas
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 12:58:22 pm
You didn't tell us which one do you choose.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 01:28:48 pm
creating buffer
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 01:29:54 pm
You picked the hardest one. :D

As usual, give me some time.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 07, 2017, 01:50:42 pm
oh thank you please take your time :)
Title: Re: how to delete the canvas rectangle
Post by: rvk on October 07, 2017, 06:55:38 pm
oh thank you please take your time :)
Why are you not trying this yourself?
Just asking for code and solutions isn't going to make you learn to code, I can assure you.

Just try it really simple.
You want to have a buffer. A buffer is just an array. Let's say an array of 5 words (5 keys maximum).

You want to do the FIFO method. (First in, First out)
So you are going to put, lets say 3 words in the array from the beginning.
All the steps:
0 0 0 0 0
Then you put 1 in
1 0 0 0 0
Then you put 2 in
2 1 0 0 0
Then put 3 in
3 2 1 0 0

Now when you want to take out a number you just go from the end and traverse back to the beginning until you have a word <> 0. You change the direction but that's for later concern.

Could you code the first step... putting those 3 numbers in the array? As you can see for the second number you need to push the whole array to the right 1 place. Remember we did this for you highscore ranking procedure. Just try to program this first (trust me, otherwise you'll never learn).

Framework:
Code: Pascal  [Select]
  1. Program test;
  2. var
  3.   Buffer: array[1..5] of Word;
  4. begin
  5.   // go ahead. first set all the buffer to 0
  6.   // and then put 1, 2 and 3 in, shifting the numbers on each step.
  7. end.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 09:51:56 pm
@rvk

I don't think TS was asking for the code. He perhaps never know what a data buffer is.

@ shs

I won't explain what a buffer is, because you can read here:
https://en.wikipedia.org/wiki/Data_buffer

I also won't explain the advantages of using buffer, why and when because someday you will know it by yourself.

First, you need to know why your code doesn't work (as what you want) if you press arrow keys quickly.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 07, 2017, 09:54:22 pm
So what we're going to do is to add a buffer to your code. But you need understand how the concept works.
Title: Re: how to delete the canvas rectangle
Post by: rvk on October 07, 2017, 11:04:40 pm
I don't think TS was asking for the code.
Mmm, Ok, I thought otherwise.

umm nope more explanations please?
can you give me an example of the code please?
I also explained a lot about arrays in another topic to TS. So he could use that to at least begin coding this himself (just in a simple console project to get an understanding of a buffer). (see the topic about calendar which turned into a highscore ranking and sorting topic).

Seeing the level of understanding there, I don't think he's quite there yet....

But I'll leave it up to you how fast or slow you are going with this.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 08, 2017, 07:00:34 am
I also explained a lot about arrays in another topic to TS. So he could use that to at least begin coding this himself (just in a simple console project to get an understanding of a buffer). (see the topic about calendar which turned into a highscore ranking and sorting topic).

Seeing the level of understanding there, I don't think he's quite there yet....

I knew and I agree. And that's why I don't want to join in the 2 balls game.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 08, 2017, 07:04:00 am
@shs

Many data collections can be used to create a data buffer. Using array or TList are suitable for your case. Because rvk already gave you the explanation of using array, I will provide you the method of using TList.

I think you have pushed yourself to try to do the things beyond your level of understanding. So I will provide you the code of KeyBuffer, which I wrote in simple and easy to understand way so you can study it if you want.

You need to put this in the data declaration section:
Code: Pascal  [Select]
  1. var
  2.   KeyBuffer: TList;

And this is the code of KeyBuffer:
Code: Pascal  [Select]
  1. procedure KeyBufferSetSize(NewSize: Integer);
  2. var
  3.   DataSegment: ^Word;
  4.   i: Integer;
  5. begin
  6.   if (NewSize < 1) or (NewSize > 10) then Exit;
  7.   if (NewSize < KeyBuffer.Count) then
  8.     for i := NewSize to (KeyBuffer.Count-1) do // Free the allocated memory
  9.       begin
  10.         DataSegment := KeyBuffer[i];
  11.         Freemem(DataSegment);
  12.       end;
  13.   KeyBuffer.Capacity := NewSize;
  14. end;
  15.  
  16. procedure KeyBufferStore(Key: Word);
  17. var
  18.   DataSegment: ^Word;
  19. begin
  20.   if (KeyBuffer.Count = KeyBuffer.Capacity) then Exit; // The buffer is full
  21.   New(DataSegment);
  22.   DataSegment^ := Key;
  23.   KeyBuffer.Add(DataSegment);
  24. end;
  25.  
  26. function KeyBufferUse: Word;
  27. var
  28.   DataSegment: ^Word;
  29. begin
  30.   Result := VK_UNDEFINED;
  31.   if (KeyBuffer.Count <= 0) then Exit; // The buffer is empty
  32.   DataSegment := KeyBuffer[0];
  33.   Result := DataSegment^;
  34.   Freemem(DataSegment);
  35.   KeyBuffer.Delete(0);
  36. end;
  37.  
  38. function KeyBufferPeek(Index: Integer): Word;
  39. var
  40.   DataSegment: ^Word;
  41. begin
  42.   Result := VK_UNDEFINED;
  43.   if (KeyBuffer.Count <= 0) then Exit; // The buffer is empty
  44.   if (Index < 0) or (Index >= KeyBuffer.Count) then Exit; // Invalid index
  45.   DataSegment := KeyBuffer[Index];
  46. end;

Note:
Item on the lower position in the buffer uses smaller index, the bottommost has index = 0.
Title: Re: how to delete the canvas rectangle
Post by: bytebites on October 08, 2017, 08:11:01 am
TQueue would apply here.

Code: Pascal  [Select]
  1. uses contnrs;
  2. var KeyBuffer:TQueue;
  3.  
  4. function KeyBufferUse: Word;
  5. var
  6.   DataSegment: ^Word;
  7. begin
  8.   DataSegment := KeyBuffer.Pop;
  9.   if DataSegment=nil then exit(VK_UNDEFINED);
  10.   Result := DataSegment^;
  11.   Freemem(DataSegment);
  12. end;      
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 08, 2017, 08:15:32 am
Yes, you're right.

TQueue will be more appropriate for this case but because the TS has just learned how to use TList recently so I show him using TList. Also he's just a novice, it will hard for him to understand if we show him too many new things in a very short period.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 09, 2017, 10:08:11 am
wow that's a lot thank you so much i will ask you some questions if i don't get it.
also for restarting the game

i did this
Code: Pascal  [Select]
  1.  For i := 0 to (SnakeBody.Count - 1) do
  2.     begin
  3.       SnakeSegment := SnakeBody[i];
  4.       FreeMem(SnakeSegment);
  5.     end;
  6.    SnakeBody.clear;        

but the tails are still remained
Title: Re: how to delete the canvas rectangle
Post by: bytebites on October 09, 2017, 01:07:49 pm
Code: Pascal  [Select]
  1. ClearWorld;
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 09, 2017, 01:40:16 pm
+1 bytebites

Also don't forget the reset all game variables (score, SnakeIsGrowing, FoodPos, etc). :-X

Note:
The explanation about using buffer in the game has not finished. I still need more time.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 09, 2017, 04:07:31 pm
i did that but it is still not working
Title: Re: how to delete the canvas rectangle
Post by: shs on October 09, 2017, 04:10:27 pm
Code: Pascal  [Select]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3. SnakeSegment:prect;
  4. begin
  5.   count.visible:=true;
  6.   nameeee.visible:=false;
  7.   scoreeeee.visible:=false;
  8.   shape2.visible:=false;
  9.   button2.visible:=false;
  10.    button3.visible:=false;
  11.  
  12.  
  13.    snakepos.left:=random(24);
  14.    snakepos.top:=random(24);
  15.  
  16.  
  17.    timer1.enabled:=true;
  18.    timer2.enabled:=true;
  19.    xdirection:=0;
  20.    ydirection:=0;
  21.    score:=0;
  22.      For i := 0 to (SnakeBody.Count - 1) do
  23.     begin
  24.       SnakeSegment := SnakeBody[i];
  25.       FreeMem(SnakeSegment);
  26.     end;
  27.    SnakeBody.clear;
  28.  
  29.    snakeisgrowing:=false;
  30.    clearworld;            
this is my code
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 09, 2017, 04:39:08 pm
You made 2 mistakes:
1. Don't put the code in a button, it will have issue with your form's KeyDown event.
2. If you want to replay the game, you need to 'reintroduce' the snake.

So you can try to put the code on FormKeyDown:
Code: Pascal  [Select]
  1.   if Key = VK_ESCAPE then
  2.     begin
  3.       // Reset the game
  4.       xDirection     := 0;
  5.       yDirection     := 0;
  6.       Score          := 0;
  7.       Timer1.Enabled := True;
  8.       Timer2.Enabled := True;
  9.       SnakeIsGrowing := False;
  10.       For i := 0 to (SnakeBody.Count - 1) do
  11.         begin
  12.           SnakeSegment := SnakeBody[i];
  13.           FreeMem(SnakeSegment);
  14.         end;
  15.       SnakeBody.Clear;
  16.       ClearWorld;
  17.       // Reintroduce the snake
  18.       SnakePos.Left := Random(24);
  19.       SnakePos.Top  := Random(24);
  20.       New(SnakeSegment);
  21.       SnakeSegment^ := SnakePos;
  22.       SnakeBody.Add(SnakeSegment);
  23.   end;

Not critical but important:
You should write your code nicely. See my code above. When your code grow larger, you will be hard to maintain if you do not format the code neatly.
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 09, 2017, 07:01:02 pm
Here is the code you're waiting for.

1. Add Varible "KeyBuffer" on The Declaraction Section

Code: Pascal  [Select]
  1. var
  2.   KeyBuffer: TList;

2. Add The KeyBuffer Code

Code: Pascal  [Select]
  1. procedure KeyBufferSetSize(NewSize: Integer);
  2. var
  3.   DataSegment: ^Word;
  4.   i: Integer;
  5. begin
  6.   if (NewSize < 1) or (NewSize > 10) then Exit;
  7.   if (NewSize < KeyBuffer.Count) then
  8.     for i := NewSize to (KeyBuffer.Count-1) do // Free the allocated memory
  9.       begin
  10.         DataSegment := KeyBuffer[i];
  11.         Freemem(DataSegment);
  12.       end;
  13.   KeyBuffer.Capacity := NewSize;
  14. end;
  15.  
  16. procedure KeyBufferStore(Key: Word);
  17. var
  18.   DataSegment: ^Word;
  19. begin
  20.   if (KeyBuffer.Count = KeyBuffer.Capacity) then Exit; // The buffer is full
  21.   New(DataSegment);
  22.   DataSegment^ := Key;
  23.   KeyBuffer.Add(DataSegment);
  24. end;
  25.  
  26. function KeyBufferUse: Word;
  27. var
  28.   DataSegment: ^Word;
  29. begin
  30.   Result := VK_UNDEFINED;
  31.   if (KeyBuffer.Count <= 0) then Exit; // The buffer is empty
  32.   DataSegment := KeyBuffer[0];
  33.   Result := DataSegment^;
  34.   Freemem(DataSegment);
  35.   KeyBuffer.Delete(0);
  36. end;
  37.  
  38. function KeyBufferPeek(Index: Integer): Word;
  39. var
  40.   DataSegment: ^Word;
  41. begin
  42.   Result := VK_UNDEFINED;
  43.   if (KeyBuffer.Count <= 0) then Exit; // The buffer is empty
  44.   if (Index < 0) or (Index >= KeyBuffer.Count) then Exit; // Invalid index
  45.   DataSegment := KeyBuffer[Index];
  46. end;

3. Initial The KeyBuffer When The Program Start

Put this on FormCreate:
Code: Pascal  [Select]
  1.   KeyBuffer := TList.Create;
  2.   KeyBufferSetSize(2);

The size = 2 should be good enough. But you can change it to other values. If you can press the keyboard really fast, you may need to change the value higher (3, 4, or 5).

4. Free The KeyBuffer When Not Needed

Put this on FormClose:
Code: Pascal  [Select]
  1.   KeyBuffer.Free;

5. Replace The Code of FormKeyDown

Go to your FormKeyDown, remove all the code inside. And replace them with this:
Code: Pascal  [Select]
  1.   if (KeyBufferPeek(KeyBuffer.Count-1) <> Key) then KeyBufferStore(Key);

I move the direction calculation to Timer1Timer. Because FormKeyDown should only perform key handling not direction decision.

6. Add Some Code To Timer1Timer Code

Go to your Timer1Timer, add this at the beginning of the procedure:
Code: Pascal  [Select]
  1.   if (score >= 1) then
  2.     case KeyBufferUse of
  3.       vk_left:  if not(direction = 2) then direction := 1;
  4.       vk_right: if not(direction = 1) then direction := 2;
  5.       vk_up:    if not(direction = 4) then direction := 3;
  6.       vk_down:  if not(direction = 3) then direction := 4;
  7.     end;
  8.   if (score = 0) then
  9.     case KeyBufferUse of
  10.       vk_left:  direction := 1;
  11.       vk_right: direction := 2;
  12.       vk_up:    direction := 3;
  13.       vk_down:  direction := 4;
  14.     end;

Well-done it should work now. I tested on my computer, it works as what it should be.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 10, 2017, 10:29:35 am
what does freemem do?
Title: Re: how to delete the canvas rectangle
Post by: rvk on October 10, 2017, 10:34:06 am
what does freemem do?
Before you ask such a question, can't you just type freemem lazarus in Google ???

You get here with the first page:
http://lazarus-ccr.sourceforge.net/docs/rtl/system/freemem.html
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 10, 2017, 07:50:41 pm
Oops, I forgot to do freemem in FormClose. Thanks for reminding me.

Because calling KeyBufferUse will free a single requested memory item, so by repeating it until the TList.Count = 0 will free all the requested memory.

Please replace my previous code for FormClose with this one:
Code: Pascal  [Select]
  1.   repeat
  2.     KeyBufferUse;
  3.   until (KeyBuffer.Count <= 0);
  4.   KeyBuffer.Free;
Title: Re: how to delete the canvas rectangle
Post by: shs on October 11, 2017, 11:54:22 am
@rvk
sorry i will try to search it up next time

@hondoko
thank you so much. But i don't my understanding level for pointer is not that high (it's not your fault you're a great teacher), so i want to practice more by myself. are there any exercises i can practice?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 11, 2017, 01:17:34 pm
As I already mentioned, pointer always is the headache thing for beginners. With some more practices you will be proficient in using it.

Here I found some good explanations how to use pointer in Pascal:
http://www.pascal-programming.info/articles/pointers.php
http://www.tutorialspoint.com/pascal/pascal_pointers.htm
http://pjhayward.net/pascal/questions/pointers.php

The most important thing about using pointer is
If you allocated some space for the pointer, you have to free it.

You can think pointers are like the shortcuts/links in your computer, TList is a folder. If you create several shortcuts and store it in a folder. You should manually remove the 'real' files if you delete the shortcuts in the folder. Because if you just delete the shortcuts but forget to delete the 'real' files. The leftovers take up the space in your harddisk, it is bad.

Do you play computer games? You may notice some games will run slower or buggy after hours of playing. But simply restarting the computer and the game, problem solves. That is memory leak issue. Which usually happens because the programmer does not handle the memory (pointers) properly.

And don't forget about array, it is very important. rvk already provided lots of explanations about using array. You should reread his posts and study what he said.

One task for you, if you really want to improve your programming skill:
Rewrite your snake game using arrays only.
I know it will be a bit hard for you, but I believe: you can!
Title: Re: how to delete the canvas rectangle
Post by: shs on October 11, 2017, 11:56:26 pm
hi i managed to delete array if it's bigger than 4 but i can delete them on the screen
Code: Pascal  [Select]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. begin
  3.   box[0].left:=box[0].left+(xd*scl);
  4.   box[0].top:=box[0].top+(yd*scl);
  5.  
  6.  
  7.   boxlength:=boxlength+1;
  8.   setlength(box,boxlength);
  9.  
  10.   for i:= 1 to boxlength do
  11.   begin
  12.   box[i]:=tshape.create(self);
  13.   box[i].parent:=self;
  14.   box[i].width:=scl;
  15.   box[i].height:=scl;
  16.   box[i].shape:=strectangle;
  17.   box[i].top:=box[0].top;
  18.   box[i].left:=box[0].left;
  19.  
  20.  
  21.   end;
  22.  
  23.   if 4< boxlength then
  24.     begin
  25.     for i:= 1 to boxlength do
  26.     begin
  27.     box[i]:=box[i+1]
  28.     end;                      
  29.  
  30.     setlength(box, boxlength-1);
  31.     boxlength:=boxlength-1;
  32.  
  33.  
  34.     end;        
Title: Re: how to delete the canvas rectangle
Post by: bytebites on October 12, 2017, 08:00:43 am
I will produce range check errors. Check your array indexes.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 16, 2017, 10:00:24 am
hi i made snake game using array
it works okay but when i get many tails, it gets laggy how can i fix that?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 16, 2017, 10:29:57 am
Great job. You managed to create the snake game using array only. I am sure now you have better understanding about using array.

Many things can be done to improve the performance. But in your case, there is a big issue in your code.

I checked your code, you call TShape.Create to do the drawing. I guess you do not know TShape is class, by calling Create, it will create the object and it needs memory to store it. So you created lots of TShape objects but unfortunately you does not free it. It is not okay.

You should free the TShape object. The code should be put on your TForm1.Delete.

For comparison, rvk's code (for the balls) did not call TShape.Free. That because the total of the balls is static. In your snake game, your code creates a new TShape on Timer1Timer. That's the different.

Basically, there are 2 quick solutions for your case:
- Use canvas rectangle instead of TShape
- Free the shape object by calling TShape.Free

Canvas rectangle is a painting command, it does not create new object and you do not need to free it. Read more about canvas rectangle:
http://wiki.freepascal.org/Drawing_with_canvas
Title: Re: how to delete the canvas rectangle
Post by: shs on October 16, 2017, 10:56:32 am
so to free the shape object, i just have to do tshpae.free on Tform1.delete?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 16, 2017, 10:58:44 am
Yes, right after you set its color to game.brush.color.

Code: Pascal  [Select]
  1. box[index].Free;

edit:
Actually, you do not need to set the color. Because TShape is object, by freeing it, it will also remove what it already painted and set it to the background. So, you can simple remove these lines too:
Code: Pascal  [Select]
  1.     box[index].brush.color:=game.brush.color;
  2.     box[index].pen.color:=game.brush.color;
Title: Re: how to delete the canvas rectangle
Post by: shs on October 16, 2017, 11:38:14 am
oh wow thank you i didn't know that

also how can use array for canvas ?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 16, 2017, 01:11:46 pm
No need to use array of canvas, but you should use array of TRect to store the coordinates.

Your mainform already provide you a canvas. The canvas is an object too, but you do not need to free it manually because the canvas is created automatically by the the form, it will also be freed automatically when you exit the program.

Because it is created by the mainform (or active form), you use the form's name to refer to it, for example:
Form1.canvas.rectangle (10, 10, 20, 20);

Usually, we use self to refer to the active form (without need to know what is the form's name):
self.canvas.rectangle (10, 10, 20, 20);

But on most cases, you do not need to use form's name or self because it will automatically refer to the active form. So your code should become:

procedure TForm1.add;
var
  index: Integer;
begin
    setlength(box, length(box)+1);

    index:=length(box)-1;
    box[index]:=TRect.Create(self);
    box[index].width:=scl;
    box[index].height:=scl;

    case direction of
      1: begin //right
           box[index].top:=box[index-1].Top;
           box[index].left:=box[index-1].left+scl;
         end;
      2: begin //left
           box[index].top:=box[index-1].Top;
           box[index].left:=box[index-1].left-scl;
         end;
      3: begin //up
           box[index].top:=box[index-1].Top-scl;
           box[index].left:=box[index-1].left;
         end;
      4: begin //down
           box[index].top:=box[index-1].Top+scl;
           box[index].left:=box[index-1].left;
         end;
    end;
   
    Canvas.Pen.Color := game.Brush.Color;
    if index < 2 then Canvas.Brush.Color := clred
      else if (2 <= index)and (index < 4) then Canvas.Brush.Color := clyellow
      else if (4 <= index)and (index < 6) then Canvas.Brush.Color := clblue;
    Canvas.Rectangle(box[index]);

    ....

Title: Re: how to delete the canvas rectangle
Post by: shs on October 16, 2017, 01:20:06 pm
i got error for this line
Code: Pascal  [Select]
  1.     box[index]:=TRect.Create(self);
unit1.pas(44,34) Error: Incompatible type for arg no. 1: Got "TForm1", expected "TRect"
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 16, 2017, 01:31:27 pm
Sorry my fault. TRect is an advanced record, no need to call Create. So you can simply delete that line.

You still need to modify the declaration section for the variable "box". You may also need to modify all things related with the "box". On your IDE you can use shortcut "CTRL" + "F" and type "box" to find the text in your source code.

Note:
TShape is a class that will create an object so you should free it manually. But TRect is an advanced record (which looks like class), you do not call Create and you do not need to free it.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 17, 2017, 02:35:12 am
so what do i have to to delete the array?
Title: Re: how to delete the canvas rectangle
Post by: shs on October 17, 2017, 10:11:03 am
the snake is working good but food isn't
Code: Pascal  [Select]
  1. if (box[length(box)-1].left = food.left) and  (box[length(box)-1].top = food.top) then
  2.   begin
  3.  
  4.    food.topleft:=point(rx,ry);
  5.    food.bottomright:=point(rx+20,ry+20);
  6.  
  7.    boxlength:=boxlength+1;
  8.  
  9.   end;                    

i think this part is wrong but i don't know how i can fix it please find the attachment
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 17, 2017, 12:04:10 pm
Your code has several issues.

1. Put Your Data Initialization Code In FormCreate

I saw you put that code in FormPaint, it is not good. You can put that code in FormPaint and make it works correctly, but that code will be complicated and hard to maintain.

Your FormCreate now should be like this:
Code: Pascal  [Select]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   index: Integer;
  4. begin
  5.   // Prepare snake
  6.   setlength(box, 1);
  7.   index:=length(box)-1;
  8.   box[index].width:=scl;
  9.   box[index].height:=scl;
  10.   box[index].left:=0;
  11.   box[index].top:=0;
  12.   // Prepare food
  13.   randomize;
  14.   rx:=random((width-scl)div scl)*scl;
  15.   ry:=random((height-scl)div scl)*scl;
  16.   food.topleft:=point(rx,ry);
  17.   food.bottomright:=point(rx+20,ry+20);
  18. end;

2. Don't Use FormPaint

You used FormPaint to draw the food, it is not a good thing to do. There is a thing you need to know about FormPaint. When you draw something on FormPaint, it will not update the screen immediately. It sounds weird, isn't it? But this is how they design OnPaint (including FormPaint). You need to force it to update/refresh/repaint. And this is the issue that causes the food won't show up.

I won't explain much about FormPaint. You now can simply remove the code in FormPaint because we will merge the code to Timer1Timer.

3. Do The Painting On Timer1Timer

I think I do not need to explain, because the code below is easy to understand:

Code: Pascal  [Select]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. var
  3.   index: Integer;
  4. begin
  5.  
  6.   // Paint the snake
  7.   index:=length(box)-1;
  8.   canvas.brush.color:=clwhite;
  9.   canvas.pen.color:=form1.color;
  10.   canvas.rectangle(box[index]);
  11.  
  12.   // Paint the food
  13.   food.topleft:=point(rx,ry);
  14.   food.bottomright:=point(rx+20,ry+20);
  15.   canvas.brush.color:=clred;
  16.   canvas.pen.color:=form1.color;
  17.   canvas.rectangle(food);
  18.  
  19.   // Move the snake
  20.   adding();
  21.   if length(box) = boxlength then delete();
  22.  
  23.   // Collision Test
  24.   if (box[length(box)-1].left = food.left) and  (box[length(box)-1].top = food.top) then
  25.   begin
  26.     // Show new food
  27.     randomize;
  28.     rx:=random((width-scl)div scl)*scl;
  29.     ry:=random((height-scl)div scl)*scl;
  30.     food.topleft:=point(rx,ry);
  31.     food.bottomright:=point(rx+20,ry+20);
  32.     // Increase snake's length
  33.     boxlength:=boxlength+1;
  34.   end;
  35. end;

So far the code should work now. Although there still many things can be done to improve the code's quality.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 17, 2017, 01:42:00 pm
what should i do to improve code's quality?
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 17, 2017, 03:08:10 pm
1. No Need To Use "(" and ")"

If the procedure doesn't have any parameter, you can remove that brackets:
Code: Pascal  [Select]
  1.     procedure Adding;
  2.     procedure Delete;

You don't have to, but I personally will move the Adding and Delete declaration to private section. I left the auto generated by IDE on the top (like FormCreate, FormKeyDown, Timer1Timer). See point #2 below.

2. Put Things On The Lowest Scope

There are 3 visibilities (actually there are 6) in class:
- Private
- Protected
- Public

It is good to put your variables, functions or procedures on the lowest visibility scope unless you really need to put them on the higher visibility levels.

So your "box" and "food" declaration should be in private section:
Code: Pascal  [Select]
  1.   private
  2.     Box:  array of TRect;
  3.     Food: TRect;
  4.   end;
I personally will put my procedures and functions on this section too (Adding, Delete).

3. BoxLength Should Be Started With 1

Do you not think it is strange, your BoxLength started with the value of 2? So, change the declaration of BoxLength with this:
Code: Pascal  [Select]
  1.   BoxLength: Integer = 1;

And you also need to change the code of BoxLength in Timer1Timer to become:
Code: Pascal  [Select]
  1.   if Length(Box) > BoxLength then Delete;

4. Use Better Names

Code: Pascal  [Select]
  1.   rx, ry : Integer;
  2.   scl = 20;

If you ever tried to the read codes written by professionals, you will notice they always use meaningful names. It will make the code easier to understand especially when the code grows to thousands of lines. Except for i, j, k ..., which are used for looping.

5. Use Space To Make It More Readable

This code below will look nicer:
Code: Pascal  [Select]
  1.   case Key of
  2.     vk_Right: Direction := 1;
  3.     vk_Left:  Direction := 2;
  4.     vk_Down:  Direction := 3;
  5.     vk_Up:    Direction := 4;
  6.   end;

6. No Need To Use Begin-End For Single Line For-Loop

You can simplify the for-loop statement by removing the begin-end if it only contains 1 command. So the for-loop in your Delete procedure will become:
Code: Pascal  [Select]
  1.   for i:= 1 to Length(Box)-1 do Box[i-1] := Box[i];

Alternatively, some programmers will prefer to write it in 2 lines:
Code: Pascal  [Select]
  1.   for i:= 1 to Length(Box)-1 do
  2.     Box[i-1] := Box[i];

7. Use Camel Case

Except for reserved keywords, using camel case will make the code looks better.
https://en.wikipedia.org/wiki/Camel_case
Title: Re: how to delete the canvas rectangle
Post by: molly on October 17, 2017, 03:21:45 pm
@Handoko:
you're in your element it seems, and you seem to be more in line with TS  8-)

additionally to your otherwise excellent suggestions i would personally a) add a separate snake class as that makes more sense to have things "tucked away" in there, b) draw to an off-screen bitmap to prevent flickering, c) at least rename the box array into something like snake or bodyparts (after all it is a snake game).

How much of that would be feasible for TS i have no idea.

edit: Oh, and i just noticed: you might already noticed i'm an advocate of using Low()/High() and Succ()/Pred() instead of length-1 and hard-coded start-index-values  :)
Title: Re: how to delete the canvas rectangle
Post by: Munair on October 17, 2017, 03:33:27 pm
Except for i, j, k ..., which are used for looping.
Most programmers start the first for-loop with variable 'i'. This is common practice and goes back to the 1960s if I remember correctly. i stands for 'iteration'. Nested loops then follow with 'j', 'k', etc to keep some 'logic' to the variable naming. ;)
Title: Re: how to delete the canvas rectangle
Post by: shs on October 17, 2017, 11:21:49 pm
@hondoko
 Thank you :)
Code: Pascal  [Select]
  1. draw to an off-screen bitmap to prevent flickering,
how can i do that?
Title: Re: how to delete the canvas rectangle
Post by: J-G on October 18, 2017, 10:47:21 am
Most programmers start the first for-loop with variable 'i'.  i stands for 'iteration'.
Not in my mind - i = 'Index'  indicating the [index] of the array. It could just as easily be 'x' which from antiquity is the 'Unknown quantity' or even 'c' for Count.
Title: Re: how to delete the canvas rectangle
Post by: Munair on October 18, 2017, 11:21:07 am
Most programmers start the first for-loop with variable 'i'.  i stands for 'iteration'.
Not in my mind - i = 'Index'  indicating the [index] of the array. It could just as easily be 'x' which from antiquity is the 'Unknown quantity' or even 'c' for Count.
If you follow back the naming convention then 'x' wasn't a candidate. It seems to have its roots in Fortran where i,j,k were implicitely declared as integer, while x,y,z were of type real.

According to Wikipedia and other sources the naming convention dates back further to mathematical notation:

"This style is generally agreed to have originated from the early programming of FORTRAN, where these variable names beginning with these letters were implicitly declared as having an integer type, and so were obvious choices for loop counters that were only temporarily required. The practice dates back further to mathematical notation where indices for sums and multiplications are often i, j, etc."

So yes, index may have been the original meaning coming from the world of mathematics. However, in programming, loops are not always used to go through indices of arrays. Often calculations are done using the iterator.

Anyway, I picked it up long ago in a programming book where it was explained that i means iterator. I guess we'll never know the original reason why the Fortran developer(s) chose i. It may have been Integer, Index, Iterator or even Item. You will find them all if you search the internet.
Title: Re: how to delete the canvas rectangle
Post by: molly on October 18, 2017, 11:21:51 am
Quote from: pascal
draw to an off-screen bitmap to prevent flickering,
how can i do that?
That quote was actually mine  :)

Given your canvas.rar example you
a) create a global bitmap (don't forget to assign it the proper dimensions)
b) whenever you need to draw your graphics you do that on the bitmap.canvas instead.
c) Whenever you want your graphics displayed on the Form you then copy the contents of that bitmap to your forms's canvas.
d) when your app is done, don't forget to free the bitmap again   :)

Seems' i'm unable to find a good example that resembles your exact situation but if i would have to choose one then it would be this small snippet (http://wiki.freepascal.org/Developing_with_Graphics#Create_a_custom_control_which_draws_itself). But, don't let yourself confused by the separate class. I only pointed to it for you to see how to create and destroy a bitmap, draw to it and how to copy the contents of the bitmap to somewhere else.

But if you want you could take that example almost literally and implement your own 'game-screen' component in a similar fashion (if you are new to bitmaps i would advise against that right now and advise to just play around with bitmaps first for the experience).
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 18, 2017, 01:44:13 pm
I wrote 2 codes to show the flickering effect and the solution to solve it following what molly said:

a) create a global bitmap (don't forget to assign it the proper dimensions)
b) whenever you need to draw your graphics you do that on the bitmap.canvas instead.
c) Whenever you want your graphics displayed on the Form you then copy the contents of that bitmap to your forms's canvas.
d) when your app is done, don't forget to free the bitmap again

Flickering and performance are the most basic things a game programmer need to pay attention about. Flickering issue can be solved easily.

For simple games (like snake), flickering rarely happens. It will become noticeable when lots of objects needed to paint on the screen. Below is the common things that may happen when running a game:
  repeat
     clear the screen
     paint object #1
     paint object #2
     ...
     paint object #1000
  end

If only few objects needed to paint on the screen (for example only 10 objects), flickering issue almost never happen. The time between clearing the screen and painting the last object is the key factor, the longer it is the more noticeable flickering effect. It is because the monitor need time to perform the update/refresh.

The solution is simple: "don't paint every single object on the screen directly".

We paint all the objects on a memory buffer, and then we update the screen using a single command to show (or copy) the memory buffer to the screen. TBitmap is good to use as the memory buffer for this case.

My Demo1 shows flickering on my Core2 Quad GeForce 430 Dell monitor if I set the TotalParticles >= 1000. On different hardware, the value may be lower or higher.

The code of Demo2 is basically the same as Demo1, but it does not paint the screen directly so there will no noticeable flickering (I tried to set TotalParticles = 5000).

The differences between the 2 codes are:
- The declaration of variable "TempOutput: TBitmap;"
- Preparing TempOutput on FormCreate
- Freeing TempOutput on FormDestroy
- The procedure DrawGameWorld

Code: Pascal  [Select]
  1. procedure TForm1.DrawGameWorld;
  2. const
  3.   Padding = 2;
  4. var
  5.   X, Y: Integer;
  6.   ScreenX, ScreenY: Integer;
  7. begin
  8.  
  9.   // Clear the temporary output
  10.   TempOutput.Canvas.Brush.Color := clWhite;
  11.   TempOutput.Canvas.FillRect(0, 0, Width, Height);
  12.  
  13.   // Draw on the canvas of the temporary output
  14.   for X := 1 to WorldWidth do
  15.     for Y := 1 to WorldHeight do
  16.       begin
  17.         ScreenX := (X-1) * Scale;
  18.         ScreenY := (Y-1) * Scale;
  19.         TempOutput.Canvas.Brush.Color := GameWorld[X,Y].Color;
  20.         case GameWorld[X, Y].Item of
  21.           Empty: ; // do nothing
  22.           Box: TempOutput.Canvas.Rectangle(ScreenX, ScreenY, ScreenX+Scale-Padding, ScreenY+Scale-Padding);
  23.           Circle: TempOutput.Canvas.Ellipse(ScreenX, ScreenY, ScreenX+Scale-Padding, ScreenY+Scale-Padding);
  24.           end;
  25.       end;
  26.  
  27.   // Draw the temporary output to the active form's canvas
  28.   Canvas.Draw(0, 0, TempOutput);
  29. end;
Title: Re: how to delete the canvas rectangle
Post by: shs on October 18, 2017, 02:01:13 pm
so do i have to put drawgame procedure or can i do it without it?
because i didn't put that in my snake game coding
Title: Re: how to delete the canvas rectangle
Post by: Handoko on October 18, 2017, 02:07:16 pm
Drawgame procedure? Did you mean DrawGameWorld?

For simple games, you do not need it. For complex games, it is a good practice to put all the drawing commands into a single procedure for easier maintaining reason. You can see, I can change the Demo1 to Demo2 to solve the flickering effect easily because I already put all the drawing commands into DrawGameWorld.
Title: Re: how to delete the canvas rectangle
Post by: shs on October 18, 2017, 02:49:33 pm
so i just have to put tempout before canvas right?

for example tempout.canvas.rectangle(box) like that?
Title: Re: how to delete the canvas rectangle
Post by: molly on October 18, 2017, 02:54:41 pm
for example tempout.canvas.rectangle(box) like that?
Yes.

But, with regards to your canvas.rar example you have something else to consider. Your example draws to the canvas on separate locations inside your code (e.g. you draw and delete at exactly the same time that your game-logic adds or deletes items). You should not do that. Instead you should make one routine that draw everything in one go to the canvas (whatever that canvas might belong to, form or bitmap).

But hold on for a moment. I lost track. Do you already have a working snake-game that works with drawing rectangles to the canvas ? Or is your current working solution only working with TShapes that are placed on the form ?
Title: Re: how to delete the canvas rectangle
Post by: rvk on October 18, 2017, 03:07:11 pm
But hold on for a moment. I lost track. Do you already have a working snake-game that works with drawing rectangles to the canvas ? Or is your current working solution only working with TShapes that are placed on the form ?
TS already had a working canvas-version.
See here http://forum.lazarus.freepascal.org/index.php/topic,38136.msg262927.html#msg262927
Title: Re: how to delete the canvas rectangle
Post by: molly on October 18, 2017, 03:15:34 pm
Thank you very much rvk !

Ok, in that case, the following still stands:
...
But, with regards to your canvas.rar example you have something else to consider. Your example draws to the canvas on separate locations inside your code (e.g. you draw and delete at exactly the same time that your game-logic adds or deletes items). You should not do that. Instead you should make one routine that draw everything in one go to the canvas (whatever that canvas might belong to, form or bitmap).
...

First try to rewrite things so that everything gets drawn to the form canvas (inside event .FormPaint() ) in one go. So try to 'remove' the drawing command from methods .adding() and .delete(). If you are able to accomplish that then updating that code to use a off-screen bitmap becomes much easier.