Amateur Zone
Flicker Free Graphics
The first problem you find when trying to write a graphical program with moving objects is flicker. The required process of drawing background, drawing foreground objects and then repeating that process many times a second means that the viewer has a short period when they can see the objects disappear, i.e flicker. To avoid this you must use a system called page or screen flipping (if you already know this technique then skip down to the part that describes how to do it on Windows). This involves having a hidden screen that everything is drawn to, when drawing has finished completely you then display that screen. There are 2 ways to do that, either changing the memory address that the monitor displays to that of the hidden screen or copying over what is on the hidden screen to the visible screen. This is the basic technique that all games and visual programs use.
Screen Flipping Under Windows
Screen flipping is quite easy under Windows, but there are a few restrictions.
You cannot change the screen address, this means that the only way to do it
is to copy from the hidden screen to the visible using BITBLT. Also you really
should allow for all video modes, you cannot change the resolution or colour
depth of the screen (without using DirectX or messing up all the desktop
icons), so you have to be able react to whatever set-up your program is run on.
The first thing to do is to create the hidden screen, you can do this using
Windows API commands or GFA commands, I will use GFA commands for this
example as they are simpler for drawing, but APIs are generally better
(the equivalents for this bit are CreateCompatibleDC, CreateCompatibleBitmap
and SelectObject). You need to create a DC
(Device Context) and a bitmap. A DC can be thought of as a frame and the
bitmap as a piece of paper, the frame sits over the paper and holds all the
tools (objects) that are used when you draw on it. There are many different
types of objects, they include the colours
(brushes/pens) that will be used to draw and the typeface (font).
So here's the code to create a DC and bitmap both
compatible with the current screen mode and then select the bitmap into the
DC:
DEFWRD "a-z"
OPENW #1,100,100,422,252,%0001110000
HiddenDC=MEMDC(_DC(1))
HiddenBMP=CREATEBMP(400,200)
SETBMP HiddenDC,HiddenBMP
Now you have a hidden screen of size 400x200 to do with what you want.
Drawing to it is quite a simple process, if you use API commands then it is
really easy, all of them have a parameter of type HDC, just pass HiddenDC
for that and drawing will be directed to the HiddenBMP. If you want to use
GFA commands then you have to tell GFA where to draw using SETDC:
SETDC HiddenDC&
DEFFILL 0
GRAPHMODE R2_COPYPEN,TRANSPARENT
Now you just need a loop to do the drawing to the hidden screen and then
copy that to the visible screen (the window):
bx=200, by=100
bxv=RAND(2)*2-1, byv=RAND(2)*2-1
DO
PEEKEVENT
EXIT IF _Mess=WM_KEYDOWN OR MENU(1)=4
RGBCOLOR 0
PBOX 0,0,400,200
RGBCOLOR RGB(255,255,255)
PELLIPSE bx,by,20,20
BITBLT HiddenDC,0,0,400,200,_DC(1),10,10,SRCCOPY
ADD bx,bxv
ADD by,byv
IF bx>380
bx=380-(bx-380), bxv=-bxv
ELSE IF bx<20
bx=20+(20-bx), bxv=-bxv
ENDIF
IF by>180
by=180-(by-180), byv=-byv
ELSE IF by<20
by=20+(20-by), byv=-byv
ENDIF
LOOP
That's it, you now have a non-flicker animation of a circle bouncing around,
it's rubbish but then this is just an example. One thing to always remember
to do last is cleanup anything that has been created:
FREEDC HiddenDC
FREEBMP HiddenBMP
CLOSEW #1
Note here that you must free the DC first, you cannot delete an object while
it is still selected into a DC.
Click here to download the source code for this example
Windows Messages
Windows is a very complicated thing, you probably know that if you've tinkered with integrating your programs into the Windows environment. At the base of it all is the Windows messaging system. In GFA Basic there are so many ways to read windows messages, MENU(), _Mess and CB all have their advantages. I prefer CB for 90% of messages because wherever in your program messages have occurred, INKEY$, MOUSE or PEEKEVENT it calls the procedure you tell it to.
If you're not familiar with CB it stands for callback.
CB WIN(1),WM_PAINT,evalmsg
This means every time window #1 receives a WM_PAINT message it will call evalmsg. A routine used in CB must be like the following:
PROCEDURE evalmsg(handle&,message&,wordval&,longval%)
Handle& is the window the message is going to. Message& is the message e.g. WM_PAINT, WM_CLOSE etc.. Wordval& and longval% are different for each message, for example for WM_CHAR wordval& is the ASCII code of the key that was pressed. If you don't have the full version of GFA Basic you probably won't have a list of the windows messages. If this is the case maybe you should buy it, but if you haven't decided whether GFA Basic is for you yet the fantastic help file from Dale Bryant has detailed lists of windows messages, you can download it free from his web site. For Windows 95 messages, help files that come with other free programming languages such as Delphi and C++ Builder have got all the messages listed (of course there are no examples in BASIC, they're in C or Pascal). Remember when looking in other help files that GFA Basic doesn't recognise the Windows 3.1 (or newer) messages, API commands or constants, you can access them though, see the expert zone for details of using Windows 95 functions.
Be careful with callbacks, you can get some strange things happening. Don't ever call a PEEKEVENT or the like inside a callback routine, it's an instant, horrible crash. You can return values by using RETVAL. Usually it follows that returning 0 means that you've dealt with the message but some messages are different. For example WM_NCHITTEST returns what part of the window has been clicked on.
Manipulating CONTROLs
There is something about CONTROLs that the help files don't tell you and really makes you look at Windows in a different way when you know it. The fact is that all CONTROLs are windows, in the same way as the browser window you are reading this in is a window. Every time you see a combobox, or an edit box, it is a seperate window. When you know this manipulating CONTROLs is very easy. I find it best to use API commands to manipulate CONTROLs as the GFA equivilents are designed to be used with main windows. So, say you created a dialog and control like this (using the RCS):
DIALOG #0,70,50,300,200,"GFA Basic",$10000000 CONTROL "Text",100,"button",$50000000,20,20,80,30 ENDDIALOG SHOWDIALOG #0
Now to disable the button you can use:
~EnableWindow(DLGITEM(0,100),0)
You can move and resize it:
~MoveWindow(DLGITEM(0,100),5,5,100,100,1)
You can hide it:
~ShowWindow(DLGITEM(0,100),SW_HIDE)
And you can get rid of it forever:
~DestroyWindow(DLGITEM(0,100))
You can do almost anything to it that you can do to a window created with OPENW. To find out a lot of commands to manipulate windows have a look in the Windows 3.1 SDK (see downloads page).
There is also a short example of how to create CONTROLs using CreateWindowEx instead of CONTROL in the expert zone. This gives you a lot more control over how they look and act, and allows you to create CONTROLs in dialog boxes after the ENDDIALOG.
If you have any questions about animation, callbacks, Windows messages, CONTROLs or anything else to do with GFA Basic Windows programming you can e-mail me. I'll e-mail you back or if I get lots of the same questions I'll tackle it on this site.