Recent

Author Topic: how to delete the canvas rectangle  (Read 73257 times)

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #15 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.

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #16 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.

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #17 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.

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #18 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.
« Last Edit: September 02, 2017, 01:14:57 pm by Handoko »

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #19 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;

carl_caulkett

  • Sr. Member
  • ****
  • Posts: 306
Re: how to delete the canvas rectangle
« Reply #20 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  :)
"It builds... ship it!"

Mac Mini M1
macOS 13.6 Ventura
Lazarus 2.2.6 (release version)
FPC 3.2.2 (release version)

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: how to delete the canvas rectangle
« Reply #21 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..

The only true wisdom is knowing you know nothing

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #22 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.

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: how to delete the canvas rectangle
« Reply #23 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.
The only true wisdom is knowing you know nothing

cpicanco

  • Hero Member
  • *****
  • Posts: 618
  • Behavioral Scientist and Programmer
    • Portfolio
Re: how to delete the canvas rectangle
« Reply #24 on: September 04, 2017, 05:09:12 pm »
Hi rhong7, what about hosting your game in a github repository?
Be mindful and excellent with each other.
https://github.com/cpicanco/

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: how to delete the canvas rectangle
« Reply #25 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 :(

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #26 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.

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: how to delete the canvas rectangle
« Reply #27 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

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #28 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.
« Last Edit: September 16, 2017, 06:03:51 pm by Handoko »

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: how to delete the canvas rectangle
« Reply #29 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

 

TinyPortal © 2005-2018