Starting with BlitzMax


Job Open: Blitz3D/C++ DLL RPLidar Programming. View Job Posting
(Posted 7 months ago) tricky

Below I will set up some quick start courses to explain BlitzMax to those who start working with BlitzMax. I take it you know the global basis of programming in General and if not, maybe you'll pick it up. This is only a quick guide, so not too much in dept, just a quick guide to get on the road. Please note, I am talking of "Vanilla" BlitzMax and not "NG"

Debug or Release

When you are practising I strongly recommend to use the debug setting for compiling only. When creating a project you are really gonna sell or something Release mode is better. In Debug mode your program runs slower, but errors will be properly caught and runtime error messages will be thrown and when you use an IDE well set up for BlitzMax, it will point to the line where the error occurred. In Release mode your program runs faster, and then BlitzMax will try to compete with C, which is one of the languages with the fastest runtime. But as C gets is speed from lack of protection of any kind, and thus no error catching, neither will BlitzMax do. The built in memory management will work, so the headaches C can give on that won't be there. Woohooo!

Hello World

In BlitzMax that is a one line program

Print "Hello World"

BlitzMax is a bit based on BASIC, but despite that most of all a procedurial language, relying on functions. Print is a function which just prints a string (in this case "Hello World"). When you use functions that return no value or when you don't expect a value returned you can just put it the way as I said above. Brackets are not required in this situation, like in C.

Quick intro to variables

BlitzMax basically has two kinds of variables. Globals and locals. Globals live in your entire program, and locals in the function or subscoop in which they are declared. There is also the field variable as a 3rd kind, but that comes later.

Global H:String = "Hello World"
Print H

You can declare and assign in one go, so that makes things easier, doesn't it? When declaring a string without assinging a value BlitzMax will automatically turn it into an empty string, any nummeric variables will contain the number zero by default and objects (to which I come later) will be null. "Global" tells BlitzMax you're gonna declare a global variable. H is the name of the variable and :String is the type. When no type is given BlitzMax will assume it to be Int.

Global N:String = "Jeroen"
Global A = 43
Print "My name is "+N+" and I am "+A+" years old."

This works in BlitzMax. Nummeric values are just concatenated when added to a string.

Calling functions returning values

A nice demonstration of this is this:

Global N:String = Input("What is your name? ")
Global A = Input("How old are you? ").ToInt()
Print "Hello, "+N+"; so you're "+A+" years old, eh?"

This time brackets ARE needed, and it goes pretty simple, eh? Input is a function of the "string" type and it asks a question on the console and will return whatever the user typed as a string. Now, the second line is a bit more advanced, but let's say that you can add ".ToInt()" to any string to turn it into a nummeric value (it will return 0 when this fails). So this works:

Print "4".ToInt() + "5".ToInt() ' Returns 9

That's just handy to know.... How this works exactly is for a more advanced tutorial ;)

Creating a function yourself in BlitzMax

For that we have the keyword "Function", and it works like this:

Function CapInput:String(Question:string)
    Local r:String = Input(Question)
    Return Upper(r)
End Function

Print CapInput("Enter your name: ")

Now this program will return the name the user enters in full caps. Now how does this work? Function creates a function. CapInput was the name I gave it and :String indicates the returned value is a string. Here goes too that when you do not give a type BlitzMax will assume it to be int. Between the brackets I give the parameters I expect. If there are none I'd just type (). Yeah, unfortuantely that is required. Local works the same as "Global" except that the "r" variable I created this way only lives inside the function. The "Return" keyword returns the value, similar as in C and also terminates the function call. End Function just ends the function, I suppose that is obvious. Programmers who are used to program Pascal may need to get used to "CapInput:='blahblah';" but that kind of Syntax is illegal in BlitzMax.

Void in BlitzMax

C programmers are used to use "void" types for functions which do not return values. Pascal programmers are used to use Procedures in stead of Functions to get the same effect. BlitzMax has NO SUPPORT AT ALL for void functions. This because the creators of BlitzMax felt you don't need them. In stead you can just use an int function and just don't return a value (the compiler will make it return 0 anyway, but your program simply doesn't notice).

So this C program:

void Hello(void){
    printf("Hello World!\n");
}
int main(void){
   Hello();
   return 0;
}

Can just be put this way in BlitzMax

Function Hello()
    Print "Hello World!"
End Function

Hello

IF

Global A = 10
If A=10 Then
    Print "Ten"
Else
   Print "Not Ten"
End If

Above is the simple setup to using "If" in BlitzMax. So "if" contains the value 10 then print "ten" and otherwise print "not ten", I think no further explanation is required. Now "If" is set up in a pretty dirty way in BlitzMax, but it does make it rather powerful.

First of all the keyword "Then" is optional and can be left out. So this will have the same effect

Global A = 10
If A=10
    Print "Ten"
Else
   Print "Not Ten"
End If

"If" is in BlitzMax also unique that you can set it up in Scoop form, as I did above, but as a variant to BASIC the classic BASIC one line setup for "If" is also supported:

Global A=10
If A=10 Print "Ten" Else Print "Not Ten"

If you do that the "End If" closure is not needed. This is basically only recommended if you have one instruction after the "Then" or "Else" clauses, and be adviced to use it with care, as it does create some "dirty code".

A few notes:

  • In BlitzMax we use a single "=" for comparing and not a double "==" like in C and many languages dirived from C. This is, because BlitzMax derives from BASIC where a single "=" has always been the standard.
  • If you are a hardcore C coder, you may not like this, but no, you cannot assign variables in an "If" statement.
  • BlitzMax does NOT support Boolean variables, but any value that is either 0, an empty string or Null is considered False, everything else is considered True. The keywords True and False contain the values 1 and 0 respectively.

To demonstrate the last note:

Global A:String = "Jeroen"
Global B = 20
If A Print "Variable A is not empty"
If B Print "Variable B does contain a value that is not zero"

WHILE and REPEAT

WHILE is a command BASIC adopted from C and has tradionally been closed with "WEND". And "REPEAT" has been adopted from Pascal and works almost the same as in Pascal. Both commands make your program loop as long as a certain condition is true. However, Repeat checks at the end of the loop and While at the start.

Global A=0
While A<10
    Print A
        A = A + 1 '(in BlitzMax you can also write A:+1 for short)
Wend

So A is set to 0 at the start. As long as A is lower than 10 perform everything that is between the WHILE and the WEND instructions.

Now let's do this with REPEAT

Global A = 0
Repeat
    Print A
        A = A + 1
Until A>10

Now C programmers must be alert, since although REPEAT is used in the same manners as "do {...} while" loops, C will repeat as long as the condtion is true and BlitzMax will repeat UNTIL the condition is true.

Now Repeat is also the official way in BlitzMax to create an INFINITE loop with the "FOREVER" keyword.

Repeat
     Print "This goes on forever!"
Forever

FOR loops

The For command is most of all based on how BASIC uses it. So complex C syntaxes such as "for (int i=0;i<=10;i++)" kind of syntaxes won't work here.... Downside is that you have less possibilities than in C, but the big plus is that for is therefore easier to use. Let's for now focus on a regular For (I'll get to ForEach later).

For Local I=1 to 10
     Print I
Next

The "next" keyword has in BASIC always been the keyword to end a FOR loop, so BlitzMax followed that example. I personally always prefer to make the variable I use for For a local just declared in the For Loop. Just prevents a lot of shit. For C programmers or those used to the C style of For, the code above would be this in C:

for (int i=1;i<=10;i++){
    printf("%d\n",i);
}

I starts at value 1 and each time the "Next" keyword passes I in incremented by 1, and the loop repeats. When 10 has been reached, the commands within the scoop will be run one last time and then Next will end it and go to the next instruction.

Now BlitzMax has a nice variant to this approach:

For Local I=1 Until 10
    Print I
Next

If you run the first program you'll see that all numbers from 1 till 10 are imprinted. The second program will show the numbers 1 till 9 in stead of 10. For will then stop immediately when 10 is reached and execute the first instruction after Next. This may seem pretty useless, but when you are going to mess with memory banks or want to dive into arrays in a more professional way, For Until can prevent you a lot of headaches, so I recommend you to memorize it.

Lastly is the "Step" keyword which works like this:

For Local i=0 To 20 Step 5
      Print I
Next

Now I will increment with 5 in stead of 1. This also works with until:

For Local i=0 Until 20 Step 5
      Print i
Next

And if you want For to count down in stead of up "Step" is also needed:

For Local i=20 to 1 Step -1
      Print i
Next

One important note. "Step" only takes CONSTANTS as values. Why that is, is beyond me, but you must be aware of that!

TYPE a first sniff

Type is where BlitzMax gets interesting. Type is basically what "class" does in C# and other OOP languages.

Type MyData
    Field Name:String
        Field Age
End Type

Global MD:MyData = new MyData
MD.Name="Jeroen"
MD.Age=43
Print "My name is:"+MD.Name
Print "My age is:"+MD.Age

Types are in BlitzMax very important if you really wanna make your stuff go round. Type will declare a type (duh) an I named it "MyType" Field are the field variables, these variables work roughly the same as Locals and Globals, but they only live inside the type. Now we can use MD as the variable of the type we made. "New MyData" will allocate the memory needed to store all the data.

Now one thing needs to be taken in mind (especially when you are a C or Pascal programmer). BlitzMax ONLY uses pointers for types and extra marks like "*" (in C) or "^" (in Pascal) are NOT required (in fact, don't use them at all).

Type MyData
    Field Name:String
        Field Age
End Type

Global MD:MyData = new MyData
MD.Name="Jeroen"
MD.Age=43
Global MD2:MyData = MD
MD2.Age=45
Print "My name is:"+MD.Name
Print "My age is:"+MD.Age

If you copy the program above you will see that MD.Age will display 45 and not 43. That is because MD2 and MD use the same pointer, or in simple English the same memory address thanks to the "MD2=MD" instruction. Also:

If MD=MD2

Will check if MD and MD2 have the same pointer and not the same value, so if the values are the same, but the pointers are not this will be handled as "False".

Now what you will see is that I did allocate memory for my objects, but I did not free that memory. You never need to do that. When you no longer need MD,just leave it be or add "MD=Null" and eventually the memory will be freed and when your program terminates normally this will happen too. BlitzMax uses a garbage collector, so that makes things easier, doesn't it?

Linked List

BlitzMax has an in-built module for linked lists. Unfortunately it only works for strings and objects and not for nummeric values. This is also where working with types can be interesting.

Type MyEnemy
    Field X,Y
        Field Speed
end Type
Global Enemies:Tlist = new Tlist

For local i=1 to 10
    local MyE:MyEnemy = New MyEnemy
        MyE.X = Rand(1,300)
        MyE.Y = Rand(1,300)
        MyE.Speed=Rand(1,25)
        ListAddLAst Enemies,MyE
Next

For Local Enemy:MyEnemy = EachIn Enemies
     Print "Enemy approaching at: "+Enemy.X+","+Enemy.Y+" with speed: "+Enemy.Speed
next

Any idea how this works? I created 10 objects of the type Enemy and I stored them all in my list After that I process them with the EachIn one by one. This allows you to put in any number of enemies without being bothered by maximums. Downside is that random access is possible but will slow your game down, so it's not recommended, but in normal approach random access is not needed. If the player destroys the current enemy you can just be as quickly as "ListRemove Enemies,Enemy" and the enemy in question will be removed from the list (and eventually be removed from the memory by the garbage collector) and no the "For Each" loop will not be bothered. Now mastering this part is where you can make your game shine.

End of tutorial

This is the end of this quick guide. You're not there yet, as you still have a lot to learn, but here you got the feeling of how some basic core features of BlitzMax work. If you want I can also set up a guide for some more advanced features of BlitzMax, like Methods, Abstracts, Maps and stuff like that. What do you think, did BlitzMax trigger you interest?

(Posted 7 months ago)

Great tutorial Tricky! Thanks for sharing

Reply To Topic

Please log in to reply