Basic4GL, Copyright (C) 2003-2016 Tom Mulgrew
Programmers' guide
25-Apr-2016
Tom Mulgrew
This document describes the various built-in functions, and how to use them to do something useful in Basic4GL.
It does not go into great detail about the language syntax itself (see the Language Guide if that is what you need).
Also some groups of functions have been separated out into their own help files, so see the Sprite Library Guide for 2D sprite and tilemap functions, or Network Engine Guide for Basic4GL's real-time-game focused networking routines.
Basic text output is performed using the "Print" or "Printr" function.
Format:
Print text-parameters ;
Or:
Print text-parameters
Where text-parameters is a list of parameters, separated by semicolons (;).
"Print" leaves the cursor positioned after the last character printed.
"Printr" will automatically move the cursor to the start of the next line
after the text has been printed.
If the cursor reaches the bottom of the
screen, the text will scroll up the screen to make room for the new line.
Example:
Printr "Hello ";
Printr "and welcome to ";
Printr "Basic4GL"
Print
Print "Have a nice day"
"Print" behaves slightly differently when "traditional BASIC" syntax is enabled, or "Basic4GL with traditional print" syntax is enabled.
You can enable the "traditional BASIC" syntax by placing a
language traditional
line at the top of your program, or
language traditional_print
for just the print command syntax.
In this mode, the "print" command will move the cursor to the next line if it does not end with a trailing semicolon (;)
For example:
language traditional
print "Line 1"
print "Line 2"
print "Line 3"
Will print:
Line1
Line2
Line3
If the "print" command does end with a semicolon, then the cursor will remain on the same line. So:
language traditional
print "Welcome ";
print "to ";
print "Basic4GL"
Will print:
Welcome to Basic4GL
Thus the "printr" command is not required in this syntax (but it is still available for compatibility sake).
Locate positions the text cursor on the screen.
Format:
Locate X-position, Y-position
The Basic4GL text cursor is invisible. It determines to where on the screen "Print" and "Printr" will write.
By default the Basic4GL displays 40 characters across by 25 characters down (this can be changed using the "ResizeText()" function).
The topmost row is row 0.
The leftmost column is column 0.
Example:
dim d#
while True
cls
locate sin(d#) * 15 + 18, 10
print "Hello"
sleep(100)
d# = d# + 0.1
wend
CursorCol() returns the column the cursor is on.
CursorRow() returns the
row the cursor is on.
The topmost row is row 0.
The leftmost column is column 0.
Sets the text colour.
Format:
Color (red, green, blue)
Where red, green and blue are integers between 0 and 255 inclusive indicating the intensity of their respective colour component.
Once the text colour is set, any text printed will be in that colour until the text colour is changed.
Example:
dim t
TextMode(TEXT_BUFFERED)
while true
for t = 1 to 10: color(rnd()%255, rnd()%255, rnd()%255): print chr$(rnd()%255): next
DrawText()
wend
Cls clears all text from the screen and repositions the cursor to the top left.
ClearLine () clears the current line (the one which the cursor is on).
Example:
dim i
SetTextScroll(false)
for i = 0 to 24: printr i: next
locate 0, 10
ClearLine() ' Line 10 is cleared
Cears a rectangular region of the screen.
Format:
ClearRegion (x1, y1, x2, y2)
Where x1, y1, x2, y2 are integers that define the top left column and row (x1, y1) and the bottom right column and row (x2, y2) of the rectangular region to be cleared.
Example:
dim x, y
SetTextScroll(false)
TextMode(TEXT_BUFFERED)
for y = 1 to TextRows()
for x = 1 to TextCols()
print "#"
next
next
ClearRegion(5, 5, 35, 9)
locate 13, 7: print "Cleared region"
DrawText()
TextRows () returns the number of text columns.
TextCols () returns the
number of text rows.
ResizeText (x, y) resizes the text display to y rows by x columns and clears the text.
Example:
dim i, a$
a$ = "Basic4GL"
i = 100
while i >= 4
ResizeText(i * 2 + 1, i + 1)
Locate (TextCols() - Len(a$)) / 2, TextRows() / 2
Print a$
Sleep(50)
i = i - 2
wend
Advancing the cursor past the end of the line causes it to wrap around onto the next line.
Advancing the cursor past the end of the bottom-most line, or performing a Printr on the bottom-most line causes the text to scroll up by one line.
Example 1:
Print glGetString(GL_EXTENSIONS)
Example 2:
dim d#
while true
locate sin(d#)*15+17, TextRows()-1
Printr "Hello"
Sleep(50)
d# = d# + 0.3
wend
Alternatively you can disable text scrolling with the TextScroll command.
SetTextScroll() enables or disables text scrolling when the cursor reaches the bottom of the text screen.
Format:
SetTextScroll(scroll)
Where scroll can equal true to enable text scrolling or false to disable it. Text scrolling is enabled by default.
Example:
SetTextScroll(false)
dim row
print "########################################"
for row = 2 to 24
print "# #"
next
print "########################################"
TextScroll() returns true if text scrolling is enabled, or false if it isn't.
Basic4GL fonts are special transparent images, consisting of a 16 x 16 grid of characters. You can set a new font by calling:
Font(texture)
Where texture is an OpenGL texture handle (usually returned from LoadTex()).
Example:
printr "Normal font"
dim texture
texture = LoadTex("data\charset2.png")
Font(texture)
printr "charset2.png font"
To get the texture handle for the default font, call:
DefaultFont ()
Example:
dim texture
texture = LoadTex("data\charset2.png")
Font(texture)
printr "charset2.png font"
Font (DefaultFont ())
printr "Normal font"
Basic4GL has 3 different modes for rendering text on the screen. You choose one by executing the appropriate TextMode() call:
The default mode is TEXT_SIMPLE.
In this mode, Basic4GL redraws the screen
after each "Print", "Printr", "Cls" or "ResizeText()".
This mode is easy to use, and the results are instant. However there are a number of situations where you may find it favourable to use TEXT_BUFFERED.
In TEXT_BUFFERED mode, Basic4GL does not update the screen until you call
DrawText().
This has advantages if you are animating a large amount of
text:
However, you must remember to call DrawText() or the user won't see any changes.
Example:
TextMode(TEXT_BUFFERED)
dim d#, t
while true
for t = 1 to 10
Locate sin(d#*t/19.0+t)*14+14,t*2+1
print " Thing "
next
DrawText()
Sleep(10)
d# = d# + .1
wend
TEXT_OVERLAID mode is used to combine OpenGL graphics with text.
This mode
is necessary if you wish to use OpenGL graphics commands and text at the
same time.
This would cause problems in TEXT_SIMPLE or TEXT_BUFFERED mode, as both modes automatically clear the screen before rendering the text.
In TEXT_OVERLAID mode the DrawText() function will not clear the screen, or
copy the result to the front buffer. It will simply render the current text
transparently over the top of the current scene.
You must therefore manually
clear the screen and swap it to the font buffer at the appropriate times.
The advantage of this mode is that it gives you a finer degree of control, and allows you to combine text and other graphics, such as OpenGL rendered objects.
Example:
TextMode(TEXT_OVERLAID)
locate 12, 12: print "This is a square"
dim a#
while true
glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity()
glTranslatef(0, 0, -2)
glRotatef(a#, 0, 0, 1)
glBegin(GL_QUADS)
glColor3f(1, 0, 0): glVertex2f( 1, 1)
glColor3f(0, 1, 0): glVertex2f(-1, 1)
glColor3f(0, 0, 1): glVertex2f(-1,-1)
glColor3f(1, 1, 1): glVertex2f( 1,-1)
glEnd()
DrawText()
SwapBuffers()
a# = a# + 0.3
wend
Format:
DrawText()
DrawText(flags)
The DrawText command is used to draw text and/or sprites. The default (no parameter) version draws all text and sprites that are on the screen. Alternatively you can control what it draws by passing it a bitmask composed of one or more of the following flags:
DRAW_TEXT | Draw text |
DRAW_SPRITES_BEHIND | Draw all sprites behind the text |
DRAW_SPRITES_INFRONT | Draw all sprites infront of the text |
DRAW_SPRITES | Draw all sprites behind or infront of the text |
Example:
TextMode(TEXT_OVERLAID)
glDisable(GL_DEPTH_TEST)
' Create some bouncing balls
const ballcount = 100
dim tex = LoadTex("data/ball.png")
dim sprites(ballcount), i
for i = 1 to ballcount
sprites(i) = NewSprite(tex)
if rnd()%2 then SprSetZOrder(-1) endif
SprSetPos(rnd() % 640, rnd() % 480)
if rnd()%2 then SprSetXVel(1) else SprSetXVel(-1) endif
if rnd()%2 then SprSetYVel(1) else SprSetYVel(-1) endif
next
do
' Clear the screen background
glBegin(GL_QUADS)
glColor3f(.5, 0, 0)
glVertex3f(-10, 10, -5)
glVertex3f( 10, 10, -5)
glColor3f(0, 0, .5)
glVertex3f( 10, -10, -5)
glVertex3f(-10, -10, -5)
glEnd()
' Draw behind sprites and small text
ResizeText(80, 50)
locate 35, 20: print "Small text"
DrawText(DRAW_SPRITES_BEHIND or DRAW_TEXT)
' Draw large text and infront sprites
ResizeText(20, 12)
locate 6, 7: print "Big text"
DrawText(DRAW_TEXT or DRAW_SPRITES_INFRONT)
' Show completed frame
SwapBuffers()
' Animate bouncing balls
while SyncTimer(10)
AnimateSprites()
for i = 1 to ballcount
BindSprite(sprites(i))
if SprX() < 0 or SprX() > 640 then
SprSetXVel(-SprXVel())
endif
if SprY() < 0 or SprY() > 480 then
SprSetYVel(-SprYVel())
endif
next
wend
loop
CharAt$(x, y) returns the character at column x and row y.
Example:
TextMode(TEXT_BUFFERED)
dim d#, t, x, y, crash: crash = false: x = TextCols()/2
while not crash
for t = 1 to 5: locate sin(d#+t)*15+15,t*2+2: print" Thing! ": next
y=y-1
if y<0 then
y = TextRows()-1: cls
else
if ScanKeyDown(VK_LEFT) and x > 2 then x = x - 1 endif
if ScanKeyDown(VK_RIGHT) and x < 36 then x = x + 1 endif
crash = CharAt$(x,y)<>" "
locate x, y: print"X"
endif
DrawText()
WaitTimer(80)
d# = d#+0.06
wend
Pauses execution for a number of milliseconds.
Format:
Sleep(milliseconds)
Note: The application is completely unresponsive while sleeping. Therefore
Basic4GL will not sleep for more than 5000 msec (5 seconds) at a time.
To
sleep for more than 5 seconds, use a loop.
For example:
dim i
for i = 1 to 60: Sleep(1000): next
Will pause for 60 seconds, but still give the user the opportunity to break out of the program if he/she wishes.
This function is similar to Sleep, and indeed has the same format:
WaitTimer(milliseconds)
The difference is that WaitTimer waits until milliseconds milliseconds has elapsed from the previous WaitTimer call.
This difference is significant if WaitTimer is used inside an animation loop,
with other code that may take some time to execute (such as rendering a
frame).
For example:
while true
Draw a frame
WaitTimer(100)
wend
If Draw a frame were to take 40 milliseconds, then WaitTimer will pause for only 60 milliseconds, ensuring that the loop is correctly iterated 10 times a second.
Even simple animations can potentially take up to the resync period of the monitor (anything from 1/100th to 1/50th of a second), if the user's graphics card is configured to wait for retrace before drawing.
SyncTimer returns true if you need to update the internal state of the application to catch up to the clock.
This can be used to force an animation to update internally so many times per second, regardless of a PC's rendering speed, and is intended to be used as follows:
while main-loop-condition
Render scene
while
SyncTimer (delay)
Update state
wend
For example, if delay was 10 milliseconds, then Update state will execute 100 times per second, regardless of whether the computer is capable of rendering 20 or 100 frames per second.
Example:
dim x, y, a#, b#
while true
glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity()
glTranslatef(0, 0, -16)
glRotatef(a#, 0, 0, 1)
for y = -5 to 5: for x = -5 to 5
glPushMatrix()
glTranslatef(x * 3, y * 3, 0)
glRotatef((x + y) * 60 + b#, 1, 0, 0)
glBegin(GL_QUADS)
glColor3f(1, 0, 0): glVertex2f( 1, 1)
glColor3f(0, 1, 0): glVertex2f(-1, 1)
glColor3f(0, 0, 1): glVertex2f(-1,-1)
glColor3f(1, 1, 1): glVertex2f( 1,-1)
glEnd()
glPopMatrix()
Next: Next
SwapBuffers()
while SyncTimer(10)
a# = a# + 0.9: b# = b# + 3.6
wend
wend
Reads a text string from the keyboard.
Format:
Input variable
Input "prompt"; variable
Input "prompt", variable
Input will pause the program and wait until the user types in some text and
hits enter. The text will be displayed on the screen as the user types.
If a
prompt is given, it will be displayed on the screen. The first format
(with the semicolon) automatically displays a question mark after the
prompt. The second format (with the comma) simply displays the
prompt and nothing else.
Once the user has hit enter, the program will continue, and variable will contain the resulting text or number that the user entered.
Examples:
dim name$
input "What is your name"; name$
print "Hello " + name$
dim number
input "Please enter a number: ", number
print "The square root of " + number + " is " + sqrt(number)
Be aware that Basic4GL's implementation of "input" is not as complete as other BASICs.
Basic4GL does not support inputting multiple variables with the same input command.
Also, Basic4GL will not prompt the user to "Redo from start" if the text he/she entered cannot be converted into the destination variable type. Instead it will simply set the destination variable to 0.
Note 2: There is an older Input$() function that has the syntax:
variable = Input$()
This is an old syntax, and kept only for backwards compatibility with older Basic4GL programs.
Determines whether a key is currently pressed or released.
Format:
KeyDown(character)
ScanKeyDown(scan-code)
KeyDown takes the first character of the string argument passed to it.
ScanKeyDown takes a numeric virtual key code, often a VK_x constant (such as
VK_UP e.t.c. Click "Help|Functions and Constants list..." then the "Constants"
tab for a list).
Both functions return true (-1) if the key is being pressed or false (0) if otherwise.
Note: KeyDown("") will always return false.
Example 1:
ResizeText(5, 1)
while true
locate 0, 0
if KeyDown("A") then print "Down"
else print " Up "
endif
wend
Example 2:
dim a#
while true
glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity()
glTranslatef(0, 0, -5)
glRotatef(a#, 0, 0, 1)
glBegin(GL_TRIANGLES)
glVertex2f( 0, 1.5)
glVertex2f(-1,-1)
glVertex2f( 1,-1)
glEnd()
SwapBuffers()
while SyncTimer(10)
if ScanKeyDown(VK_LEFT) then a# = a# + 3: endif
if ScanKeyDown(VK_RIGHT) then a# = a# - 3: endif
wend
wend
Format:
Inkey$()
InScanKey()
Basic4GL buffers characters and raw scan codes typed into the output window.
Inkey$() returns characters typed as single character strings. If no characters are buffered, Inkey$() will return an empty string.
InScanKey() returns scan codes as integers. If no scan codes are buffered, InScanKey() returns 0.
Example:
while true: print Inkey$(): wend
Format:
ClearKeys()
ClearKeys() clears the keyboard buffer, throwing away any keypresses that have yet to be handled by Inkey$() or InScanKey().
ClearKeys() is equivalent to the following code:
While Inkey$() <> "": wend
While InScanKey() <> 0: wend
The following functions can be used to read the mouse.
These functions return the position of the mouse in relation to the OpenGL window (if in windowed mode), or the screen (fullscreen mode).
Mouse_X() returns the X (horizontal) position.
Mouse_Y() returns the Y
(vertical) position.
Both functions return a real value between 0 (far left, or top) and 1 (far right, or bottom).
Example 1:
print Mouse_X() + ", " + Mouse_Y(): run
Example 2:
ResizeText(80, 50)
dim x, y, char$
while true
if not Mouse_Button(MOUSE_LBUTTON) then
locate x, y: print char$
endif
x = Mouse_X() * TextCols()
y = Mouse_Y() * TextRows()
char$ = CharAt$(x, y)
locate x, y: print "X"
wend
Mouse_Button(index) returns true if button index is being pressed, or false if it isn't.
The left mouse button is index 0, the right is index 1 and the middle is
index 2.
Alternatively you can use the following constants:
Left button: MOUSE_LBUTTON
Right button: MOUSE_RBUTTON
Middle button:
MOUSE_MBUTTON
Example:
dim i
print "Press the mouse buttons!"
while true
locate 0, 2
for i = 0 to 2: printr Mouse_Button(i) + " ": next
wend
Mouse_Wheel() returns how many notches the mouse wheel has turned since the last time Mouse_Wheel() was called (or the program started).
For example:
dim i
print "Turn the mouse wheel!"
while true
i = i + Mouse_Wheel()
locate 0, 2: print i + " "
wend
These functions return how far the mouse has moved since the last time Mouse_XD() or Mouse_YD() was called (respectively).
Mouse_XD() returns the X (horizontal) distance.
Mouse_YD() returns the Y
(vertical) distance.
These functions are useful for first person shooter type movement, where the mouse is used to turn the player, instead of controlling a pointer on the screen.
Mouse_XD() and Mouse_YD() work internally by positioning the mouse pointer in the middle of the window and measuring how far the mouse moves from that position. This means that using Mouse_X() or Mouse_Y() will produce unexpected results, and it is recommended you stick to one method or the other.
Example:
dim x#, z#
while true
glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity()
glTranslatef(0, 0, -4)
glRotatef(z#, 0, 0, 1)
glRotatef(x#, 1, 0, 0)
glBegin(GL_TRIANGLES)
glVertex2f(0, 1)
glVertex2f(-.5, -1)
glVertex2f( .5, -1)
glEnd()
SwapBuffers()
z# = z# - Mouse_XD() * 100
x# = x# + Mouse_YD() * 100
wend
Note: A big thanks to Tyler Bingham for implementing the joystick support!
Basic4GL supports input from a single joystick. If more than one joystick is attached to a PC, Basic4GL will use whatever one the operating system says is first.
The following functions can be used to read the joystick.
Joy_Keys() takes a snapshot of the joystick and generates appropriate keypresses. Arrow keys are generated for stick movement, and space bar and control (Ctrl) keypresses are generated for joystick buttons 0 and 1 respectively.
The keypresses can then be detected with the keyboard input functions:
This effectively provides a simple and easy way of incorperating joystick and keyboard support into a program.
Example:
dim x, y
x = TextCols() / 2
y = TextRows() / 2
while true
Joy_Keys()
if not ScanKeyDown(VK_SPACE) then
locate x, y: print " "
endif
if ScanKeyDown(VK_LEFT) and x > 0 then x = x - 1 endif
if ScanKeyDown(VK_RIGHT) and x < TextCols() - 1 then x = x + 1 endif
if ScanKeyDown(VK_UP) and y > 0 then y = y - 1 endif
if ScanKeyDown(VK_DOWN) and y < TextRows() - 1 then y = y + 1 endif
locate x, y: print "X"
Sleep(30)
wend
Joy_X() returns the X (horizontal) position.
Joy_Y() returns the Y
(vertical) position.
Both functions return a value from -32768 (far left, or top) to 32767 (far
right or bottom).
0 is the centre of each axis. (If you have a stable,
properly calibrated digital joystick.)
Example:
print Joy_X() + ", " + Joy_Y(): run
Joy_Button(index) returns true if button index is currently being pressed, or false if isn't.
The first joystick button is index 0. The second is index 1 e.t.c
Example:
dim i
for i = 0 to 9
if joy_button(i) then print i: else print " ": endif
next
run
Joy_Left() returns true if the joystick is more than 100 units to the left.
(This is equivalent to: Joy_X() < -100)
Joy_Right() returns true if the
joystick is more than 100 units to the right. (This is equivalent to: Joy_X()
> 100)
Joy_Up() returns true if the joystick is more than 100 units
upwards. (This is equivalent to: Joy_Y() < -100)
Joy_Down() returns true
if the joystick is more than 100 units downwards. (This is equivalent to: Joy_Y
() > 100)
There are also explicit functions for each joystick button from 0 through to 9.
Joy_0() returns true if the first joystick button is being pressed. (This is
equivalent to: Joy_Button(0)).
...
Joy_9() returns true if the 10th
joystick button is being pressed. (This is equivalent to: Joy_Button(9)).
To "poll" the joystick means to take a snapshot of it's current state, including the readings of the X and Y axis and whether each button is up or down at the time of the poll.
Basic4GL automatically polls the joystick whenever one of the joystick
functions is called, so you don't have to tell it to explicitly.
For
example:
while true: printr Joy_X() + " " + Joy_Y() + " " + Joy_0() + " " + Joy_1(): wend
You may want to explicitly tell Basic4GL when to poll the joystick, in order
to make the program run faster.
Polling takes time (at least on older
analogue joysticks). It is more efficient to poll the joystick once, and then
act on the X axis, Y axis and button data captured in that poll than to poll the
joystick for each axis and button that you read.
UpdateJoystick() polls the joystick and takes a snapshot of the X and Y axis
and the state of all the buttons.
Any Joy_? calls will now return the data
captured at the time of the UpdateJoystick() call.
For example:
while true: UpdateJoystick(): printr Joy_X() + " " + Joy_Y() + " " + Joy_0() + " " + Joy_1(): wend
Now instead of reading the joystick 4 times each time around the loop, we are
only reading it once.
This runs significantly faster than the previous
example on my PC (although my PC has an older analogue joystick attached to it..
I can't comment on digital joysticks.)
As soon as you call UpdateJoystick(), Basic4GL switches to explicit
joystick updates, and stays that way until your program finishes
executing. Therefore you must keep calling UpdateJoystick() at the appropriate
times to ensure the joystick data is up to date.
If you don't, the joystick
will appear frozen, for example:
UpdateJoystick()
while true: printr Joy_X() + " " + Joy_Y() + " " + Joy_0() + " " + Joy_1(): wend
Here we have moved the UpdateJoystick() call out of the main loop, so it is
only called once at the start of the program.
Because we don't ever call it
again, each joystick functions will simply return the same value each time, i.e
the state of the joystick at the start of the program when UpdateJoystick() was
called.
So manual polling can be faster, but you must do it right!
Basic4GL standalone programs can accept commands from the command
line.
Command line arguments are entered after the program name when a
program is run from the command line. For example, if we built a standalone exe
called "CmdTest", and ran it in a command prompt window with the command:
cmdtest 1 banana 2 cucumber 3 "Tomato sandwich"
Then we have passed it 6 parameters:
We can access these parameters with the ArgCount and Arg functions.
ArgCount() returns the number of command line arguments.
Arg(index) returns parameter number index as a text string,
where index is 0 to return the first parameter.
index
should be between 0 and ArgCount() - 1, otherwise Arg(index)
returns a blank string.
To set the command line arguments that a program will see when it is running inside the Basic4GL IDE, click "Program|Arguments..."
Display all arguments
dim i
printr ArgCount(); " argument(s) found"
for i = 0 to ArgCount() - 1
printr Arg(i)
next
Compile and run another program:
dim prog
if ArgCount() = 0 then
printr "No program name!"
end
endif
prog = CompileFile(Arg(0), "__")
if CompilerError() <> "" then
printr CompilerError()
end
endif
Execute(prog)
if CompilerError() <> "" then
print CompilerError()
end
endif
Basic4GL programs can only read and write files from the directory where the Basic4GL program was saved (or any subdirectory thereof).
This is for security, and is intended to protect people new to programming when trying out example programs from the internet and other sources. For all we know, the person who wrote the program might think that overwriting files in the Windows system directory is a hilarious practical joke. This way the potential damage is restricted to a small subfolder, and the people can download and run Basic4GL programs with confidence.
Obviously this means that if you distribute Basic4GL programs that use File I/O, you will have to ensure that the files read/written to end up in the appropriate directory so that they can be reached.
This security restriction applies to the general purpose File I/O routines described below. Other functions that load data from disk are not subject to these restrictions, in particular the image and texture loading functions can load any file they want.
As of version 2.4.12, you can switch off these safety features by unchecking
"Safe mode" in the options screen.
If you do this, then you will need to make
sure any program you run in Basic4GL will not damage your computer.
Also, standalone executables created with Basic4GL ALWAYS run with "safe mode" switched OFF.
Files are opened like so:
(For writing):
dim file
...
file = OpenFileWrite("Files/filename.ext")
(For reading):
dim file
...
file = OpenFileRead("Files/filename.ext")
Where filename.ext is the filename and extension that is to be opened.
file is an integer variable that will store the file handle. This is a number that Basic4GL generates to identify the file that was just opened, and will be passed to other file routines to read data from or write data to the file.
If a file is opened for writing, it replaces any file that was their previously. If no file exists, one is created.
If a file I/O routine fails, the Basic4GL program simply keeps running, without performing the particular file operation that it attempted.
You can test whether the operation succeeded by calling the FileError() function. This is updated after every file operation. If the operation succeeded, it will be set to an error message, describing what went wrong.
For example:
dim file
file = OpenFileRead("c:\autoexec.bat")
if FileError()
<> "" then print FileError(): end endif
' Carry on...
It is good practice to close the file once you've finished with it as follows:
CloseFile(file)
If you forget, or your program stops for any reason before it can close the file, Basic4GL will close it automatically, the next time you run a Basic4GL program or when you close down Basic4GL.
The file must have been opened with OpenFileRead for these routines to work correctly.
ReadLine(file) reads a line from a text file and returns it as a string. The lines are separated by carriage return and/or newline characters.
ReadText(file, skipEOL) skips over whitespace (spaces, tabs
e.t.c) until it finds some text. It then returns all the consecutive text at
that point until a whitespace character has been reached, as a
string.
SkipEOL is a boolean (true/false) parameter. If it is true,
then ReadText will skip over any end-of-line characters it finds in the file. If
false, it will stop at the end-of-line and return a blank string.
This can be used to break up a text files into words.
ReadChar(file) reads a single character from the file and returns it as a string.
ReadByte(file) reads a single binary byte from the file and returns it as an integer.
ReadWord(file) reads a two byte "word" from the file and returns it as an integer.
ReadInt(file) reads a four byte integer from the file and returns it as an integer.
ReadFloat(file) reads four bytes as a four byte floating point number and returns it as a real.
ReadDouble(file) reads eight bytes as an eight byte floating point number and returns it as a real.
ReadReal(file) is a synonym for ReadFloat(file) in the current version of Basic4GL on the Windows platform. (Basic4GL's "real" type is equivalent to a "float" in C).
The file must have been opened with OpenFileWrite for these routines to work correctly.
WriteLine(file, text) writes text to the file and
automatically appends a carriage return/newline pair.
text is a
string value.
WriteString(file, text) writes text to the file.
No carriage return or linefeed is appended. A zero byte string terminator is
NOT appended..
text is a string value.
WriteChar(file, text) writes the first character of
text to the file as a single character.
text is a string
value.
WriteByte(file, intval) writes intval to the file
as a single byte value.
intval is an integer value.
WriteWord(file, intval) writes intval to the file
as a two byte "word" value.
intval is an integer value.
WriteInt(file, intval) writes intval to the file
as a four byte integer value.
intval is an integer value.
WriteFloat(file, realval) writes realval to the
file as a four byte floating point value.
realval is an real
value.
WriteDouble(file, realval) writes realval to the
file as an eight byte floating point value.
realval is an real
value.
WriteReal(file, realval) is a synonym for WriteFloat (file, realval)
EndOfFile(file) applies to files opened for reading, and returns true if we have reached the end of the file.
Seek(file, offset) applies to files opened for reading, and attempts to reposition the reading position to offset bytes from the beginining of the file.
DeleteFile(filename) will delete a file. This routine is
only available when "Safe mode" is switched OFF.
If the delete
succeeds, DeleteFile() returns true. Otherwise DeleteFile() returns false, and
FileError() can be used to retrieve the text of the error.
FindFirstFile(mask) returns the filename of the first file that matches the text string mask.
Example:
dim filename$
filename$ = FindFirstFile("*.gb")
print filename$
Example 2:
print FindFirstFile("files\*.*")
Directory listing is subject to the same restrictions as general file access. That is, the directory must be the same directory as where the Basic4GL program is saved, or a subdirectory.
If no matching file is found, "FindFirstFile" returns an empty string ("").
FindNextFile() returns the filename of the next matching file in the
directory.
This function uses the same mask as was passed to "FindFirstFile",
and therefore will only work after a successful "FindFirstFile" call.
"FindNextFile" will keep returning the next filename until there are no more matching files, at which point it returns an empty string ("").
Example:
dim filename$
filename$ = FindFirstFile("*.gb")
while filename$ <> ""
printr filename$
filename$ = FindNextFile()
wend
FindClose()
FindClose() will free resources after a FindFirstFile..FindNextFile directory search.
It is not strictly required as Basic4GL will do this for you automatically when the program finishes. However it is good practice.
In Windows it is good practice to write application data to the "application data" Windows system folder. This can avoid Windows User Access Control (UAC) permission related issues, as well as provide separate storage for different Windows users (if required).
To open an application data file for writing, use:
Where subfolder is the sub-folder within the Windows application data folder, and filename is the file to create. allusers determines whether to use the shared application data folder for all Windows users (true) or the current user's personal application data folder (false).
OpenAppDataWrite() behaves much like OpenFileWrite(). It returns a number that can be passed to the file I/O routines to write the file. Once you have finished, you should call CloseFile(file) to close the file. As with OpenFileWrite(), if the file already exists it will be overwritten. Otherwise a new file will be created.
To open an application data file for reading, use:
This takes the same parameters as OpenAppDataWrite().
It in turn behaves much like OpenFileRead() in that it returns a number that can be passed to the file I/O routines to read the file, and you should call CloseFile(file) to close the file when done.
Basic4GL actually reads/writes files from:
Programs are permitted to access this folder even when safe mode is switched on.
If you really don't want your files in the Basic4GL folder you can specify a relative subfolder like "..\MyApp". Be aware however that the file will fail to read/write unless safe mode is turned off.
Basic4GL uses the Audiere sound library, which supports a number of different sound formats such as .wav, streamed music formats such as Ogg Vorbis, and "mod" formats like .mod, .s3m, .xm and .it. See the Audiere home page for more information.
If you use sound or music functions in your program, and you wish to distribute it as a standalone executable (see Creating Standalone Exes) be aware that you must also distribute:
which must be placed in the same folder as your standalone .exe file. Otherwise your program will run silently.
Sounds are loaded as follows:
dim sound
...
sound = LoadSound(filename)
Filename must refer to a file of a supported sound format.
Once the sound has been loaded, it can be played as follows:
PlaySound(sound)
or
PlaySound(sound, volume, looped)
Here sound is the sound handle that was returned from
LoadSound(...).
Volume is the sound volume, where 1 = full volume,
0.5 = half volume etc. (You can also use values greater than 1, but be warned
that the sound may "clip" and become distorted.)
Setting looped to
true will cause the sound to play continuously in a loop.
Note: If volume and looped are not specified they default to volume = 1 and looped = false.
PlaySound(...) returns the number of the "voice" that was chosen to play the
sound. Basic4GL supports 10 voices, which defines the maximum number of sounds
that can be played simultaneously.
This number is useful if you want to stop
the sound later (especially for looped sounds like footsteps), as you can pass
it to the StopSoundVoice(...) function.
DeleteSound(sound) deletes the sound from memory.
If you don't
explicitly delete them, Basic4GL will automatically do so when your program
finishes.
To stop a sound playing, use:
StopSoundVoice(voice)
Voice is the number of the voice you wish to stop playing.
This
number is returned from PlaySound(...) when the sound was started.
You can also stop all sounds with:
StopSounds()
These functions are used to stream in and play music files, such as Ogg Vorbis, or "mod" files (.mod, .s3m, .xm, .it etc).
Start playing a music file with:
PlayMusic(filename)
or
PlayMusic(filename, volume, looped)
Filename must be a file of a supported music format. Volume and looped behave the same as with PlaySound(...).
This will open the file and start playing it immediately.
Unlike regular
sound files music files are "streamed". This means that the file is not loaded
into memory all at once. Instead the file is loaded in continuously while the
music is playing.
Basic4GL supports playing one music file at a time only. If
a music file is already playing, it will stop and the new file will play
instead.
Example:
dim filename$
printr"Filename:": input filename$
PlayMusic(filename$)
if SoundError() <> "" then printr SoundError(): end endif
while MusicPlaying(): Sleep(100): wend
StopMusic() will stop music file from playing.
MusicPlaying() returns true while the music file is playing.
To set the music volume while music is playing, use:
SetMusicVolume(volume)
Where volume behaves the same as with PlaySound() or PlayMusic().
If a sound or music function fails, Basic4GL will store a description of the error, which can be retrieved with the SoundError() function.
SoundError() returns a text string describing the result of the last sound or music function call.
If the call was successful, SoundError() returns the empty string (""). Otherwise it returns the text of the error message.
Example:
dim sound, i
sound = LoadSound("c:\windows\media\chimes.wav")
if SoundError() <> "" then
printr SoundError()
else
PlaySound(sound)
Sleep(2000)
endif
These functions are used for general purpose operations, such as mathematics equations and string manipulation.
Abs(x) returns the absolute value of x.
ArrayMax(array) returns the index of the highest element of
array. Iterating elements 0..ArrayMax(array) will therefore
visit every element inside the array.
ArrayMax is a special function in that
array can be any type, so long as it is an array.
Asc(x) takes a single string parameter x, and returns the
ASCII value of the first character.
This is the opposite of the chr$
function
Atn(x) returns the Arc Tangent value of x, in radians.
Atnd(x) returns the Arc Tangent value of x, in degrees.
Atn2(x, y) returns the Arc Tangent value of x, y, in radians.
Atn2(x, y) returns the Arc Tangent value of x, y, in degrees.
Beep() causes the computer to beep.
Chr$(x) takes a single integer parameter x, and returns a string character whose ASCII value is x.
Example:
Printr Chr$(72)+Chr$(101)+Chr$(108)+Chr$(108)+Chr$(111)
Cos(x) returns the Cosine of x, where x is measured in radians.
Cosd(x) returns the Cosine of x, where x is measured in degrees.
Exp(x) returns e raised to the power of x.
Exp is the inverse of Log.
Int(x) casts a real valued x to an integer.
The rounding
is slightly different to the implicit type cast when a real value is assigned to
an integer.
Int(x) rounds x towards negative infinity,
whereas implicit type casting always rounds towards 0.
Example:
dim a#, i1, i2: a# = -5.1
i1 = a#
i2 = Int(a#)
printr "i1 = " +
i1
printr "i2 = " + i2
Left$(s,c) returns a string containing the first c
characters of s.
s is a string value, c is an
integer value.
For example, Left$("ABCDEFG", 3) returns "ABC"
LCase$(x) returns x converted to lowercase.
Len(x) returns the length of the string x in characters.
Log(x) returns the natural logarithm of x.
Log is the inverse of Exp.
Mid$(s,i,c) returns a string containing c consecutive characters of string s, starting from the ith character.
For example, Mid$("ABCDEFG", 4, 3) returns "DEF".
PerformanceCounter() returns the number of milliseconds that have elapsed
since the computer was turned on.
This function is very similar to
TickCount(), except PerformanceCounter() is accurate to1 millisecond whereas
TickCount() is only accurate to 10ms.
Therefore I strongly recommend using PerformanceCounter() for any timing operations.
The old TickCount() function is retained only for backwards compatibility with existing Basic4GL programs.
Pow(x,y) returns x raised to the power of y.
Right$(s,c) returns a string containing the last c characters of s.
For example, Right$("ABCDEFG", 3) returns "EFG"
Rnd() returns a random integer value, between 0 and RND_MAX.
(RND_MAX =
32767, but could be different in future ports of Basic4GL to different platforms
or operating systems.)
To return a random number between 0 and x-1 (inclusive), use:
Rnd() % x
To return a random number between 1 and x (inclusive), use:
Rnd() % x + 1
Sgn(x) returns:
1, if x is greater than 0
0, if x equals 0
-1, if
x is less than 0
Sin(x) returns the Sine of x, where x is measured in radians.
Sind(x) returns the Sine of x, where x is measured in degrees.
Sqr(x) returns the square root of x.
(Actually the square root of the absolute value of x.)
Sqrt(x) is exactly the same as Sqr(x)
Str$(x) converts an integer value x into a string representation of x.
For example, Str$(-13.4) returns "-13.4".
Tan(x) returns the Tangent of x, where x is measured in radians.
Tand(x) returns the Tangent of x, where x is measured in degrees.
Tanh(x) returns the Hyperbolic Tangent of x, where x is measured in radians.
TickCount() returns the number of milliseconds that have elapsed since the
computer was turned on.
Note: This function is only accurate to about 10ms. I
strongly advise using PerformanceCounter() instead.
UCase$(x) returns x converted to uppercase.
Val(x) converts a string x into a numeric value.
If
x cannot be converted into a number, then Val(x) returns
0.
For example, Val("27.2") returns 27.2.
Val is the opposite of Str$.
Basic4GL contains built in support for matrix and vector arithmetic, through a library of trigonometry functions, and also through extensions to standard mathematical operators (+, -, * e.t.c) to work with vector and matrix types.
Vectors are stored as an array of reals. For example:
dim vec#(3)
vec# = vec4(1, 2, 3, 1) ' Create a vector and assign it to
vec#
To be elegible for use with the built in trigonometry functions, the array must have 2, 3 or 4 elements. (Remember that declaring an array as size 3 actually results in 4 elements, 0 through 3 inclusive).
Element 0 stores the x component, element 1 stores y component, 2 stores z and 3 stores w.
Certain trigonometry functions that operate on 4 component vectors will automatically substitue z = 0 and/or w = 1 when short version vectors are passed in.
A matrix is a 4 x 4 array of reals, and must always be "DIM"med as:
matrixname#(3)(3)
Example:
dim matrix#(3)(3)
matrix# = IdentityMatrix() ' Assign a matrix to matrix#
The first array dimension corresponds to the x coordinate of the matrix, and the second to the y.
Basic4GL vector and matrix storage format and operations are designed to
mirror those of OpenGL.
As such vectors are multiplied as column vectors on
the right hand side of matrices. Matrices are stored as an array of column
vectors.
Vectors are just arrays, so you can read from and write to them like any other array.
dim v#(3), i
for i = 0 to 3: v#(i) = i: next ' Create a (0 1 2 3) vector
dim v1#(3), v2#(3), dotProd#
dotProd# = v1#(0)*v2#(0) + v1#(1)*v2#(1) + v1#(2)*v2#(2)
' Calculate the vector dot product
' (Note: we could also have said dotProd# = v1# * v2#)
However there are a set of routines for creating vectors quickly and simply:
vec4(x, y, z, w) returns a 4 component vector with x, y, z and w components initialised accordingly.
vec3(x, y, z) returns a 3 component vector with x, y and z components initialised accordingly.
vec2(x, y) returns a 2 component vector with x and y components initialised accordingly.
Examples:
dim lightsource#(3)
lightsource# = vec4(0, 100, 0, 1) ' Lightsource at (0 100 0)
This is exactly equivalent to:
dim lightsource#(3)
lightsource#(0) = 0
lightsource#(1) = 100
lightsource#(2) = 0
lightsource#(3) = 1
The first version is simply a more compact alternative.
Certain mathematics operators have been extended to accept vectors and or matrices as input, and (where appropriate) return a vector or a matrix as a result.
vec = A vector
matrix = A matrix
real = A
real value
Expression | Result |
-vec | Returns vec negated. That is vec scaled by -1 |
-matrix | Returns matrix negated. I.e matrix scaled by -1 |
vec * real or real * vec |
Returns vector scaled by real |
matrix * real or real * matrix |
Returns matrix scaled by real |
matrix * vec | Returns vec multiplied as a column vector on the right hand side of matrix. The result is another vector. |
matrix1 * matrix2 | Returns matrix2 multiplied on the right hand side of matrix1. The result is another matrix. |
vec1 * vec2 | Returns the dot product of vec1 and vec2, as a real value. |
vec / real | Returns vec scaled by 1 / real |
matrix / real | Returns matrix scaled by 1 / real |
vec1 + vec2 | Returns vec2 added to vec1 as a vector |
matrix1 + matrix2 | Returns matrix2 added to matrix1 as matrix |
vec1 - vec2 | Returns vec2 subtracted from vec1 as a vector |
matrix1 - matrix2 | Returns matrix2 subtracted from matrix1 as a matrix |
These are based on the OpenGL matrix functions (glTranslate-, glRotate-, e.t.c).
MatrixZero() returns a matrix where every element is zero.
dim m#(3)(3)
m# = MatrixZero()
MatrixIdentity() returns the identity matrix.
MatrixScale(scale) returns a scale matrix
MatrixTranslate(x, y, z) returns a translation matrix.
MatrixRotateX(angle) returns a matrix that rotates anticlockwise around the positive X axis by angle degrees.
Likewise MatrixRotateY(angle) and MatrixRotateZ(angle) return matrices that rotate around their respective axes.
MatrixRotate(angle, vec) returns a matrix that rotates anticlockwise around the specified vec by angle degrees. Vec in this case is a 3D vector.
MatrixBasis(vecx, vecy, vecz) creates a matrix from 3 basis vectors.
MatrixCrossProduct(vec) creates a cross product matrix for vec. This matrix has the property that when multiplied with a vector v, the result is vec x v. That is the cross product of vec and v.
You can copy a standard matrix into OpenGL, replacing the perspective,
model-view or texture matrix (whatever was last selected by glMatrixMode
()).
You can also multiply the current OpenGL matrix with a standard
matrix.
The new matrix will transform vertices passed to OpenGL (or texture
coordinates for the texture matrix), just as if you had built the matrix with
glRotate-, glTranslate-, glScale-,... commands.
glLoadMatrixf (matrix) will replace the current OpenGL matrix with matrix.
glMultMatrixf(matrix) will multiply the current OpenGL matrix by matrix. The resulting matrix replaces the previous OpenGL matrix.
(Note: glLoadMatrixd and glMultMatrixd also work. However as Basic4GL works with floats internally rather than doubles, there is no particular advantage in using these functions.)
Examples:
The following examples all draw a square 10 units "into the
screen", rotated anticlockwise by 20 degrees.
1.
' Standard OpenGL matrix routines
glLoadIdentity()
glTranslatef(0, 0, -10)
glRotatef(20, 0, 0, 1)
glBegin(GL_QUADS)
glVertex2f(-1, 1): glVertex2f(-1, -1): glVertex2f(1, -1): glVertex2f(1, 1)
glEnd()
SwapBuffers()
2.
' Using glMultMatrixf to multiply in basic matrices
glLoadMatrixf(MatrixIdentity())
glMultMatrixf(MatrixTranslate(0, 0, -10))
glMultMatrixf(MatrixRotateZ(20))
glBegin(GL_QUADS)
glVertex2f(-1, 1): glVertex2f(-1, -1): glVertex2f(1, -1): glVertex2f(1, 1)
glEnd()
SwapBuffers()
3.
' Build a complete matrix and load into OpenGL in one go
glLoadMatrixf(MatrixTranslate(0, 0, -10) * MatrixRotateZ(20))
glBegin(GL_QUADS)
glVertex2f(-1, 1): glVertex2f(-1, -1): glVertex2f(1, -1): glVertex2f(1, 1)
glEnd()
SwapBuffers()
4.
' Matrix stored in a variable
dim m#(3)(3)
m# = MatrixTranslate(0, 0, -10) * MatrixRotateZ(20)
glLoadMatrixf(m#)
glBegin(GL_QUADS)
glVertex2f(-1, 1): glVertex2f(-1, -1): glVertex2f(1, -1): glVertex2f(1, 1)
glEnd()
SwapBuffers()
Alternatively we could simply transform the vertices before passing them to OpenGL
dim m#(3)(3)
m# = MatrixTranslate(0, 0, -10) * MatrixRotateZ(20)
glBegin(GL_QUADS)
glVertex3fv(m# * vec3(-1, 1, 0))
glVertex3fv(m# * vec3(-1, -1, 0))
glVertex3fv(m# * vec3(1, -1, 0))
glVertex3fv(m# * vec3(1, 1, 0))
glEnd()
Which works just as well.
However, keep in mind that if we perform the
transformations ourselves we deny OpenGL the opportunity to perform the
transformations, and make use of any optimisations such as hardware
transformations supported on modern 3D graphics cards.
CrossProduct(vec1, vec2) returns the vector cross product of vec1 and vec2. The result is a vector.
Length(vec) returns the length of vec.
This is
equivalent to sqr(vec*vec)
Normalize(vec) returns vec scaled to length 1.
This is
equivalent to vec / Length(vec)
Determinant(matrix) returns the matrix determinant of matrix. The result is a real value.
Transpose(matrix) returns matrix transposed. (That is matrix mirrored about the diagonal.)
RTInvert(matrix) returns matrix inverted, for any
matrix containing only rotations and translations.
If
matrix contains any other transformations apart from rotations and
translations then the result is undefined, and will not be the
inverse of matrix.
Orthonormalize(matrix) returns an orthonormal matrix by performing a series of normalizations and cross products on the basis vectors of matrix.
This is useful for matrices that are nearly orthonormal. For example to ensure a matrix (that should be orthonormal) hasn't accumulated rounding errors after a large number of transformations.
Some of the above functions (such as CrossProduct) and operators (such as +) take two vectors and return a single result vector. Basic4GL sets the w coordinate of the resulting vector as follows:
If this is not the behaviour that you want, you will have to set the w coordinate manually.
There is no special treatment of w when multiplying a vector by a matrix, w is calculated like any other component. You will need to divide through by w manually if this is the behaviour you require.
dim vec#(3), matrix#(3)(3)
...
vec# = matrix# * vec# ' Multiply vector by matrix
vec# = vec# / vec#(3) ' Divide through by w
As of version 2.5.1 the following functions are deprecated. If at all possible you should use the "comp", "compFile" and "exec" commands that are documented in the language guide (as they are now built in language commands).
These old functions are still available for backwards compatibility with older Basic4GL programs. However they cannot be used in combination with user functions and subs, and you will get a runtime error if you try to do so.
The exception is the following functions: CompilerError, CompilerErrorLine and CompilerErrorCol.
These are still considered current, and can be used with the new "comp" command.
The following functions can be used to compile and execute code at runtime.
Compile(text) compiles text into program code, and returns an integer that identifies the compiled code.
Example:
dim code
code = Compile("printr " + chr$(34) + "Hello world" + chr$(34))
Execute(code)
Text can either be a string, or an array of strings.
If the text
compiles successfully, "Compile" returns an integer handle that can be passed to
the "Execute" function.
If the text does not compile, "Compile" returns 0,
and the error description and position can be extracted using the CompilerError,
CompilerErrorLine and CompilerErrorCol functions.
CompileFile(filename) loads a file from disk, compiles it, and returns an integer that identifies the compiled code.
Example:
dim code
code = CompileFile("md2viewer.gb")
if code = 0 lor not Execute(code) then
printr CompilerError()
endif
Important:
CompileFile can compile just about any program that can be loaded into Basic4GL and compiled manually. However there are some limitations:
Execute(handle) executes a block of compiled
code.
handle must be a valid handle returned from a successful
"Compile" or "CompileFile".
If the code completes without any errors, "Execute" returns true. Otherwise "execute" returns false, and the error message can be read using the "CompilerError", "CompilerErrorLine" and "CompilerErrorCol" functions.
The compiled code will execute until one of the following occurs:
The program will then continue executing from the next instruction after the "Execute" call.
These special versions of the above functions can be used to compile code with an automatic "symbol prefix":
The symbol prefix is a text string that is automatically prefixed to the front of every variable name, label name or structure name that the runtime compiled program refers to.
For example:
dim text$, program, i, __value
text$ = "value = value * value"
program = Compile(text$, "__")
printr CompilerError()
for i = 1 to 10
__value = i
execute(program)
printr i + ", " + __value
next
Here our code to compile at runtime is "value = value * value". But because we pass a symbol prefix of "__" (double underscore) to the Compile() command, what actually gets compiled is "__value = __value * __value".
The important thing here is that the runtime code cannot access any of the main program's variables except those that we have prefixed with "__". It cannot access "i" for example, as "i = 5" would effectively be compiled as "__i = 5".
We can use this to limit exactly which variables, labels and structure types the runtime compiled program has access to.
CompilerError() returns the error message generated by the last "Compile", "CompileFile" or "Execute" call. If there was no error (i.e. the call was successful), "CompilerError" returns an empty string ("").
CompilerErrorLine() and CompilerErrorCol() return the line and column of the
last error (if applicable).
For a "Compile" or "CompileFile" call, this is
the position of the error in the code being compiled.
For an "Execute" call,
this is the instruction that caused the run-time error.
There are a some potential issues with runtime compiled
code that need to be kept in mind.
It is tempting to think that runtime
compiled code is crash-proof, because any runtime errors immediately return
control to the calling program which can deal with the error
appropriately.
This is true. However the runtime compiled code has access to
everything in the parent program (by default, although this can be controlled by
"symbol prefixes" - see above), including all variables, labels etc, and can
often mess things up enough that the program will not run correctly when control
is returned.
For example,the following program:
dim text$, code
text$ = "return"
gosub ExecuteIt
end
ExecuteIt:
code = Compile(text$)
if code = 0 lor not Execute(code) then
printr CompilerError()
endif
return
stops with a runtime error "Return without gosub", because the runtime compiled code actually returns to the line after the "gosub", before finally ending when it reaches the "end" instruction. This returns control back to the instruction after the "Execute(code)" instruction which attempts to "return" again!
Another issue is that the executed code may never return
at all!
Consider:
print "Starting"
Execute(Compile("while true: wend"))
print "Ending"
The last line is never executed, because the dynamically compiled code ("while true: wend") never exits, and control is never returned.
The default window configuration is setup in "Settings|Basic runtime settings...", or in the create standalone executable dialog options (for standalone executables).
You can also configure the window using BASIC commands.
For example:
' Specify window settings
SetWindowWidth(320)
SetWindowHeight(240)
SetWindowFullscreen(false)
SetWindowBorder(true)
SetWindowResizable(false)
' Apply the settings to the window
UpdateWindow()
' Output text in updated window
ResizeText(12,8)
print "Hello world"
When UpdateWindow() is called, the previous OpenGL window is closed, and a new OpenGL window opened using the new parameters.
Before calling UpdateWindow() you must specify the parameters for the new window.
The following commands are used to read and write the window parameters.
Note that reading the window state returns the values that will be applied when UpdateWindow() is called, rather than the state of the current window. For example, if you call SetWindowWidth(320) but haven't applied it yet with UpdateWindow(), the WindowWidth() function will return 320 even if the current window width is different.
Format:
These commands read and write the window dimensions in pixels.
width and height are integers.
This is the size of the area used for output, and does not include the window border (if it has one). So the full size of the window may be bigger.
In fullscreen mode, SetWindowWidth()/-Height() sets the resolution of the screen. You are responsible for choosing a mode that the monitor supports, otherwise UpdateWindow() may fail.
Format:
Specifies whether to use fullscreen mode (isFullscreen is true) or windowed mode (isFullscreen is false).
Format:
Whether to include a window border.
The window border displays the window title, and the close and minimize buttons.
This setting has no effect on fullscreen windows.
Format:
Read and write the window colour depth as bits-per-pixel.
Typically this will be 16 or 32. Attempting to set a value that your monitor or graphics card doesn't support may be ignored, or cause UpdateWindow() to fail.
This setting applies to fullscreen windows only. Non fullscreen windows always have the same colour depth as the Windows desktop.
Format:
Specifies whether the window should have a stencil buffer (isStencilEnabled = true) or no stencil buffer (isStencilEnabled = false).
The stencil buffer is only required if you are specifically writing stencil buffer OpenGL code. For most programs it is not required.
Format:
Read and write the window title.
title is a text string.
The window title is displayed on the window border (in windowed mode), and represents the window in the Windows task manager, when you hover the mouse over the icon in the task bar, and when you Alt-Tab between applications.
Format:
Whether the window can be resized by dragging the sides and corners (isResizable = true), or cannot be resized (isResizable = false).
This setting has no effect on fullscreen windows.
Format:
Closes the current window, and creates a new one with the parameters specified. (Any parameters that aren't specified remain the same as for the previous window.)
UpdateWindow() destroys the current window completely and replaces it with a new one.
Any OpenGL state your program has setup prior to calling UpdateWindow() will be lost, and must be redone.
Any textures your program has loaded before calling UpdateWindow() will be unloaded, and must be reloaded. (The charset.png font texture is automatically reloaded though.)
Returns true if the window was created successfully.
If UpdateWindow() returns false, it means Windows was unable to create the window with the parameters specified. Because the previous window has closed, this means there is no output window.
Basic4GL uses GLFW to create the OpenGL output window. GLFW typically treats the window parameters as "hints", meaning if it cannot create a window exactly with the parameters specified, it may instead create a window as close to the requested parameters as it can manage, rather than fail completely. Because of this it is difficult to cause UpdateWindow() to actually fail.