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 rectangleThere'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.
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?
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
unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, StdCtrls, ExtCtrls, Spin; type { TForm1 } TForm1 = class(TForm) Button1: TButton; Label1: TLabel; SpinEdit1: TSpinEdit; Timer1: TTimer; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormPaint(Sender: TObject); procedure Timer1Timer(Sender: TObject); end; var Form1: TForm1; implementation type TItem = record X1, X2, Y1, Y2: Integer; Color: TColor; end; PItem = ^TItem; var Items: TFPList; // list of PItem {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject); begin Timer1.Enabled := not(Timer1.Enabled); if Timer1.Enabled then Button1.Caption := 'Pause' else Button1.Caption := 'Start'; end; procedure TForm1.FormCreate(Sender: TObject); begin Items := TFPList.Create; end; procedure TForm1.FormDestroy(Sender: TObject); begin Timer1.Enabled := False; Items.Free; end; procedure TForm1.FormPaint(Sender: TObject); var Item: TItem; i: Integer; begin for i := 0 to (Items.Count-1) do begin Item := PItem(Items[i])^; Canvas.Brush.Color := Item.Color; Canvas.Rectangle(Item.X1, Item.Y1, Item.X2, Item.Y2); end; end; procedure TForm1.Timer1Timer(Sender: TObject); var Item: PItem; X1, Y1, X2, Y2: Integer; Selected: Integer; begin if (Random(10) > 3) then // new rectangle begin if (Items.Count >= SpinEdit1.Value) then Exit; X1 := Random(Width); Y1 := Random(Height); X2 := X1 + Random(80); Y2 := Y1 + Random(80); New(Item); Item^.X1 := X1; Item^.Y1 := Y1; Item^.X2 := X2; Item^.Y2 := Y2; Item^.Color := RGBToColor(Random(255), Random(255), Random(255)); Items.Add(Item); end else // remove rectangle begin if (Items.Count <= 0) then Exit; Selected := Random(Items.Count); Items.Delete(Selected); end; Repaint; end; end.
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?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 ;)
can you make snake game with those ideas?
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
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'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.
after you draw something on the screen, you should forget it.
How to delete the canvas rectangle?
Before we start the coding part, you should know that in game programming:And then went on to illustrate it with more and more detail.Quoteafter you draw something on the screen, you should forget it.
That was a really nicely explained series of posts, Handoko.
prior to painting, you can call that to erase background and then draw your
snake. This is cause flicker.
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.
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.
also what's the difference betweenTRect is a record structure for a rectangle. With Left, Top etc.and
Trect
^Trect
^Trect
But where are the random-lines Handoko is speaking of?
what does that do?
TItem = (Emtpy, Snake, Fruit);
why are there two different var?
can't you put it together?
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.
Don't do any screen drawing on TForm.OnCreate event.
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 does that mean the head becomes 0 in the list and the previous head becomes 1?
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;
@Thaddyhttps://community.embarcadero.com/blogs/entry/a-case-when-freeandnil-is-your-enemy-38916
Not just simply freeing the canvas but FreeAndNil(Canvas);
https://community.embarcadero.com/blogs/entry/a-case-when-freeandnil-is-your-enemy-38916Hmm, Thaddy, but did you read carefully that blog post you are referring to?
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.
is there any other of writing this code by the way?
case direction of 1,3,4: if key=vk_left then begin direction:=1; end; 2: if key=vk_left then begin direction:=2; end; end; case direction of 2,3,4: if key=vk_right then begin direction:=2; end; 1: if key=vk_right then begin direction:=1; end; end; case direction of 1,2,3: if key=vk_up then begin direction:=3; end; 4: if key=vk_up then begin direction:=4; end; end; case direction of 1,2,4: if key=vk_down then begin direction:=4; end; 3: if key=vk_down then begin direction:=3; end; end;
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)
... simple call the Time1Timer after the keypressing event.
As I told you, it will introduce new problem: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)
Your snake can speed up by pressing forward. :D
As I told you, it will introduce new problem: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)
Your snake can speed up by pressing forward. :D
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:
var DontChangeDirection: Boolean = true; procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if DontChangeDirection then exit; DontChangeDirection := true;
procedure TForm1.Timer1Timer(Sender: TObject); begin 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 positionThat is strange.
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).
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)
Was I misunderstand that TS said?No, I think we all understand it correctly.
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.
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.
the direction still changes when the snake has not moved positionThat 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)?
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 codeAlso please read the last few post. It has multiple solutions for your problem.
oh thank you please take your time :)Why are you not trying this yourself?
I don't think TS was asking for the code.Mmm, Ok, I thought otherwise.
umm nope more explanations 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).
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....
what does freemem do?Before you ask such a question, can't you just type freemem lazarus in Google ???
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. ;)
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.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.
That quote was actually mine :)Quote from: pascaldraw to an off-screen bitmap to prevent flickering,how can i do that?
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
for example tempout.canvas.rectangle(box) like that?Yes.
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.
...
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).
...