Last time our variables contained either numbers or strings (as example - word). Now I am introducing new variable type called TABLE.
If we would think variables as books (title name being the variables name, and content of a book being the content of a variable), then tables would be like bookshelves, meaning that these bookshelves can contain several books (variables) inside them.
To make a table we use:
Code: Select all
mytable = {}
this is now simply an empty table without any content. It is just like empty bookshelf in that you can see it, watch it, and do all the stuff that you can with bookshelves, but as long as its empty, it is just taking space.
to have some content, we have several ways to do it, we could add content when we create it:
Code: Select all
mytable = { name="Andreas", familyname="Falkenhahn"}
Code: Select all
mytable = {}
mytable.name = "Andreas",
mytable.familyname="Falkenhahn"
Code: Select all
print(mytable.name.." "..mytable.falkenhahn)
by otherwords, instead of using print(x), we will now use print(mytable.x), otherwise they work exactly like previous tutorials variables, and you can do all the same things as you can do to previous examples variables.
as example:
Code: Select all
mynumbers = {x=1, Y=2}
sum = mynumbers.x + mynumbers.y
print(sum)
Tables dont necessary have to be containing variables themselves, but they can also contain list of info, as example:
Code: Select all
mynumbers = {2, 4, 6, 8, 10}
print(mynumbers[2])
Here are a couple of things to notice. First of all, each datapiece (in this case, numbers 2,4,6,8,10) need to be separated by ",".
Then to access these, we will use tablename plus at end put number inside "[" and "]". This [n]-part is called index number.
This is to say that in our example, we are wanting to access from table called "mynumbers" a databit in location 2.
And here comes a thing to notice. Databit that resides in location two is not 4, but 6? Why is this?
This is because Hollywood starts counting lists from location 0. Hence the first databit in a list is not located at index location 1, but in index location 0. Therefore, although number 6 is the third number put into list when it was being formed, it was put to index place 2, since first one went to 0, second one to 1, and third one goes to 2.
We can also change and access these databits individually, just like we would access and change for example variable "x" by using this index location:
Code: Select all
mynumber = {2, 4, 6, 8, 10}
print(mynumber[2])
mynumber[2] = 1
print[mynumber[2])
Tables can also contain other tables inside them:
Code: Select all
mytable = {}
mytable[1] = {"a", "b", "C"}
mytable[2] = {1, 2, 3}
print(mytable[1][1]..mytable[2][0])
when table contains table inside it, we call it multidimensional table. These are very useful to for example maps, in which we could use map[x][y] as example.
in this example, i first created empty table called "mytable"
After that i created to this "mytable" to index location 1, another table that contained letters a,b and c.
then created another table to index location 2 containing numbers 1, 2, 3.
Therefore to access them, i had to use 2 index numbers [n][n] instead of just one [n].
First index number after mytable (mytable[n]) was to tell me which table, table 1 or 2, i was wanting to access. Then the next index number (mytable[n][n]) would be telling the index number of the databit i wanted to access of that subtable.
Notice, that i skipped index location 0. This is not a problem as long as i dont try to access it. hollywood is very flexible. I could have instead created these subtables to index locations 23 and 78 if i had wanted to, and hollywood wouldnt mind about it.
however, if you would now try:
Code: Select all
print(mytable[0][1])
similarly if you would try:
Code: Select all
mytable[1][3]
This same can also easily happen other ways too:
Code: Select all
mytable[1] = {1, 2, 3}
Code: Select all
mytable = {}
mytable[1] = {1, 2, 3}
Instead of creating subtables afterwards (which is often clearer actually), you could make them at once:
Code: Select all
mytable = {
[1] = {1, 2, 3},
[2] = {4, 5, 6}
}
print(mytable[1][1])
There is no reason from hollywood point of view, why these should reside in different lines, but usually it is easier to read them putting them to different lines, as for example if you are making a map.
Code: Select all
mytable = { [1] = {1, 2, 3}, [2] = {4, 5, 6} }
Notice that after first subtables closing, there is "," but that there is no "," after second subtables closing.
This is because these subtables are simply treated as items in a list. Hence, similarly as you would put ","s between "a", "b", "c", so you also need to do with tables, a table is simply an item, just like "a". Also, the last table doesnt receive the dot at the end, since there are no more items coming after it, this is same as with {"a", "b", "c"} in which "c" doesnt receive "," either.
It might be easier to see this tables "," part, when creating empty tables inside a table:
mytable = { 1, 2, 3, 4, 5}
mytable = { {}, {}, {}, {}, {} }
that the tables have some actual content, makes no difference, it just makes them bigger sized that still need the dots between closing "}" and next opening "{". If there are two closing "}"s in a row, to those you dont put "," between.
Tables can contain mixed information:
Code: Select all
mytable = {"a", "b", 1, 2, name="Andreas", lastname="Falknehahn", {1, 2, 3}, {"a", "b", "c"}, [123] = "d", numbers={7, 8, 9} }
print(mytable[0]..mytable[5][1]..mytable.name..mytable[123]..mytable.numbers[1])
Here you can see couple of things again. that index location 0 prints "a", is nothing to wonder, but why does index location [5] access the first subtable? Shouldnt index location 5 be "familyname"?
No. when we use something like "name=Andreas", this "name" is actually same as having [1], except that instead of having index number, it is having index naming, in this case [name].
By otherwords, you could access this piece as wel by usingl:
Code: Select all
print(mytable["name"])
Notice, that while you create using naming, you simply use NAME, but when you access it at later point using index[n] method, you have to use '"' between the name. ie. ["NAME"].
there is no difference wether you access this kind of info using mytable.name or mytable["name"], they both do exactly same thing, Hollywood is just flexible in giving you opportunity to do either way you prefer.
For this reason, in previous example when tables index list is being named, "name" and "familyname" dont increse the index number, as they are their own index numbers (or index names rather) and hence the next table that becomes formed nameless, by simply using opening and closing brackets instead of for example "name = {}", Hollywood assigns next index number to the table.
Or to put it otherway. Hollywood automatically puts index number to each item that has no index numbering (or naming) in itself. Hence the "name" and "familyname" have their index numbering put manually, and hollywood hence continues putting next available index numbering to the next unindexed item, which is that table.
As a last thing about tables, you can have as many subtables as you like:
Code: Select all
map = {}
map[1] = {}
map[1][1] = {}
map[1][1][1] = {status=1}
print(map[1][1][1].status)
this by the way could be map[x][y][z]
When creating lists, or going through them, FOR-NEXT command is quite useful. FOR-NEXT is once again so called program flow command, just like last times IF stuff was.
Code: Select all
for n=1 to 5
print(n)
next
what happens here is that program keeps looping between for and next command until it is done with what FOR says. In this case it says that first time program enters this loop, value of variable n is set to 1 (and variable n is also created if it didnt exist before). When it comes to "NEXT" it jumps back to this "FOR" line, and increases the value of n by 1, making it at next loop be 2. This will continue, until value of variable n is 5, which will be the last time it still goes through this loop, until it continues past the NEXT.
Therefore this results in 12345 printed (which is the value of n each time).
You could also have:
Code: Select all
for n=1 to 10 step 2
print(n)
next
Now it is instructed to increase n by 2 each loop. Notice, that in this case loop is executed last time at point n is 9, because next time n:s value would be 11, and it was supposed to go through this loop only as long as n was between 1 and 10.
You can also use:
Code: Select all
for n=10 to 1 step -1
Lets combine tables and for together:
Code: Select all
mytable={}
for n=1 to 5
mytable[n] = n*2
next
print(mytable[3])
by otherwords, we created table:
Code: Select all
mytable = {2, 4, 6, 8, 10}
For n=1 to 100
print(n)
next
and another version closer to previous tutorial examples logic:
for n=0 to 9
for i=0 to 9
print(n..i)
next
next
Notice that the first one will simply have "1, 2, 3..." instead of "01, 02" like in previous tutorial. Second example on the other hand goes from "00" to "99" only, missing the 100 and having that "00" in it as first one.
this works similar to last time, that first it goes inside a FOR loop, that keeps raising variable n:s value from 1 to 10, but before increasing it up by one (or by otherwords, before having gone through for NEXT loop once), it will first go through another loop, which will keep increasing variable i:s value from 1 to 10 and then it keeps printing through variable n and i similar to previous tutorial examples logic.
Notice that these n and i could be any other variables as well, their names dont really matter, they are just variables that FOR command creates at the spot and their names can be anything according to normal variable name rules. I am just using n and i as standard for meaning some numbers, so it is easier to understand what is happening here (n as number, i as something that clearly doesnt remind letter n so you wouldnt get confused between these two, like n and m could do)
Now it is time for the game example. I was playing p1xl groups ios game called p1xl party, and it had this bomber game which was remake of some classic game and i figured it would actually be good tutorial game for tables usage. Hence, here is simple bomber game for you:
Code: Select all
@SCREEN {Mode = "FakeFullScreen"}
@DISPLAY {Width = 1920, Height = 1080, Borderless = True, ScaleMode = #SCALEMODE_AUTO, FitScale=True}
resetstuff = {bomberx=50, bombery=100}
bomber = {x = 50, y = 100, width=100, height=50, speed=300}
bomb = {speed = 3000, size = 30}
building = {size = 100}
level = {}
level[1] = { speed=300, height=2,
[2] = {0, 0, 0, 1, 1, 0, 0, 0},
[1] = {1, 0, 1, 1, 1, 1, 0, 1}
}
level[2] = { speed=700, height=3,
[3] = {0, 0, 0, 1, 0, 0, 0, 0},
[2] = {0, 0, 0, 1, 0, 0, 0, 0},
[1] = {0, 0, 0, 1, 0, 0, 0, 0}
}
bombdropping=False
quitgame = False
curlvl=1
BeginDoubleBuffer()
StartTimer(1)
Repeat
Flip()
Cls(#BLACK)
bomber.x = bomber.x + bomber.speed * timemultiplier
If bomber.x > 1880
bomber.x = resetstuff.bomberx
bomber.y = bomber.y + 100
EndIf
Box(bomber.x, bomber.y, bomber.width, bomber.height, #WHITE)
If bombdropping=True
bomb.y = bomb.y + bomb.speed * timemultiplier
If bomb.y > 1080 Then bombdropping = False
Circle(bomb.x, bomb.y, bomb.size, #WHITE)
EndIf
buildingsleft=False
For n=1 To level[curlvl].height
For i = 0 To 7
If level[curlvl][n][i] = 1
buildingsleft = True
Box(i*200, 1080-(100*n), building.size, building.size, #WHITE)
colcheck = Collision(#BOX, bomber.x, bomber.y, bomber.width, bomber.height, i*200, 1080-(100*n), building.size, building.size)
If colcheck = True
TextOut(#CENTER, #CENTER, "GAME OVER!")
Flip()
WaitRightMouse()
End()
EndIf
If bombdropping=True
colcheck = Collision(#BOX, bomb.x, bomb.y, bomb.size*2, bomb.size*2, i*200, 1080-(100*n), building.size, building.size)
If colcheck = True
bombdropping=False
level[curlvl][n][i] = 0
EndIf
EndIf
EndIf
Next
Next
If buildingsleft = False
curlvl=curlvl+1
If curlvl = 3
TextOut(#CENTER, #CENTER, "GAME COMPLETE!")
Flip()
WaitRightMouse
End()
EndIf
bomber.x = resetstuff.bomberx
bomber.y = resetstuff.bombery
bomber.speed = level[curlvl].speed
EndIf
If IsLeftMouse() = True and bombdropping = False
bombdropping = True
bomb.x = bomber.x
bomb.y = bomber.y
EndIf
Repeat
timepassed = GetTimer(1)
Until timepassed > 3
ResetTimer(1)
timemultiplier = timepassed / 1000
If IsRightMouse() = True Then quitgame=True
Until quitgame=True
Game itself is very simple. There are buildings at bottom of screen, and player is a bomber that moves from left to right. Bomber starts from top, and after it reaches right edge, it transports itself to left edge of screen and goes slightly down.
Players object is to drop bombs (only one bomb at a time) and destroy all the buildings. If succesful, player will move to next level, until all levels are complete (example has only 2 levels). If bomber however gets down enough to collide with one of the buildings, then it is game over.
Use rightmouse to quit.
Code: Select all
@SCREEN {Mode = "FakeFullScreen"}
@DISPLAY {Width = 1920, Height = 1080, Borderless = True, ScaleMode = #SCALEMODE_AUTO, FitScale=True}
Code: Select all
resetstuff = {bomberx=50, bombery=100}
bomber = {x = 50, y = 100, width=100, height=50, speed=300}
bomb = {speed = 3000, size = 30}
building = {size = 100}
resetstuff are used when level changes. It is simply the co-ordinates from where bomber should start at beginning of level each time.
Notice that the bombers x and y are set to same as resetstuffs x and y and speed is set to same as first levels speed is set. This has to do how level changing is handled in this program and i will talk more about it, when that part comes.
Code: Select all
level = {}
level[1] = { speed=300, height=2,
[2] = {0, 0, 0, 1, 1, 0, 0, 0},
[1] = {1, 0, 1, 1, 1, 1, 0, 1}
}
level[2] = { speed=700, height=3,
[3] = {0, 0, 0, 1, 0, 0, 0, 0},
[2] = {0, 0, 0, 1, 0, 0, 0, 0},
[1] = {0, 0, 0, 1, 0, 0, 0, 0}
}
each level has speed, and height. Speed is the speed by which players bomber moves from left to right. Bigger number, bigger speed. Speed is in how many pixels per second. Height is very important, height must be exactly the amount of [n] numbers you have in your level. if height is lower, then some of the buildings are missed, if too high, program will try to access index place that doesnt exist, and will crash. There would be better way to do this, which would take care of the height itself, but maybe in next tutorial. In this one i came to conclusion that code is easier to understand when done this way.
Notice how i have put index numbering wrong way around, that instead of starting from 1 and then next line going for 2, i star from last one, and go downwards in numbers instead. This is simply to make it easier for programmer to see how that level should look like. Otherwise he would need to turn the "image" upside down to see how it looks like.
1 simply means that block has a building, and 0 means there is nothing in that location.
Code: Select all
bombdropping=False
quitgame = False
curlvl=1
BeginDoubleBuffer()
StartTimer(1)
Bombdropping is used to tell if bomb is currently dropping or not, quitgame obviously to quit the game. curlvl is same as currentlevel, which starts from 1, if you change it to 2, you can directly jump to level 2, but you also need to adjust the starting speed right for bomber manually at earlier location due to way level changing is handled. For basically level is not changed at beginning, but code could be said to be starting from middle of level.
rest are same to PONG tutorials.
Code: Select all
Repeat
Flip()
Cls(#BLACK)
Code: Select all
bomber.x = bomber.x + bomber.speed * timemultiplier
If bomber.x > 1880
bomber.x = resetstuff.bomberx
bomber.y = bomber.y + 100
EndIf
Box(bomber.x, bomber.y, bomber.width, bomber.height, #WHITE)
Last line is drawing our bomber (a white lined rectangle)
Code: Select all
If bombdropping=True
bomb.y = bomb.y + bomb.speed * timemultiplier
If bomb.y > 1080 Then bombdropping = False
Circle(bomb.x, bomb.y, bomb.size, #WHITE)
EndIf
drawing is done using command CIRCLE. IN this one you tell the x and y co-ordinates of the circle, and after that you tell the size of radius and as last, the color, and hollywood will then draw it using given x and y as the center of the circle.
Code: Select all
buildingsleft=False
For n=1 To level[curlvl].height
For i = 0 To 7
next two lines make two FORs from which other FOR is inside other FOR loop. Just like in that 00-99 example.
Here we come to the importance of height being right. What these for loops are effectively doing, is to go through the whole level[currentlevel] table to check for buildings. This height is same as telling how many level[n][1], level[n][2], level[n][3]... there are in this level[n]table, and that way going through each one of them.
next for loop is to go through every number (0 or 1) in that level[n][n]. (by otherwords, it is going through list of for example level[n][n] = {1, 0, 1, 1, 1, 1, 0 ,1} At that point I am using hardcoded 0 to 7, since i know they are always having 8 numbers inside them, as i put it that way. I could add another variable in addition to height, called width for example, and have different amounts of indexes in each, and then change this line to 0 to width, but it is easier to understand as it is now i believe.
Code: Select all
If level[curlvl][n][i] = 1
buildingsleft = True
Box(i*200, 1080-(100*n), building.size, building.size, #WHITE)
As last, it also draws that building, simply a rectangle in specific location. However, how is buildings location defined is worh a deeper look.
command is simply(x, y, width, height, color)
x part is i*200 this means that when it goes through location [n] = {1, 0, 1, 1, 1,1, 0, 1}, it is using variable i for these locations. When i=0, the first number 1 is being accessed, when i=1, then at index place[1] there is 0, index place[2] has 1 again. Each one means that buildings is drawn.
Idea is that if index location is [2], then i is 2 as well. And this way we can decide that each building is 200 pixels apart from previous ones start location, and hence we can simply use i*200, which in this case would be 2*200=400. So while first one (index location[0] is drawn to x-location 0*200 = 0, the next esixsting building at location index[2] is drawn to x-location 2*200=400, making them apart. This way i have nice way of equally distributing the buildings, without having a need to give them each their own unique x-location, but can instead just use this mathematical formula to make them apart from each other.
Then the y-co-ordinate is based upon 1080-(100*n).
reason why we are minusing from 1080 is, because 1080 is the bottom edge of the screen, by using minus instead of plus, we are getting things higher to screen.
After this instead of using i variable (which is telling the index location of 0 and 1:s we are using variable n, since that is telling in which line of 0 and 1s we are going of the current level.
This way, if we are drawing a building from line 3, it will effectievly make them be drawn higher that buildings in line 2. example: line 1 building goes to 1080-(1*100)=1080-100 = 980, while line 3 buildings are drawn to 1080-(3*100)=1080-300=780, which is higher than 980 is.
Code: Select all
colcheck = Collision(#BOX, bomber.x, bomber.y, bomber.width, bomber.height, i*200, 1080-(100*n), building.size, building.size)
If colcheck = True
TextOut(#CENTER, #CENTER, "GAME OVER!")
Flip()
WaitRightMouse()
End()
EndIf
If collision check shows to be true, it means bomber have crashed against building, and then it will be printed to middle of screen (thats what the #CENTER, #CENTER means, that x-spot is center of screen, and so is y-spot a center of screen), and notice that i have had to put flip here. If i hadnt, then player wouldnt be seeing the text as nothing is drawn (including text) until flip command comes. After this program halts its execution until rightmousebutton is being pressed, after which it simply ends the program.
Code: Select all
If bombdropping=True
colcheck = Collision(#BOX, bomb.x, bomb.y, bomb.size*2, bomb.size*2, i*200, 1080-(100*n), building.size, building.size)
If colcheck = True
bombdropping=False
level[curlvl][n][i] = 0
EndIf
EndIf
If collision check return true, it means the bomb have hit a building, in this case bombdropping is set to false, so no more bomb related stuff would be executed (including dawing it or collision checks with it) and in addition the building that the bomb collided with, will be removed. This is done by setting the index location of that buildings 1, into 0, which means it is not existing anymore.
Code: Select all
EndIf
Next
Next
Code: Select all
If buildingsleft = False
curlvl=curlvl+1
If curlvl = 3
TextOut(#CENTER, #CENTER, "GAME COMPLETE!")
Flip()
WaitRightMouse
End()
EndIf
bomber.x = resetstuff.bomberx
bomber.y = resetstuff.bombery
bomber.speed = level[curlvl].speed
EndIf
However, if none of the buildings are up anymore, then that line that sets buildingsleft to true, will never be executed. In this case, we need to change the level.
we start by putting curlvl = curlvl+1 which means that curlvl (current level) is now one number higher than before.
Then we first check if curlvl = 3, which would mean that game would have been completed, as i have only 2 levels in this example. If this happens, then it displays a text to screen telling "game complete!" and uses flip to actually display that message, just like before and waits for right mouse to continue and then ends the program.
If level 3 have not been reached, then it first sets bomber x and y to resetstuffs x and y. Next is sets bomber.speed to current level tables speed (you do remember there was this speed variable inside level tables). after this code simply continues executing and now next levels table is used for all the building checkings and effectively, level have been changed.
If yo uwish to make more levels, you can simpl make more tables with same method the first two have been done, and then change the number of if curlvl=3 into higher. That if you for example make 5 more levels, making you having 7 levels in total, simply chagned if curlvl=3 to if curlvl=8 and it will work just fine as long as you didnt mess something else.
Code: Select all
If IsLeftMouse() = True AND bombdropping = false
bombdropping = True
bomb.x = bomber.x
bomb.y = bomber.y
EndIf
Code: Select all
Repeat
timepassed = GetTimer(1)
Until timepassed > 3
ResetTimer(1)
timemultiplier = timepassed / 1000
If IsRightMouse() = True Then quitgame=True
Until quitgame=True