Venture - behind the scenes!
#1
Brick 
Ok, so in this thread I'm going to get behind the scenes of my Venture game (it isn't fully done yet but a prototype demo version can be downloaded in my signature), you can also ask questions on how things are done and I will try my best to answer them. 

[Image: venture_proto.JPG]

First, a little background; when I started programming Venture I used QB 4.5 with the aid of the directqb library. I ran out of memory after a while and to continue I had to port it to QB64, which would have been easy if it wasn't for the directqb library which wasn't supported by QB64 (as it is a compiled library for QB 4.5).

So I had to port the library using QB64 commands to replace the ones in the library, I finally succeeded and could continue on the game in QB64 instead.

I will use QB64 commands to illustrate things instead of the directqb commands that are used in the game.
Reply
#2
The first thing I did was movement, I used a simple circle (as I didn't have any graphics for Venture yet, and experimented with it so that it would behave as I wanted).

Movement in the x direction is done using speed variables, so:

x = x + speed

When the user press the right key then I increase the speed:

speed = speed + .1

I set a limit according to if Vent is walking or running:

IF ctrlkey THEN speedlimit = 1 else speedlimit = .5
IF speed > speedlimit THEN speed = speedlimit

I do the same for the left key (but using speed = speed - .1 instead).

When Vent is on the ground I decrease the speed (friction) but in the air Vent will move even though there is no press on the directional keys. Many games today has the character stop when the user releases the key while it is in the air. But I wanted the game to feel like Super Mario.

Jumping and falling is very hard to do well, in my opinion, I did it using a variable that had a large value at first then slowly decreased until gravity took over (where gravity is simply y = y + 1 if Vent isn't touching the ground).

So:

y = y + jumpspeed
' first the jump needs to be initiated (just when the user presses the jump key)
IF initiatejump = 1 THEN jumpspeed = -7:initiatejump = 0 '-7 because I want Vent to move upwards when he jumps Smile

IF jump = 1 THEN
jumpspeed = jumpspeed  + .1 '(or what suits, this is the kind of thing that needs to be experimented upon to arrive at the right value).
IF jumpspeed > 0 THEN jumpspeed = 0: jump = 0
END IF


The jumpspeed variable is set to a negative value at first to make him go up and then decreased at a rate that looks good and is compatible with the heights of objects/obstacles etc. in the game.

The values are probably all wrong and I probably used many more variables than that to make the jumping and movement better, but this is the basic principles on how the movement was done for the Venture game. Experimentation is the key, there are formulas for it as well, but I like homebrewn instead Smile
Reply
#3
This is Cool! Cool Stuff Cyperium...explaining how you make your Games! Games are Not! My thing as far as writing Platform Games goes...but how someone does something can always help in other Programs!


Anthony.
Reply
#4
I will divide the "tutorial" into sections, so some sections might not apply to much more than platform games, but some sections might apply to many applications. Even so it might just be fun to see how something works Smile, I've always been very interested in how things works, I constantly disassembled my toys as a child and I had such a awe of how a complete video game could fit into that little cassette Smile
A favourite site of mine, except qb64.net and this site is HowStuffWorks, there are really interesting stuff there Smile
Reply
#5
Now that we have covered movement (somewhat) we move on to collision detection, in a game we need to know if the character is standing on the ground, hitting a enemy or a object. There are many ways to achieve this and my game Venture uses a combination of two ways:
  • Mask detection
  • Bounding Box detection
We'll start with Mask detection as that was the first thing introduced in my game, I simply wanted the main character (Vent) to be able to stand and be able to walk on something. So I had made a "ground" object which Vent could stand on. I put it on a image and displayed it but since there can be background objects and other colorful objects which Vent can't stand on I can't simply use POINT and see if there is color at the position where Vent is (to know that something is there), instead I made a box in a different image with the color 1, the color 1 would then be represented as solid objects that Vent can stand on. Now I can use POINT on that image to see if Vent is standing on something.

I'll show you two images, one is of the level as seen by the player, and one is of the mask "behind the scenes":




So you can see how the mask covers the areas that Vent can stand on or be hindered by, but it also covers the ladder with a different color, so when I use POINT it gives 2 (or something) instead of 1 so I won't stop Vent but instead allow him to climb if he is within color 2 of the mask image. I also have a color for water and a color for lava and other stuff. 

I use the mask for things that doesn't move, so I don't have to clear the mask image but only move it a bit and fill in the blanks as Vent moves through the level.

For objects that moves I use Bounding Box detection, which I will cover in my next post.
Reply
#6
Cyperium,

I wasn't for sure if you were going to continue your tutorial or, so I download you sample.

It looks interesting, but it so tiny on the high-resolution screens of today. I also was hoping to see the source code to it.

How do you play it?

Still great though.


Walter Whitman
The Joyful Programmer
Dedicated to empowering computer programming hobbyists, tinkerers, amateurs, and enthusiasts.
profile for Walter Whitman at Stack Overflow, Q&A for professional and enthusiast programmers


Reply
#7
(07-06-2014, 09:34 AM)Waltersmind Wrote: Cyperium,

I wasn't for sure if you were going to continue your tutorial or, so I download you sample.

It looks interesting, but it so tiny on the high-resolution screens of today. I also was hoping to see the source code to it.

How do you play it?

Still great though.


Walter Whitman
The Joyful Programmer
I will continue the tutorial, I just haven't had much time recently (with my sons birth and all), I will continue on it later though. The final release will be fullscreen when you start it, but you can get fullscreen by pressing Alt+Enter if you want. You can use the cursor keys on the titlescreen to get to Help which will show you how to play. The goal is simply to get from one side of the level to the other (reaching a flagpole at the end like in Super Mario). You can control the main character by using the cursor keys and Ctrl and Alt, Ctrl to run while holding the direction you want to run at, and Alt to jump. You can also use Z and X to run and jump if Ctrl and Alt doesn't behave as it should on your computer. Use the left Alt since the right Alt doesn't work well with this game (it produces both Ctrl and Alt at the same time). You start at the map screen where you select a level to play, you press Alt to select the level. (all of the controls use either the direction keys, Ctrl or Alt, use Ctrl to talk to people, etc.). 
Reply
#8
Cyperium,

Silly me. I apologize.

When you do start back into this working in this area (side scroller games) let me know. I have a little bit knowledge of my own that would benefit you. I can show you how to get away from masks for floor detections. Also, we could start a side-scroller section (I would create a whole new category just for it) that we be all about it.


Walter Whitman
The Joyful Programmer
Dedicated to empowering computer programming hobbyists, tinkerers, amateurs, and enthusiasts.
profile for Walter Whitman at Stack Overflow, Q&A for professional and enthusiast programmers


Reply
#9
Ok, so now we move to Bounding Box detection, all my objects are 10*10 in size so I have a flag set at the beginning if the character is colliding with the current object. 

So basically:

Code:
FOR currentobject = 1 TO numberofobjects

collide = 0 'remember to clear the collide flag before testing
IF ventx + 10 >= objectx(currentobject) AND ventx <= objectx(currentobject) + 10 THEN
IF venty + 10 >= objecty(currentobject) AND venty <= objecty(currentobject) + 10 THEN
collide = 1
END IF
END IF
'after this comes the handling of the objects themselves
IF objecttype(currentobject) = 1 THEN
END IF
NEXT

When defining a bounding box, we have a "hotspot", that's the spot the coordinates are relative too, the hotspot in Venture is the default (at the top left corner), that's why I use ventx + 10 so that the collision is tested against the right side of the character (if the right side of the character is colliding with the left side of the object), I use objectx(currentobject) + 10 to test if the left side of the character is colliding with the right side of the object for the same reason.

This effectively creates a box that is tested against and is basically all that is needed, but in Venture I also test if the collision was at the top of the object or at the bottom, to see if our character can jump on the enemy to stomp it (like in Super Mario) or if Vent is jumping at the bottom of a enemy, so that the jump is stopped.


Next section will be how to handle the objects themselves.
Reply
#10
Objects:

In Venture I use DirectQB to put images, which is a library for QB 4.5 that I converted (at least the parts that was needed) to QB64. As it is much easier to use _PUTIMAGE I will use that command here to illustrate how objects are handled in Venture.

The easiest way to place objects is to use this syntax:
_PUTIMAGE (xpos, ypos), objectimage

The default "hotspot" is at coordinate 0,0 of the objectimage but this can quite easily be changed, you do that by moving the object to the coordinate you want the hotspot to be, so in essence, if you want the hotspot to be at 10, 10 of the object, then you need to move the object -10, -10.

As so:
hotspotx = 10
hotspoty = 10

_PUTIMAGE(xpos - hotspotx, ypos - hotspoty), objectimage


Venture uses the default hotspot so we only need xpos and ypos.

I store the images of the objects in a array (as there are so many of them), all the filenames that are objects begin with "v" so I search the directory in which the images are in for filenames starting with "v", next to the "v" is the number that represent the object (like v01coin.bmp), I load the image and store the reference in the array at the position shown by the filename. I also count the number of images using a variable so that I can use that to loop through all the objects later.

I made a level editor that can list and place the objects where I want them to be. The level editor basically draws individual pixels on a image where each pixel represent a object and the color of the pixel tells us what object it is.

I have several different types of objects, some are static objects that only need to be drawn (with a mask behind them so that I don't need to handle them anymore), this is for example, ladders, ground objects, and basically anything that doesn't move and isn't background. I also have background and foreground objects, which isn't handled any further, background objects are placed on one image, and foreground are placed on another so that the main character (and any other object) is drawn before the foreground objects and thus will appear to be placed behind them.

Then we come to the moving objects, like enemies and items, basically anything that can be interacted with, change appearence, disappear and so on. These are constantly refreshed and I can move them as I want, animate them and take them away.

Every object is handled when refreshing the screen (the entire screen at first when loading the level, and only the sides of the screen needs to be refreshed when the level scrolls), I use POINT to determine what object is encountered.

This code illustrates the routine for refreshing the entire screen (when loading the level, or when going into a room, or going down a pipe).

xxxx + posiposix and yyyy + steppy is the coordinates of the map image that I had loaded (the one that the level editor used to place pixels representing objects).

posiposix is relative to scrolling, since there is no up/down scrolling steppy is increased by a full screen when going down a pipe or going down a ladder (it moves in steps of a whole screen) hence the name.


It is easiest to explain using code, this is taken directly from the game at the relevant part, DQBpoint is the POINT equivalent in QB64 and DQBput is the _PUTIMAGE equivalent (but instead of using VARSEG and VARPTR you would only use the image handle in _PUTIMAGE as it is much more user-friendly).

Code:
FOR xxxx = 0 TO 31     
    FOR yyyy = 2 TO 18 
        objobj = DQBpoint(7, xxxx + posiposix, yyyy + steppy) 


        IF objobj > 0 THEN 

             
                SELECT CASE objobj 
                       
                    CASE 13, 16, 19 TO 33  'background
                        DQBput 2, xxxx * 10, yyyy * 10, VARSEG(item(0, objobj)), VARPTR(item(0, objobj)) 

                        
                    CASE 225 TO 230 'nothing (handled elsewhere)
                    

                    CASE 2, 4, 7, 10, 14, 17, 18, 34, 47 TO 49 'solid
                            
                        DQBput 2, xxxx * 10, yyyy * 10, VARSEG(item(0, objobj)), VARPTR(item(0, objobj))
                        DQBboxf 5, xxxx * 10, yyyy * 10, (xxxx * 10) + 9, (yyyy * 10) + 10, 1
                        
                         
                    CASE 25, 26, 27, 28, 35, 36, 46 'door
                        DQBput 2, xxxx * 10, yyyy * 10, VARSEG(item(0, objobj)), VARPTR(item(0, objobj))
                        DQBboxf 5, xxxx * 10, yyyy * 10, (xxxx * 10) + 10, (yyyy * 10) + 10, 2 
                         
                    CASE 69, 138 'ladder
                        DQBput 2, xxxx * 10, yyyy * 10, VARSEG(item(0, objobj)), VARPTR(item(0, objobj))
                        DQBboxf 5, xxxx * 10, yyyy * 10, (xxxx * 10) + 10, (yyyy * 10) + 10, 3 

                         
                    CASE 149, 150 'water
                        DQBput 2, xxxx * 10, yyyy * 10, VARSEG(item(0, objobj)), VARPTR(item(0, objobj))
                        DQBboxf 5, xxxx * 10, yyyy * 10, (xxxx * 10) + 10, (yyyy * 10) + 10, 5 

                    CASE 250 TO 252 'semisolid (you can jump through it, but then stand on it)
                        DQBput 2, xxxx * 10, yyyy * 10, VARSEG(item(0, objobj)), VARPTR(item(0, objobj))
                        DQBline 5, xxxx * 10, yyyy * 10, (xxxx * 10) + 10, (yyyy * 10), 6 

                        
                    CASE 1, 6, 40 TO 42, 73 TO 80 'objects 
                            enenum = enenum + 1 

                            IF enex(enenum) < ventx THEN enedir(enenum) = 2 
                            IF enex(enenum) > ventx THEN enedir(enenum) = 1 
                            'reset the object variables so that there are no leftovers 
                            enex(enenum) = xxxx * 10: eney(enenum) = yyyy * 10: enedir(enenum) = 0 
                            enespx(enenum) = 0: enespy(enenum) = 0: enetyp(enenum) = 0 
                            enempx(enenum) = xxxx + posiposix: enempy(enenum) = yyyy + steppy 
                            enelife(enenum) = 0: eneflag(enenum) = 0: eneflag2(enenum) = 0 
 
                            IF objobj = 1 THEN 
                                enetyp(enenum) = 1 
                                enelife(enenum) = -1 
                            END IF 
                            IF objobj = 6 THEN 
                                enetyp(enenum) = 2 
                                enelife(enenum) = -1 
                            END IF 
                            IF objobj = 40 THEN 
                                enedir(enenum) = 1 
                                enespx(enenum) = enspeed: enetyp(enenum) = 3 
                                enelife(enenum) = 1 
                            END IF 
                            IF objobj = 41 THEN 
                                enetyp(enenum) = 4 
                                enelife(enenum) = -1 
                            END IF 
                            IF objobj = 42 THEN 
                                enedir(enenum) = 1 
                                enespx(enenum) = .25: enetyp(enenum) = 5 
                                enelife(enenum) = 1 
                            END IF 


                    CASE ELSE 'foreground (anything that isn't captured by the CASEs are treated as foreground)


                        DQBput 4, xxxx * 10, yyyy * 10, VARSEG(item(0, objobj)), VARPTR(item(0, objobj)) 
           END SELECT
     END IF

    NEXT
NEXT


The code has been shortened of course and simplified, but that should give you an idea of how I did it. I also have other "sets" of objects that I treat the same way (so that I could handle more than 255 of them).

The objects are actually not "handled" here, that's at another part of the program. Best illustrated with a example of handling two of them:

Code:
IF enenum > 0 THEN 
collisions = 0 
 
FOR enenen = 1 TO enenum 
 
collide = 0 
takeaway = 0 
ontop = 0 
onbottom = 0 
onleft = 0 
onright = 0 
destroy = 0 

IF heart > 0 AND ventx + 9 >= enex(enenen) AND ventx <= enex(enenen) + 9 AND venty + 9 >= eney(enenen) AND venty <= eney(enenen) + 10 THEN 

    collide = 1 
    collisions = collisions + 1 
    IF venty + 9 >= eney(enenen) AND venty + 9 <= eney(enenen) + 3 THEN ontop = 1 
    IF venty >= eney(enenen) + 7 AND venty <= eney(enenen) + 11 THEN onbottom = 1 
    IF ventx + 9 >= enex(enenen) AND ventx + 9 <= enex(enenen) + 5 THEN onleft = 1 
    IF ventx >= enex(enenen) + 5 AND ventx <= enex(enenen) + 10 THEN onright = 1 
END IF 

SELECT CASE enetyp(enenen) 
    CASE 1 
        eneani(enenen) = eneani(enenen) + 1: IF eneani(enenen) > 20 THEN eneani2(enenen) = eneani2(enenen) + 1: eneani(enenen) = 0 
        IF eneani2(enenen) = 4 THEN eneani2(enenen) = 0 
        IF eneani2(enenen) = 0 THEN DQBput 3, enex(enenen), eney(enenen), VARSEG(item(0, 1)), VARPTR(item(0, 1)) 
        IF eneani2(enenen) = 1 THEN DQBput 3, enex(enenen) + 1, eney(enenen), VARSEG(ncoin1(0)), VARPTR(ncoin1(0)) 
        IF eneani2(enenen) = 2 THEN dqbput 3, enex(enenen) + 1, eney(enenen), VARSEG(ncoin2(0)), VARPTR(ncoin2(0)) 
        IF eneani2(enenen) = 3 THEN dqbput 3, enex(enenen) + 1, eney(enenen), VARSEG(ncoin1(0)), VARPTR(ncoin3(0)) 
        'IF eneani2(enenen) = 4 THEN dqbput 3, enex(enenen), eney(enenen), VARSEG(ncoin1(0)), VARPTR(ncoin4(0)) 
        'IF eneani2(enenen) = 5 THEN dqbput 3, enex(enenen), eney(enenen), VARSEG(ncoin1(0)), VARPTR(ncoin1(0)) 
 
        IF collide = 1 AND enespy(enenen) >= -1 THEN score = score + 10: money = money + 1: takeaway = 1: collide = 0: destroy = 1: IF soundoff = 0 THEN _SNDPLAY coinhand 
        ontop = 0 
        onbottom = 0 
        IF enespy(enenen) < 0 THEN eney(enenen) = eney(enenen) - 1: enespx(enenen) = enespx(enenen) + 1 
        IF enespx(enenen) > 20 THEN takeaway = 1: enespx(enenen) = 0 
 
    CASE 2 
        DQBput 3, enex(enenen), eney(enenen), VARSEG(item(0, 6)), VARPTR(item(0, 6)) 
 
 
        IF collide = 1 THEN score = score + 250: heart = heart + 1: takeaway = 1: collide = 0: destroy = 1 

    CASE 17 

        'turtle 
        DQBmPut 3, enex(enenen), eney(enenen), VARSEG(item(0, 121)), VARPTR(item(0, 121)), flips 
        IF enelife(enenen) > 0 THEN 
            IF collide = 1 AND invinc = 0 AND ontop = 0 AND starpower = 0 THEN 
                gothit = 1 
            END IF 
 
            IF ontop = 1 THEN 
                enetyp(enenen) = 18: enedir(enenen) = 0: jump = 1 
                score = score + 100 
                IF jmpkey = 0 THEN jmppwr = 2 
                IF jmpkey THEN jmppwr = 3 
                ontop = 0 
            END IF 
            xl = DQBpoint(5, enex(enenen), eney(enenen) + 8) 
            xr = DQBpoint(5, enex(enenen) + 9, eney(enenen) + 8) 
            yd = DQBpoint(5, enex(enenen) + 5, eney(enenen) + 9) 
            yd2 = DQBpoint(5, enex(enenen) + 5, eney(enenen) + 10) 
            IF enedir(enenen) = 0 AND enetyp(enenen) = 17 THEN enedir(enenen) = 2 
            IF xl = 1 THEN enedir(enenen) = 2 
            IF xr = 1 THEN enedir(enenen) = 1 
            IF yd <> 1 THEN eney(enenen) = eney(enenen) + 1 
            IF yd2 = 1 OR yd2 = 6 THEN eney(enenen) = eney(enenen) - 1 
            IF enedir(enenen) = 1 THEN enex(enenen) = enex(enenen) - enespx(enenen) 
            IF enedir(enenen) = 2 THEN enex(enenen) = enex(enenen) + enespx(enenen) 
        END IF 

    CASE ELSE 

        takeaway = 1 
END SELECT
NEXT
END IF

CASE 1 is a coin that is animated (it isn't animated in the demo though).

CASE 2 is a heart (when you get hit by a enemy you loose a heart, when you loose all hearts you loose a life). 

CASE 17 is a koopa enemy that is transformed to another object when it is stomped on, the koopa shell (which is then handled seperately).
Reply
#11
Cyperium,

_PUTIMAGE takes a little getting use to I can assure you, but it is powerful if you are using a spritesheet which contains your animated objects. _PUTIMAGE allows you to cut a section out of a larger image and blast it to the screen.

If you think _PUTIMAGE is hard, try using _MAPTRIANGLE. _MAPTRIANGLE requires a good bit of transformation math to get it to the simple things, but it provides the most power. _MAPTRIANGLE is now designed to use hardware images and if the machine which a the demo runs on has a graphics card, you will see a huge difference in speed.

If you take a look at my "Explosion" demo located at: http://www.thejoyfulprogrammer.com/qb64/...273#pid462 you will see what I am talking about. On my very old laptop at the house, I was able to blit 2,000 exploding images and get around 58 frames-per-second. I was also able to blit 5,000 explosions and get 31 frames-per-second, and 10,000 explosions and get 15 frames-per-second. On my work computer, which is a high-end machine, I was able to get 32 frames-per-second when I blitted 10,000 explosions.

If I used software images instead of hardware images in my demo on my old laptop at the house, I would get 4 frames-per-second when I blitted 500 explosions.

Oh, before I forget, you can resize the demo's window to any size you want it updates accordingly. You can click and drag the corner of the window and move it around as fast or slow as you want it, and the content repositions itself accordingly.


Thanks,
Walter Whitman
The Joyful Programmer
Dedicated to empowering computer programming hobbyists, tinkerers, amateurs, and enthusiasts.
profile for Walter Whitman at Stack Overflow, Q&A for professional and enthusiast programmers


Reply
#12
(08-07-2014, 08:04 AM)Waltersmind Wrote: Cyperium,

_PUTIMAGE takes a little getting use to I can assure you, but it is powerful if you are using a spritesheet which contains your animated objects. _PUTIMAGE allows you to cut a section out of a larger image and blast it to the screen.

If you think _PUTIMAGE is hard, try using _MAPTRIANGLE. _MAPTRIANGLE requires a good bit of transformation math to get it to the simple things, but it provides the most power. _MAPTRIANGLE is now designed to use hardware images and if the machine which a the demo runs on has a graphics card, you will see a huge difference in speed.

If you take a look at my "Explosion" demo located at: http://www.thejoyfulprogrammer.com/qb64/...273#pid462 you will see what I am talking about. On my very old laptop at the house, I was able to blit 2,000 exploding images and get around 58 frames-per-second. I was also able to blit 5,000 explosions and get 31 frames-per-second, and 10,000 explosions and get 15 frames-per-second. On my work computer, which is a high-end machine, I was able to get 32 frames-per-second when I blitted 10,000 explosions.

If I used software images instead of hardware images in my demo on my old laptop at the house, I would get 4 frames-per-second when I blitted 500 explosions.

Oh, before I forget, you can resize the demo's window to any size you want it updates accordingly. You can click and drag the corner of the window and move it around as fast or slow as you want it, and the content repositions itself accordingly.


Thanks,
Walter Whitman
The Joyful Programmer
I use software images (which behind the scene use _PUTIMAGE, it is simply a wrapper for the directqb routines) simply because QB64 GL don't yet work well with the game. Venture 2 will use _PUTIMAGE and hopefully if QB64 GL is up to it will also use hardware acceleration (last time I tried using QB64 GL with Venture 2 the program lagged too much, I haven't yet tried with the new hardware acceleration though, might have solved the issue of "pauses" from time to time). The SDL version works perfectly for both Venture 1 and 2 in the meantime.
Reply
#13
Cyperium,

Could you share the source code for your game? If you don't want the source on the forum, could you email it to me? I want to test it in QB64-GL. I have noticed in some of my demos I wrote in QB64-SDL, were a little slower in QB64-GL, but it had something to do with timers.

Oh, I just read on QB64.net that Galleon has made _PUTIMAGE use hardware accelerated images.


Thanks,
Walter Whitman
The Joyful Programmer
Dedicated to empowering computer programming hobbyists, tinkerers, amateurs, and enthusiasts.
profile for Walter Whitman at Stack Overflow, Q&A for professional and enthusiast programmers


Reply
#14
I decided to throw in the latest version of Venture, you can actually beat the game in this version but the ending isn't yet finished (and a couple of other things isn't finished yet either, but it's fully playable).


https://dl.dropboxusercontent.com/u/8443...retest.rar


It would be really nice if you could get it to work in GL or pinpoint what the problems might be. Perhaps the timers issue has been fixed? That would be really nice!

Oh, and don't worry about the main character appearing at a different place than Start when you start the game, it was just a minor bug that is easy to solve (and doesn't depend on differences between SDL and GL)


EDIT:
I tried myself to download the latest QB64 GL dirty build and compile Venture and at first I only got a black screen. Later when I opened the .exe I got a error 5 at the line where the included file directqb.bm is, don't know exactly what is wrong in there. I'll try copy and paste the contents of the file instead of including it to see where the error actually is in the file.

EDIT 2:
_PUTIMAGE (x, y)-(x + _WIDTH(alba) - 1, y + _HEIGHT(alba) - 1), alba, dqblayer(layer)

This is the line that fails (line 8227 when pasting directqb.bi instead of including it), it gave error 5.

dqblayer(layer) is just a array of image handles. My guess is that _WIDTH and _HEIGHT doesn't work properly.

There could be other errors than this, but I don't have time to test now.

EDIT 3:

The problem is probably that it is a 256 color image, I heard that QB64 GL still don't work well with these images. I tested _WIDTH and _HEIGHT in a new program and it works flawlessly. The handle "alba" was opened using _LOADIMAGE(thefile$, 256), as this is a problem they already know about I won't bother posting a new bug in the forums, instead I just have to wait until it is worked on.
Reply
#15
This is Venture 2, on my computer in GL it shows jerky motion, in SDL it runs smooth.

Venture 2

It is important that it runs smooth as the intention right now is to get a good jump and movement by experimentation.
Reply
#16
Hi Johnno, 

Long time since we've had a cup of coffee...

Someone was checking this game out, was it you?

It should be you because it is a platform game and I remember you were asking and attempting such a game.

What have you been doing lately? sorry to OP for digressing
B += x
Reply