Recent

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

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: how to delete the canvas rectangle
« Reply #60 on: September 27, 2017, 04:57:09 pm »
Okay thank you :) :)

Handoko

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

shs

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

Handoko

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

shs

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

Handoko

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

shs

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

Handoko

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

Handoko

  • Hero Member
  • *****
  • Posts: 5151
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #68 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;
« Last Edit: October 02, 2017, 05:47:49 am by Handoko »

shs

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

Handoko

  • Hero Member
  • *****
  • Posts: 5151
  • My goal: build my own game engine using Lazarus
Re: how to delete the canvas rectangle
« Reply #70 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.
« Last Edit: October 02, 2017, 05:54:37 am by Handoko »

shs

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


Handoko

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

rvk

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

shs

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

 

TinyPortal © 2005-2018