In this section we will create a graphical Hello World application. Copy and paste the following code into the editor. This is the same basic template we will use for all graphical applications.
program Workspace;
uses FTGraphics;
procedure Initialize;
begin
{Place any initialization code here.}
{This subroutine will be executed once when the program starts.}
end;
procedure OnEnterFrame;
begin
{Place drawing code here.}
end;
begin
{Do not execute code here because the window has not been created yet}
RunGraphicalApp(@Initialize,@OnEnterFrame);
{Do not execute code here because the window has already been destroyed}
end.
The anatomy of this code template has similarities to the console application template used previously, but there are some clear differences so let's go through it step-by-step. First, note the uses reserved word on Line 2. In order to display graphics, we need to create a window. Every operating system (Windows, Mac, Linux) does this differently and every operating system has different methods for drawing graphics onto windows. To greatly simplify this process, this book uses a library called FTGraphics that takes care of all of these differences and makes writing graphical apps much easier. This once again is an example of abstraction. We tell the compiler that we want to include the code in FTGraphics with the uses clause on Line 2.
Next, let's skip to the bottom where the begin..end statement is.
begin
{Do not execute code here because the window has not been created yet}
RunGraphicalApp(Initialize,OnEnterFrame);
{Do not execute code here because the window has already been destroyed}
end.
Notice the comments warn not to place any code inside the begin..end statement other than the call to RunGraphicalApp. This is a key difference between graphical applications and console applications. In console applications, much of the code will go between the main begin..end statements. Why would we write graphical applications with only 1 command in the main begin..end block? The reason is that graphical applications often need to respond to continuous user input. The program model we have used previously is a write-to-screen-then-wait model. Text is written and then the program waits to receive user input which is signaled by the press of the enter key. This is obviously not a model one would want for a game in which things continuously happen on the screen while the user is responding with the keyboard, mouse, or game console. The solution is known as a message loop. In a message loop, messages (such as keyboard or mouse input) are continuously logged for handling by the application. The single call to RunGraphicalApp creates the window, starts the message loop, and does not return from the call until the program exits (i.e., the close button is pressed). Thus, if we wrote code before the call to RunGraphicalApp, it would likely be invalid because the window has not even been created yet. If we write code after RunGraphicalApp, it will never be executed until the window is destroyed which is likely too late if we wanted anything graphical to happen.
So where do we write our code? Two subroutines--Initialize and OnEnterFrame--are provided for this purpose. Initialize is called once just after the graphical window has been created. OnEnterFrame is called several times per second to continuously update the graphics on the screen. The references to Initialize and OnEnterFrame are passed to RunGraphicalApp so that it can call them at the appropriate times (in programming terms, we say that Initialize and OnEnterFrame are
callbacks).
Because it is called multiple times per second, OnEnterFrame allows for animation. If we were dealing only with static pictures, we would simply draw lines and shapes on a 2D digital "canvas." However, since we will be dealing with animations, we want to draw multiple canvases and display them very rapidly to create motion. Each "canvas" we draw is called a
frame. How fast we display frames is known as the
frame rate. We measure frame rates in either frames per second (fps) or Hertz (the common metric unit for cycles per second also known as frequency). The higher the frame rate, the smoother the motion. For example, a frame rate of 1 frame per second (fps), does not create very realistic motion. A frame rate of 30fps does a very good job for most of the animations we will create. In fact, video cameras and movies routinely use 30fps as their frame rate. There is no need to go much higher than this for two reasons. First, the human brain has its own limitations in what it can actually perceive. For example, fluorescent bulbs flicker at a rates between 50-60Hz (depending on your country of residence), but the human brain perceives this lighting as near continuous. (Note that we say human brain here. Insect brains can actually perceive this flicker because their visual systems are faster due to the physical size of their nervous systems.) The second reason is that graphics take time to draw depending on the processor speed and the complexity of the drawing. Simple graphics on a 3GHz processor may take less than 1 millisecond while complex 3D graphics in games can take 20ms. To achieve 30fps, a single frame can take no longer than
to draw. If a frame takes longer than 33ms (which some of our graphics will), the FTGraphics engine will automatically slow the frame rate down so that the application does not become backlogged with drawing commands.
A final thing to note is that we no longer use the directive
{$APPTYPE CONSOLE} . We no longer need a console window because RunGraphicalApp creates a graphical window for us. If we leave
{$APPTYPE CONSOLE} at the top the program will still run, but two windows will be created. There are times when one might want both a graphical window and a console window, but we generally have no reason for both.
Run this program without making any changes and you will see that it creates a window with a blank background. Now press Ctrl+G and you will see the following:
All of the drawing commands in the FTGraphics library reference X and Y coordinates where X is the horizontal (left-right) position on the screen and Y is the vertical (up-down) position on the screen. The grid can be toggled on or off by using Ctrl+G. Another useful shortcut is Ctrl+C which will make a copy of the screen so it can be pasted into another application (generally using Ctrl+V though depending on the application it may have a different shortcut). Also, a copy can be saved as a bitmap image using Ctrl+S. This can become useful when you want to draw something in a simple paint program and then use the coordinates of the drawing to program it.
Excercise 1-1. In this exercise, we will draw a simple scene that includes the ground, the sun, and the sky (the world) along with the lines "Hello World".
FTGraphics defines the command FillRect(Left,Top,Right,Bottom,Color) which creates a rectangle filled with "Color" using the coordinates Left,Top,Right,Bottom. Create the "sky" and "ground" part of our world so that it looks like the following (Hint: The color constant for blue is clBlue and for brown is clBrown. You will need to add code only to OnEnterFrame.):
The rectangle is part of the scene so FillRect must be called inside of OnEnterFrame. Either the entire screen can be filled with blue and then a brown rectangle can be drawn on top of it or nonoverlapping blue and brown rectangles can be drawn. Either is equally valid:
Let's add the sun to the scene. The command for a filled ellipse is FillEllipse. A circle is simply an ellipse with equal axes. In other words FillEllipse(0,0,2,2) is a circle centered at (1,1). Place a yellow sun at (-11.5,7.5) with a radius of 1.5 (Hint: Expressions like -11.5+1.5 are perfectly valid as arguments to a procedure).
Here's one way to do this:
Finally, add the text Hello and World to the screen on two separate lines. We can do this with two commands: SetFont and TextOut. SetFont takes three arguments: FontName, FontColor, FontSize. For example, calling SetFont('Arial',clRed,56) gives red text at 56 points in the Arial font. TextOut takes three arguments: X, Y, and Text which are self-explanatory. A fourth argument, Alignment is an optional string which specifies the horizontal and vertical alignment of the text. By default, text is centered horizontally and vertically. You can try mixing and matching the letters 'l', 'c', and 'r' for horizontal alignment with 't', 'c', or 'b' for vertical alignemnt to see how text positioning is affected (with the grid on, of course).
Here's one way to do this by centering the word Hello at (0,1.5) and the word World at (0,-1.5):
procedure OnEnterFrame;
begin
FillRect(-14,10,14,-10,clBlue);
FillRect(-14,-7,14,-10,clBrown);
FillEllipse(-11.5-1.5,7.5-1.5,-11.5+1.5,7.5+1.5,clYellow);
SetFont('Arial',clRed,56);
TextOut(0,1.5,'Hello');
TextOut(0,-1.5,'World');
end;
Thus, after completing this exercise you've created your first graphical application: