Lesson 20: Libraries

What is a Library?

A library is a collection, or depository, of knowledge and completed works that can be referenced when needed. This is also true for programming libraries as they contain source code (the knowledge) that can be called upon when needed. Programming libraries allow you to store code that has been written and then the ability to reuse, or reference, that code at a later time. When writing software you are going to create many of the same routines over and over again, such as functions to convert between values, check for user input, and in the case of games, for instance,  detect when objects collide with one another. 

The QB64 functions and subroutines you create can be stored in library files and then attached to your code when needed. There is no need to write these same routines every time you start a new piece of code, in other words, you do not have to reinvent the wheel. Many programmers, such as myself, also share their libraries with other programmers to benefit from. Not only do these libraries help in writing code quickly but are also an excellent source of learning material for the new programmer. I've learned many new programming techniques by studying the libraries provided freely by others (looking at you SMcNeill and RhoSigma ... thank you!).

QB64 library files come in two flavors:

Note: The library file extensions .BI and .BM are not mandatory naming conventions but instead an agreed upon standard that QB64 programmers follow. Furthermore, the IDE recognizes files with the extensions of .BI and .BM as being QB64 source code files. Library files that need to be loaded at the top of source code could have the extension of .TOP and those loaded at the bottom have the extension of .BOT if you prefer. Just keep in mind, especially if you decide to share your libraries with other programmers, deviating from the agreed upon .BI and .BM extensions may be confusing.

QB64 library files are included within your source code through the use of two metacommand IDE directives:

Building a Library

You'll often write code that contains a function or subroutine that is very useful and can be used in other code projects you create. When this happens (and it will happen often) you can simply convert that code into the necessary .BI and .BM library files for later use. During this lesson we will be creating an object collision library by going through the steps needed to convert the useful code into library files. When finished with this lesson you'll have a library you can use to detect rectangular collisions, circular collisions, line collisions, and pixel perfect image collisions, something every game programmer needs to have in their library toolbox.

You have spent the last two days writing collision detection routines and have created four small programs as a result:

Load each of these programs into your IDE and execute them to see how they operate. When finished load RectCollide.BAS into your IDE so we can examine the code together.

( This code can be found at .\tutorial\Lesson20\RectCollide.BAS )

' Rectangle Collision Function
' Terry Ritchie 05/16/24
' quickbasic64@gmail.com
'
' Detect a collision between rectangular areas
'
TYPE POINT '                           2D point definition
    x AS INTEGER '                     x coordinate
    y AS INTEGER '                     y coordinate
END TYPE

TYPE RECTANGLE '                       2D rectangle definition
    TopLeft AS POINT '                 top left x,y location
    BottomRight AS POINT '             bottom right x,y location
    Width AS INTEGER '                 rectangle width
    Height AS INTEGER '                rectangle height
END TYPE

CONST SWIDTH% = 640 '                  screen width
CONST SHEIGHT% = 480 '                 screen height
CONST GREEN~& = _RGB32(0, 255, 0) '    color green
CONST RED~& = _RGB32(255, 0, 0) '      color red
CONST YELLOW~& = _RGB32(255, 255, 0) ' color yellow
DIM Rect_Intersect AS RECTANGLE '      intersection of collisions
DIM GreenRect AS RECTANGLE '           green rectangle
DIM YellowRect AS RECTANGLE '          yellow rectangle
DIM Collision AS INTEGER '             RectCollide return value (-1 if in collision)

GreenRect.Width = 100 '                                                    stationary rectangle size
GreenRect.Height = 100
GreenRect.TopLeft.x = (SWIDTH - GreenRect.Width) \ 2 '                     stationary rectangle location
GreenRect.TopLeft.y = (SHEIGHT - GreenRect.Height) \ 2 '                   (centered on screen)
YellowRect.Width = 50 '                                                    user rectangle size
YellowRect.Height = 50
SCREEN _NEWIMAGE(SWIDTH, SHEIGHT, 32) '                                    graphics screen
_MOUSEHIDE '                                                               hide mouse pointer
DO '                                                                       begin main program loop
   _LIMIT 30 '                                                             30 frames per second
   CLS '                                                                   clear screen
    Center 2, "Rectangle Collision Library Demo" '                         print header
    Center 28, "Use mouse to move object, ESC to exit." '                  print instructions
   WHILE _MOUSEINPUT: WEND '                                               get latest mouse update
    YellowRect.TopLeft.x = _MOUSEX '                                       get mouse pointer x location
    YellowRect.TopLeft.y = _MOUSEY '                                       get mouse pointer y location
    Collision = RectCollide(GreenRect, YellowRect) '                       test for a collision
   LINE (GreenRect.TopLeft.x, GreenRect.TopLeft.y)-_
        (GreenRect.BottomRight.x, GreenRect.BottomRight.y), GREEN, BF '    draw stationary rectangle
   LINE (YellowRect.TopLeft.x, YellowRect.TopLeft.y)-_
        (YellowRect.BottomRight.x, YellowRect.BottomRight.y), YELLOW, BF ' draw user rectangle
   IF Collision THEN '                                                     was there a collision?
       LINE (Rect_Intersect.TopLeft.x,Rect_Intersect.TopLeft.y)-_
            (Rect_Intersect.BottomRight.x,Rect_Intersect.BottomRight.y), RED, BF 'yes, draw red rectangle
        Center 4, "COLLISION!" '                                           inform user collision occurring
   END IF
   _DISPLAY '                                                              update screen with changes
LOOP UNTIL _KEYDOWN(27) '                                                  leave when ESC key pressed
SYSTEM '                                                                   return to operating system

'------------------------------------------------------------------------------------------------------------
FUNCTION RectCollide (R1 AS RECTANGLE, R2 AS RECTANGLE)
    '--------------------------------------------------------------------------------------------------------
    '- Checks for a collision between two rectangular areas.                                                -
    '- Returns: 0 if no collision, -1 if collision                                                          -
    '-        : Rect_Intersect.TopLeft.x, Rect_Intersect.TopLeft.y, Rect_Intersect.BottomRight.x, and       -
    '-        : Rect_Intersect.BottomRight.y will contain the x,y coordinates of the collision              -
    '-        : overlapping area.                                                                           -
    '-        : Rect_Intersect.Width and Rect_Intersect.Height will contain the width and height of the     -
    '-        : collision overlapping area.                                                                 -
    '--------------------------------------------------------------------------------------------------------
   SHARED Rect_Intersect AS RECTANGLE ' intersection of collision

    R1.BottomRight.x = R1.TopLeft.x + R1.Width - 1 '                 calculate bottom right x of rectangle 1
    R1.BottomRight.y = R1.TopLeft.y + R1.Height - 1 '                calculate bottom right y of rectangle 1
    R2.BottomRight.x = R2.TopLeft.x + R2.Width - 1 '                 calculate bottom right x of rectangle 2
    R2.BottomRight.y = R2.TopLeft.y + R2.Height - 1 '                calculate bottom right y of rectangle 2
    RectCollide = 0 '                                                assume no collision
   IF R1.BottomRight.x >= R2.TopLeft.x THEN '                        check for overlapping coordinates
       IF R1.TopLeft.x <= R2.BottomRight.x THEN
           IF R1.BottomRight.y >= R2.TopLeft.y THEN
               IF R1.TopLeft.y <= R2.BottomRight.y THEN '            all checks passed, must be a collision

                    ' calculate collision area and return as a rectangle in Rect_Intersect

                   IF R1.TopLeft.x > R2.TopLeft.x THEN
                        Rect_Intersect.TopLeft.x = R1.TopLeft.x
                   ELSE
                        Rect_Intersect.TopLeft.x = R2.TopLeft.x
                   END IF
                   IF R1.TopLeft.y > R2.TopLeft.y THEN
                        Rect_Intersect.TopLeft.y = R1.TopLeft.y
                   ELSE
                        Rect_Intersect.TopLeft.y = R2.TopLeft.y
                   END IF
                   IF R1.BottomRight.x < R2.BottomRight.x THEN
                        Rect_Intersect.BottomRight.x = R1.BottomRight.x
                   ELSE
                        Rect_Intersect.BottomRight.x = R2.BottomRight.x
                   END IF
                   IF R1.BottomRight.y < R2.BottomRight.y THEN
                        Rect_Intersect.BottomRight.y = R1.BottomRight.y
                   ELSE
                        Rect_Intersect.BottomRight.y = R2.BottomRight.y
                   END IF
                    Rect_Intersect.Width = Rect_Intersect.BottomRight.x - Rect_Intersect.TopLeft.x
                    Rect_Intersect.Height = Rect_Intersect.BottomRight.y - Rect_Intersect.TopLeft.y
                    RectCollide = -1 '                             return that a collision was detected
               END IF
           END IF
       END IF
   END IF

END FUNCTION
'------------------------------------------------------------------------------------------------------------
SUB Center (row AS INTEGER, text AS STRING)
    '--------------------------------------------------------------------------------------------------------
    '- Centers the text on screen in the provided row -
    '-                                                -
    '- row  - the text row                            -
    '- text - the text string to center               -
    '--------------------------------------------------

   LOCATE row, (SWIDTH \ 8 - LEN(text)) \ 2 ' locate the cursor
   PRINT text; '                              print the text

END SUB
'------------------------------------------------------------------------------------------------------------

Figure 1: Rectangle Collision Demo

In this code the function we would like to use in other programs for rectangular collision detection is RectCollide starting on line 60 and ending on line 112. Before a function or subroutine can be converted into a library .BM file a few questions need to be answered first:

Not all libraries need to have both a .BI and .BM library file associated with them. Many times a function or subroutine does not need any external variable declarations to operate correctly, in other words the function or subroutine can operate stand alone as a mini program itself. In this case only a .BM library file is needed. Some .BI files contain nothing but a listing of constants, such as common colors or keyboard scan codes, and don't have the need for an associated .BM library file.

However, with RectCollide this will not be possible. The RectCollide FUNCTION line itself tells us this:

FUNCTION RectCollide (R1 AS RECTANGLE, R2 AS RECTANGLE)

There is no variable type known as RECTANGLE that QB64 recognizes. RECTANGLE is a user defined type (UDT) that was created in lines 12 through 17 of the code:

TYPE RECTANGLE '                       2D rectangle definition
    TopLeft AS POINT '                 top left x,y location
    BottomRight AS POINT '             bottom right x,y location
    Width AS INTEGER '                 rectangle width
    Height AS INTEGER '                rectangle height
END TYPE

Furthermore, the RECTANGLE type incorporates another user defined type called POINT that is defined in lines 7 through 10 of the code:

TYPE POINT '                           2D point definition
    x AS INTEGER '                     x coordinate
    y AS INTEGER '                     y coordinate
END TYPE

Looking further into the function we see another reference to RECTANGLE on line 70:

   SHARED Rect_Intersect AS RECTANGLE ' intersection of collision

The variable Rect_Intersect is being SHARED here meaning that is was declared at the top of the code. We find that declaration in line 24:

DIM Rect_Intersect AS RECTANGLE '      intersection of collisions

What this means is that our rectangle collision library is going to need an associated .BI library file that includes these variable declarations so the RectCollide function can operate correctly. 

Create the .BI Library File

Start a new program in your IDE and type in, or copy and paste, the code from the original program listing to create the new library file:

( This code can be found at .\tutorial\Lesson20\LIB_RectCollide.BI )

' Collision Library V1.0
' Terry Ritchie 05/16/24
' quickbasic64@gmail.com
'
' LIB_RectCollide.BI
'
' To be placed at the top of the main source code before variable and type definitions
'
' Detect a collision between rectangular areas

TYPE POINT '                      2D point definition
    x AS INTEGER '                x coordinate
    y AS INTEGER '                y coordinate
END TYPE

TYPE RECTANGLE '                  2D rectangle definition
    TopLeft AS POINT '            top left x,y location
    BottomRight AS POINT '        bottom right x,y location
    Width AS INTEGER '            rectangle width
    Height AS INTEGER '           rectangle height
END TYPE

DIM Rect_Intersect AS RECTANGLE ' intersection of collisions

Save the code listing as LIB_RectCollide.BI once you are finished.

We now have a library include file that contains the variable declarations needed for the RectCollide function to operate correctly. All that is left to do now is create the .BM library file that contains the RectCollide function.

Create the .BM Library File

Start a new program in your IDE and type in, or copy and paste, the code from the original program listing to create the new .BM library file:

( This code can be found at .\tutorial\Lesson20\LIB_RectCollide.BM )

' Rectangle Collision Library V1.0
' Terry Ritchie 05/16/24
' quickbasic64@gmail.com
'
' LIB_RectCollide.BM
'
' To be placed at the bottom of the main source code.
'
' LIB_RectCollide.BI is required at the top of the main source code for this library to function.
'
' *****************************************************
' * Rectangle collision between two rectangular areas *
' *****************************************************
'
'    collision% = RectCollide(Rectangle1, Rectangle2)
'
'    collision% will contain 0 (zero) if the two supplied rectangular areas are not in collision or -1
'    if the areas are in collision (0 for false and -1 for true).
'
'    The rectangular areas are defined using the RECTANGLE type definition found in RectCollide.BI:
'
'    DIM Rectangle1 AS RECTANGLE ' rectangular areas
'    DIM Rectangle2 AS RECTANGLE
'
'    The RECTANGLE type definition contains the following subvariables that can be set:
'
'       .TopLeft.x     (integer) - the top left X coordinate of the rectangular area      *
'       .TopLeft.y     (integer) - the top left Y coordinate pf the rectangular area      *
'       .BottomRight.x (integer) - the bottom right X coordinate of the rectangular area
'       .BottomRight.y (integer) - the bottom right Y coordinate of the rectangular area
'       .Width         (integer) - the width of the rectangular area                      *
'       .Height        (integer) - the height of the rectangular area                     *
'
'       * = these subvariables must be set for the RectCollide() function to return useful results.
'
'                   RecTangle1.TopLeft.x,
'                   Rectangle1.TopLeft.y
'                            +-------------------------------------------------+
'                            |              ^                                  |
'                            |<-------------|----- Rectangle1.Width ---------->|
'                            |              |                                  |
'                            |              |                                  |
'                            |     Rectangle1.Height                           |
'                            |              |                                  |
'                            |              |                                  |
'                            |              V                                  |
'                            +-------------------------------------------------+
'                                                                  Rectangle1.BottomRight.x,
'                                                                  Rectangle1.BottomRight.y
'
'    When the RectCollide() function is called .BottomRight.x and .BottomRight.y are automatically
'    calculated from the supplied .Topleft.x, .TopLeft.y, .Width, and .Height values supplied.
'
'    If a collision is detected the variable Rect_Intersect will contain the overlapping coordinates of the
'    collision:
'
'       Rect_Intersect.TopLeft.x     (integer) - the top left X coordinate of the overlapping area
'       Rect_Intersect.TopLeft.y     (integer) - the top left Y coordinate of the overlapping area
'       Rect_Intersect.BottomRight.x (integer) - the bottom right X coordinate of the overlapping area
'       Rect_Intersect.BottomRight.y (integer) - the bottom right Y coordinate of the overlapping area
'       Rect_Intersect.Width         (integer) - the width of the overlapping area
'       Rect_Intersect.Height        (integer) - the height of the overlapping area
'
'       +----------------------------------------------------------------------------+
'       | Rectangle1                                                                 |
'       |                                                                            |  * = overlapping area
'       |   Rect_Intersect.TopLeft.x,                                                |
'       |   Rect_Intersect.TopLeft.y                                                 |
'       |        --------> +*********************************************************+----------------------+
'       |                  *       ^                                                 *                      |
'       |                  *<------|------- Rect_Intersect.Width ------------------->*                      |
'       |                  *       |                                                 *                      |
'       |                  *   Rect_Intersect.Height                                 *                      |
'       |                  *       |                          (overlapping area)     *                      |
'       |                  *       V                                                 *                      |
'       +------------------+*********************************************************+ <--------            |
'                          |                                           Rect_Intersect.BottomRight.x,        |
'                          |                                           Rect_Intersect.BottomRight.y         |
'                          |                                                                                |
'                          |                                                                     Rectangle2 |
'                          +--------------------------------------------------------------------------------+
'
'------------------------------------------------------------------------------------------------------------
FUNCTION RectCollide (R1 AS RECTANGLE, R2 AS RECTANGLE)
    '--------------------------------------------------------------------------------------------------------
    '- Checks for a collision between two rectangular areas.                                                -
    '- Returns: 0 if no collision, -1 if collision                                                          -
    '-        : Rect_Intersect.TopLeft.x, Rect_Intersect.TopLeft.y, Rect_Intersect.BottomRight.x, and       -
    '-        : Rect_Intersect.BottomRight.y will contain the x,y coordinates of the collision              -
    '-        : overlapping area.                                                                           -
    '-        : Rect_Intersect.Width and Rect_Intersect.Height will contain the width and height of the     -
    '-        : collision overlapping area.                                                                 -
    '--------------------------------------------------------------------------------------------------------

   SHARED Rect_Intersect AS RECTANGLE ' intersection of collision (declared in RectCollide.BI)

    R1.BottomRight.x = R1.TopLeft.x + R1.Width - 1  '                calculate bottom right x of rectangle 1
    R1.BottomRight.y = R1.TopLeft.y + R1.Height - 1 '                calculate bottom right y of rectangle 1
    R2.BottomRight.x = R2.TopLeft.x + R2.Width - 1 '                 calculate bottom right x of rectangle 2
    R2.BottomRight.y = R2.TopLeft.y + R2.Height - 1 '                calculate bottom right y of rectangle 2
    RectCollide = 0 '                                                assume no collision
   IF R1.BottomRight.x >= R2.TopLeft.x THEN '                        check for overlapping coordinates
       IF R1.TopLeft.x <= R2.BottomRight.x THEN
           IF R1.BottomRight.y >= R2.TopLeft.y THEN
               IF R1.TopLeft.y <= R2.BottomRight.y THEN '            all checks passed, must be a collision

                    ' calculate collision area and return as a rectangle in Rect_Intersect

                   IF R1.TopLeft.x > R2.TopLeft.x THEN
                        Rect_Intersect.TopLeft.x = R1.TopLeft.x
                   ELSE
                        Rect_Intersect.TopLeft.x = R2.TopLeft.x
                   END IF
                   IF R1.TopLeft.y > R2.TopLeft.y THEN
                        Rect_Intersect.TopLeft.y = R1.TopLeft.y
                   ELSE
                        Rect_Intersect.TopLeft.y = R2.TopLeft.y
                   END IF
                   IF R1.BottomRight.x < R2.BottomRight.x THEN
                        Rect_Intersect.BottomRight.x = R1.BottomRight.x
                   ELSE
                        Rect_Intersect.BottomRight.x = R2.BottomRight.x
                   END IF
                   IF R1.BottomRight.y < R2.BottomRight.y THEN
                        Rect_Intersect.BottomRight.y = R1.BottomRight.y
                   ELSE
                        Rect_Intersect.BottomRight.y = R2.BottomRight.y
                   END IF
                    Rect_Intersect.Width = Rect_Intersect.BottomRight.x - Rect_Intersect.TopLeft.x
                    Rect_Intersect.Height = Rect_Intersect.BottomRight.y - Rect_Intersect.TopLeft.y
                    RectCollide = -1 '                             return that a collision was detected
               END IF
           END IF
       END IF
   END IF

END FUNCTION

Save the code listing as LIB_RectCollide.BM once you are finished.

Notice that not only was the RectCollide function added to the .BM file above, instructional documentation on how to use the function was also written to accompany it. Get into the habit of documenting your libraries! Yes, it takes time, but I guarantee your libraries will mean nothing without it. Six months from now you will not remember how to use this function. Furthermore, if you share the library without instructional documentation others will be unable to benefit from it.

Note: Since LIB_RectCollide.BI and LIB_RectCollide.BM are supplied within the tutorial asset file you more than likely elected not to type them in but instead load them into your IDE. I don't blame you, that .BM file is a lot to type in. You will eventually need to do this though when it comes time to create your own libraries. Obviously, copying and pasting the code into the .BI and .BM files is the easiest method to use. You can do this using a secondary text editor such as the Windows Notepad, NotePad++, or open a second instance of the IDE (yes, you can have multiple instances of the IDE running). The choice is yours but you will need some sort of secondary text editor to make creating library files from existing code easier.

Now we need to test the library files we created by modifying the original code to use them.

The $INCLUDE IDE Metacommand

The $INCLUDE IDE metacommand is used to include library files into your code with the following syntax:

'$INCLUDE:'LibraryName'

LibraryName is the name of the library file, such as LIB_RectCollide.BI, that you wish to include in your source code. The $INCLUDE metacommand must be preceded by a REM ( ' ) statement. The library name must be surrounded by single quotes ( ' ) (the same character as the REM statement) as well.

Modify the original program, RectCollide.BAS, to the code seen in the listing below and then save the new code as LIB_RectCollide_Demo.BAS:

( This code can be found at .\tutorial\Lesson20\LIB_RectCollide_Demo.BAS )

'
' Rectangular Collision Demonstration Using Rectangle Collision Library
'
' Include the rectangle collision detection library variable declarations
'
' $INCLUDE:'.\tutorial\Lesson20\lib_rectcollide.bi'
'
CONST SWIDTH% = 640 '                  screen width
CONST SHEIGHT% = 480 '                 screen height
CONST GREEN~& = _RGB32(0, 255, 0) '    color green
CONST RED~& = _RGB32(255, 0, 0) '      color red
CONST YELLOW~& = _RGB32(255, 255, 0) ' color yellow
DIM GreenRect AS RECTANGLE '           green rectangle  (RECTANGLE TYPE provided by library)
DIM YellowRect AS RECTANGLE '          yellow rectangle (RECTANGLE TYPE provided by library)
DIM Collision AS INTEGER '             RectCollide return value (-1 if in collision)

GreenRect.Width = 100 '                                                    stationary rectangle size
GreenRect.Height = 100
GreenRect.TopLeft.x = (SWIDTH - GreenRect.Width) \ 2 '                     stationary rectangle location
GreenRect.TopLeft.y = (SHEIGHT - GreenRect.Height) \ 2 '                   (centered on screen)
YellowRect.Width = 50 '                                                    user rectangle size
YellowRect.Height = 50
SCREEN _NEWIMAGE(SWIDTH, SHEIGHT, 32) '                                    graphics screen
_MOUSEHIDE '                                                               hide mouse pointer
DO '                                                                       begin main program loop
   _LIMIT 30 '                                                             30 frames per second
   CLS '                                                                   clear screen
    Center 2, "Rectangle Collision Library Demo" '                         print header
    Center 28, "Use mouse to move object, ESC to exit." '                  print instructions
   WHILE _MOUSEINPUT: WEND '                                               get latest mouse update
    YellowRect.TopLeft.x = _MOUSEX '                                       get mouse pointer x location
    YellowRect.TopLeft.y = _MOUSEY '                                       get mouse pointer y location
    Collision = RectCollide(GreenRect, YellowRect) '                       test for a collision
   LINE (GreenRect.TopLeft.x, GreenRect.TopLeft.y)-_
        (GreenRect.BottomRight.x, GreenRect.BottomRight.y), GREEN, BF '    draw stationary rectangle
   LINE (YellowRect.TopLeft.x, YellowRect.TopLeft.y)-_
        (YellowRect.BottomRight.x, YellowRect.BottomRight.y), YELLOW, BF ' draw user rectangle
   IF Collision THEN '                                                     was there a collision?
       LINE (Rect_Intersect.TopLeft.x,Rect_Intersect.TopLeft.y)-_
            (Rect_Intersect.BottomRight.x,Rect_Intersect.BottomRight.y), RED, BF 'yes, draw red rectangle
        Center 4, "COLLISION!" '                                           inform user collision occurring
   END IF
   _DISPLAY '                                                              update screen with changes
LOOP UNTIL _KEYDOWN(27) '                                                  leave when ESC key pressed
SYSTEM '                                                                   return to operating system

'
' Local subroutines and functions
'

'------------------------------------------------------------------------------------------------------------
SUB Center (row AS INTEGER, text AS STRING)
    '--------------------------------------------------------------------------------------------------------
    '- Centers the text on screen in the provided row -
    '-                                                -
    '- row  - the text row                            -
    '- text - the text string to center               -
    '--------------------------------------------------

   LOCATE row, (SWIDTH \ 8 - LEN(text)) \ 2 ' locate the cursor
   PRINT text; '                              print the text

END SUB
'------------------------------------------------------------------------------------------------------------

'
' Include the rectangle collision detection library subroutines and/or functions
'
' $INCLUDE:'.\tutorial\Lesson20\lib_rectcollide.bm'

Go ahead and run the code to see that we get the same result. Line 6 of the code:

' $INCLUDE:'.\tutorial\Lesson20\lib_rectcollide.bi'

added the variable declarations needed for the RectCollide function that was added by the $INCLUDE metacommand in line 69 of the code:

' $INCLUDE:'.\tutorial\Lesson20\lib_rectcollide.bm'

As far as the IDE is concerned the code contained in LIB_RectCollide.BI and LIB_RectCollide.BM is part of your source code as though you typed it in along with the rest of the code. Annotations were also made in the body of the source code to identify where library user data types were used:

DIM GreenRect AS RECTANGLE '           green rectangle  (RECTANGLE TYPE provided by library)
DIM YellowRect AS RECTANGLE '          yellow rectangle (RECTANGLE TYPE provided by library)

A programmer would know to use RECTANGLE by the instructional documentation you provided within the library code. Furthermore, the structure of the RECTANGLE user data type would also be explained so the programmer can set and use the associated values as needed as seen in lines 17 through 22 of the code:

GreenRect.Width = 100 '                                                     stationary rectangle size
GreenRect.Height = 100
GreenRect.TopLeft.x = (SWIDTH - GreenRect.Width) \ 2 '                      stationary rectangle location
GreenRect.TopLeft.y = (SHEIGHT - GreenRect.Height) \ 2 '                    (centered on screen)
YellowRect.Width = 50 '                                                     user rectangle size
YellowRect.Height = 50

Again, that documentation is important! Without it you'll be wondering six months from now, "what the heck was I thinking here?"

This same procedure now needs to be done with the remaining collision detection programs CircCollide.BAS, LineCollide.BAS, and PixelCollide.BAS. I took the liberty of completing that task for you. The following library files were created and can be found in the .\tutorial\Lesson20 subdirectory:

Take some time to look through each of these code files and run the demos to see the results. You can now use these individual libraries to detect collisions within your software. However, there is a problem lurking within these libraries that we can reveal. What would happen if I wanted to add two or more of these libraries at the same time? I want to write code that detects both rectangular and circular collisions. Is this possible? Yes, you can include as many library files as you like as long as they are properly prepared. The libraries we just created are not properly prepared.

Start a new program in the IDE and type the following two lines of code in:

'$INCLUDE:'.\tutorial\Lesson20\lib_rectcollide.bi'

'$INCLUDE:'.\tutorial\Lesson20\lib_circcollide.bi'

As soon as you finish typing in the second line of code the IDE generates an error in the status window (your file location will differ):

Name already in use (POINT) in line 11 of C:\Users\Terry\Desktop\QB64PE\tutorial\Lesson20\lib_circcollide.bi included on line 2

This error is generated because both of the .BI library files contain the following identical lines of code:

TYPE POINT '                      2D point definition
    x AS INTEGER '                x coordinate
    y AS INTEGER '                y coordinate
END TYPE

The user defined type POINT has already been declared in LIB_RectCollide.BI so when LIB_CircCollide.BI attempts this again the error is generated because the name POINT has already been used meaning a duplicate definition has occurred. One way to handle this would be to remove the POINT user defined type declaration from one of the .BI files. This however would render the library in which it was removed useless as a standalone library. Another method to handle this would be to create another library that contains both the rectangular collision and circular collision code and name the library files LIB_RectCircCollide.BI and LIB_RectCircCollide.BM. But what if another one of your libraries contains the user define type POINT and you wish to use it too? Do you need to create yet another set of library files?

These questions and issues have been recently resolved with the addition of a new library metacommand that addresses this.

The $INCLUDEONCE IDE Metacommand

The ultimate goal is for the user defined type POINT to only be included once even though it appears multiple times in the library files. The $INCULDEONCE IDE metacommand was created to do this.

Start a new program in your IDE and type in the following code and then save it as LIB_TYPE_POINT_INTEGER.BI:

( This code can be found at .\tutorial\Lesson20\LIB_TYPE_POINT_INTEGER.BI )

' LIB_TYPE_POINT_INTEGER.BI
'
' Defines an x,y integer point pair
'
$INCLUDEONCE

TYPE POINT '        2D x,y point definition
    x AS INTEGER '  x integer coordinate
    y AS INTEGER '  y integer coordinate
END TYPE

Note: unlike $INCLUDE, $INCLUDEONCE does not require a REM ( ' ) statement to precede it.

What we have created here is a library include file to be used within other library include files. The $INCLUDEONCE statement tells the IDE to only load this library file one time. Subsequent attempts to load this file will be ignored. Yes, this generates more library files but the payoff is well worth it.

You can now remove the TYPE POINT...END TYPE construct from LIB_CircCollide.BI, LIB_LineCollide.BI, LIB_PixelCollide.BI, and LIB_RectCollide.BI replacing it with a single $INCLUDE statement:

' Circle Collision Library V1.0
' Terry Ritchie 05/16/24
' quickbasic64@gmail.com
'
' LIB_CircCollide.BI
'
' To be placed at the top of the main source code before variable and type definitions
'
' Detect a collision between circular areas

'$INCLUDE:'.\tutorial\Lesson20\lib_type_point_integer.bi'

TYPE CIRCLE '                 circle definition
    Center AS POINT '         center of circle
    Radius AS INTEGER '       circle radius
END TYPE

DIM Circ_Intersect AS POINT ' circle collision point

The above $INCLUDE statement will load and create the user defined type POINT in each one of your library .BI files. This will now work correctly:

'$INCLUDE:'.\tutorial\Lesson20\lib_rectcollide.bi'

'$INCLUDE:'.\tutorial\Lesson20\lib_circcollide.bi'

LIB_RectCollide.BI will create the user defined type POINT. When LIB_CircCollide.BI attempts to do this however the $INCLUDEONCE statement will detect that LIB_TYPE_POINT_INTEGER.BI has already been loaded preventing it from loading again. Now you can mix and match your four individual collision detection libraries as needed.

Why was _INTEGER attached to the .BI filename LIB_TYPE_POINT_INTEGER.BI? Not all coordinate points are created equal. Many times, especially when dealing with vector math, you'll want to store your coordinate points as SINGLE values to achieve greater precision during movement updates for instance. Therefore, I also have this handy library file for just such occasions:

' LIB_TYPE_SPOINT_SINGLE.BI
'
' Defines an x,y single point pair
'
$INCLUDEONCE

TYPE SPOINT '      2D x,y point definition
    x AS SINGLE '  x single coordinate
    y AS SINGLE '  y single coordinate
END TYPE

I also CAPITALIZE what I call my common use libraries to make them stand out in directory listings. My common use libraries are those that are used in multiple other libraries and incorporate the $INCLUDEONCE statement. (See the Maintaining Libraries section below)

In retrospect after creating this lesson I should probably rename my LIB_TYPE_POINT_INTEGER.BI library to LIB_TYPE_IPOINT_INTEGER.BI to be consistent with LIB_TYPE_SPOINT_SINGLE.BI. Or better yet simply name them LIB_TYPE_IPOINT.BI and LIB_TYPE_SPOINT.BI since the S and I identify their types. Yes, that is what I'll do. For consistency of the lesson however we'll leave the name the same. This is what I love about programming, always thinking of better ways to do things.

Putting It All Together

Instead of having four separate collision detection libraries lets combine them into one master collision detection library. The QB64 compiler will only include library code that is actually referenced and used. Therefore, you could have a library with hundreds of functions and subroutines attached to your code but only those functions and subroutines that are used are included during the compilation of your code. In other words a large library file included in the code does not equate to a larger .EXE file when compiled. The code that is not used is ignored.

First, we'll need to convert the remaining user data TYPEs in the .BI files to individual .BI files. Those data types, such as RECTANGLE, CIRCLE, LINE, and SPRITE, are also useful elsewhere and run the risk of being added to future code. Creating each data TYPE in a separate .BI file that includes $INCLUDEONCE ensures this won't come back to haunt you later.

Create the following .BI files that incorporate the data TYPEs starting with LIB_TYPE_CIRCLE.BI:

( This code can be found at .\tutorial\Lesson20\LIB_TYPE_CIRCLE.BI )

' LIB_TYPE_CIRCLE.BI
'
' Defines a circle
'
$INCLUDEONCE

'$INCLUDE:'.\tutorial\Lesson20\lib_type_point_integer.bi'

TYPE CIRCLE '           circle definition
    Center AS POINT '   center x,y coordinate of circle (defined in LIB_TYPE_POINT_INTEGER.BI)
    Radius AS INTEGER ' radius of circle
END TYPE

Since this .BI file is dependent on the POINT user data type defined in LIB_TYPE_POINT_INTEGER.BI we'll need to $INCLUDE that .BI file as seen above.

Now create LIB_TYPE_LINE.BI:

( This code can be found at .\tutorial\Lesson20\LIB_TYPE_LINE.BI )

' LIB_TYPE_LINE.BI
'
' Defines a line segment
'
$INCLUDEONCE

'$INCLUDE:'.\tutorial\Lesson20\lib_type_point_integer.bi'

TYPE LINE '           line segment definition
    Start AS POINT '  x,y start of line segment (defined in LIB_TYPE_POINT_INTEGER.BI)
    Finish AS POINT ' x,y end of line segment   (defined in LIB_TYPE_POINT_INTEGER.BI)
END TYPE

Next, create LIB_TYPE_RECTANGLE.BI:

( This code can be found at .\tutorial\Lesson20\LIB_TYPE_RECTANGLE.BI )

' LIB_TYPE_RECTANGLE.BI
'
' Defines a rectangle
'
'$INCLUDEONCE

'$INCLUDE:'.\tutorial\Lesson20\lib_type_point_integer.bi'

TYPE RECTANGLE '           rectangle definition
    TopLeft AS POINT '     top left x,y coordinate     (defined in LIB_TYPE_POINT_INTEGER.BI)
    BottomRight AS POINT ' bottom right x,y coordinate (defined in LIB_TYPE_POINT_INTEGER.BI)
    Width AS INTEGER '     rectangle width
    Height AS INTEGER '    rectangle height
END TYPE

And finally, create LIB_TYPE_SPRITE.BI:

( This code can be found at .\tutorial\Lesson20\LIB_TYPE_SPRITE.BI )

' LIB_TYPE_SPRITE.BI
'
' Defines a sprite image
'
$INCLUDEONCE

'$INCLUDE:'.\tutorial\Lesson20\lib_type_point_integer.bi'

TYPE SPRITE '              sprite definition
    Image AS LONG '        sprite image
    TopLeft AS POINT '     top left x,y coordinate     (defined in LIB_TYPE_POINT_INTEGER.BI)
    BottomRight AS POINT ' bottom right x,y coordinate (defined in LIB_TYPE_POINT_INTEGER.BI)
END TYPE

Think of these .BI files as building blocks or LEGO pieces to build more advanced libraries. Let's name our final collision library as LIB_Collision which will require a .BI as well as a .BM library file.

Create the first collision library file LIB_Collision.BI:

( This code can be found at .\tutorial\Lesson20\LIB_Collision.BI )

' Collision Library V1.0
' Terry Ritchie 05/16/24
' quickbasic64@gmail.com
'
' LIB_Collision.BI
'
' Collision library definitions
'
' To be included at the top of source code
'
' LIB_Collision.BM is required at the bottom of the source code for this library to function
'
'$INCLUDE:'.\tutorial\Lesson20\lib_type_point_integer.bi'
'$INCLUDE:'.\tutorial\Lesson20\lib_type_circle.bi'
'$INCLUDE:'.\tutorial\Lesson20\lib_type_line.bi'
'$INCLUDE:'.\tutorial\Lesson20\lib_type_rectangle.bi'
'$INCLUDE:'.\tutorial\Lesson20\lib_type_sprite.bi'

DIM Circ_Intersect AS POINT '     circle intersection x,y point (defined in LIB_TYPE_POINT_INTEGER.BI)
DIM Line_Intersect AS POINT '     line intersection x,y point   (defined in LIB_TYPE_POINT_INTEGER.BI)
DIM Pixel_Intersect AS POINT '    pixel intersection x,y point  (defined in LIB_TYPE_POINT_INTEGER.BI)
DIM Rect_Intersect AS RECTANGLE ' rectangle intersection        (defined in LIB_TYPE_RECTANGLE.BI    )

As you can see we used our building block .BI library files to create the variable declaration structure we need for the library. All of the .BI files include the POINT TYPE but since the LIB_TYPE_POINT_INTEGER.BI library file contains the $INCLUDEONCE metacommand all subsequent references to this file will be ignored. You can use these .BI library building blocks in later programs you write without worrying about duplicate definitions.

Finally we need to build the library's .BM file. To do this simply copy and paste all the code contained in LIB_CircCollide.BM, LIB_LineCollide.BM, LIB_PixelCollide.BM, and LIB_RectCollide.BM, into a new library file named LIB_Collision.BM. LIB_Collision.BM will contain 454 lines of code (including the instructional documentation) so It won't be displayed here but you can find it at .\tutorial\Lesson20\LIB_Collision.BM.

No library is complete without some demo code to prove the library works and demonstrate how the library is intended to be utilized. By combining the code from the individual collision demo programs, LIB_CircleCollide_Demo.BAS, LIB_LineCollide_Demo.BAS, LIB_PixelCollide_Demo.BAS, and LIB_RectCollide_Demo.BAS, we'll create LIB_Collision_Demo.BAS:

( This code can be found at .\tutorial\Lesson20\LIB_Collision_Demo.BAS )

' Collision Library V1.0
' Terry Ritchie 05/16/24
' quickbasic64@gmail.com
'
' LIB_Collision_Demo.BAS
'
' Collision library demo

'+------------------------------------+
'| Load library variable declarations |
'+------------------------------------+

'$INCLUDE:'.\tutorial\Lesson20\lib_collision.bi'

'+-----------------------------+
'| Local variable declarations |
'+-----------------------------+

CONST SWIDTH% = 640 '                  screen width
CONST SHEIGHT% = 480 '                 screen height
CONST GREEN~& = _RGB32(0, 255, 0) '    color green
CONST RED~& = _RGB32(255, 0, 0) '      color red
CONST YELLOW~& = _RGB32(255, 255, 0) ' color yellow
DIM GreenRect AS RECTANGLE '           green rectangle     (RECTANGLE TYPE provided by Library)
DIM YellowRect AS RECTANGLE '          yellow rectangle    (RECTANGLE TYPE provided by library)
DIM GreenCircle AS CIRCLE '            green circle        (CIRCLE    TYPE provided by library)
DIM YellowCircle AS CIRCLE '           yellow circle       (CIRCLE    TYPE provided by library)
DIM GreenLine AS LINE '                green line          (LINE      TYPE provided by library)
DIM yellowline AS LINE '               yellow line         (LINE      TYPE provided by library)
DIM GreenOval AS SPRITE '              green oval image    (SPRITE    TYPE provided by library)
DIM YellowOval AS SPRITE '             yellow oval image   (SPRITE    TYPE provided by library)
DIM Plot(359) AS POINT '               x,y rotation points (POINT     TYPE provided by library)
DIM c AS SINGLE '                      0 to 2*PI radian counter
DIM c1 AS INTEGER '                    starting point of rotating line
DIM c2 AS INTEGER '                    ending point of rotating line
DIM Collision AS INTEGER '             collision return value (-1 if in collision)
DIM KeyPress AS STRING '               key user pressed
DIM Selection AS INTEGER '             selection user chose from menu (1 to 5)

'+--------------------------------+
'| Rectangle collision demo setup |
'+--------------------------------+

GreenRect.Width = 100 '                                            stationary rectangle size
GreenRect.Height = 100
GreenRect.TopLeft.x = (SWIDTH - GreenRect.Width) \ 2 '             stationary rectangle location
GreenRect.TopLeft.y = (SHEIGHT - GreenRect.Height) \ 2 '           (centered on screen)
YellowRect.Width = 50 '                                            user rectangle size
YellowRect.Height = 50

'+-----------------------------+
'| Circle collision demo setup |
'+-----------------------------+

GreenCircle.Radius = 50 '                                          stationary circle size
YellowCircle.Radius = 25 '                                         user circle size
GreenCircle.Center.x = SWIDTH \ 2 '                                stationary circle location
GreenCircle.Center.y = SHEIGHT \ 2 '                               (centered on screen)

'+---------------------------+
'| Line collision demo setup |
'+---------------------------+

FOR c1 = 0 TO 359 '                                                cycle through 360 radian points
    Plot(c1).x = SWIDTH \ 2 + 150 * SIN(c) '                       calculate points on a circle
    Plot(c1).y = SHEIGHT \ 2 + 150 * -COS(c) '                     using center of screen as origin point
    c = c + .0174533 '                                             increase 1/360th (2 * PI / 360)
NEXT c1
c1 = 0 '                                                           starting point of rotating line
c2 = 179 '                                                         ending point of rotating line

'+------------------------------------+
'| Pixel perfect collision demo setup |
'+------------------------------------+

YellowOval.Image = _LOADIMAGE(".\tutorial\Lesson20\yellowoval.png", 32) ' load user oval
GreenOval.Image = _LOADIMAGE(".\tutorial\Lesson20\greenoval.png", 32) '   load stationary oval
GreenOval.TopLeft.x = SWIDTH \ 2 - _WIDTH(GreenOval.Image) \ 2 '          stationary oval location
GreenOval.TopLeft.y = SHEIGHT \ 2 - _HEIGHT(GreenOval.Image) \ 2 '        (centered on screen)

'+--------------------+
'| Begin main program |
'+--------------------+

SCREEN _NEWIMAGE(SWIDTH, SHEIGHT, 32) '                                         graphics screen
_MOUSEHIDE '                                                                    hide mouse pointer
DO '                                                                            begin main program loop
   CLS '                                                                        clear screen
    Center 2, "COLLISION LIBRARY DEMO" '                                        print header
    Center 4, "Make Your Selection (1 to 5)" '                                  print instructions
    Center 7, "1. Rectangle Collision          "
    Center 9, "2. Circle Collision             "
    Center 11, "3. Line Intersect Collision     "
    Center 13, "4. Pixel Perfect Collision      "
    Center 15, "5. Exit back to operating system"
   DO '                                                                         begin user input loop
       _LIMIT 30 '                                                              30 frames per second
        KeyPress = INKEY$ '                                                     get any key pressed
   LOOP UNTIL KeyPress <> "" '                                                  leave when a key pressed
    Selection = VAL(KeyPress) '                                                 get value of key press
   IF Selection > 0 AND Selection < 5 THEN '                                    continue if valid key press
       DO '                                                                     begin demo loop
           _LIMIT 30 '                                                          30 frames per second
           CLS '                                                                clear screen
            Center 28, "Use mouse to move object, ESC to exit." '               print instructions
           WHILE _MOUSEINPUT: WEND '                                            get latest mouse update
           SELECT CASE Selection '                                              which demo did user request?
               CASE 1 '                                                         the rectangle collision demo
                    Center 2, "Rectangle Collision Library Demo" '              print header
                    YellowRect.TopLeft.x = _MOUSEX '                            get mouse pointer x location
                    YellowRect.TopLeft.y = _MOUSEY '                            get mouse pointer y location
                    Collision = RectCollide(GreenRect, YellowRect) '            test for a collision
                   LINE (GreenRect.TopLeft.x, GreenRect.TopLeft.y)-_
                        (GreenRect.BottomRight.x, GreenRect.BottomRight.y), GREEN, BF 'draw stationary rect
                   LINE (YellowRect.TopLeft.x, YellowRect.TopLeft.y)-_
                        (YellowRect.BottomRight.x, YellowRect.BottomRight.y), YELLOW, BF 'user rectangle
                   IF Collision THEN '                                          was there a collison?
                   LINE (Rect_Intersect.TopLeft.x,Rect_Intersect.TopLeft.y)-_
                        (Rect_Intersect.BottomRight.x,Rect_Intersect.BottomRight.y), RED, BF 'yes, red rect
                   END IF
               CASE 2 '                                                         the circle collision demo
                    Center 2, "Circle Collision Library Demo" '                 print header
                    YellowCircle.Center.x = _MOUSEX '                           get mouse pointer x location
                    YellowCircle.Center.y = _MOUSEY '                           get mouse pointer y location
                    Collision = CircCollide(YellowCircle, GreenCircle) '        test for a collision
                   CIRCLE (GreenCircle.Center.x, GreenCircle.Center.y), GreenCircle.Radius, GREEN 'circle
                   PAINT (GreenCircle.Center.x, GreenCircle.Center.y), GREEN, GREEN 'paint stationary circle
                   CIRCLE (YellowCircle.Center.x, YellowCircle.Center.y), YellowCircle.Radius, YELLOW 'user
                   PAINT (YellowCircle.Center.x, YellowCircle.Center.y), YELLOW, YELLOW 'paint user circle
                   IF Collision THEN '                                          was there a collision?
                       CIRCLE (Circ_Intersect.x, Circ_Intersect.y), 5, RED '    yes, draw red circle
                       PAINT (Circ_Intersect.x, Circ_Intersect.y), RED, RED '   paint red collision circle
                   END IF
               CASE 3
                    Center 2, "Line Collision Library Demo" '                   print header
                   LINE (GreenLine.Start.x, GreenLine.Start.y)-_
                        (GreenLine.Finish.x, GreenLine.Finish.y), GREEN '       draw rotating line
                   LINE (yellowline.Start.x, yellowline.Start.y)-_
                        (yellowline.Finish.x, yellowline.Finish.y), YELLOW '    draw user line
                    Collision = LineCollide(yellowline, GreenLine) '            test for a collision
                   IF Collision THEN '                                          was there a collision?
                       CIRCLE (Line_Intersect.x, Line_Intersect.y), 4, RED '    yes, draw red circle
                       PAINT (Line_Intersect.x, Line_Intersect.y), RED, RED '   paint red circle
                   END IF
                    yellowline.Start.x = _MOUSEX - 50 '                         user line start x
                    yellowline.Finish.x = yellowline.Start.x + 100 '            user line end x
                    yellowline.Start.y = _MOUSEY '                              user line start y
                    yellowline.Finish.y = yellowline.Start.y '                  user line end y
                    GreenLine.Start = Plot(c1) '                                rotating line start point
                    GreenLine.Finish = Plot(c2) '                               rotating line end point
                    c1 = c1 + 1 '                                               increment start point
                   IF c1 = 360 THEN c1 = 0 '                                    reset when needed
                    c2 = c2 + 1 '                                               increment end point
                   IF c2 = 360 THEN c2 = 0 '                                    reset when needed
               CASE 4
                    Center 2, "Pixel Collision Library Demo" '                  print header
                   _PUTIMAGE (GreenOval.TopLeft.x, GreenOval.TopLeft.y), GreenOval.Image 'stationary oval
                   _PUTIMAGE (YellowOval.TopLeft.x, YellowOval.TopLeft.y), YellowOval.Image 'user oval
                    YellowOval.TopLeft.x = _MOUSEX '                            get mouse x location
                    YellowOval.TopLeft.y = _MOUSEY '                            get mouse y location
                    Collision = PixelCollide(GreenOval, YellowOval) '           test for a collision
                   IF Collision THEN '                                          was there a collision?
                       CIRCLE (Pixel_Intersect.x, Pixel_Intersect.y), 4, RED '  yes, draw red circle
                       PAINT (Pixel_Intersect.x, Pixel_Intersect.y), RED, RED ' paint red circle
                   END IF
           END SELECT
           IF Collision THEN Center 4, "COLLISION!" '                           inform user of collision
           _DISPLAY '                                                           update screen with changes
       LOOP UNTIL _KEYDOWN(27) '                                                leave when ESC key pressed
       _AUTODISPLAY '                                                           allow screen auto updating
   END IF
LOOP UNTIL Selection = 5 '                                                      leave when user selected 5
_FREEIMAGE GreenOval.Image '                                                    remove images from RAM
_FREEIMAGE YellowOval.Image
SYSTEM '                                                                        return to operating system

'+---------------------------------+
'| Local subroutines and functions |
'+---------------------------------+

'------------------------------------------------------------------------------------------------------------
SUB Center (row AS INTEGER, text AS STRING)
    '--------------------------------------------------------------------------------------------------------
    '- Centers the text on screen in the provided row -
    '-                                                -
    '- row  - the text row                            -
    '- text - the text string to center               -
    '--------------------------------------------------

   LOCATE row, (SWIDTH \ 8 - LEN(text)) \ 2 ' locate the cursor
   PRINT text; '                              print the text

END SUB
'------------------------------------------------------------------------------------------------------------

'+--------------------------------------------+
'| external library subroutines and functions |
'+--------------------------------------------+

'$INCLUDE:'.\tutorial\Lesson20\lib_collision.bm'

Figure 2: The collision library demo

Now with the addition of just two statements:

'$INCLUDE:'.\tutorial\Lesson20\lib_collision.bi'

at the top and:

'$INCLUDE:'.\tutorial\Lesson20\lib_collision.bm'

at the bottom, your programs will have access to all four collision detection routines.

Maintaining Libraries

Creating, maintaining, and collecting libraries is key to writing code quickly and efficiently but those library files can get cluttered and lost very quickly without some sort of cataloging system. You'll need to come up with your own library cataloging and naming system to keep track of your library files. I personally maintain a subdirectory off of the QB64PE directory named LIB:

' QB64PE
' |
' +-----LIB
'       |
'       +-----COMMON
'       |     |
'       |     +-----LIB_TYPE_POINT_INTEGER.BI
'       |     +-----LIB_TYPE_SPOINT_SINGLE.BI
'       |     +-----LIB_TYPE_RECTANGLE.BI
'       |     +-----LIB_CONST_KEYSCAN.BI
'       |     +-----LIB_CONST_BUTTON.BI
'       |     +-----LIB_CONST_COLORS.BI
'       |     +-----LIB_CENTERTEXT.BM
'       |     +-----etc..
'       |
'       +-----COLLISION
'       |     |
'       |     +-----LIB_Collision.BI
'       |     +-----LIB_Collision.BM
'       |     +-----LIB_Collision.DOC
'       |
'       +-----SPRITE
'       |     |
'       |     +-----LIB_Sprite.BI
'       |     +-----LIB_Sprite.BM
'       |     +-----LIB_Sprite.PDF
'       |
'       +-----MENU
'       |     |
'       |     +-----LIB_Menu.BI
'       |     +-----LIB_Menu.BM
'       |     +-----LIB_Menu.PDF
'       |
'       +-----BUTTON
'       |     |
'       |     +-----LIB_Button.BI
'       |     +-----LIB_Button.BM
'       |     +-----LIB_Button.PDF
'       +-----RHOSIGMA
'       |     |
'       |     +-----(libraries shared by RhoSigma)
'       |
'       +-----SMCNEILL
'       |     |
'       |     +-----(libraries shared by SMcNeill)
'      etc..

I can then quickly and easily set up the library framework I need for a given task. For example, if I'm going to write a program that uses Windows style menus and buttons I would include these library declaration .BI files at the top of my code:

'$INCLUDE:'.\lib\button\lib_button.bi'
'$INCLUDE:'.\lib\menu\lib_menu.bi'
'$INCLUDE:'.\lib\common\lib_const_keyscan.bi'

Then include these library subroutine and function .BM files at the bottom of my code:

'$INCLUDE:'.\lib\button\lib_button.bm'
'$INCLUDE:'.\lib\menu\lib_menu.bm'

I then open the associated library documentation files as a reference on how to use the libraries I chose and begin coding. Sometimes that documentation is a PDF, other times a Word document, or, as we did, the documentation is simply encoded right into the library files. Keeping all library related files in the LIB subdirectory also allows for quick moving and/or copying of my library files to a new QB64PE version install when one becomes available.

I have also made a habit of starting all library file names with LIB_ as was done in this lesson. I do not do this though when sharing libraries and leave the renaming of the library files up to the programmer to match their personal cataloging system.

Unwritten Rules

When writing a library you plan to share with others there are a few considerations you should take into account. These "rules" are merely suggestions but will greatly aid others when utilizing your libraries. These rules are also good for you personally too!

Preface all exposed variables, subroutines, and functions with a unique identifier.

For example, let's say you are writing a unit conversion library that converts between various scales such as temperature, distance, weights, and so on and one of the functions in your library is named Metric(). There is a possibility that someone using  your library may either try to use Metric as a variable name, TYPE, or even another subroutine or function. Perhaps someone is using your library to upgrade a piece of code from 1996 and that code already contains a variable named Metric. By prefacing all of your library's variables, TYPEs, constants, subroutines, and functions with UCL_ you'll avoid others from having to deal with these duplicate definitions. Your function named Metric() would become UCL_Metric() (UCL stands for Unit Conversion Library). This also has the side effect of being able to easily identify elements used within a program as belonging to your library. If we were to share the collision detection library written above all of the TYPEs should be prefaced:

TYPE CL_RECTANGLE
TYPE CL_CIRCLE
TYPE CL_LINE
TYPE CL_SPRITE
TYPE CL_POINT

Any exposed variables should also be prefaced:

CL_Rect_Intersect
CL_Circ_Intersect
CL_Line_Intersect
CL_Pixel_Intersect

And finally the functions prefaced:

CL_RectCollide ()
CL_CircCollide ()
CL_LineCollide ()
CL_PixelCollide ()

Now there is no mistaking that anything starting with CL_ is part of the Collision Library. More importantly, the likelihood of someone else creating these exact same names or vintage code having these names is highly unlikely. 

Write functions and procedures to be as self sufficient as possible.

Try to write your library's functions and subroutines so they do not rely on external variables. For example, you've written a little library that given the radius of a circle can calculate the diameter, circumference, and area of the circle. Your library's .BI file contains a single line of code:

CONST PI = 3.1415926

and your library's .BM file contains three subroutines:

SUB Diameter (radius AS SINGLE, result AS SINGLE)

    result = 2 * radius

END SUB

SUB Circumference (radius AS SINGLE, result AS SINGLE)

    result = 2 * PI * radius

END SUB

SUB Area (radius AS SINGLE, result AS SINGLE)

    result = PI * radius * radius

END SUB

To test the library you wrote the following code:

DIM Answer AS SINGLE

Diameter 10, Answer
PRINT "Diameter ------>"; Answer
Circumference 10, Answer
PRINT "Circumference ->"; Answer
Area 10, Answer
PRINT "Area ---------->"; Answer

Two of the three subroutines use the constant PI provided in the .BI file however there is no need for the .BI file. The constant can be placed directly into the subroutines. Subroutines and functions allow for the use of the CONST, TYPE...END TYPE and DIM statements to declare variables. Furthermore, instead of passing the results back in the form of a parameter ( result AS SINGLE ) the subroutines should instead be functions that pass the value back directly. Here is the modified .BM file:

FUNCTION Diameter! (radius AS SINGLE)

    Diameter! = 2 * radius

END FUNCTION

FUNCTION Circumference! (radius AS SINGLE)

   CONST PI = 3.1415926

    Circumference! = 2 * PI * radius

END FUNCTION

FUNCTION Area! (radius AS SINGLE)

   CONST PI = 3.1415926

    Area! = PI * radius * radius

END FUNCTION

and the modified code to test the functions:

PRINT "Diameter ------>"; Diameter(10)
PRINT "Circumference ->"; Circumference(10)
PRINT "Area ---------->"; Area(10)

The use of the .BI file has been completely removed and the functions don't rely on any external variables to operate except for the parameter radius which is passed in. Granted this is a highly simplistic example but the point is to make the .BM file do as much of the work for the library as possible.

Many times you'll come across libraries that include these constants in their .BI file:

CONST FALSE = 0
CONST TRUE = NOT FALSE

These constants are typically used to set truth flags in programs. For example:

IF EnemyBulletHit(PlayerShip) THEN DestroyShip ' blow ship up if hit with bullet

FUNCTION EnemyBulletHit (obj AS RECTANGLE)

   SHARED EnemyBullet() AS RECTANGLE ' need access to bullet array
   DIM bcount AS INTEGER '             bullet counter

    EnemyBulletHit = FALSE '                           assume all bullets missed
   DO '                                                begin bullet loop
       IF RectCollide(obj, EnemyBullet(bcount)) THEN ' did bullet hit object?
            EnemyBulletHit = TRUE '                    return true
           EXIT DO '                                   exit do loop
       END IF
        bcount = bcount + 1 '                          increment bullet counter
   LOOP UNTIL bcount = 10 '                            leave when all bullets checked

END FUNCTION

You can eliminate the need for the constants by substituting 0 (zero) for FALSE and -1 for TRUE:

FUNCTION EnemyBulletHit (obj AS RECTANGLE)

   SHARED EnemyBullet() AS RECTANGLE ' need access to bullet array
   DIM bcount AS INTEGER '             bullet counter

    EnemyBulletHit = 0 '                               assume all bullets missed (FALSE)
   DO '                                                begin bullet loop
       IF RectCollide(obj, EnemyBullet(bcount)) THEN ' did bullet hit object?
            EnemyBulletHit = -1 '                      return (TRUE)
           EXIT DO '                                   exit do loop
       END IF
        bcount = bcount + 1 '                          increment bullet counter
   LOOP UNTIL bcount = 10 '                            leave when all bullets checked

END FUNCTION

It's a very bad idea to include commonly used variables such as TRUE and FALSE in your libraries. Many programmers use them in their own code and duplicate definitions will arise when this happens.

Test your libraries with OPTION _EXPLICIT

Always create some sort of demonstration code when finished with your library to make sure the library functions as intended. When creating that demonstration code always make the first line of your code OPTION _EXPLICIT. This will ensure that all variables are declared properly in your library's code. If they are not now is the time to correct them. Many programmers use OPTION _EXPLICIT as their first line of code to ensure variable integrity (and if they don't they should). If you don't test your code a programmer using OPTION _EXPLICIT may find that your library is unusable because of undeclared variables that are present and you did not take the time to resolve them.

Create instructional documentation

This has already been hammered to death previously in this lesson but it can't be said enough. Creating documentation is time consuming and for some boring but it must be done. I'm only going to say this one more time ... if you don't take the time to create documentation it will come back to haunt you later.

Create demonstration code

While it's nice to have demonstration code in your documentation nothing replaces ready to run code that a potential user of your library can see and execute first hand. I can't tell you how many times I've referred back to my own demo code to regain an understanding of a library I wrote months or even years prior. It does help.

Windows API Calls

QB64 also allows libraries within Windows (.DLL files) to be used as subroutines and functions. This functionality is beyond the scope of this tutorial but you need to be aware this exists. This is an advanced topic but opens up a whole new world of possibilities to the QB64 programmer. The following code uses a subroutine within the Windows kernel to report the physically installed amount of RAM within your computer.

Note: This code was written by Spriggsy and shared on the QB64 forum here.

( This code can be found at .\tutorial\Lesson20\PhysicalRAM.bas)

OPTION EXPLICIT
$NOPREFIX
$CONSOLE:ONLY

CONST KILOBYTE = 1024
CONST MEGABYTE = 1024 ^ 2
CONST GIGABYTE = 1024 ^ 3
CONST TERABYTE = 1024 ^ 4

DECLARE DYNAMIC LIBRARY "Kernel32"
   SUB GetPhysicallyInstalledSystemMemory (BYVAL TotalMemoryInKilobytes AS OFFSET)
END DECLARE

DIM AS UNSIGNED INTEGER64 memory
GetPhysicallyInstalledSystemMemory OFFSET(memory)

memory = memory * KILOBYTE

SELECT CASE memory
   CASE IS < KILOBYTE
       PRINT USING "   ####  B"; memory
   CASE IS < (MEGABYTE) AND memory >= KILOBYTE
       PRINT USING "####.## KB"; (memory / KILOBYTE)
   CASE IS < (GIGABYTE) AND memory >= (MEGABYTE)
       PRINT USING "####.## MB"; (memory / (MEGABYTE))
   CASE IS < (TERABYTE) AND memory >= (GIGABYTE)
       PRINT USING "####.## GB"; (memory / (GIGABYTE))
END SELECT

If you wish to explore using Windows API calls the best place to start investigating and asking questions is the QB64 Phoenix Edition forum.