Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Topics - Slydog

Pages: [1] 2
Code Snippets / Random Textured Rolling Hills
« on: 2012-May-16 »
This will generate random, endless rolling hills.
It scrolls into/out of view at a specified speed.

Points that scroll off the screen (to the left) are removed from the array.
When a new hill is needed, it creates one (toggling the direction up or down).
It uses this new hill (really just a height value) and slices up the hill horizontally into segments.
It will scroll this new hill into view, then repeat endlessly.
In memory, the array should only have enough segment slices to fill the screen, plus a bit before, and the next hill yet to be shown.

[<-] - Cursor Left - Decrease Speed (by 0.05)
[->] - Cursor Right - Increase Speed (by 0.05)
[1] - Change display mode to 'MODE_TEXTURE'
[2] - Change display mode to 'MODE_TEXTURE_WIRE'
[3] - Change display mode to 'MODE_WIRE'
[4] - Change display mode to 'MODE_POLYGON'
[5] - Change display mode to 'MODE_SOLID'
(The display modes are explained in the source code, nothing much really, just for debugging)

I'm not a graphics guy, so I just whipped up a sample texture to demo this.
If you notice the light brown bottom dirt border, and if you switch to 'MODE_TEXTURE_WIRE', the middle line follows this dirt border.
(This is configurable in code by a percentage - it is set to 25%)

While this may not affect the display too much, you can use the mid-line coordinates to wire up a physics engine and use that line for the colliders.
(It does affect the display a bit, kinda confusing, check out the code where ever I used that variable).

I tried commenting the code, but I ran out of time.  May update it later.
I attached the png texture I used.
And I zipped up the project too.
I hope it works, I'm in a rush!

- a 'hill' is just the x/y coordinates of a peak or valley
- segments are how I slice up the hill and use a COSINE function to provide the rolling effect
- when the texture runs out, I just start it over again, but it may not have fully displayed the full texture, so when it joins with the next segment, it may not join up nicely - not sure how to get around this
- play with the hill settings, but if it is too much of a slope, the polygons will overlap, I could provide a work around, but it is simpler to just live with this constraint

Code: GLBasic [Select]
GLOBAL sx%, sy%
GLOBAL speed# = 4.0
GLOBAL sp_texture%

GLOBAL hills AS THillPoints
GLOBAL hills2 AS THillPoints

        GETSCREENSIZE sx, sy
        SEEDRND MID$(PLATFORMINFO$("time"), 17, 2)
        LIMITFPS 60
                DEBUG "Media Folder Not Found . . . Exiting!\n"

        // Initialize 'hill' TYPE
        sp_texture = GENSPRITE()
        LOADSPRITE "grass.png", sp_texture // Load texture
        hills.sp_texture = sp_texture
        hills.x_width = 15                                                                                                              // How wide each section strip is, the wider, the more 'chunky' looking hills
        GETSPRITESIZE sp_texture, hills.texture_width, hills.texture_height             // Sprite size of the ground texture
        hills.texture_ht_up = hills.texture_height * 0.75                                               // How tall to make the top portion
        hills.texture_ht_dn = hills.texture_height * 0.25                                               // How tall to make the bottom portion (together should add to 1.0)
        hills.x_change_min = 180                                                                                                // When creating a new hill peak, it must be at LEAST this distance from previous
        hills.x_change_max = 220                                                                                                // When creating a new hill peak, it must be at MOST this distance from previous
        hills.y_change_min = 50                                                                                                 // When creating a new hill peak, the peak must be at LEAST this much higher/lower
        hills.y_change_max = 190                                                                                                // When creating a new hill peak, the peak must be at MOST this much higher/lower
        hills.y_min = sy * 0.40                                                                                                 // Keep hills below top 20% of screen
        hills.y_max = sy * 0.95                                                                                                 // Keep hills above bottom 5% of screen
        hills.rgb_bottom = RGB(100,60,20)
        GenerateHills(hills)                                                                                                    // Initial seeding of hills to fill screen at start

        // Initialize 'hill2' TYPE
        sp_texture = GENSPRITE()
        LOADSPRITE "snow.png", sp_texture // Load texture
        hills2.sp_texture = sp_texture
        hills2.x_width = 25                                                                                                             // How wide each section strip is, the wider, the more 'chunky' looking hills
        GETSPRITESIZE sp_texture, hills2.texture_width, hills2.texture_height   // Sprite size of the ground texture
        hills2.texture_ht_up = hills2.texture_height * 0.25                                             // How tall to make the top portion
        hills2.texture_ht_dn = hills2.texture_height * 0.75                                             // How tall to make the bottom portion (together should add to 1.0)
        hills2.x_change_min = 220                                                                                               // When creating a new hill peak, it must be at LEAST this distance from previous
        hills2.x_change_max = 280                                                                                               // When creating a new hill peak, it must be at MOST this distance from previous
        hills2.y_change_min = 70                                                                                                // When creating a new hill peak, the peak must be at LEAST this much higher/lower
        hills2.y_change_max = 220                                                                                               // When creating a new hill peak, the peak must be at MOST this much higher/lower
        hills2.y_min = sy * 0.15                                                                                                // Keep hills below top 20% of screen
        hills2.y_max = sy * 0.60                                                                                                // Keep hills above bottom 5% of screen
        hills2.rgb_bottom = RGB(30,45,60)
        GenerateHills(hills2)                                                                                                   // Initial seeding of hills to fill screen at start

        // Main Loop
        WHILE TRUE
                // Speed: "<-" and "->" cursor to decrease / increase
                IF KEY(203)
                        DEC speed, 0.05
                        IF speed < 0.1 THEN speed = 0.1
                ELSEIF KEY(205)
                        INC speed, 0.05
                        IF speed > 20 THEN speed = 20

                // Draw Mode: Good for see what is happening behind the scenes
                LOCAL key_hit$
                key_hit$ = INKEY$()
                SELECT key_hit$
                CASE "1"
                        draw_mode = MODE_TEXTURE
                CASE "2"
                        draw_mode = MODE_TEXTURE_WIRE
                CASE "3"
                        draw_mode = MODE_WIRE
                CASE "4"
                        draw_mode = MODE_POLYGON
                CASE "5"
                        draw_mode = MODE_SOLID

                CLEARSCREEN RGB(0,100,220)                      // Blue background - azure
                ALPHAMODE -1.0                                          // In case your texture has alpha values
                SMOOTHSHADING FALSE                                     // Don't pixelate the graphics
                hills2.Draw(draw_mode)                          // Draw current hills using current draw mode
                hills2.Scroll(speed*0.3)                        // Scroll the hills left based on speed - if needed, a new hill will be generated
                hills.Draw(draw_mode)                           // Draw current hills using current draw mode
                hills.Scroll(speed)                                     // Scroll the hills left based on speed - if needed, a new hill will be generated

FUNCTION GenerateHills: hill AS THillPoints

        WHILE hill.hill_next.x < (sx + hill.x_change_max)

Code: GLBasic [Select]
CONSTANT MODE_TEXTURE% = 1                      // Normal textured display
CONSTANT MODE_TEXTURE_WIRE% = 2         // Wire outline overlayed on textured display
CONSTANT MODE_WIRE% = 3                         // Wire outine of all calculated vectors
CONSTANT MODE_SOLID% = 4                        // Fills in quads will solid colour - kinda neat when fast!
CONSTANT MODE_POLYGON% = 5                      // Same as 'SOLID', except shades each polygon to see each polygon

CONSTANT PI = 3.14159

// General vector routine, makes other code cleaner
TYPE TVector

        FUNCTION Set: x#, y#
                self.x = x
                self.y = y

        FUNCTION Copy: v AS TVector
                self.x = v.x
                self.y = v.y

        FUNCTION Scale: amount#
                self.x = self.x * amount
                self.y = self.y * amount

        FUNCTION AverageWith AS TVector: v AS TVector
                LOCAL rv AS TVector
                rv.x = (self.x + v.x) / 2.0
                rv.y = (self.y + v.y) / 2.0
                RETURN rv

        // Calc the magnitude / length of a vector
        FUNCTION Magnitude#:
           RETURN SQR((self.x * self.x) + (self.y * self.y))

// A hill point is a point along the curved hill, a new one is generated every 'self.x_width' pixels
TYPE THillPoint
        pu AS TVector                                   // Higher point - used for top portion of texture (grass)
        pm AS TVector                                   // Base/mid point - good to use for physics engine as the 'contact' location
        pd AS TVector                                   // Lower point - used for the bottom portion of texture (dirt border)
        texture_x#                                              // How far along texture this point is

TYPE THillPoints
        points[] AS THillPoint                  // All of the points along all of the hills
        x_width%                                                // How far between curve segments (the more, the smoother the curve)
        texture_height#                                 // How tall the ground texture is
        texture_ht_up#                                  // How tall the ground texture is - above the base point
        texture_ht_dn#                                  // How tall the ground texture is - below the base point
        texture_width#                                  // How wide the ground texture is
        y_min%                                                  // How high up the screen a hill peak can reach
        y_max%                                                  // How low on the screen a hill valley can reach
        x_change_min%                                   // New hill peaks/valleys must be at least this 'x' distance from previous
        x_change_max%                                   // New hill peaks/valleys must be at most this 'x' distance from previous
        y_change_min%                                   // New hills must be at least this far above or below
        y_change_max%                                   // New hills must be at most this far above or below
        peak_valley_toggle%                             // Remember if previous hill was a peak or a valley, toggle each time
        sp_texture%                                             // Sprite ID of hill top texture
        rgb_bottom%                                             // Colour below the curve and texture (the bottom dirt colour)

        hill_prev AS TVector                    // Used when calculating new hills
        hill_next AS TVector                    // The current hill vector will be stored here

        // Used to clear / initialize the data
        FUNCTION Clear:
                LOCAL y_adjust% = 30            // Used to make sure we don't start too high or too low
                DIM self.points[0]                      // Clear previous points
                self.hill_next.Set(0, RND(self.y_max - self.y_min - (y_adjust * 2)) + self.y_min + y_adjust)    // Initialize 'hill_next' at a random valid 'y' pos
                self.hill_prev.Set(0, 0)                                                                                                                                                // Initialize 'hill_prev'

        // Draw current hill points - various draw modes are available
        FUNCTION Draw: mode% = MODE_TEXTURE
                LOCAL hx%
                LOCAL xy[] AS TVector
                LOCAL uv[] AS TVector

                SELECT mode

                CASE MODE_WIRE
                        FOR hx = 1 TO LEN(self.points[]) - 1
                                DRAWLINE self.points[hx-1].pu.x, self.points[hx-1].pu.y, self.points[hx].pu.x, self.points[hx].pu.y, RGB(255,255,255)
                                DRAWLINE self.points[hx-1].pm.x, self.points[hx-1].pm.y, self.points[hx].pm.x, self.points[hx].pm.y, RGB(255,255,255)
                                DRAWLINE self.points[hx-1].pd.x, self.points[hx-1].pd.y, self.points[hx].pd.x, self.points[hx].pd.y, RGB(255,255,255)
                                DRAWLINE self.points[hx].pu.x,   self.points[hx].pu.y,   self.points[hx].pd.x, self.points[hx].pd.y, RGB(255,255,255)

                CASE MODE_POLYGON
                        STARTPOLY -1, 2
                        FOR hx = 1 TO LEN(self.points[]) - 1
                                PolyQuick(self.points[hx-1].pu, self.points[hx].pu, self.points[hx-1].pd, self.points[hx].pd, RGB(250,175,0), RGB(175,120,0))

                CASE MODE_SOLID
                        STARTPOLY -1, 2
                        FOR hx = 1 TO LEN(self.points[]) - 1
                                PolyQuick(self.points[hx-1].pu, self.points[hx].pu, self.points[hx-1].pd, self.points[hx].pd, RGB(250,180,0), RGB(250,180,0))

                CASE MODE_TEXTURE
                        DIM xy[2]
                        DIM uv[4]
                        xy[0].y = sy
                        xy[1].y = sy

                        // Bottom area first (dirt?)
                        STARTPOLY -1, 2
                        FOR hx = 1 TO LEN(self.points[]) - 1
                                IF self.points[hx].pm.x < sx + (self.x_width*2)
                                        xy[0].x = self.points[hx-1].pd.x
                                        xy[1].x = self.points[hx].pd.x
                                        PolyQuick(self.points[hx-1].pm, self.points[hx].pm, xy[0], xy[1], self.rgb_bottom, self.rgb_bottom)

                        // Now draw hills
                        uv[0].y = 0
                        uv[1].y = 0
                        uv[2].y = self.texture_height
                        uv[3].y = self.texture_height
                        STARTPOLY self.sp_texture, 2
                        FOR hx = 1 TO LEN(self.points[]) - 1
                                IF self.points[hx].pm.x < sx + (self.x_width*2)
                                        uv[0].x = self.points[hx-1].texture_x
                                        uv[1].x = self.points[hx].texture_x
                                        IF uv[0].x = self.texture_width THEN uv[0].x = 0
                                        IF uv[1].x < uv[0].x
                                                uv[1].x = uv[0].x + self.x_width
                                        uv[2].x = uv[0].x
                                        uv[3].x = uv[1].x
                                        PolyDraw(self.points[hx-1].pu, self.points[hx].pu, self.points[hx-1].pd, self.points[hx].pd, uv[0], uv[1], uv[2], uv[3])

                CASE MODE_TEXTURE_WIRE
                        self.Draw(MODE_TEXTURE)         // First draw in texture mode
                        self.Draw(MODE_WIRE)            // Then overlay with wire mode


        FUNCTION Scroll: x#
                FOREACH p IN self.points[]
                        DEC p.pu.x, x#
                        DEC, x#
                        DEC p.pd.x, x#
                        IF (p.pu.x < -(self.x_width*2)) AND (p.pd.x < -(self.x_width*2)) THEN DELETE p          // Delete point if too far to the left offscreen
                DEC self.hill_prev.x, x#
                DEC self.hill_next.x, x#

                // Generate new hill yet?
                IF self.hill_next.x < (sx + (self.x_width * 2)) THEN self.GenerateNewHill()

        FUNCTION GenerateNewHill:
                LOCAL x_dist%
                ALIAS h0 AS self.hill_prev
                ALIAS h1 AS self.hill_next

                h0.Copy(h1)             // Move old 'next' to 'prev'

                // Random 'x' distance from previous peak
                x_dist = self.x_change_min + RND(self.x_change_max - self.x_change_min)
                x_dist = (x_dist / self.x_width) * self.x_width                                 // Snap to 'hillPoints.x_width'
                h1.x = h0.x + x_dist

                // Random 'y' distance from previous peak
                IF self.peak_valley_toggle = TRUE
                        // New point will be below previous (ie: a 'valley')
                        h1.y = h0.y + (self.y_change_min + RND(self.y_change_max - self.y_change_min))
                        // New point will be above previous (ie: a 'peak')
                        h1.y = h0.y - (self.y_change_min + RND(self.y_change_max - self.y_change_min))

                // Adjust 'y' to keep in range
                IF h1.y < (self.y_min + self.texture_ht_up) THEN h1.y = (self.y_min + self.texture_ht_up)
                IF h1.y > (self.y_max - self.texture_ht_dn) THEN h1.y = (self.y_max - self.texture_ht_dn)

                // Toggle peak / valley bit for next entry
                self.peak_valley_toggle = NOT self.peak_valley_toggle


        // Fill in curve between these two 'hillPeaks' entries
        FUNCTION FillCurve:
                LOCAL segments#, sx#
                LOCAL dx#, da#, ymid#, ampl#
                LOCAL hillPoint AS THillPoint
                LOCAL v_delta AS TVector
                LOCAL partial#, overage#
                LOCAL length#
                ALIAS h0 AS self.hill_prev
                ALIAS h1 AS self.hill_next
                segments = (h1.x - h0.x) / self.x_width

                dx = (h1.x - h0.x) / segments
                da =  PI / segments
                ymid = (h0.y + h1.y) / 2.0
                ampl = (h0.y - h1.y) / 2.0

                FOR sx = 0 TO segments-1
               = h0.x + sx*dx
               = ymid + ampl * COS(sx*da * 180.0/PI)
                        IF LEN(self.points[]) = 0
                                hillPoint.texture_x = 0
                                v_delta.Set( - self.points[-1].pm.x, - self.points[-1].pm.y)
                                length = v_delta.Magnitude()
                                hillPoint.texture_x = self.points[-1].texture_x + length
                                // Are we at the end of the texture, time to start at the beginning again
                                IF hillPoint.texture_x > self.texture_width
                                        overage = hillPoint.texture_x - self.texture_width                      // How far over the end of the texture are we?
                                        partial = overage / length                                                                      // As a percentage (between 0 and 1)
                                        // If it's really small, just stretch it to tne end of the texture
                                        IF partial < 0.1
                                                hillPoint.texture_x = self.texture_width
                                        // If it's really large, snap the previous point to the end of the texture, and start this entry over
                                        ELSEIF partial > 0.9
                                                hillPoint.texture_x = length
                                                self.points[-1].texture_x = self.texture_width
                                        // Somewhere in between?  Then create a mid-point entry by splitting this polygon in two
                                                // Mid point entry
                                                hillPoint.texture_x = self.texture_width
                                       = h0.x + (sx-partial)*dx
                                       = ymid + ampl * COS((sx-partial)*da * 180.0/PI)
                                                // Regular entry
                                                hillPoint.texture_x = overage
                                       = h0.x + sx*dx
                                       = ymid + ampl * COS(sx*da * 180.0/PI)

        FUNCTION AddPoint: h AS THillPoint
                DIMPUSH self.points[], h

                IF LEN(self.points[]) = 1
                        self.points[0].pu.Set(0, self.points[0].pm.y - self.texture_ht_up)
                        self.points[0].pd.Set(0, self.points[0].pm.y + self.texture_ht_dn)

                ALIAS h0 AS self.points[-2]
                ALIAS h1 AS self.points[-1]
                LOCAL angle#
                LOCAL v1 AS TVector
                LOCAL v0 AS TVector

                // Calculate 'p2' of HillPoint - form rectangle with previous point
                angle = ATAN( -, -  -90// Angle of the two points

                v1.x = COS(angle) * self.texture_ht_up +
                v1.y = SIN(angle) * self.texture_ht_up +
                v0.x = COS(angle) * self.texture_ht_up +
                v0.y = SIN(angle) * self.texture_ht_up +
                h0.pu = h0.pu.AverageWith(v0)

                v1.x = COS(angle) * -self.texture_ht_dn +
                v1.y = SIN(angle) * -self.texture_ht_dn +
                v0.x = COS(angle) * -self.texture_ht_dn +
                v0.y = SIN(angle) * -self.texture_ht_dn +
                h0.pd = h0.pd.AverageWith(v0)

FUNCTION PolyQuick: v1 AS TVector, v2 AS TVector, v3 AS TVector, v4 AS TVector, rgb1%, rgb2%
        POLYVECTOR v1.x, v1.y, 0, 0, rgb1               // TL
        POLYVECTOR v3.x, v3.y, 0, 1, rgb1               // BL
        POLYVECTOR v2.x, v2.y, 1, 0, rgb2               // TR
        POLYVECTOR v4.x, v4.y, 1, 1, rgb2               // BR

FUNCTION PolyDraw: v1 AS TVector, v2 AS TVector, v3 AS TVector, v4 AS TVector,  uv1 AS TVector, uv2 AS TVector, uv3 AS TVector, uv4 AS TVector
        LOCAL rgb1% = RGB(255,255,255)
        POLYVECTOR v1.x, v1.y, uv1.x, uv1.y, rgb1               // TL
        POLYVECTOR v3.x, v3.y, uv3.x, uv3.y, rgb1               // BL
        POLYVECTOR v2.x, v2.y, uv2.x, uv2.y, rgb1               // TR
        POLYVECTOR v4.x, v4.y, uv4.x, uv4.y, rgb1               // BR

[EDIT] Fixed most / all bugs.  Updated demo for parallax.  See post below for details.

[attachment deleted by admin]

Userlibs [ *.gbal] / Dictionary / Hash list
« on: 2012-Apr-24 »
Here's a quick dictionary / hash library I created when first starting with GLBasic.
I have never used it (changed my mind!), and I don't think it was ever tested.
It was created for simplicity, and perhaps should not be used too much in your main game loop.
I haven't tested it so I don't know if it works, or how fast it is:

Oh, and it only works for storing numerical values (floats) against a string. 
It could easily be update for strings.
Code: GLBasic [Select]
//  D I C T I O N A R Y
TYPE TDictionaryItem

TYPE TDictionary
        table[]         AS TDictionaryItem

        FUNCTION Clear:
                DIM self.table[0]

        FUNCTION Add: name$, value
                // If 'name$' already exists, call 'Set' instead
                IF self.Exists(name$)
                        self.Set(name$, value)

                LOCAL item AS TDictionaryItem
      $ = name$
                item.value = value
                DIMPUSH self.table[], item

        FUNCTION Get: name$, defaultValue=-1
                LOCAL rv# = defaultValue

                FOREACH item IN self.table[]
                        IF$ = name$
                                rv = item.value

                RETURN rv

        FUNCTION Set%: name$, value
                // If the 'name$' doesn't exist in the table, call 'Add' instead
                IF NOT self.Exists(name$)
                        self.Add(name$, value)

                FOREACH item IN self.table[]
                        IF$ = name$
                                item.value = value

        FUNCTION Exists%: name$
                FOREACH item IN self.table[]
                        IF$ = name$
                                RETURN TRUE
                RETURN FALSE


Code: GLBasic [Select]
GLOBAL salaries AS TDictionary
salaries.Add("Peter", 49000)
salaries.Add("Paul", 58000)
salaries.Add("Mary", 63000)
DEBUG "Paul makes $" + salaries.Get("Paul") + " per year\n"

salaries.Set("Paul", 67000)
DEBUG "Paul now makes $" + salaries.Get("Paul") + " per year\n"

IF NOT salaries.Exists("Slydog") THEN DEBUG "Slydog doesn't exist in employee table"

Dell Canada (maybe US or other countries also?) has the ST2220T Multi-touch monitor on sale for the day (the '12 Days of Dell' promotion):

Does anybody know if we could use this with GLBasic, and that GLBasic will pick up multiple mice / inputs?
If so, this would be great for testing multi-touch for tablet and phone games without having to install on the device each time. 
(It is supposed to work with the iOS simulator, but the simulator doesn't work with GLB)

Windows 7 has touch / multi-touch built in, but not sure of the limitations, it may be gesture only (pinch, two finger swipe, etc) and not report multiple 'mice' to the applications.

Also, a company I buy from frequently, has a keyboard with a multi-touch panel on sale, and was wondering the same question, if it would work with GLBasic:


GLBasic - en / Default TYPEs for Functions
« on: 2011-Aug-31 »
I noticed in the latest logfile:
Code: GLBasic [Select]
// 10.090
   // Compiler:
   //    Can assign types when declaring:
   //      LOCAL foo as Tfoo = bar

Are we closer to be able to use this in FUNCTION parameters for specifying a default for TYPE variables, such as:
Code: GLBasic [Select]
GLOBAL fontDefault AS TFont
GLOBAL fontHeading AS TFont

FontPrint "Using the default font", 0, 0
FontPrint "This is for a heading", 0, 100, fontHeading

FUNCTION FontPrint: text$, x%, y%, font AS TFont = fontDefault

If not, it would be nice to have optional TYPE parameters by using a 'NULL' concept such as:
Code: GLBasic [Select]
FUNCTION FontPrint: text$, x%, y%, font AS TFont = NULL
    IF font = NULL THEN font = fontDefault

I created a dynamic 3d object in code for my game's title. 
(It's made from my maze drawing routines to look like a maze in the shape of my title)

I want this title model to move around (maybe based on touch or device tilting) the 3d world space.
It will move left / right / up / down, plus zoom in and out, rotate, etc (not decided exactly what yet).
Basically it will wander around inside a virtual 3d box.

Is there a way to tell if the entire model is within the view range (ie, can you see it)?
That way I can detect when it goes a bit out of view and change the direction to 'bounce' it back into view.
I can hard code this perhaps with trial and error, but constantly rotating it changes it's x/y screen edge boundaries so I wont know precisely when it goes off screen.

Here's a screen of how it looks now, it's tilted a bit to show what I mean.

[attachment deleted by admin]

3D / 3D Camera Rotation Based on Angles
« on: 2011-Jul-15 »
Is the current method of pointing the camera limited to a 'look at' location point?

My problem is when the player wants to turn around 180°.
Using the 'look at' method and interpolating the old location to the new location, the look point passes directly through the player creating a weird effect.  What I would like is to have the camera rotate around the player, so the 'look at' point path is circular.

I've been working on a custom camera system that lets you specify the camera rotation by using a y-axis rotation (yaw) and an x-axis rotation (pitch).  I'm keeping track of two variables (custom TYPE), 'look_cur' and 'look_dst', and each frame I adjust the camera rotation until it reaches the destination.  The rotation speed for yaw and pitch are specified also.

So far the yaw works great and I can now rotate around a point by specifying an angle.
I'm having a b**ch of a time with the pitch!   :blink:
I'm not even (yet?) considering adding 'roll'!

Could perhaps this feature be added directly to GLBasic as an alternative camera looking option?
You could simply specify the yaw, pitch, roll and the camera will point in that direction.

How, I don't know.  Currently I'm using SIN / COS.  Quaternions or matrices would be better I'm sure, but out of my scope.
If not, no problem.  I'll keep at it until it works.

Code Snippets / File Handling Custom Type
« on: 2011-Jul-07 »
I needed a break from my game, so I thought I'd contribute something.

Programming with so many languages, I hate having to learn each language's methods of doing things, such as file handling.
What I like to do is wrap those commands in a common wrapper, so my code appears similar across the different languages.

Here's my attempt at simplifying the file handling commands of GLB.
It only covers the basic types of file commands people need.

Code: GLBasic [Select]


        fh%                                             // File Handle
        name$                                   // File Name
        data$                                   // File Contents
        eof%                                    // File is at end?

        // Reads entire file into '$'.  Closes file when finished.
        // 'name$' > Optional.  Will use previously set '$' if blank.
        // Returns > [TRUE if succeed | FALSE if fail]
        FUNCTION Get%: name$=""
                IF name$ <> "" THEN$ = name$
      $ = ""
                LOCAL line$ = ""
                IF self.Open(FILE_MODE_READ)
                        WHILE ENDOFFILE(self.fh) = FALSE
                                READLINE self.fh, line$
                                INC$, line$
                        CLOSEFILE self.fh
                        LOG(">FIL:{TFile.Get$      } *** Error Getting File: [" +$ + "] ***")
                        RETURN FALSE
                RETURN TRUE

        // Reads the next line in an already opened file.  Closes file when end is reached.
        // Returns > [line data if succeed | 'EOF$' if fail]
        FUNCTION GetLine$:
                LOCAL line$
                IF ENDOFFILE(self.fh)
                        line$ = EOF$
                        self.eof = TRUE
                        self.eof = FALSE
                        READLINE self.fh, line$
                RETURN line$

        // Writes '$' to file
        // 'name$' > Optional.  Will use previously set '$' if blank.
        // Returns > [TRUE if succeed | FALSE if fail]
        FUNCTION Write%: name$=""
                IF name$ <> "" THEN$ = name$
                IF self.Open(FILE_MODE_WRITE)
                        WRITESTR self.fh,$
                        LOG(">FIL:{TFile.Write$    } *** Error Writting File: [" +$ + "] ***")
                        RETURN FALSE
                RETURN TRUE

        // Appends string to end of file.
        // 'extra$'> Data to append
        // 'name$' > Optional.  Will use previously set '$' if blank.
        // Returns > [TRUE if succeed | FALSE if fail]
        FUNCTION Append%: extra$, name$=""
                IF name$ <> "" THEN$ = name$
                IF self.Open(FILE_MODE_APPEND)
                        INC$, extra$
                        WRITESTR self.fh, extra$
                        LOG(">FIL:{TFile.Append$   } *** Error Appending File: [" + extra$ + "] ***")
                        RETURN FALSE
                RETURN TRUE

        // Opens file for procesing.
        // 'name$' > Optional.  Will use previously set '$' if blank.
        // Returns > [TRUE if succeed | FALSE if fail]
        FUNCTION Open%: mode%, name$=""
                IF name$ <> "" THEN$ = name$
                self.fh = GENFILE()
                self.eof = FALSE
                IF OPENFILE(self.fh,$, mode) = FALSE
                        LOG(">FIL:{TFile.Open      } *** Error Opening File: [" +$ + "] ***")
                        self.eof = TRUE
                        RETURN FALSE
                RETURN TRUE

        // Closes file to further processing
        FUNCTION Close%:
                IF self.fh >= 0 THEN CLOSEFILE self.fh
                self.fh = -1
                self.eof = TRUE

        // Checks if file exists
        // Returns > [TRUE if exists | FALSE if doesn't]
        FUNCTION Exists%:
                RETURN DOESFILEEXIST($)

        // Returns file name portion from a longer file path
        // eg. For a '$' of [C:/Programs/GLBasic/glbasic.exe] will return [glbasic.exe]
        // 'slash$' > Optional.  Folder Seperator.  Use if your path uses '\' instead. Uses '/' as default
        FUNCTION GetName$: slash$="/"
                LOCAL slash%
                slash = REVINSTR($, slash$)
                IF slash <=0 THEN RETURN$
                RETURN MID$($, slash+1)


FUNCTION LOG: message$, add_linefeed% = TRUE, show_time% = TRUE
        STATIC datetime% = -1                                                                   // Log timer
        LOCAL elapsed#                                                                                  // How much time has elapsed since program start (or manual reset)
        IF datetime < 0 THEN datetime = GETTIMERALL()                   // Initialize log timer if not already set
        IF message$ = "RESET" THEN datetime = GETTIMERALL()             // Manually reset log timer
        elapsed = (GETTIMERALL() - datetime) / 1000.0
        ?IFDEF WIN32
                //?IFDEF GLB_DEBUG
                IF show_time = TRUE THEN DEBUG "[" + FORMAT$(8, 3, elapsed) + "] "
                DEBUG message$
                IF add_linefeed THEN DEBUG "\n"

Here's some basic usages examples:

Code: GLBasic [Select]
LOCAL file AS TFile
LOCAL line$

// Read settings
IF NOT file.Get("settings.ini") THEN RETURN FALSE
DEBUG "Data:" +$

// Write new settings data$ = "abcdefghijklmnopqrstuvwxyz"
file.Append("ZYXWVUTSR", "settings.ini"

// Optional method by setting file name directly$ = "scores.txt"
IF NOT file.Exists() THEN RETURN FALSE$ = "slydog | 12345"
file.Append("glbasic | 12346")

// Parsing a file line by line
IF NOT file.Open(FILE_MODE_READ, "graphics/fonts/comics.dat") THEN RETURN FALSE  // Exit and return 'FALSE' if can't open

    line$ = file.GetLine$()
    IF file.eof THEN GOTO SKIP                                                                                  // End of file  -> safe usage of 'GOTO' IMO :)
    IF LEN(line$) <= 0 THEN GOTO SKIP                                                                   // Skip blank lines
    //  continue with parsing . . .
UNTIL file.eof

The sample code wasn't tested, so no guarantees! 
And other commands weren't tested much either, so let me know if I have any bugs.
(or calls to my other functions I didn't include!)

I included my 'LOG()' command too as I find it useful for logging errors or for debugging code.
Also, it shows the time in ms since the program started so could be useful for identifying bottlenecks.
Here's a typical Output window for my game using this command:
Code: GLBasic [Select]
[   0.001] >GFX:{Sprite_Load     } fn:[Graphics/skin.png] | id:[1]
[   0.117] >GFX:{Sprite_Load     } fn:[Graphics/preview.png] | id:[3]
[   0.120] >GFX:{Sprite_Load     } fn:[Graphics/Fonts/blambot_secret_origins_32x50.png] | id:[4]
[   0.182] >GFX:{Sprite_Load     } *** File NOT FOUND!: [Graphics/Fonts/Kartika.png]
[   0.183] >FIL:{TFile.Open      } *** Error Opening File: [Graphics/Fonts/Kartika.dat] ***
[   0.184] >FON:{TGlyph.New%     } *** Error Opening Glyph File: [Graphics/Fonts/Kartika.dat] ***
[   0.186] >GFX:{Sprite_Load     } fn:[Graphics/Skins/Skin_Basic.png] | id:[5]
[   0.207] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[240,200,  0] | uv:[187,178]-[227,218]
[   0.234] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[  0,  0,255] | uv:[  0,192]-[ 48,240]
[   0.235] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[  0,  0,255] | uv:[ 48,192]-[ 96,240]
[   0.237] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[  0,  0,255] | uv:[ 96,192]-[144,240]
[   0.238] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[  0,  0,255] | uv:[144,192]-[186,240]
[   0.243] >GAM:{State           } STATE_MENU_MAIN
[   3.643] >GAM:{State           } STATE_MENU_WORLD
[   6.510] >GUI:{Scroll.IsScroll } FLICK Touch . . .
[   6.626] >GUI:{Scroll.IsScroll } FLICK Canceled: Finger Off
[   6.631] >GUI:{MenuWorld Select} id:[1000] | name:[Easy Peasy]
[   6.634] >GAM:{State           } STATE_MENU_LEVEL
[   6.728] >MAZ:{Maze_Generate   } >>>
[   6.742] >MAZ:{Maze_Generate   } <<< Done!
[   6.771] >GFX:{ShaderLoad      } fn:[/TwistedMaze/]
[   6.887] >GFX:{ShaderLoad      } fn:[/TwistedMaze/]
[   8.770] >MAZ:{Maze_Generate   } >>>
[   8.777] >MAZ:{Maze_Generate   } <<< Done!
[   8.782] >GAM:{State           } STATE_PLAYING_MAZE
[   8.897] >GAM:{HudDraw         } stars:1
[  10.714] >GAM:{GLB_ON_QUIT     } Exiting >>>

GLBasic - en / Pointer to a TYPE instance
« on: 2011-Jun-02 »
I have a common TYPE (A) that I initialize an instance with values at the program start.
I have another TYPE (B) that has a member of the first TYPE (A).

I want to use my previously initialized instance of (A) in multiple instances of (B).
This works fine automatically when I assign an instance of (A) to the member of an instance of (B).
It just copies the current values from (A).

But if I change values in the source instance of (A), those changes aren't reflected in the instance of (B).
Plus (B) needs as much extra memory as the size of TYPE (A).

What I would love is the ability to declare the member inside TYPE (B) as a pointer to TYPE (A).
That way when I change the instance (A), instances of (B) automatically update.
Plus I don't need the extra memory, just enough to hold the address of instance (A).

Is there a way to do this now?
Even if I have to go INLINE C?

Whenever you run and quit your game, the IDE returns with the 'Jumps' tab selected.
I never use the 'Jumps' tab and this is very annoying.  (I live exclusively in the 'Files' tab since I have 25 source files)

It would be nice if the IDE remembered which tab you where in and returned to that same tab.
(or why is it even changing the tab to begin with?)

Actually I would prefer the 'Jumps' and 'Files' windows to be separate from each other.
I would use 'Jumps' more often if it were in front of me, but now I have to click on the tab first, then back to 'Files' when I'm done.
This may be difficult to do of course, but just throwing it out there in case other people agree, or it isn't that hard.

A good split for the windows could be to instead of having the 'Output' window fill the entire bottom, have it the same width as the editor window, and have the 'Jumps' / 'Files' window stretch to the bottom.  (Then you could display both 'Jumps' and 'Files' on top of each other.)

And, just curious about two other bugs that were previously recorded a while ago that are still occurring.
  • <CTRL>+<TAB> to switch code windows only works for a while but stops at some point (after running the game?)
    I'm suspecting this is because either the control receiving keyboard input isn't in focus or has changed somehow.

  • Incredibuild still isn't 100% working.  It still doesn't detect code changes to certain modules.  Interestingly it's always the same modules that it misses, while others it always picks up just fine. Now I'm experiencing a new situation where it says it is skipping a module (that has changed), but when I run the game it seems to have picked up the change.   

    I don't know how this is all handled and I'm surprised this is a problem, as you'd think the file date/time would always update and this would be a safe and accurate method to determine if a module has changed.

And all of these bugs seems to happen on both Windows XP 32bit and Windows 7 64bit.

Gernot, I know these may be difficult to pin down, since so few people are reporting these problems.  And, I don't know how to recreate them using a simple example, but I would be willing to send my entire project to you, as it could be a problem specific to my situation or setup.  (I can trust you right? ha  =D)

(ignore these smilies, just wanted to know if it looked like he was getting sick in the toilet:   :nw: :shit:     )

Here's the <SRCFILES> section of my project file (.gbap), which was manually ordered:
Code: GLBasic [Select]
      <FILE PATH=".\MazeBall.gbas" />
      <FILE PATH=".\__LIB\_Common.gbas" />
      <FILE PATH=".\__LIB\_File.gbas" />
      <FILE PATH=".\__LIB\_Font.gbas" />
      <FILE PATH=".\__LIB\_gCamera.gbas" />
      <FILE PATH=".\__LIB\_gEntity.gbas" />
      <FILE PATH=".\__LIB\_gMesh.gbas" />
      <FILE PATH=".\__LIB\_gShader.gbas" />
      <FILE PATH=".\__LIB\_gSprite.gbas" />
      <FILE PATH=".\__LIB\_GUI.gbas" />
      <FILE PATH=".\__LIB\_gVector.gbas" />
      <FILE PATH=".\__LIB\_Input.gbas" />
      <FILE PATH=".\__LIB\_Sound.gbas" />
      <FILE PATH=".\__LIB\_Tween.gbas" />
      <FILE PATH=".\__Menus\Menu_Main.gbas" />
      <FILE PATH=".\__Menus\Menu_World.gbas" />
      <FILE PATH=".\__Menus\Menu_Level.gbas" />
      <FILE PATH=".\__Menus\Menu_Play.gbas" />
      <FILE PATH=".\__Play\Play_Maze.gbas" />
      <FILE PATH=".\__Play\Game_Over.gbas" />
      <FILE PATH=".\LevelDraw.gbas" />
      <FILE PATH=".\LevelMain.gbas" />
      <FILE PATH=".\Maze.gbas" />
      <FILE PATH=".\Gizmos.gbas" />
      <FILE PATH=".\_Main.gbas" />

But, whenever I recompile in the latest version of GLBasic [ver 9.033] it immediately sorts those entries by file name (ignoring the folder name) and resaves the .gbap project file's <SRCFILES> section as:
Code: GLBasic [Select]
      <FILE PATH=".\MazeBall.gbas" />
      <FILE PATH=".\__LIB\_Common.gbas" />
      <FILE PATH=".\__LIB\_File.gbas" />
      <FILE PATH=".\__LIB\_Font.gbas" />
      <FILE PATH=".\__LIB\_gCamera.gbas" />
      <FILE PATH=".\__LIB\_gEntity.gbas" />
      <FILE PATH=".\__LIB\_gMesh.gbas" />
      <FILE PATH=".\__LIB\_gShader.gbas" />
      <FILE PATH=".\__LIB\_gSprite.gbas" />
      <FILE PATH=".\__LIB\_GUI.gbas" />
      <FILE PATH=".\__LIB\_gVector.gbas" />
      <FILE PATH=".\__LIB\_Input.gbas" />
      <FILE PATH=".\_Main.gbas" />
      <FILE PATH=".\__LIB\_Sound.gbas" />
      <FILE PATH=".\__LIB\_Tween.gbas" />
      <FILE PATH=".\__Play\Game_Over.gbas" />
      <FILE PATH=".\Gizmos.gbas" />
      <FILE PATH=".\LevelDraw.gbas" />
      <FILE PATH=".\LevelMain.gbas" />
      <FILE PATH=".\Maze.gbas" />
      <FILE PATH=".\__Menus\Menu_Level.gbas" />
      <FILE PATH=".\__Menus\Menu_Main.gbas" />
      <FILE PATH=".\__Menus\Menu_Play.gbas" />
      <FILE PATH=".\__Menus\Menu_World.gbas" />
      <FILE PATH=".\__Play\Play_Maze.gbas" />

The problem is that I NEED the files to be compiled in a specific order or I get errors when declaring variables of a TYPE that was defined in another project file later in the compile order.  The solution for that error that worked for me was to ensure that the file defining the TYPE was always before any files that declared variables of that TYPE.
Oh, and this isn't a problem with my version 8 GLBasic.

I guess a work around would be to name files alphabetically by the order I want them compiled!  :blink:

Bug Reports / 'UNDO' bug ([CTRL] + Z)
« on: 2010-Nov-30 »
I haven't seen anybody else mention this yet, and it's been happening for quite a while now.
A lot of times when I hit [CTRL] + Z (undo), instead of undoing the latest change, it actually undoes the latest two changes.
This is on multiple lines, which I know it shouldn't do.
It's like the undo buffer is brought back two steps instead of one.

It's not every time, but it happens about once a day.
This can be potentially dangerous because sometimes it may not be obvious and you may not notice the second undo.
Just wondering if anybody else experienced this?

GLBasic - en / OpenGL Vertice Count
« on: 2010-Nov-01 »
Is there a way to display the current vertice (or poly) count from OpenGL (or from GLBasic if it has that info)?

I'm adjusting the FAR CLIP limit of the camera in hopes of saving some polys on an iPhone.
But I don't know how to figure out the benefits if I don't know how many polys I'm saving.

Or it would also be handy for dynamic worlds.
You could limit how much detail to draw up until you reach your vertice limit for that device.

Simply counting the number of vertices/poly's in all my models doesn't help since I don't know what models will be clipped from view.

I know this wont be easy to do, but a nice feature would be the ability to 'auto hide' those tabs, similar to how Visual Studio does it for the Tool Box, Solution Explorer, Properties, etc.

Just a small vertical tab would be visible until you hover over it, then it would auto-expand displaying its contents.
It would be extra nice to be able to dock each tab separately to either side of the IDE.
And, each tab could either be Pinned to a side, or have an Auto-Hide option.

This would increase the real estate on the screen available for viewing / editing the code.

Or, it would at least be nice to have the 'Jumps' and 'Files' tabs to be both open at once without having to click each tab to change.  I use those two tabs frequently and its inconvenient to keep switching.
Either have them above/below each other with a splitter bar separating them, or have them beside each other (less real estate!), or one on each side of the IDE (with an option to choose how you want them displayed).

While we're at it, the 'Output' and 'Search' window at the bottom could also Auto-Hide!

And, does anybody else find the opening splash screen to be intrusive?  Its always an extra click to open your project because its in the way.  Just my opinion.

Ha, simple!  Just thought I'd throw this out there.

I'm very new to 3D programming, and therefore 3D math.

I'm creating a path system where you create a bunch of path points and have an object (model, camera) follow that path.
Its working fairly well and follows the path, interpolating the position between the current two points based on those points time duration.  (It's only a linear interpolating for now, no smoothing).

I'm trying to implement an 'Orient To Path' option that will rotate the object to face the direction of the current path.
(Similar to the camera's 'LookAt' parameters).
So the numbers I have are the path start point and end point, and they are stored in a 3D vector TYPE (x,y,z).
I can take the difference of the two (end point - start point) to end up with one vector (named 'v') starting at (0,0,0).

Now, I want to rotate my object to match that vector's angle/direction.
I have the 'Y' rotation working with this:
   v_rotate.y = ATAN(v.z, v.x)
But even that acts weird and I had to add 180 or something to get it working as expected.
I'm not even sure of the range of numbers ATAN returns?  (-180 to 180?)  So then just add 180?
Or I've seen this?:  v_rotate.y = MOD(ATAN(v.z, v.x), 360)

But, I can't even come close to getting the 'X' rotation working.  (And I figured out that the 'Z' rotation is needed too?)
I had thought that if I rotate on the 'Y' axis first so I'm heading in the desired direction, then I could just figure out my X angle (relative to the direction I'm facing, ie. how much to tilt up or down).  It would be the same formula somewhat.  But I think I've come to learn that the 'X' rotation is always 'World' aligned?  And I can't make it relative to the new 'Y' rotation?  I hope that makes sense.

I think I have to somehow figure out this 'Angle' for the 'X' rotation, and split that value between 'X' and 'Z' somehow?

I've tried this many times and have failed.  (I find so many 2D examples, but that third dimension is eluding me!)
Any ideas?


I have a library that I call from my main project module.
In this library, I set up some global constants.
Then, I tried to make another constant for a default value that assigns the value of one of the above constants, like this: (In Tween.gbas)
Code: GLBasic [Select]
CONSTANT kTweenTransition_Linear = 1
CONSTANT kTweenTransition_Spring = 2
CONSTANT kTweenTransition_Bounce = 3

CONSTANT kTweenTransition_Default = kTweenTransition_Spring

But, in my main module, when I refer to the constant that was assigned as default (kTweenTransition_Spring in this case), I get this C++ error:
error: `kTweenTransition_Spring' was not declared in this scope.

But when I assign a hard coded value like:
Code: GLBasic [Select]
CONSTANT kTweenTransition_Default = 2
everything works fine.

It seems assigning a constant to a constant causes the original constant to be declared earlier in the compiling cycle?

Or never gets created in the first place? 
Or because it would have been created as a standard float in the first pass?

Pages: [1] 2