Making a Text RPG can be very easy. You need to know exactly what you want it to do though. Things like is it real time or turn based. Can you shoot, can the player pick up things, are you going to have a map editor. These effect what kind of map data you use. Some are easyer then others because they allow less code to move around, and are easier to understand. Some are alittle difficult to use and have more code.
I have programmed as many possible types of maps that can be used. These can be combined to make use of different features. They all hold the same practical idea.
I will start with a simple one. If you copy the code and paste it in a new text file and name it "textrpg.bas". then open it in QBasic it should work unless you screwed it somehow, If you didn't it could have been me.
Most of this makes sense. The only thing you might be confused about is why I use 00,02,10,11 in the data instead of using 1,2,3,4. Well if you look at my code in the keys part you will notice it checks the map for something. "map(px, py - 1) <= 9" This checks to see if the position it wants to move to is a walkable tile or a wall. I have used 00-09 as walkable tiles and 10-99 as wall tiles. This way I'm setup for when I add more walkable tiles. If you really want to be safe you can have 0-50 as walkables and 50-whatever as walls.
If you wanted to make items laying around then you should use a fake and chck when the player ontop of it by using
Put it after the keys so it happens as soon as you move onto it. It checks to see if the place where the player is a 3 on the map and if it is then it sets that part of the map to a normal walkable tile.
Now that you have taken all that code in you are ready to see the next map type.
This one is a little more complex. I only just came up with it. This one makes it easier to make a new tile. The tile can be any number and it doesn't effect the where the player moves. The 0 and the 1 change that.
The 0 at the end tells the player that he can move onto it, if it was a 1 then the player could not walk on it. This is the code it uses to check if it can go somewhere.
Looks the same as the the code in the 1st program. It really is very much the same. It goes and sees what tile number is in the position and then if the tile has a 0, if so then the player moves.
Those two are pretty good but I still have more. Heres the 3rd one. It is kind similar to the one above.
Nice eh? This makes use of moulding information together. This has saved space. I could have blended then wall data into the map data by changing the data in the map to "101760" which breaks up into
As a challenge you might want to do that and also make an editor to make maps. I recconmend that you make an editor for that data, because it aint all that much fun to put in. If I get enough e-mails(lets say 1) I will wright a tut on editors and make one just for this.
This code is a little different for when it displays it. it first has a look at the first two digits of the number
this tells it what color it will be. The next line finds out what the last 3 digits are and uses them with CHR$()
PRINT CHR$(VAL(MID$(STR$(map(x, y)), 3, 6)))
When the player moves it checks the 2nd map to see if there is a 0 on the position it wants to move too. If it is a 1 it won't move there, thus acting like a wall. Simple huh?
I'll give one last example of a map and then show you how to link up a couple of maps and store the data of each map in a tidy easy to use way.
CLS
'gets map ready
DIM map(100, 100)
FOR y = 1 TO 10
FOR x = 1 TO 10
READ map(x, y)
IF map(x, y) = 0 THEN LOCATE y, x: COLOR 2: PRINT CHR$(176)
IF map(x, y) = 2 THEN LOCATE y, x: COLOR 6: PRINT CHR$(176)
IF map(x, y) = 10 THEN LOCATE y, x: COLOR 10: PRINT CHR$(6)
IF map(x, y) = 11 THEN LOCATE y, x: COLOR 7: PRINT CHR$(111)
NEXT x
NEXT y
'sets player x and player y
px = 2
py = 9
'start of big loop
top:
'gets what is behind player
bg = SCREEN(py, px)
bgc = SCREEN(py, px, 1)
'puts player on the screen
LOCATE py, px: COLOR 15: PRINT CHR$(2)
'checks to see if a key is pressed
DO: P$ = INKEY$: LOOP UNTIL P$ <> ""
'clears player off screen and replaces with background
LOCATE py, px: COLOR bgc: PRINT CHR$(bg)
'checks to see if a certian key was pressed so that the player x or player y
'can be moved to a new location.
IF P$ = "8" AND SCREEN(py - 1, px) <> 6 THEN py = py - 1
IF P$ = "5" AND SCREEN(py + 1, px) <> 6 THEN py = py + 1
IF P$ = "6" AND SCREEN(py, px + 1) <> 6 THEN px = px + 1
IF P$ = "4" AND SCREEN(py, px - 1) <> 6 THEN px = px - 1
IF P$ = CHR$(27) THEN END
'goes back to the top
GOTO top
'map data
DATA 10,10,10,10,10,10,10,10,10,10
DATA 10,00,00,00,00,00,00,00,02,10
DATA 10,00,00,00,00,00,11,02,02,10
DATA 10,00,00,00,02,02,02,02,10,10
DATA 10,00,00,00,02,10,00,00,00,10
DATA 10,00,00,02,02,00,00,00,00,10
DATA 10,00,02,02,00,10,00,00,10,10
DATA 10,00,02,00,10,10,10,00,00,10
DATA 10,02,02,00,00,00,00,00,00,10
DATA 10,10,10,10,10,10,10,10,10,10
Now this one is almost the same as the 1st program. The only difference is that instead of check the map to see if it can move it checks the screen. This can useful sometimes but others useless. If you printed character 006 randomly over the screen and did not have a map for it then this is useful. It would create a forest type type effect also. You could put any other character on the screen and you would be able to walk over it because it is not character 6. To change that just change the sixs in the keys part to anothe character, for example 219
IF P$ = "8" AND SCREEN(py - 1, px) <> 219 THEN py = py - 1
IF P$ = "5" AND SCREEN(py + 1, px) <> 219 THEN py = py + 1
IF P$ = "6" AND SCREEN(py, px + 1) <> 219 THEN px = px + 1
IF P$ = "4" AND SCREEN(py, px - 1) <> 219 THEN px = px - 1
Instead of checking the character yo could check the color. to do that all you have to do is add a 1. Like this
SCREEN(py - 1, px,1) <> 4
If you use that instead you will not be able to walk on anything that is dark red.
Now for the end of this. I will show you a program that can change to different maps easily. After all it would be stupid not to show this in a RPG tut, because thats one of the main features.
CLS
'gets map ready
DIM map(10, 11, 11)
FOR m = 1 TO 3
FOR y = 1 TO 10
FOR x = 1 TO 10
READ map(m, x, y)
NEXT x
NEXT y
NEXT m
'sets player x and player y
px = 2
py = 9
'sets the map you start on
m = 1
'draws the current map you are on
redraw:
FOR y = 1 TO 10
FOR x = 1 TO 10
IF map(m, x, y) = 0 THEN LOCATE y, x: COLOR 2: PRINT CHR$(176)
IF map(m, x, y) = 2 THEN LOCATE y, x: COLOR 6: PRINT CHR$(176)
IF map(m, x, y) = 10 THEN LOCATE y, x: COLOR 10: PRINT CHR$(6)
IF map(m, x, y) = 11 THEN LOCATE y, x: COLOR 7: PRINT CHR$(111)
NEXT x
NEXT y
'start of big loop
top:
'gets what is behind player
bg = SCREEN(py, px)
bgc = SCREEN(py, px, 1)
'puts player on the screen
LOCATE py, px: COLOR 15: PRINT CHR$(2)
'checks to see if a key is pressed
DO: P$ = INKEY$: LOOP UNTIL P$ <> ""
'clears player off screen and replaces with background
LOCATE py, px: COLOR bgc: PRINT CHR$(bg)
'checks to see if a certian key was pressed so that the player x or player y
'can be moved to a new location.
IF P$ = "8" AND map(m, px, py - 1) <= 9 THEN py = py - 1
IF P$ = "5" AND map(m, px, py + 1) <= 9 THEN py = py + 1
IF P$ = "6" AND map(m, px + 1, py) <= 9 THEN px = px + 1
IF P$ = "4" AND map(m, px - 1, py) <= 9 THEN px = px - 1
IF P$ = CHR$(27) THEN END
IF px = 11 THEN
px = 1
IF m = 1 THEN m = 2: GOTO redraw
END IF
IF px = 0 THEN
px = 10
IF m = 2 THEN m = 1: GOTO redraw
END IF
IF py = 11 THEN
py = 1
IF m = 2 THEN m = 3: GOTO redraw
END IF
IF py = 0 THEN
py = 10
IF m = 3 THEN m = 2: GOTO redraw
END IF
'goes back to the top
GOTO top
'map data
DATA 10,10,10,10,10,10,10,10,10,10
DATA 10,00,00,00,00,00,00,00,00,10
DATA 10,11,00,00,00,00,11,00,00,10
DATA 10,00,00,00,02,02,02,02,10,00
DATA 10,00,00,00,02,10,00,02,02,02
DATA 10,00,00,02,02,00,00,00,00,00
DATA 10,00,02,02,00,10,00,00,10,10
DATA 10,00,02,00,10,10,10,00,00,10
DATA 10,02,02,00,00,00,00,00,11,10
DATA 10,10,10,10,10,10,10,10,10,10
DATA 10,10,10,10,10,10,10,10,10,10
DATA 10,00,00,00,00,00,10,00,10,10
DATA 10,00,00,00,11,00,00,00,00,10
DATA 00,00,00,00,00,00,00,00,10,10
DATA 02,02,02,02,00,00,10,00,00,10
DATA 00,00,00,02,00,10,00,00,00,10
DATA 10,00,00,02,00,00,00,00,00,10
DATA 10,10,00,02,02,02,00,10,00,10
DATA 10,00,00,11,00,02,00,00,00,10
DATA 10,10,10,10,00,02,00,10,10,10
DATA 10,10,10,10,00,02,00,10,10,10
DATA 10,00,00,00,00,00,00,00,00,10
DATA 10,00,00,00,00,00,00,00,00,10
DATA 10,00,00,00,00,00,00,00,00,10
DATA 10,00,00,00,00,00,00,00,00,10
DATA 10,00,00,00,00,00,00,00,00,10
DATA 10,00,00,00,00,00,00,00,00,10
DATA 10,00,00,00,00,00,00,00,00,10
DATA 10,00,00,00,00,00,00,00,00,10
DATA 10,10,10,10,10,10,10,10,10,10
The big difference in this program is the 3 different maps. To switch from map to map all I had to do was change m to the desired map number so that that map data was used. When you reach the end of the map it checks to see what map you are on and then send you to the map that you need to goto. You can have many more levels and to change to a different map you can change what m equals. eg instead of going to map 2 you could change the
IF m = 1 THEN m = 2: GOTO redraw
to
IF m = 1 THEN m = 3: GOTO redraw
This would take you to map 3 and put you ontop of a wall because map 3 was not designed to link to map 1. All ya have to do is change the map data to make it look like you can.
Experiment with all of these and mix and match the map data use. This way you will be able to understand the code better and make better things. Try adding extra features to the programs like in the last one, try adding more maps. Make more tiles. Even try to make NPC's, people or monsters walk around on the map as well.
I hope this has inspired you, or at the least made you consider making a text RPG, or anyother text game. I hope to release another tutorial on Editors or AI's next month. I can't believe I wrote all this in one afternoon.
If you have any problems e-mail me at cant.get.nixon@gmail.com
or visit Pure Text and post on the forum www.Pure-Text.com
-Nixon
Download this tutorial: textrpg.txt
Z-clipping Triangles
Written by Shashi Ponraja
I decided to write this short tutorial to make absolutely sure I didn't forget how to clip 3d triangles. Then I realized I could post it on QB Express as
I decided to write this short tutorial to make absolutely sure I didn't forget how to clip 3d triangles. Then I realized I could post it on QB Express as well! So why the hell not.
Z-clipping Triangles
Whenever you have a point in 3d space, it will either be in front(z >0), behind you (z<0) or on you (z=0). The easiest way to eliminate this problem would
be to make sure all points of a triangle are visible, so if one isn't visible the triangle isn't drawn. Of course, as I discovered, this looks terrible
for large polygons, creating dumb-looking see through glitches. So what's the remedy? Remove the parts of the triangle that are not visible, leaving only
the visible parts. I suggest for the code you use double-precision(defdbl a-z for the poly clipping sub), I used that cuz sometimes errors occurred that
I thought might have been because of accuracy.
Starting off....
Let's say you have each point of your triangle in an array of vertex(3) as vtxinf
where
type vtxinf
x as single
y as single
z as single
u as single 'x-coord in texture map, I use between 0..1 but you can use whatever is appropriate
v as single 'y-coord in texture map, I use between 0..1 but you can use whatever is appropriate
end type
The 3 points are in vertex(0), vertex(1), vertex(2). (The extra element is there for a reason.....as you might expect)
To simplify coding this routine, we could start off by arranging it so the vertex with the largest z starts at the top, like this
If vertex(0).z < vertex(1).z then
swap vertex(0).x, vertex(1).x
swap vertex(0).y, vertex(1).y
swap vertex(0).z, vertex(1).z
swap vertex(0).u, vertex(1).u
swap vertex(0).v, vertex(1).v
endif
And so on and so forth until vertex(0).z is highest, and vertex(2).z is the lowest.
Now to quickly test whether any portion of the polygon is visible on-screen, just check this
if vertex(0).z > zclipplane then polyvisible = true
'where zclipplane is the minimum z can reach. Think of it as a line that extends to forever, and if a poly is behind it, it can't be seen
Since vertex(0).z is now the highest z, if that's not visible no point is. Anyway now we go into the actual z-clipping.
Just to give you a picture of what's going on, draw a triangle on a paper. This is your poly top-down. Draw a horizontal line(the z clip plane) that removes
two points from the poly. If you did it right, you'll notice all that needs to be done is make the two edges to intersect the z-clip plane.
Now try the same thing, but this time make the z-clip plane remove only one vertex. You'll see to get the visible poly you'll need
to add an extra vertex, resulting in a 4-sided poly.
Since clipping when 2 points are invisible is easier, I'll show you that first.
If vertex(1).z < zclipplane AND vertex(2).z < zclipplane then
'start off processing the first edge....
dx = vertex(1).x - vertex(0).x
dy = vertex(1).y - vertex(0).y
dz = vertex(1).z - vertex(0).z
du = vertex(1).u - vertex(0).u 'necessary to recalculate the new texture co-ordinates because otherwise it gets warped
dv = vertex(1).v - vertex(0).v
'find the positions the edge intersects the z-clip plane
'zclipplane = vertex(0).z + t * dz rearrange to find t
t = (zclipplane - vertex(0).z) / dz
'now find the new x, y, z, u and v co-ordinates
vertex(1).x = vertex(0).x + t * dx
vertex(1).y = vertex(0).y + t * dy
vertex(1).u = vertex(0).u + t * du
vertex(1).v = vertex(0).v + t * dv
vertex(1).z = zclipplane 'if zclipplane <> vertex(0).z + t * dz there is an error in your code somewhere
'repeat the above line of code, but change the vertex(1) to a vertex(2)
end if
There. Now the array vertex contains your perfect z-clipped triangle. But how do we do it when we have one invisible vertex?
Well consider the following. Since vertex(2).z is the smallest z value, it follows that if one vertex is invisible, it has to be
vertex(2).z . Think about that if it doesn't make sense. Now go back to that diagram you drew of the triangle with one invisible
vertex. Label the point with the largest z as 0, 2nd largest as 1, and the smallest as 2. To find the position of the clipped vertices, we
must find where the edge vertex(0) to vertex(2) intersects the z-clip plane, which is the first clipped vertex. The 2nd clipped vertex is where the edge
vertex(1) to vertex(2) intersects the z-clip plane.
So here's some code for those of us who are lazy...
extravtx = 0
if vertex(2).z < zclipplane then
extravtx = 1 'so the code will remember there is now an extra vertex
dx = vertex(2).x - vertex(0).x
dy = vertex(2).y - vertex(0).y
dz = vertex(2).z - vertex(0).z
du = vertex(2).u - vertex(0).u
dv = vertex(2).v - vertex(0).v
t = (zclipplane - vertex(0).z) / dz
vertex(3).x = vertex(0).x + t * dx
vertex(3).y = vertex(0).y + t * dy
vertex(3).z = vertex(0).z + t * dz
vertex(3).u = vertex(0).u + t * du
vertex(3).v = vertex(0).v + t * dv
dx = vertex(2).x - vertex(1).x
dy = vertex(2).y - vertex(1).y
dz = vertex(2).z - vertex(1).z
du = vertex(2).u - vertex(1).u
dv = vertex(2).v - vertex(1).v
t = (zclipplane - vertex(1).z) / dz
vertex(2).x = vertex(1).x + t * dx
vertex(2).y = vertex(1).y + t * dy
vertex(2).z = vertex(1).z + t * dz
vertex(2).u = vertex(1).u + t * du
vertex(2).v = vertex(1).v + t * dv
end if
Now we have all the points sitting around in our array vertex. How do we go about displaying it on the screen (I assume you can only tmap triangles)?
Well the first triangle would be vertex(0), vertex(1), vertex(2) . Obviously you have to do your 3d to 2d transformations and such....but you take care of that yourself.
Now look at extravtx. If extravtx = 1, we have an extra vertex, which means, we have an extra triangle. Look back to the triangle diagram. It makes sense
if I say the 2nd triangle would be vertex(0), vertex(2), vertex(3), right? Well yes right. That's it! A perfectly z-clipped triangle!
What from here.......
This code can be modified to do frustum culling as well, so you don't have to clip the triangle in 2d anymore! I'll leave that for you to figure out.
I got lazy with my coding and didn't bother either. UGL does the 2d-clipping for me.
If you want to contact me about errors, you need help, or whine about this not working, just contact me, Shashi of SBM Productions at theshashiman@gmail.com
Contact Shashi, or download this tutorial: zclip.txt
x.t.r. Graphics Animation Tutorial
Written by Rattrapmax6 (Kevin)
Introduction
Okay, here I will discuss animation in QBasic. This is probably standard stuff, but I've never read any QBasic animation tutorials. I just sat and thought of the best way I could animate something and did it. So I'm just writing this for those who don't won't to ponder and just want to learn (which is much easier). Enough of my typing, let us get to it!
Frames (Multiple Sprites)
Before you can animate, you need frames, or a bunch of sprite of the same thing just in different positions. Right, so lets draw a object to animate...
SCREEN 13
DATA 23,23,23,23,00,00,00,00,00,00,00,00,00,00,00,00,23,23,23,23
DATA 22,22,22,22,00,00,00,00,00,00,00,00,00,00,00,00,22,22,22,22
DATA 21,21,21,21,00,53,53,53,53,53,53,53,53,53,53,00,21,21,21,21
DATA 22,22,22,22,28,55,55,55,55,55,55,55,55,55,55,28,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,28,55,55,55,55,55,55,55,55,55,55,28,21,21,21,21
DATA 22,22,22,22,00,01,01,01,01,01,01,01,01,01,01,00,22,22,22,22
DATA 21,21,21,21,00,00,00,00,00,00,00,00,00,00,00,00,21,21,21,21
DATA 20,20,20,20,00,00,00,00,00,00,00,00,00,00,00,00,20,20,20,20
FOR y = 1 TO 20
FOR x = 1 TO 20
READ z
PSET (x, y), z
NEXT
NEXT
DIM sprt1(20 * 20): GET (1, 1)-(20, 20), sprt1
Okay, in QBasic that makes a robot. We need to make an exact copy of this and change the tracks position. We are going to create the effect of rolling tracks. To come up with this, we move the track's treads up one position, or back one, either way the effect will be the same, look at this...
CLS
DATA 23,23,23,23,00,00,00,00,00,00,00,00,00,00,00,00,23,23,23,23
DATA 21,21,21,21,00,00,00,00,00,00,00,00,00,00,00,00,21,21,21,21
DATA 22,22,22,22,00,53,53,53,53,53,53,53,53,53,53,00,22,22,22,22
DATA 21,21,21,21,28,55,55,55,55,55,55,55,55,55,55,28,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,28,55,55,55,55,55,55,55,55,55,55,28,22,22,22,22
DATA 21,21,21,21,00,01,01,01,01,01,01,01,01,01,01,00,21,21,21,21
DATA 22,22,22,22,00,00,00,00,00,00,00,00,00,00,00,00,22,22,22,22
DATA 20,20,20,20,00,00,00,00,00,00,00,00,00,00,00,00,20,20,20,20
FOR y = 1 TO 20
FOR x = 1 TO 20
READ z
PSET (x, y), z
NEXT
NEXT
DIM sprt2(20 * 20): GET (1, 1)-(20, 20), sprt2
Animation:
All right, we have two frames ready for animation. We now need to make a master loop and a control loop inside of that one. Sound confusing? Its really easier than it sounds, take a look...
DO ' Master LOOP
FOR i = 1 TO 10 'Control LOOP
WAIT &H3DA, 8 'Keeps it clean..
FOR a = 1 TO 1000: NEXT 'Controls speed..
NEXT
LOOP UNTIL INKEY$ = CHR$(27) 'Ends LOOP on Keypress ..
Now how to use the controll loop to controll the animation. The FOR.. NEXT loop cycles 10 times. We will use this to change the pictures every 5 cycles...
IF i <= 5 THEN PUT (160, 100), sprt1, PSET 'First frame on LOOP 1 - 5
IF i > 5 THEN PUT (160, 100), sprt2, PSET 'Second frame on LOOP 6 - 10
Notice the: "IF i <= 5". "i" is the loop number created by the FOR: "FOR i = 1 TO 10".
Now with: "IF i <=5" on the first five LOOPs PUTs the first frame. And the same with: "IF i > 5" PUTs the second frame on the last five LOOPs.
You can use any number of LOOPs. If you want faster animations, you could use: "FOR i = 1 TO 6" and cycle every 3 LOOPs. Make sure the control LOOPs can divide by two, this way you can PUT the different frames for the equal amount of time. And if you wanted three frames, the control LOOP needs to divide by three and so on... For this tutorial, I’ll leave with this working code.
SCREEN 13
DATA 23,23,23,23,00,00,00,00,00,00,00,00,00,00,00,00,23,23,23,23
DATA 22,22,22,22,00,00,00,00,00,00,00,00,00,00,00,00,22,22,22,22
DATA 21,21,21,21,00,53,53,53,53,53,53,53,53,53,53,00,21,21,21,21
DATA 22,22,22,22,28,55,55,55,55,55,55,55,55,55,55,28,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,28,55,55,55,55,55,55,55,55,55,55,28,21,21,21,21
DATA 22,22,22,22,00,01,01,01,01,01,01,01,01,01,01,00,22,22,22,22
DATA 21,21,21,21,00,00,00,00,00,00,00,00,00,00,00,00,21,21,21,21
DATA 20,20,20,20,00,00,00,00,00,00,00,00,00,00,00,00,20,20,20,20
FOR y = 1 TO 20
FOR x = 1 TO 20
READ z
PSET (x, y), z
NEXT
NEXT
DIM sprt1(20 * 20): GET (1, 1)-(20, 20), sprt1
CLS
DATA 23,23,23,23,00,00,00,00,00,00,00,00,00,00,00,00,23,23,23,23
DATA 21,21,21,21,00,00,00,00,00,00,00,00,00,00,00,00,21,21,21,21
DATA 22,22,22,22,00,53,53,53,53,53,53,53,53,53,53,00,22,22,22,22
DATA 21,21,21,21,28,55,55,55,55,55,55,55,55,55,55,28,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,00,55,55,55,55,55,55,55,55,55,55,00,22,22,22,22
DATA 21,21,21,21,00,55,55,55,55,55,55,55,55,55,55,00,21,21,21,21
DATA 22,22,22,22,28,55,55,55,55,55,55,55,55,55,55,28,22,22,22,22
DATA 21,21,21,21,00,01,01,01,01,01,01,01,01,01,01,00,21,21,21,21
DATA 22,22,22,22,00,00,00,00,00,00,00,00,00,00,00,00,22,22,22,22
DATA 20,20,20,20,00,00,00,00,00,00,00,00,00,00,00,00,20,20,20,20
FOR y = 1 TO 20
FOR x = 1 TO 20
READ z
PSET (x, y), z
NEXT
NEXT
DIM sprt2(20 * 20): GET (1, 1)-(20, 20), sprt2
CLS
DO ' Master LOOP
FOR i = 1 TO 10 'Control LOOP
WAIT &H3DA, 8 'Keeps it clean..
IF i <= 5 THEN PUT (160, 100), sprt1, PSET 'First frame on LOOP 1 - 5
IF i > 5 THEN PUT (160, 100), sprt2, PSET 'Second frame on LOOP 6 - 10
FOR a = 1 TO 1000: NEXT 'Controls speed..
NEXT
LOOP UNTIL INKEY$ = CHR$(27) 'Ends LOOP on Keypress ..
Hay! The tracks move!! I'll cover creating animation only when you move, and also how to move the animation in another tutorial. This way you can practice on this alittle, and I'll have something to submit for Pete's QB Express next month....
Have fun making animation, and until next time, happy animating!!!
Contact Me: Rattrapmax6@aol.com
Webpage: http://members.aol.com/rattrapmax6/qbfiles/qbsite.html
-Kevin (x.t.r.GRAPHICS)
Download this tutorial in MS Word DOC format.
GUI Programming In FreeBASIC: Part II
Written by Nekrophidius
In the last tutorial, I demonstrated how to create a window and add some buttons. Now, I'm going to expand on the original tutorial and add more fun stuff. :)
Keep in mind that my tutorials are not intended to be completely cut-n-paste, and that they are intended to point you in the right direction rather than just give all the answers away. :)
*Adding An Editbox*
An editbox is a control that allows you to enter text. You see them all the time in Windows applications for things like login screens, search dialogs, even Notepad itself is just an editbox. At the level we're working at, there is a vast number of styles we can create the box with. Here's one example:
edithWnd1 = CreateWindowEx(0, "EDIT", "EDIT Control", WS_VISIBLE Or WS_CHILD Or ES_MULTILINE OR ES_AUTOHSCROLL OR ES_AUTOVSCROLL OR WS_VSCROLL OR WS_HSCROLL OR ES_LEFT Or ES_WANTRETURN, 250, 30, 200, 80, hwnd, null, hInstance, null)
Quite a selection of styles, no? :) Most of them shouldn't need explaining. A few, however, might be worth looking at closer.
- ES_AUTOHSCROLL: Automatically scrolls text to the right by 10 characters when the user types a character at the end of the line. When the user presses the ENTER key, the control scrolls all text back to position zero.
- ES_AUTOVSCROLL: Automatically scrolls text up one page when the user presses the ENTER key on the last line.
- ES_MULTILINE: This doesn't really need a lot of explaining, but there's a point to be made about it. When used on a dialog window, pressing ENTER in the editbox fires the "Default" button instead of doing a carriage return unless the following style is also used:
- ES_WANTRETURN: This style allows carriage returns to be entered without firing the dialog window's "Default" button when the editbox has focus.
In the example code, "EDIT Control" is the default text that shows up in the control when it's created. Also, this by itself is, again, going to look rather ugly, so perhaps you'd like to set a font here too?
SendMessage (edithWnd1, WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT),0)
Use this SendMessage technique whenever you create controls if you don't want them to use the ugly system font.
*Expanding On The Button Object*
Our versatile button can be used for more than just a pushbutton. The Button window style is also used to create radio buttons and checkboxes.
chkhWnd = CreateWindowEx(0, "BUTTON", "Checkbox!", BS_NOTIFY Or BS_AUTOCHECKBOX Or WS_VISIBLE Or WS_CHILD Or WS_TABSTOP Or WS_GROUP, 30, 0, 200, 17, hwnd, null, hInstance, null)
This creates a checkbox. This uses a few new styles we've not seen yet, but the most important one is BS_AUTOCHECKBOX. What this style does is create the checkbox as a two-state checkbox that automatically switches states when clicked (if this isn't used, you have to change its state manually).
radiohWnd(0) = CreateWindowEx(0, "BUTTON", "Radio 1", BS_AUTORADIOBUTTON Or WS_VISIBLE Or WS_CHILD, 230, 0, 100, 17, hwnd, null, hInstance, null)
radiohWnd(1) = CreateWindowEx(0, "BUTTON", "Radio 2", BS_AUTORADIOBUTTON Or WS_VISIBLE Or WS_CHILD, 330, 0, 100, 17, hwnd, null, hInstance, null)
radiohWnd(2) = CreateWindowEx(0, "BUTTON", "Radio 3", BS_AUTORADIOBUTTON Or WS_VISIBLE Or WS_CHILD, 430, 0, 100, 17, hwnd, null, hInstance, null)
radiohWnd(3) = CreateWindowEx(0, "BUTTON", "Radio 4", BS_AUTORADIOBUTTON Or WS_VISIBLE Or WS_CHILD, 530, 0, 100, 17, hwnd, null, hInstance, null)
Look! Control arrays! :) What this does is create four radio buttons. Like BS_AUTOCHECKBOX, BS_AUTORADIOBUTTON tells the controls to automatically switch states so we don't have to do it manually.
As with any Button, WM_COMMAND is used to handle these controls.
*Adding Menus To The Window*
It is difficult to think of a GUI-based program that doesn't use menus in one form or another. There are two ways of using menus: through resources and through direct API calls. Well, I'm going to show you the working man's version: using API calls.
You'll need some globals for this again, but I'll let you figure out what they should be from the code you're about to see. :)
hMenu = CreateMenu
This is the heart of the menu-making process. Without using CreateMenu, you can't have any menus, period. Now you have a menu handle...time to add actual menus to it.
hMenuSub(0) = CreatePopupMenu
hMenuSub(1) = CreatePopupMenu
hMenuSub(2) = CreatePopupMenu
hMenuSub(3) = CreatePopupMenu
This will create four popup menu handles to which you can menu definitions to. We'll use the first three right away, but the fourth will be used for a special purpose later on.
InsertMenu hMenu, 0, MF_BYPOSITION Or MF_POPUP Or MF_STRING, hMenuSub(0), ByVal "Menu 1"
InsertMenu hMenu, 1, MF_BYPOSITION Or MF_POPUP Or MF_STRING, hMenuSub(1), ByVal "Menu 2"
InsertMenu hMenu, 2, MF_MENUBREAK Or MF_BYPOSITION Or MF_POPUP Or MF_STRING, hMenuSub(2), ByVal "Menu 3"
Now here's where the real fun begins. This creates three top-level menus in the menu bar of the window. Note the additional style in the third line, MF_MENUBREAK. This splits the menu bar, allowing that third menu to appear as a second line in the menu. This is something you *never* see in VB! :)
Now that we have our top-level menus, we can add some actual dropdown menus.
AppendMenu hMenuSub(0), MF_STRING, &H11, Byval "MenuSub 1.1"
AppendMenu hMenuSub(0), MF_STRING, &H12, Byval "MenuSub 1.2"
AppendMenu hMenuSub(0), MF_STRING, &H13, Byval "MenuSub 1.3"
InsertMenu hMenuSub(0), 3, MF_BYPOSITION Or MF_POPUP Or MF_STRING, hMenuSub(3), ByVal "Nested Menu 1"
AppendMenu hMenuSub(3), MF_STRING, &H31, Byval "Nested SubMenu 1"
AppendMenu hMenuSub(3), MF_STRING, &H32, Byval "Nested SubMenu 2"
AppendMenu hMenuSub(3), MF_STRING, &H33, Byval "Nested SubMenu 3"
This creates a dropdown menu on the first top-level menu handle. What's more is that it also creates a submenu using that fourth popup menu handle we defined earlier.
AppendMenu hMenuSub(1), MF_STRING, &H21, Byval "MenuSub 2.1"
AppendMenu hMenuSub(1), MF_STRING, &H22, Byval "MenuSub 2.2"
AppendMenu hMenuSub(1), MF_STRING, &H23, Byval "MenuSub 2.3"
This is a pretty standard menu, nothing special needs explaining here.
AppendMenu hMenuSub(2), MF_STRING, &H31, Byval "MenuSub 3.1"
AppendMenu hMenuSub(2), MF_STRING Or MF_MENUBREAK, &H32, Byval "MenuSub 3.2"
AppendMenu hMenuSub(2), MF_STRING Or MF_MENUBARBREAK, &H33, Byval "MenuSub 3.3"
AppendMenu hMenuSub(2), MF_STRING, &H34, Byval "MenuSub 3.4"
AppendMenu hMenuSub(2), MF_STRING, &H35, Byval "MenuSub 3.5"
AppendMenu hMenuSub(2), MF_STRING, &H36, Byval "MenuSub 3.6"
AppendMenu hMenuSub(2), MF_STRING, &H37, Byval "MenuSub 3.7"
Here's another interesting usage of menu styles. You saw MF_MENUBREAK earlier on the top-level menu, but here in a submenu, what this does is split the menu horizontally. All following menu items will be displayed under this menu item, Also, we've got MF_MENUBARBREAK. This works the same as MF_MENUBREAK except it adds a vertical line, visually seperating the menu items. Again, all items following this are displayed under it. Again also, this is something you never see in VB.
SetMenu hWnd, hMenu
Okay, we've had enough menu fun for now. This command sets the menu to its parent, which will be the window we've created.
DrawMenuBar hWnd
This final step is necessary to actually show this menu.
There are a few notifications involved with menus, but the one we want to be concerned with primarily is WM_COMMAND. When a menu item is fired, lparam will be 0. So where do we get info on which menu item we want? In wparam, of course! The low word of wparam will contain the identifier of the menu item fired. Remember that small code snippet at the end of the last tutorial? Use the GetLowParam function with wparam to return the identifier of the menu fired. When the menus were created, we used a value at the third argument of AppendMenu to specify its identifier. So all you do is compare the low word of wparam to this identifier to determine which menu item was fired.
Other interesting notifications we can look at are WM_INITMENUPOPUP, which is fired when a popup menu "pops up", WM_UNINITMENUPOPUP, which is fired when a popup menu is closed, and WM_MENUSELECT, which unlike WM_COMMAND, returns the handle of the menu in lparam but otherwise functions in a similar (although a bit more complex) manner. WM_MENUSELECT is what you'll want to use when your window contains a lot of controls and you want to keep things cleaner and neater in your WndProc. :)
*The End...For Now*
Well, I hope this sheds a little more light on the wonderful (*cough*) world of GUI coding at the API level in Windows. As before, if something doesn't make sense or you would like to ask a question, feel free to contact me. I'm not hard to find. :D And I'll continue this tutorial series next month, exclusively for QB Express. :)
Contact Nekrophidius, or visit one of his many programming websites:
The Basic Network, V Planet!, Lost socK Software or Nek's FreeBasic Page!
SDL Basics With FreeBasic - Tutorial 1
Written by BastetFurry
This and the following tutorial were originally posted on the QBasic News forums. Bastetfurry submitted these to QB Express on the advice of QBNews forum readers and here they are. Enjoy!
In this tutorial you will learn to set up SDL and get an image on the screen,
pretty cool, uh?
Ok, it is just to teach you the basics of SDL.
So, have a look at this little piece of code:
'$include: "SDL\SDL.bi"
'$include: "SDL\SDL_image.bi"
' Surfaces are the A and O of SDL.
' You place your backgrounds, your sprites and everything else that has
' something to do with graphics inside a surface.
' This will hold the screen
dim shared video as SDL_Surface ptr
' This will hold our picture
dim shared temppic as SDL_Surface ptr
' Here goes the event holder
dim event as SDL_Event
' We initiasize SDL and we only need video. If this tells me anything but zero
' then we can not continiue
if SDL_Init(SDL_INIT_VIDEO) <> 0 then
print "FATAL: Couldnt init SDL"
end 1
end if
' We set up the display mode. We wont do fullscreen, if you like then OR the
' options with SDL_FULLSCREEN to get it.
' SDL_HWSURFACE means that this surface is hold in video ram.
' If you want it to be in system ram then exchange SDL_HWSURFACE with
' SDL_SWSURFACE
' SDL_DOUBLEBUF tells SDL to create two pages.
video = SDL_SetVideoMode (800, 600, 24, SDL_HWSURFACE OR SDL_DOUBLEBUF)
' If the surface holds nothing then the display is not created, then get out of
' here.
if video = NULL then
print "FATAL: Couldnt init SDL with vidmode 800x600x24"
end 2
end if
' Now we load the Picture to be displayed into system memory
' You can load bmp, pnm, xpm, xcf, pcx, gif, jpg, tif, png and lbm
' I stick with png because gif is patented and just 256 colors thick and the
' others are just too bloated or destroy information in the picture.
temppic = IMG_Load("testpic.png")
' If this is empty then something went wrong
if temppic = 0 then
print "Could not load file: "; picfile$
end 3
end if
' We blit the image to the screen.
' SDL_BlutSurface has the parameters
' (start,startcoordinates,destination, destinationcoordinates)
' Where coordinates is a simple SDL_Rect structure.
SDL_BlitSurface(temppic,NULL,video,NULL)
' With this command we flip the video ram locations so that our painting gets
' visible.
SDL_Flip(video)
' We dont need temppic anymore so get rid of it
SDL_FreeSurface(temppic)
do
' We grab the newest event from the event stack.
' I havent tryed it, but AFAIK you can even grab Win32 events like WM_CLOSE
' this way ;)
SDL_PollEvent(@event)
' We just want the type of the event
select case event.type
' Has a key been pressed?
case SDL_KEYDOWN:
exit do
' Has a mousebutton been pressed?
case SDL_MOUSEDOWN:
exit do
' In both cases we just get out of here
end select
loop
' And as we dont asume that the compiler calls it, old C habbid ;), we assure
' that it is done and SDL gets deinitialised
SDL_Quit()
In the next issue I will tell you how to move objects and achive transparency.
So Long, The Werelion!
Contact Bastetfurry, or view the original tutorial post.
SDL, FreeBASIC, Transparency, Keyboard and Other Suspects - Tutorial 2
Written by Bastetfurry
In this issue I will tell you how to add transparency and how to move your
objects around.
For this we need a graphic, preferable 32x32 with 24 bit colors.
Rip somewhere or make your own, it wont matter.
Now, get one information, what will be the transparency.
I prefer "Magic Pink" which is 255,0,255 , as long as you know which color number your transparency color has it will work.
Now, you load your image/sprite/whatever with IMG_Load as usual and after that you just do the
following:
SDL_SetColorKey(YourSpriteSurface, SDL_SRCCOLORKEY, SDL_MapRGB(YourSpriteSurface->format, 255, 0, 255))
The last three numbers represent your transparency color.
Now everytime you blit your image you will get a nice image with transparent areas as SDL now does the rest for you, cool eh?
For exercise, add a small sprite to your first SDL programm that has transparency.
So, it should look somewhat like this:
'$include: "SDL\SDL.bi"
'$include: "SDL\SDL_image.bi"
' This will hold the screen
dim shared video as SDL_Surface ptr
' This will hold our picture
dim shared temppic as SDL_Surface ptr
''' NEW CODE STARTS HERE!
' This will hold our sprite
dim shared pic as SDL_Surface ptr
' This will be explained later, its for telling SDL where to draw ;)
dim shared rect as SDL_Rect
''' NEW CODE STOPS HERE!
' Here goes the event holder
dim event as SDL_Event
' We initiasize SDL and we only need video. If this tells me anything but zero
' then we can not continiue
if SDL_Init(SDL_INIT_VIDEO) <> 0 then
print "FATAL: Couldnt init SDL"
end 1
end if
' We set up the display mode.
video = SDL_SetVideoMode (800, 600, 24, SDL_HWSURFACE OR SDL_DOUBLEBUF)
' If the surface holds nothing then the display is not created, then get out of
' here.
if video = NULL then
print "FATAL: Couldnt init SDL with vidmode 800x600x24"
end 2
end if
' Now we load the Picture
temppic = IMG_Load("testpic.png")
' If this is empty then something went wrong
if temppic = 0 then
print "Could not load file: testpic.png"
end 3
end if
''' NEW CODE STARTS HERE!
'We load our sprite into memory
pic = IMG_Load("testsprite.png")
' If this is empty then something went wrong
if pic = 0 then
print "Could not load file: testsprite.png"
end 3
end if
' Now we set the transparency
SDL_SetColorKey(pic, SDL_SRCCOLORKEY, SDL_MapRGB(pic->format, 255, 0, 255))
''' NEW CODE STOPS HERE!
' We blit the image to the screen.
SDL_BlitSurface(temppic,NULL,video,NULL)
''' NEW CODE STARTS HERE!
' This will draw our sprite with transparency on the coordinates stated
rect.x = 50
rect.y = 150
' SDL_Rect needs to be passed as a pointer! You dont need to fully understand
' what a pointer is, just do it otherwise it wont work ;)
SDL_BlitSurface(temppic,NULL,video,@rect)
''' NEW CODE STOPS HERE!
' We flip
SDL_Flip(video)
' We dont need temppic and pic anymore so get rid of it
SDL_FreeSurface(temppic)
SDL_FreeSurface(pic)
do
' We grab the newest event from the event stack.
SDL_PollEvent(@event)
' We just want the type of the event
select case event.type
' Has a key been pressed?
case SDL_KEYDOWN:
exit do
' Has a mousebutton been pressed?
case SDL_MOUSEDOWN:
exit do
' In both cases we just get out of here
end select
loop
' We have finished
SDL_Quit()
Ok, lets get on with that mysterious SDL_Rect...
This dude is a structure that holds 4 variables.
x and y for upper left corner of drawing and we have w for widht and h for
height. The last two are not interesting for the pasting part, but when you use
one big image for, for example, all enemy spaceships, you can select what
portion of your image is blited to the screen.
But now we go on on using that dude for moving something around the screen.
'$include: "SDL\SDL.bi"
'$include: "SDL\SDL_image.bi"
declare sub updatescreen()
' This will hold the keyboard data
dim keys as Uint8 ptr
' We need to know if we need to redraw the screen
dim shared redraw%
' This will hold the screen
dim shared video as SDL_Surface ptr
' This will hold our picture
dim shared temppic as SDL_Surface ptr
' This will hold our sprite
dim shared pic as SDL_Surface ptr
' This will be explained later, its for telling SDL where to draw ;)
dim shared rect as SDL_Rect
' Here goes the event holder
dim event as SDL_Event
' We initiasize SDL and we only need video. If this tells me anything but zero
' then we can not continiue
if SDL_Init(SDL_INIT_VIDEO) <> 0 then
print "FATAL: Couldnt init SDL"
end 1
end if
' We set up the display mode.
video = SDL_SetVideoMode (800, 600, 24, SDL_HWSURFACE OR SDL_DOUBLEBUF)
' If the surface holds nothing then the display is not created, then get out of
' here.
if video = NULL then
print "FATAL: Couldnt init SDL with vidmode 800x600x24"
end 2
end if
' Now we load the Picture
temppic = IMG_Load("testpic.png")
' If this is empty then something went wrong
if temppic = 0 then
print "Could not load file: testpic.png"
end 3
end if
'We load our sprite into memory
pic = IMG_Load("testsprite.png")
' If this is empty then something went wrong
if pic = 0 then
print "Could not load file: testsprite.png"
end 3
end if
' Now we set the transparency
SDL_SetColorKey(pic, SDL_SRCCOLORKEY, SDL_MapRGB(pic->format, 255, 0, 255))
do
' We grab the newest event from the event stack.
SDL_PollEvent(@event)
' We just want the type of the event
select case event.type
' Has a key been pressed?
case SDL_KEYDOWN:
''' NEW CODE STARTS HERE
' After this
redraw%=1
' Grab the state of the keyboard
keys = SDL_GetKeyState(NULL)
' Test all interesting possibilities with boundary check.
' It wont matter if you draw outside the screen, SDL clips for you
if keys[SDLK_UP] and rect.y > 0 then rect.y = rect.y - 1
if keys[SDLK_DOWN] and rect.y < 568 then rect.y = rect.y + 1
if keys[SDLK_LEFT] and rect.x > 0 then rect.x = rect.x - 1
if keys[SDLK_RIGHT] and rect.x < 768 then rect.x = rect.x + 1
if keys[SDLK_ESCAPE] then exit do
''' NEW CODE STOPS HERE
' Has a mousebutton been pressed?
case SDL_MOUSEDOWN:
print "MB pressed"
' In both cases we just get out of here
end select
if redraw% = 1 then
redraw% = 0
updatescreen()
end if
loop
' We have finished
SDL_Quit()
''' NEW CODE STARTS HERE
' It is a good habid not coding everything into the message loop
' This sub updates the screen
sub updatescreen()
' Nowadays its normal updating the whole screen as the PC has no universal
' scrolling routines, neither does it come with native sprite support.
' Besides, with SDL even an old 486 can maintain 25-30 fps
' We blit the image to the screen.
SDL_BlitSurface(temppic,NULL,video,NULL)
' We blit the sprite to the screen.
SDL_BlitSurface(temppic,NULL,video,@rect)
' We flip
SDL_Flip(video)
end sub
''' NEW CODE STOPS HERE
That's it, this is what SDL mostly is about.
Now, for exercice, have a try and make a simple labyrinth game or a simple space shooter with SDL.
The next tutorial will be about something YOU decide, the only thing I won't do is SDL+OpenGL because I dont know OGL.
And I would be very happy if someone could proofread my bad english.
So Long, The Werelion!
Contact Bastetfurry, or view the original tutorial post.
16 Megs .EXE files using overlays
Written by Na Than Assh Antti (Na_th_an)
This is a tutorial that Na_th_an wrote a while ago for his BASIC compilers website, but when the site went down, so did the tutorial. It's a quite useful tutorial that really deserves to be read (plus some more recognition than it was getting before), so I've decided to reprint it in QB Express.
-Pete
Intro
As soon as a QB programmer aims for a big project, he or she will (most likely) be trapped in the deep and dark well of memory problems. Sometimes your program refuses too compile no matter how many modules you split it into, sometimes it will compile but will do nasty things when executed, sometimes it will throw a "out of memory" error, and I can keep it going forever.
This is not just a QB issue (although it is easier to get such kind of problems with QB than with another similar compiler), but a 16-bits MS-DOS environment issue. You have 640 Kb of conventional memory to work with is real mode (QB, Turbo C++, and many other old compilers work in real mode), where you have to fit static, dynamic and code data. Far too little.
Back in the days, EMS and then XMS were introduced to have more memory to work with. This solves the problem to a point, 'cause you can only store data on EMS/XMS. No way to pass a program counter to EMS or XMS, so no way to actually execute a program from that zone. Sometimes you need more code. Many people just resignate and split their projects into several EXEs, and have to invent all sorts of tricks to have the data available from one to the next.
To solve that, Overlays were introduced. Overlays are just a pack of modules (OBJ files, to aid in understanding) that are kept on disk until they are needed. Overlaid programs contain a main module which is always in memory. When a function is called, a little control program checks if it is loaded or not. If it is not, it loads the overlay it is in from disk and calls the function. That way you can have much bigger programs.
So, how is it done?
Don't worry, you don't have to call special functions, or code in an odd way. I feared those things until I met Jonathan Simpson and he pointed me to the correct document. The version of LINK.EXE included in PDS 7.00 or later does the work for you. You only have to tell LINK which object modules are on which overlays. Sadly, this can't be done from the IDE as far as I know, but not everything has to be a piece of cake. You have to compile your BAS file to OBJs using BC.EXE, and then run LINK.EXE to link the OBJs. The syntax for LINK is very simple. For example, say that you have seven object files (OBJs) named A.OBJ, B.OBJ, ... I.OBJ, and you want to organize them in three overlays. You just have to type in (provided you have your OBJs already created):
LINK A+(B+C)+(E+F)+G+(I)
The above command will create an EXE file but it will be a special, overlaid file. On it, object modules A and G will make the main portion which is always loaded in memory. B and C will make a overlay, E and F will make another and I will make another one. Note how object modules enclosed in parentheses get together inside an overlay, and how modules that are not enclosed in parentheses will be in the main section.
The program entry point (main section of your code) must be in the main section of your EXE file (not in a overlay). In execution, if a function in B.OBJ is needed, the overlay containing B.OBJ (which also contains C.OBJ) is loaded from disk, and the function is called. Imagine than then you need a function placed in C.OBJ now: no prob, the overlay is already loaded and the function will be called. Now you need some stuff at F.OBJ: well, no prob, B+C is unloaded and E+F is loaded from disk, then the function is called.
As you see, you don't have to do anything. Overlays are loaded and unloaded automaticly, so you don't have to change a line of code, just organize your OBJs intelligently on overlays... See next point.
Organizing stuff
You probably are thinking on this already: loading and unloading (specially the former) overlays takes time. Yeah, it does. And a lot of time compared to the time that is spent to call a function. You won't notice it when the load is outside a loop, but if it is inside a critical loop you will.
Overlays were a "patch solution" to code size problems. Like every other patch solution, it has lots of downsides. But luckily, these downsides can be ignored if we do things intelligently.
Most times, your program can be "sequentialized", that is, you can see how not all the SUBs and FUNCTIONs are used always, but that there are some kind of "stages". For example, in a game, you have a menu which calls to "options", "play" and "instructions". The "Options" SUB calls its own SUBs and FUNCTIONs that have nothing to do with those functions called by the "play" SUB, same for the "instructions" SUB. I mean that you, for example, are calling "DoScroll" from your "play" SUB constantly, but this SUB won't be ever called from the "options" SUB. Now you have a hint on how to organize your stuff: you can have an overlay with the menu system, another with the options stuff, another with the game itself and another one with the instructions.
"Hey, stop it a while", you may say. Yeah, I know, all those SUBs and FUNCTIONs that we have taken appart make use of the soundsystem and the graphics library. Easy: put the most commonly used stuff in your main section. That way they will always be loaded. Imagine how killer it were to place the soundsystem in an overlay and the game in another: everytime that a sound had to be played from the game module, it would have to be unloaded and the sound overlay would have to be loaded... No way. :)
Another good division can be made in RPG games: Stuff your map engine in an overlay (along with the scripting engine and everything) and the battle engine in another. I've seen so many games that use different EXEs for the map engine and the battle engine. I always found that dirty. Now this is no longer needed :) and you have all your variables still on memory!
As you see, if you organize modules intelligently, the overlays system has no drawbacks. In most cases, even if you don't need overlaying 'cause your code is small enough, it can be better to overlay some parts of your program that are not used in critical loops so you are saving memory for data in conventional memory. Plus, with modern computers and operating systems, the time needed to load the overlay from disk is unnoticeable. Furthermore, if you are using Windows all disk accesses are cached so most likely your overlay to be loaded is somewhere in memory, so loading it is even faster. There is an even better feature: if the EMS driver is loaded, overlays are copied to EMS as well when they are loaded, and then they are retrieved from memory next time they need to be used.
Stop! There are some restrictions
Yeah, that's a bummer, but as I said before: nothing is perfect.
- Each overlay cannot be larger than 256 Kb. There is also a maximum on how many overlays can be used: 64. That makes 64*256Kb = 16 Mb. I think you have fair enough with 16 Mb for your EXE, don't you?
- When you are LINKing, an overlay cannot be specified as the first parameter. The first OBJ listed must be one of those included in the main section.
- Each mode has to be compiled (with BC.EXE) using the same (or compatible) command line switches, or you'll screw everything.
- Most EXE compressors screw up with QB overlays. Don't use them.
A lil' tip
Create a .BAT file to compile and link automaticly. Study the LINK.EXE syntax so you can use .LIB libraries and pass parameters (in the above command LINK is only passed info about which OBJs will be linked, so it will prompt interactively for the missing info; you have to add these parameters to have LINK.EXE working automaticly).
Aknowledgements
Thanks to Jonathan Simpson for pointing me in the correct direction, concretely to BotTheMad's article at Tek-Tips Forums.
Contact Na_th_an or visit his website.
FreeBasic Pointers Tutorial
Written by VonGodric
About this tutorial
This tutorial is meant to be as a guide to FreeBasi'c pointers world for beginners
or who just wish some information about them and their support in FreeBasic. This
tutorial will cover several types of pointers and their notations, also breafly about
linked lists and their types and pointers to functions/subs. As well as pointers to
pointers. Als should be noted that writing this tutorial I used fbc compiler 0.11 and
becouse of that some of the stuff won't work on older versions!
What are pointers?
To understand what are pointers, we need to understand what variables are. Variables are
references to a memory location in RAM where you can write or read data. So thus,
they store some data and have an address in memory. In basic definition Pointers
are variables that store address of another variable. That's where
the name comes from Pointer -points to something.
Pointers in FreeBASIC
FreeBASIC unlike QBasic and many other BASIC dialects has full support for them.
FB have them to following language structures:
- Variables - any variable type (Strings|bytes|integers...)
- UserDefinedTypes - Both to Unions and Types
- Functions/Subs
- Other pointers
- Unspecified pointers - can be used to point to point to anything listed above.
The notation is very similar to C with exception of @ instead of & to take
addresses. (for obvious reasons -it would conflict with { &h | &b | &o } numbering
systems.) So let's get to it.
Starting with pointers...
Here's our first small example:
dim i as integer
print @i
When you run it -it will print out some strange number, further more, every time you
run this program, it may show you different number, and it is most unlikely to
get the same number after restart. When I ran it the number was: 4218880
This number is the memory address of the variable i. You can experiment with this
a little -try different variable types for example. However be noted: that
misusing pointers may lead to program crash, in worse situations even system crash!
Creating pointers is very similar to declaring a variable with dim: Dim name Type PTR|POINTER
it doesn't matter do you use ptr or pointer, both are the same thing. so here's
another litte example:
option explicit
dim i as integer
dim ip as integer ptr
ip = @i
print @i
print ip
As you will see, both number are the same -however, You may notice that
we pass the address of the variable i to the pointer ip. and ip used as
is, is just a variable. You can also get the address of the pointer ip
itself with @ip -and it will be different, because every variable has a
unique address (it is possible to have variables with the same address
however with Union's)
Now let's do some actual use of pointers:
option explicit
dim i as integer
dim ip as integer ptr
i = 10
ip=@i
print *ip 'Print out the value to what pointer points
*ip =20 'Assign value to variable that pointer points
print i
The outcome will be 10 and 20 accordingly. The * will give the value of
the variable that our pointer points to, or write to the value. Now you
may have a question, what are they good for? Well great many things,
pointers may speed-up or/and cut down your program in size. They'r most
common use is however is in linked lists(will be looking at them
shortly) and allocating memory from the heap (we'll discuss about that
too). So here's one more example:
option explicit
dim i as integer
dim m as integer
dim ip as integer ptr
ip = @i
*ip = 10 : m = 25
print *ip * m
print sqr(*ip)
*ip = *ip -5
print *ip
print *ip / m
sleep
As you see, pointers can be used just like any
normal variables -you can assign values through them, use in
calculations, pass as arguments, and mutch more. However word of
caution: do NOT mistake *ip with just ip, one is a memory address -and
therefore holds completely different value -and the other one the value
of the variable the pointer points to. So therefore doing ip = ip * m
for example will change the address it points to -and most probably
will crash your program when you try to use it as pointer again: *ip =
100.
Pointers and arrays
Hopefully you understood previous stuff -if not try experimenting. Just
as pointers may be used with variables, they can also be used with
arrays. First a small example, then we'll analize it:
option explicit
'Define array and pointer
dim array(10) as integer
dim ap as integer ptr
dim i as integer
'Fill array with numbers
for i = 0 to 10
array(i) = i
next
'Pass the address of the first element
ap=@array(0)
'And print it out
for i = 0 to 10
print *(ap+i)
next
sleep
It will print out numbers from 0 to 10. So what
is going on in there? We pass the address of the first array element to
the pointer (ap=@array(0)), and then we go through the loop from 0 to
10 and print out. The notation *(ap + i) means: we will print out to
what points the value that is in the parenthesis. You may however
wonder why it prints out the right numbers? Shouldn't it screw the
values -meaning that pointer always points to one byte in memory, but
Integer is 4 byte? The FreeBasic compiler knows the size to what the
pointer points, and meaning + 2 means actually to step 2 integer sizes
forward.
Why is this useful: You may link what ever integer array (or of any other type you have
specified) to one pointer and use it, without having to know exactly what is in it. Here
is the same thing as above with a bit different notation:
option explicit
'Define array and pointer
dim array(10) as integer
dim ap as integer ptr
dim i as integer
'Fill array with numbers
for i = 0 to 10
array(i) = i
next
'Pass the address of the first element
ap=@array(0)
'And print it out
for i = 0 to 10
print ap[i]
next
sleep
Does the same thing -only difference now is instead of *(ap + i) ap[i]
This is just a shorter notation and is a bit cleaner. In this case you
don't need (in fact can't) use * in front of the pointer -that's
because of the [ and ]. When these are used, compiler automatically
knows that this is a pointer. ap[indx] = smth is also valid.
because of greater complexity of multidimensional arrays using pointers in FreeBasic,
we'll skip them for now.
Pointers and UDT's
Like to arrays, you can have pointers to user defined types too. Here's for starter another
example:
type mytype
i as integer
s as single
z as string
end type
dim t as mytype
dim up as mytype ptr
t.i = 10
t.s = 17.87
t.z = "UDT ptr test"
up=@t
print up->i
print up->s
print up->z
sleep
As you see, pointers with UDT's is very simle, first you define a ptr
of the udt type and then take the address of it with @t and for getting
the right field we use -> and field name. Note that * is not needed,
because of -> what says already that this is a pointer. Assigning
values through pointers is ptr->field = value. Like with arrays
-different notations can be used:
'variant 2
print *(up).i
print *(up).s
print *(up).z
'variant 3
print up[0].i
print up[0].s
print up[0].z
This second and third style, as you see is almost like arrays with *(ptr+idx), infact, you can use pointers
with arrays of UDT's too. And notation is almost same:
type mytype
i as integer
s as single
z as string
end type
dim t(5) as mytype
dim up as mytype ptr
t(3).i = 10
t(3).s = 17.87
t(3).z = "UDT ptr test"
up=@t(0)
print *(up+3).i
print *(up+3).s
print *(up+3).z
print up[3].i
print up[3].s
print up[3].z
sleep
As you see, all calculations are done for you, the index 3 will give the correct answer
and you can choose from several styles and decide what suits you best.
Arrays/UDT's outside of DIM
When you use DIM -you use the memory that has been previously allocated
for your running program by the OS (in FB the default is 1MB -however
for more see the commandline options of fb), but what if you want more?
One way to archive this would be to increase stack size allocated for
your program -but if you want to have dynamically allocatable memory
(games/programs often need that) -you are still stuck to that problem!
Solution is to allocate memory from the system heap -let's just say
that it's the rest of the free memory in your computer that is not
already used by other applications. To use memory from the heap is more
complex and dangerous then with DIM.
Why it's dangerous, is that this is often main source of crashes and
errors, you probably have heard of memory leaks? Well you're about to
get to know what they are: DIM procedure is controlled by the compiler
-so whenever you allocate with it, it get's automatically deallocated
once program exits SUB or FUNCTION for example. So memory is freed.
However allocating memory from the heap, is not deallocated(freed)
after your code has done it's work (exited sub/function or program
itself) although modern OS's do free memory for you it is a good
practice to not rely on it! Keep in mind a simple rule -once you allocate, next thing you do is make sure it get's deallocated!
Now what has this to do with pointers? Well everything, you get the address to the allocated
memory block, so only using pointers can you acces to it. Now before we move on, let's
make a simple example:
option explicit
type mytype
i as integer
s as string * 12
end type
dim p as mytype ptr
p = allocate(len(mytype))
if p = 0 then
print "ERROR: Out of memory!"
sleep
end
end if
*(p).i = 10
*(p).s = "Hello world!"
print p->i
print p->s
sleep
deallocate p
Now let's analyse that. First we created an udt, then dimmed pointer to udt.
then we allocated memory for our udt. ALLOCATE allocates memory from the heap.
Len(mytype) returns the size in bytes that udt takes. We need to know how mutch
memory to allocate. What allocate returns is a pointer to the first byte of the memory
stream. And that's all, now we can use it almost like with Dim, but with exception of
being able to access it only through pointers. The last thing is to deallocate -it
erases(frees) the memory we had allocated.
- Allocate - allocates memory, but does not clear
previous content meaning you'll get all the garbage what is in that
memory location. You can try the example above and read value of
p->i before we assigned a value to it.
- Callocate - Same as above, but also clears the memory. Meaning you'll get empty memory
block willed with chr$(0).
- Deallocate - deallocates memory.
- Reallocate - Reallocates previously allocated memory to a new size. Does not
delete the content.
Warnings!
- Always check that you wouldn't try to use and access NULL pointer. (Null pointer is
a pointer that hasn't been initalised to point somewhere) -doing so may(and probably will)
crash your program. If possible always check for the NULL ptr (if someptr<>0 then)
it will slow down, but keep your program more stable.
- It is possible that memory allocation fails (out of memory for example) -
always check after allocating if it is not a NULL pointer.
- Always make sure you deallocate memory you have allocated!
- Make sure you never use previously deallocated memory.
- When you allocate memory from the heap, make sure you absolutely NEVER
write out of your allocation bounds. Stay in limits. because doing so, not only crash your program,
but may crash your system also!
- Use pointers and memory allocation only when you need to.
- Use pointers and memory allocation only when you are sure you know what you are doing
- Avoid pointer's spaghetti!
Now that warnings have been said (reread them) we can move on and see how can we put
an arrays to the heap.
option explicit
dim i as integer
dim p as integer ptr
p = callocate(len(integer) * 10)
if p = 0 then
print "ERROR: Out of memory!"
sleep
end
end if
for i=0 to 9
p[i] = i
next
for i=0 to 9
print *(p+i)
next
sleep
deallocate p
Notice that we allocated memory by multiplying the size of integer by ten, but true
indexing ranges from 0 to 9, be aware of this! Placing UDT's to heap is done just the
same way, only use len(udt) and if you want to have an array of it, multiply it by array size.
Linked lists
Now that we have understood all previous, let's move on. As you saw pointers can point
at UDT's, but they can also be a part of the UDT, furthermore pointer part of the UDT can point to
it'own UDT -thus linked lists. All there is about these mysterious things is this that udt's point
to other udt's. Let's start with simple example:
'Simple linked list example
type linkedlist
'data that list holds
i as integer
'points to next node
listnext as linkedlist ptr
'pointe to previous node
listprev as linkedlist ptr
end type
'the head node of the list
'and two for temporary use
dim head as linkedlist ptr
dim node as linkedlist ptr
dim temp as linkedlist ptr
for i = 1 to 10
'allocate memory
node = callocate(len(linkedlist))
if node = 0 then 'see if allocation was succesful
print "ERROR: Out of memory"
sleep
'because some initalisations may have
'been successful, we need to deallocate them
goto endlinkedlist:
end
end if
node->i = i 'set our data
'point to previously allocated node
node->listprev = temp
if temp = 0 then 'see if temp is 0 -if is then it is first
'allocation
temp = node 'set temp to show current one
head = temp 'also set head
continue for
end if
'set next node of the previous node to be current one
temp->listnext = node
temp = node 'set this for next run
next
'since we save first node as head -we can use it to start reading from the list
node = head
'since final node's listnext holds 0, we can use this
'as the condition to terminate when link is over
while node
'print out data that node holds
print node->i;
'save the current node -for the next demo
temp = node
'move to the next one
node = node->listnext
wend
print
'since we saved in previous demo temp to
'be last active node, we can use it
node = temp
'Just like listNext of the lastnode is NULL pointer
'so is listPrev of the first noe. So again we can use this as
'terminate condition to make sure we exit the list.
while node
'print out data that node holds
print node->i;
'instead of next -let's move backwards
node = node->listprev
wend
sleep
endlinkedlist:
'again start from the head and move through
node = head
while node
temp = node 'to avoid getting next node from already
'deallocate node, we need to use a temp
node = node->listnext
deallocate (temp)
wend
This should be pretty easy concept to understand -node is one UDT block, that points
to previous or/and next node or have NULL pointer meaning the end of the list. Why is this good for?
Linked lists are in a way like arrays (you probably notices this by now) but they have several advantages
and disadvantages over arrays.
Pros
Linked lists: |
Arrays: |
Each node is seperated unit |
Fast -since arrays are aligned in memory. |
Easy and fast to add/remove/replace nodes with one another |
Compact -takes less memory |
because each node is seperate unit -it doesn't take large
memory block with the size of the whole List |
Lot Easier handling |
Same node can be pointed from different lists |
Are less bug prones |
Very dynamic |
Easy to copy/pass whole arrays (to a file) |
Cons
Linked lists: |
Arrays: |
Consume more memory |
Slow and compilcated remove and insert |
Slower in most cases |
Consume big memory block
(sometimes there's enough of free memory, but it is in too small
chunks) |
Introduce many risks for bugs and memory leaks |
All pros's of the Linked lists |
more complex |
|
These tables should give you some ideas -there's certanly more no doubt but these should give
you some idea. Although subject is interesting I will not however get really deep into linked lists
here, because only on this subject there have been written entire books and there
are many different types of linked lists in various shades and shapes. Some most common ones however:
- Stack - describes as First First in First out. Is pretty easy to understand and is used a lot.
'FreeBasic Linked-list example
'Stack
'By VonGodric
OPTION explicit
TYPE MyStack
DATA AS STRING
Node AS MyStack ptr
END TYPE
SUB Push (PushData AS STRING, byref Stack AS MyStack ptr)
DIM NewNode AS MyStack ptr
NewNode = CAllocate(LEN(MyStack))
NewNode->Data = PushData
NewNode->Node = Stack
Stack = NewNode
END SUB
FUNCTION Pop (byref Stack AS MyStack ptr) AS STRING
DIM OldNode AS MyStack ptr
OldNode = Stack
Pop = OldNode->Data
Stack = OldNode->Node
deallocate(OldNode)
END FUNCTION
FUNCTION Top (byref Stack AS MyStack ptr) AS STRING
Top = Stack->Data
END FUNCTION
FUNCTION IsEmpty (byref Stack AS MyStack ptr) AS INTEGER
IsEmpty = Stack=0
END FUNCTION
'----------------------------
'Main code
DIM test AS MyStack pointer
DIM a AS STRING
DO
a$=INKEY$
IF a$>"" AND a$<>chr$(13) THEN PRINT a$; : push (a$, test)
LOOP UNTIL a$=CHR$(13)
PRINT
DO WHILE NOT IsEmpty(test)
PRINT Top (test);
PRINT Pop (test);
LOOP
SLEEP
'-----------------------------
- Queue describes as first in last out:
'FreeBasic Linked List example
'Queue
'By VonGodric
OPTION explicit
TYPE MyQueue
DATA AS STRING
Node AS MyQueue ptr
END TYPE
TYPE Queue
Rear AS MyQueue ptr
Front AS MyQueue ptr
END TYPE
FUNCTION Initalise(QueuePtr AS Queue ptr) AS INTEGER
QueuePtr->Front = callocate(LEN(MyQueue))
IF QueuePtr->Front = 0 THEN Initalise = 0 : EXIT FUNCTION
QueuePtr->Rear = QueuePtr->Front
QueuePtr->Front->Node = 0
Initalise=-1
END FUNCTION
FUNCTION Join (Queue AS Queue ptr, QData AS STRING) AS INTEGER
DIM NewNode AS MyQueue Ptr
NewNode=callocate(LEN(MyQueue))
IF NewNode = 0 THEN Join = 0 : EXIT FUNCTION
NewNode->Data = QData
NewNode->Node = 0
Queue->Rear->Node = NewNode
Queue->Rear = NewNode
Join = -1
END FUNCTION
FUNCTION leave(QueuePtr AS Queue ptr) AS STRING
DIM OldNode AS MyQueue ptr
OldNode = QueuePtr->Front->Node
leave = OldNode->data
IF (QueuePtr->Front->Node->Node = 0) THEN
QueuePtr->Rear = QueuePtr->Front
ELSE
QueuePtr->Front->Node = QueuePtr->Front->Node->Node
END IF
deallocate(OldNode)
END FUNCTION
FUNCTION IsEmpty (QueuePtr AS Queue ptr) AS INTEGER
IsEmpty = (QueuePtr->Front = QueuePtr->Rear)
END FUNCTION
'--------------------------
'Main code
DIM test AS Queue
IF NOT Initalise(@test) THEN END
DIM a AS STRING
DO
a$=INKEY$
IF a$>"" AND a$<>chr$(13) THEN PRINT a$; : Join (@test, a$)
LOOP UNTIL a$=CHR$(13)
PRINT
DO WHILE NOT IsEmpty(@test)
PRINT Leave (@test);
LOOP
SLEEP
'--------------------------
- Circular queue - Sorry, no example, but the example I made with numbers -previously, is
pretty close to it. While Stack and Queue presented here are one directional (nodes don't have info about previous nodes)
then circular's lists have both directional ways. Als end is connected with the front forming a cycle that never ends.
It reaches the end and just jumps to first one. Like weekdays, after every sunday cames monday.
Pointers and functions/subs
So far We've been through a lot and there's very little left to describe -although it is a matter on what could be possible
to write heavy nooks. First We'll look at pointers in the functions/subs argument lists.
Very similar actually to defining any other pointer -you just put a pointer, and pass the pointer or address with @.
sub test (byval i as integer ptr)
print *i
end sub
dim i as integer
i=10
test ( @i )
sleep
You may notice here byval argument -it is meant that pointer(value it holds) is passed by value itself
and not reference(aka address) to that variable in what value is stored. Let's see here what byref does:
sub test (byref i as integer)
i = 10
end sub
dim i as integer
print i
test i
print i
sleep
Inside function/sub byref passed variable obtain the same address as of the variable that was passed.
Functions can also return pointers, in that case you need to do function (args...) as type PTR.
However more interesting part ( and very useful ) is pointers to subs/functions themselves. Yes, it
is possible!. Here's small example:
sub sub1 ( i as integer )
print "hello from sub1"
print "you passed value: "; i
end sub
sub sub2 ( i as integer )
print "hello from sub2"
print "you passed value: "; i
end sub
dim test as sub (i as integer = 10)
test = @sub1
test ( 6 )
test ( )
test = @sub2
test ( 1001 )
test ( )
sleep
As you can see, different sub's are called -and yes you can set default values on
sub/functions pointers too. What is importand in sub/function declarations is that the
prototype MUST match the prototype of the function/sub your are using pointers
on. You can also pass function/sub pointers inside another function/sub parameter
lists as well:
function func2 ( i as integer ) as integer
func2 = i * i
end function
function func1 ( byval tocall as function, byval value as integer )
dim i as function ( i as integer ) as integer
i = tocall
func1 = i ( value )
end function
dim s as function ( byval tocall as function(i as integer), byval value as integer ) as integer
s=@func1
print s(@func2, 10)
sleep
As you see, using function pointers is almost same, only you must specify all types
both the return one and also parameter list. Also as you can see, one pointers value can be
given to another.
option explicit
dim i as integer
dim ip1 as integer ptr
dim ip2 as integer ptr ptr
ip1 = @i
ip2 = @ip1
**ip2 = 157
print i, *ip1, **ip2
print @ip1, *ip2
ip2 = @i
*ip2 = 751
print i, *ip2
sleep
Now look at this code -it should be pretty straightforward don't you think? Anyway
declaring a pointer that points to another pointer must be declared explicitly as ptr ptr ...
with the number of maximum redirections allowed. To point to the target you use * as
many times as redirection is involved.
Typeless Pointers
Sometimes we do not really know
or need to either, the type of the pointer in our program, let's say
like when passing to the function/sub as an argument, we may be dealing
with different types in it. For that in FB is ANY ptr. Here's example:
option explicit
sub test (byval p as any ptr, byval t as integer)
dim i as integer ptr
dim b as byte ptr
dim s as string ptr
dim d as double ptr
i = p
b = p
s = p
d = p
select case t
case 1
print *i
case 2
print *b
case 3
print *s
case 4
print *d
end select
end sub
dim i as integer
dim b as byte
dim s as string
dim d as double
i = 10001
b = 127
s = "Hello world!"
d = 2005.0208
test @i, 1
test @b, 2
test @s, 3
test @d, 4
sleep
As you see, nothing difficult here either. You may experiment with passing "wrong" numbers
for wrong types. It should work except for strings. And that's more or less there
is to typeless pointers. Also addresses can be passed without a problem from one pointer
to another with different types -let's say from an integer ptr to the byte ptr.
This way you can archive sort pointer casting from one type to another.
Conclusion
Well that's pretty mutch it for this tutorial. I hope you liked it and found something useful
or new for yourself. One more thing however, throughout this tutorial I used @ symbol to obtain
and address of smth, however these functions maybe used as well:
- Procptr(sub/function name)
- varptr(variable name)
- StrPtr(string name)
Also I want to thank lillo for reviewing this bag of grammatical bugs.
And now farewell and thanks for reading this.
VonGodric
Download this tutorial.
Visit VonGodric's WizGUI site, find out about his FBIde project, or email him at vongodric@hotmail.com!
Final Word
Eleven tutorials. 211KB of text alone. The largest news briefs section yet. I really can't believe how much content there is in this magazine this month. It's absolutely phenomenal. You can't find this quantity of quality content in many professional magazines. And yet, here we are, month after month, putting together a gigantic and completely awesome magazine. Everyone involved deserves a pat on the back. This magazine would not exist without the readers -- who are also the writers.
Anyway, I'm gonna cut this letter short. This issue has been delayed enough, and I don't want to keep it out of your hands any longer than I have to. Besides, I'm extremely tired (putting this magazine together takes hours upon hours of grueling, repetitive work). But in the end it's worth it.
Next month expect a whole lot more of the same great stuff and maybe a few surprises. I'm not going to list off any specific articles here, since I never know what I'm going to get, but I can assure you that whatever ends up in Issue #8 is going to rock. Anyway, I'll see you in March!
Until next time...END.
-Pete
Copyright © Pete Berg and contributors, 2005. All rights reserverd.
This design is based loosely on St.ndard Bea.er by Altherac.