Lazarus

Programming => Graphics => Graphics and Multimedia => BGRABitmap and LazPaint => Topic started by: Roland57 on August 28, 2020, 01:22:30 pm

Title: Animation for a four-in-a-row game
Post by: Roland57 on August 28, 2020, 01:22:30 pm
Hello friends!

I started to work on a new interface for a four-in-a-row game. I would like to make the disk fall behind the grid.

I made a little demo, which works well, excepted that after animation the grid becomes pixelated, I don't know why.

I attach the project. If you have time to take a look. It's a very simple program. Click on the button "Start" to make the disk fall. That's all.  :)

P.-S. Attachment removed. New version of the project below.
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 02, 2020, 01:25:22 pm
Hi Roland,

The antialiasing goes away because the program draws many times the blue grid, so where it is opaque nothing changes, and where it is half-transparent, it becomes more and more opaque each time.

I understand you want to draw the grid to hide the disk. In this case, I would recommend another approach.

When you draw the disk, in fact draw everything. The background, the disk and the grid. To avoid redrawing everything, simply set the ClipRect to the area that is changed.

Regards
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 02, 2020, 07:06:21 pm
The antialiasing goes away because the program draws many times the blue grid, so where it is opaque nothing changes, and where it is half-transparent, it becomes more and more opaque each time.

Ah, I understand now. I should have thought of it sooner.

I understand you want to draw the grid to hide the disk. In this case, I would recommend another approach.

When you draw the disk, in fact draw everything. The background, the disk and the grid. To avoid redrawing everything, simply set the ClipRect to the area that is changed.

OK, I will try that. Thank you very much.  :)
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 03, 2020, 06:58:02 am
When you draw the disk, in fact draw everything. The background, the disk and the grid. To avoid redrawing everything, simply set the ClipRect to the area that is changed.

It works perfectly, and the code is simpler.  8-)
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 03, 2020, 07:28:26 am
With a slightly prettier grid.
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 03, 2020, 10:42:32 am
Ok, so you chose the whole column, that's a good idea.

To optimize further, you can tell the OS to invalidate only that column:

Code: Pascal  [Select][+][-]
  1. uses LCLIntf;
  2.  
  3. procedure TForm1.Timer1Timer(Sender: TObject);
  4. const
  5.   CStep = 12;
  6. var
  7.   r: TRect;
  8. begin
  9.   if not LData.Done then
  10.   begin
  11.     VirtualScreen.PutImage(0, 0, Background, dmSet);
  12.     VirtualScreen.PutImage(LData.x, LData.y, Disk, dmDrawWithTransparency);
  13.     VirtualScreen.PutImage(48 - 3, 48 * 2 - 3, Grid, dmDrawWithTransparency);
  14.     r := VirtualScreen.ClipRect;
  15.     InvalidateRect(Self.Handle, @r, false);
  16.  
  17.     if LData.y < 48 * 7 then
  18.       Inc(LData.y, CStep)
  19.     else
  20.       LData.Done := TRUE;
  21.   end;
  22. end;
  23.  
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 05, 2020, 09:42:02 am
Ok, so you chose the whole column, that's a good idea.

To optimize further, you can tell the OS to invalidate only that column:

Thank you for the tip. I remember now that you had already given it to me, but I had forgotten it.  :-[

Here is a new version of the project. Now the user can change the size and the style of the grid. The pictures are created by the program imagefactory. If you want to play with it, you have to copy arial.ttf in imagefactory folder, and to use your own wood texture. I removed these files from the ZIP file to make it not too heavy.

I am glad of the result, except maybe for the colors. The choice of colors is a pain. If I would listen to myself, I would use only shades of gray.  :)

The question about animation is solved. But if ever you have other ideas, I would be glad, of course, to hear them.

Regards.

Roland

P.-S. I also deleted the 32x32 pictures, otherwise the attachment was still too big.


Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 05, 2020, 06:12:13 pm
I am glad of the result, except maybe for the colors. The choice of colors is a pain. If I would listen to myself, I would use only shades of gray.  :)

I think I have found something that will help me.  :)

https://www.w3schools.com/colors/colors_monochromatic.asp
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 07, 2020, 01:17:12 pm
Very nice.

Some remarks:
- Would be nice to have some gravity (vertical speed increased each time)
- Personally I don't like the green background. But the blue grid with red and yellow is just like in my memories :)
- Wait for the animation to finish to allow a new one
- Store at least how many tokens are in a column, to avoid replacing them
Title: Re: Animation for a four-in-a-row game
Post by: winni on September 07, 2020, 01:49:38 pm


Some remarks:
- Would be nice to have some gravity (vertical speed increased each time)


Hi!

Remember that a falling body has NOT a linear relationship to time:

distance = 1/2 * g * sqr (time)

Winni


Title: Re: Animation for a four-in-a-row game
Post by: circular on September 07, 2020, 10:21:19 pm
Yes. Increasing the speed each time amounts to have the position as a square of time.
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 08, 2020, 01:20:04 pm
@circular, winni

Thank you for your answers.

Very nice.

Thank you.  :)

Some remarks:
- Would be nice to have some gravity (vertical speed increased each time)
- Personally I don't like the green background. But the blue grid with red and yellow is just like in my memories :)
- Wait for the animation to finish to allow a new one
- Store at least how many tokens are in a column, to avoid replacing them

All that done, excepted for the gravity. (I tried to play with TTimer.Interval but it didn't work well. I will try to play with the step value.)

Here is the new version. Only the pictures for default style ("blue") are included. To create other styles, please compile and run imagefactory, or create your own style with imagefactory_gui.

The "style" submenu is created at runtime.

 
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 08, 2020, 05:46:01 pm
For gravity, here is how you can do:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. var
  3.   LRect: TRect;
  4.   LDisk: TBGRABitmap;
  5.   maxy: Integer;
  6.   nextTurn: boolean;
  7.   cushion: integer;
  8. begin
  9.   if not LData.Done then
  10.   begin
  11.     Inc(LData.y, LData.dy);
  12.     inc(LData.dy, LScale div 12); //gravity
  13.     maxy := LScale * (8 - LRow);
  14.     nextTurn := false;
  15.     if LData.y >= maxy then //touch bottom
  16.     begin
  17.       LData.y := maxy;
  18.       cushion := LScale div 4;
  19.       if LData.dy >= cushion then //enough energy to bounce?
  20.         LData.dy := -((LData.dy - cushion) div 2)
  21.       else
  22.       begin
  23.         LData.dy := 0;
  24.         nextTurn := true;
  25.       end;
  26.     end;
  27.  
  28.     FBuffer.PutImage(0, 0, FBackground, dmSet);
  29.     if LActiveColor then LDisk := FBlackDisk else LDisk := FWhiteDisk;
  30.     FBuffer.PutImage(LData.x, LData.y, LDisk, dmDrawWithTransparency);
  31.     FBuffer.PutImage(LScale - 4, LScale * 2 - 4, FGrid, dmDrawWithTransparency);
  32.     LRect := FBuffer.ClipRect;
  33.     InvalidateRect(Self.Handle, @LRect, FALSE);
  34.  
  35.     if nextTurn then
  36.     begin
  37.       LData.Done:= TRUE;
  38.       LActiveColor := not LActiveColor;
  39.     {$IFDEF DEBUG}
  40.       WriteLn(GridToText(LGrid));
  41.     {$ENDIF}
  42.     end;
  43.  
  44.   end;
  45. end;
Note: you need to add dy variable to LData and set it to zero when starting the animation.
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 08, 2020, 06:48:43 pm
For gravity, here is how you can do:

Very well done.  :)
Title: Re: Animation for a four-in-a-row game
Post by: winni on September 08, 2020, 07:11:41 pm
Hi

And where is the graviton?

It is postulated since ~ 1930.
My physics teacher told me that it wont take long  to find it.
50 years ago.
Until now nobody has seen it.

So Roland57 should draw some little gravitons in his game.
Will be the first seen gravitons ever!

Winni
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 08, 2020, 07:12:07 pm
Something else. I discovered, by starting the demo in a terminal, that there was this warning many times repeated:

Quote
WARNING: TGtk2WidgetSet.InvalidateRect refused invalidating during paint message: TForm1

If I understand correctly, the problem is that this operation

Code: Pascal  [Select][+][-]
  1. FBuffer.Draw(IMImage.Canvas, 0, 0, True);

calls automatically the FormPaint event. So I moved it outside the FormPaint procedure,

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. { ... }
  3. begin
  4.   { ... }
  5.     FBuffer.PutImage(LScale - 4, LScale * 2 - 4, FGrid, dmDrawWithTransparency);
  6.     FBuffer.Draw(IMImage.Canvas, 0, 0, True); // <----

and removed the FormPaint procedure. No more warning, and the result is still OK.

But after that I discovered another problem. The call to InvalidateRect

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. { ... }
  3. begin
  4.   { ... }
  5.     FBuffer.Draw(IMImage.Canvas, 0, 0, True);
  6.     LRect := FBuffer.ClipRect;
  7.     InvalidateRect(Self.Handle, @LRect, FALSE); // <----

becomes redundant: I can delete the line, the animation is still OK.

So what would be the correct approach?

Title: Re: Animation for a four-in-a-row game
Post by: winni on September 08, 2020, 07:38:41 pm
Something else. I discovered, by starting the demo in a terminal, that there was this warning many times repeated:

Quote
WARNING: TGtk2WidgetSet.InvalidateRect refused invalidating during paint message: TForm1

Hi!

I have seen that message so often and from so  many applications.
For me I have decided : I don't care anymore.

And I have not seen any problems with that.

Winni
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 08, 2020, 07:58:11 pm
I have seen that message so often and from so  many applications.
For me I have decided : I don't care anymore.

Hi! Thank you for your answer. Yes, maybe I could simply ignore it... But I would prefer to understand exactly how things work.
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 09, 2020, 07:20:43 am
I believed I had found a solution.

Code: Pascal  [Select][+][-]
  1.     //FBuffer.Draw(IMImage.Canvas, 0, 0, TRUE);
  2.     LRect := FBuffer.ClipRect;
  3.     //InvalidateRect(Self.Handle, @LRect, FALSE);
  4.     FBuffer.DrawPart(LRect, IMImage.Canvas, LRect.Left, LRect.Top, TRUE);

But this doesn't solve the problem, since the OS will still refresh all the window.  :-\

As far as I understand, the problem is the following: does it make sense to call InvalidateRect after an instruction (here FBuffer.Draw) which has already automatically called a repaint?

Is there a way to draw on a TImage (or another component) without calling automatically the OnPaint event?
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 09, 2020, 11:12:27 am
I don't understand why you get this message. The general rule is:
- draw on the control canvas only in its OnPaint event, which is what you did
- call InvalidateRect outside of the OnPaint event, which is what you did as well

Do you get the warning about InvalidateRect repeatedly or just once?
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 09, 2020, 01:28:37 pm
In fact, reading the code I realized there are some problems with the painting method.

If you paint things yourself on the controls, you would rather not use TImage, because this component is designed to store a image and display it itself. Instead I suggest you use a TPaintBox.

The painting need to be in the paint handler of the control. If you draw on TPaintBox, then you would set the OnPaint event of TPaintBox, not of TForm.

To sum up:
- replace the TImage by TPaintBox
- draw in the OnPaint event of TPaintBox
- invalidate the TPaintBox not the TForm
Title: Re: Animation for a four-in-a-row game
Post by: winni on September 09, 2020, 03:11:39 pm
Hi!

Or you use the advantages of the TImage:

* You are allowed to draw at any time on the Image.canvas - The Image update itself
* You dont need an invalidate or an update - the image does it isself.

There are a lot of advantages with a TImage:

It has a bitmap inside. So you can copy the image for whatever purpose.
You can save the data as PNG, BMP, JPEG or ...
with
Image.Picture.PNG.saveToFile (or stream).

And the Image data is persistent:
You dont have to care about redraw.

Too much people think about the Tinage only as a picture container.
But itis widely used for maps or image manipulation.

Has to be said.

Winni
Title: Re: Animation for a four-in-a-row game
Post by: lucamar on September 09, 2020, 03:58:18 pm
Or you use the advantages of the TImage:

[...]

Too much people think about the Tinage only as a picture container.
But itis widely used for maps or image manipulation.

I don't know why people insist on using (and worse, recommending) TImage for something for which it wasn't designed. That control was thought out as, indeed, a container to show images. When one needs a control into which one can load an image, modify it, draw in it, or whatever, the control to use is a TPaintBox.

Not to say, of course, that TImage cannot be used for it, but TPaintBox was designed exactly for that. :-\
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 09, 2020, 04:59:07 pm
TImage has its use, but it would be redundant with TBGRABitmap (you would need to draw the BGRABitmap on the TBitmap of TImage). Also you don't have much control on update.
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 09, 2020, 06:23:03 pm
Thank you all for your answers. The TPaintBox solution works perfectly. I will post the code when it is clean. Too tired tonight to make good work.  :)
Title: Re: Animation for a four-in-a-row game
Post by: winni on September 09, 2020, 07:35:19 pm

Not to say, of course, that TImage cannot be used for it, but TPaintBox was designed exactly for that. :-\

Hi!

Lets assume you want to draw a map of Europe. You have a very lot of Multi-Polygons, Polygons, Polylines and Points.
You want to draw the borders, the rivers, the roads, the railroads, the ferries. The big and the medium cities, the mountain peaks and the historical sites. And all the captions in different size and style. With a lot af Icons for the different needs.

And that all in the OnPaint event of a PaintBox??????

Keep on dreaming .....

Winni
Title: Re: Animation for a four-in-a-row game
Post by: Handoko on September 09, 2020, 08:20:51 pm
I think I understand what winni meant.

If you need doing drawing with lots of objects, for example building a painting software like GIMP or Krita. It is better to use TImage. As already said, the pixel data is persistent.

For example, I ever did realistic photo painting using Krita, that took me hours to finish it. If using TPaintBox to build that kind of software, we need a variable to store all my painting strokes var Data: array of TPaintAction. How long the array will be to store all my strokes? That should also includes my actions of changing brush type and size, color picking, erasing, etc. I believe the length of the array to store all the actions could be thousands or maybe more. Using TPaintBox is not good for this case. Computer memory maybe won't be an issue but, the performance won't be good.

The name TPaintBox is confusing. I think it should be TDrawBox. Because it is more suitable for drawing some simple shapes, lines, texts, etc. When I hear the word painting, I will think of the activity of stroking a canvas hundreds times using many different type of brushes.

Off course, in programming impossible can become possible. For this case (not the OP's case) we still can use TPaintBox and optimize the performance by using a buffer. The buffer only keeps the last result of all the user actions. So TPaintBox's OnPaint only needs to draw the buffer, it's very fast. I can be done but not correctly done.

Sorry off topic.  :D
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 09, 2020, 10:26:10 pm
In this case, you can use TBGRAVirtualScreen. It stores the content as a TBGRABitmap so you don't have to redraw everything all the time. It is a bit like a TImage in some sense, except that it triggers a Redraw event when the bitmap needs to be redrawn, and you can say what part of the image is to be redrawn.
Title: Re: Animation for a four-in-a-row game
Post by: winni on September 09, 2020, 10:42:05 pm
Quote
WARNING: TGtk2WidgetSet.InvalidateRect refused invalidating during paint message: TForm1

@Roland57

Sometimes the things seem to get better.

I recompiled an own programm which is some years old.
It was notorious  giving the above message every second - when the timer did some graphic update stuff.
Meanwhile " my" gtk2 and fpc/Lazarus are updated.
The warning is totaly gone with the exception of the programm start .
There it is shown once.

So I don't know whom to blame: gtk2 or fpc/Lazarus

How to get the latest version of fpc/Lazarus is written in this forum.

So what about your gtk2 version???

To get the version number from the bash is ridiculous:
Depends if you work with DEB- or RPM-packages and a lot of other things.

The easiest way is Pascal:

Code: Pascal  [Select][+][-]
  1. uses ......,gtk2;
  2. ......
  3. showMessage(IntToStr(gtk_major_version)+' / '+IntToStr(gtk_minor_version));
  4. .....
  5.  
  6.  

This shows for me 2 / 24

So: Update your system? Or ignore the message like I did for a long time.

Keep on hackin'

Winni

Title: Re: Animation for a four-in-a-row game
Post by: devEric69 on September 09, 2020, 10:57:53 pm
TImage versus TPaintBox? It depends.

If it's a question of making just a simple diagram (with graphical objects such as controls of different shapes, links, selection elastics with the mouse), a TPaintBox is more adapted to the management of the "chain of responsibility" of the lm_paint events processing (OnPaint), among the parent \ child controls (using the drawing helper functions, in order to redraw \ update only the new invalid areas \ rectangles).

The essential thing is that there is a canvas :) .
Title: Re: Animation for a four-in-a-row game
Post by: winni on September 09, 2020, 11:07:46 pm

The essential thing is that there is a canvas :) .

Hi!

That is not true.
You can do all work with BGRAbitmap and friends without ever using a Canvas.

You can use a Canvas - If you like it.
But then  you dont have Floating Point coordinates.

You should use the Canvas2D if you need HTML-like graphics.

So your statement about drawing is true for the LCL.
But not for all the graphic libraries.

Winni
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 10, 2020, 04:11:11 am
devEric69 is talking about the fact that we can actually display something whether it is with TImage or TPaintBox. So it is true.

What you say would be between TImage and TBGRAVirtualScreen or between TBitmap and TBGRABitmap.
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 12, 2020, 10:27:32 am
Thank you all for the informative discussion. Here is the final version of the demo. It is ready to be used for the Velena (https://github.com/rchastain/velena) project.  :)
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 12, 2020, 06:44:52 pm
Hi,

It works nicely.

For MacOS compatibility, you need to specify the path for the images. If you run the program as you would do on Linux or Windows, i.e., not using an application bundle, then the directory would be:
Code: Pascal  [Select][+][-]
  1. function GetImageDir: string;
  2. begin
  3.   result := ExtractFilePath(Application.ExeName) + 'images' + PathDelim;
  4. end;

And then use it when you access the images:
Code: Pascal  [Select][+][-]
  1. if FindFirst(GetImageDir+'*', faAnyFile or faDirectory, LRec) = 0 then

Note that when running the program without a bundle, you will get a terminal opening as well. But making an application bundle is another story.
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 13, 2020, 04:01:20 am
For MacOS compatibility, you need to specify the path for the images.

Hi! Thank you for the information and for the snippet. I incorporated it in the code.
Title: Re: Animation for a four-in-a-row game
Post by: Roland57 on September 13, 2020, 12:24:10 pm
The new version of Velena is ready.

https://github.com/rchastain/velena

@circular
Thank you very much for your contribution.  ;)
Title: Re: Animation for a four-in-a-row game
Post by: circular on September 13, 2020, 03:11:08 pm
You're welcome my friend  :)
Title: Re: Animation for a four-in-a-row game
Post by: winni on September 22, 2020, 02:56:58 am
@Roland57 - and to whom it may concern:

Again:

Code: Text  [Select][+][-]
  1. WARNING: TGtk2WidgetSet.InvalidateRect refused invalidating during paint message: TBGRAdialog
  2. WARNING: TGtk2WidgetSet.InvalidateRect refused invalidating during paint message: TBGRAdialog
  3. WARNING: TGtk2WidgetSet.InvalidateRect refused invalidating during paint message: TBGRAdialog
  4. WARNING: TGtk2WidgetSet.InvalidateRect refused invalidating during paint message: TBGRAdialog

The " Bad Boy" is TBCButton:
It has three states: normal,  hover, clicked

Every time the mouse goes over the Button the state and the design changes: from normal to hover. And with every MouseMove the above message is triggered four times.

So: Don't care about it.
It just says that it wants to paint again while it is painting.

Poor Painterman.

Winni
TinyPortal © 2005-2018