BASIC

Author Topic: How to make a "game" in 3.5 hours  (Read 7604 times)

Offline Kitty Hello

  • code monkey
  • Administrator
  • Prof. Inline
  • *******
  • Posts: 10714
  • here on my island the sea says 'hello'
    • View Profile
    • http://www.glbasic.com
How to make a "game" in 3.5 hours
« on: 2011-Jul-08 »
Hi,

Here's the story about how I created this game:

http://www.glbasic.com/forum/index.php?topic=6556.0
in about 3.5 hours, totally. Don't be fooled that this is regular time when you start programming. I'm a quite skilled coder for over 20 years now and coding is my day time job.

The whole thing started when I was asked by my wife to train the kid in math (basic times table 1x1 to 10x10). It was late, kid was in bed, so I decided to make an App for her to play when she awakes.

So, starting at Friday, 10pm, my first thought was, that it would have to be a game that's somehow nice to play, with slightly cute visuals and a 2 player mode, because I think it's really important that we share time with our kids, rather than with programming for the kids.

I always wanted to make some sort of "RISK" game with multiplications instead of the dice. But that's rather hard to do in a short time and I don't have the right theme for that game to tie a girl to it, yet. In fact, writing games for girls is a very complex part. They usually don't like competitions, but variety, decorations and style. In the final game, e.g., I put an option to change the colour of the main game's frame for each player. When my girl can choose pink or purple, the game attracts her and thus she's also willing to have a play.

So, step 1 was a quick browsing the internet about multiplication games. There was some penguin game I found rather interesting http://www.multiplication.com/flashgames/PenguinJump.htm which I think could easily be done in a few hours.
The game is about getting a question asked as "5x4", and you have to tap the right answer to proceed.

But a direct copy? No - too boring. Also, penguin animations look boring. I needed a player that can be easier animated but look kinda cute. If the first impression rejects a kid, you have a hard time convincing them of the better. All that led me to the frog/pond theme.

Kicking CorelDraw (I really love Corel - get some time to learn it, it's worth millions. Or use Inkscape if you want something free) I started a frog:
[see attachment 1]

1. 3 ellipses for mouth and eyes
2. 2 more ellipses for an eye. Duplicated the eye (Ctrl+D) and moved it to the right -> 2 eyes
3. Sketched the legs -> just 3 ellipses. For the foot 2 ellipses.
4. selected both foot parts and used the "rear without front" clipping tool -> a foot. Duplicated and mirrored the leg -> a frog.
5. Selected the belly and used "filling - gradient". Made a radial (sphere like) light to dark gradient. Then selected belly, right click+drag to head, selecting "copy filling here" -> filled head. Selected all other limbs and played with solid colour until I was satisfied with the frog.
6. Selected the full leg. Clicked it again (toggles from "move" to "rotate" mode in Corel). Moved the pivot point to the upper limb, then rotated the whole leg. Did the same procedure for the shinbone and foot.
7. I wanted the frog as an animation for LOADANIM instead of multiple images. So, I drew a rectangle around my frog, turning the GRID OPTION on. Then duplicated the rectangle plus frog and placed it (with the grid) right aligned to that box. Pressing "Ctrl+D" (Duplicate) again, it will offset the duplicate to the right of the 2nd box and so on.
8. I now have a set of framed frogs. I quickly animated them. Later, I select the frames and disable the border and filling (making them transparent, but do export them so I have a proper tile set for LOADANIM :)

When making graphics for a quick game, make something that looks nice or you will lose interest. But don't go too much into detail or you will never see light at the end of the tunnel.

The lily was even easier. A circle, overlaid with a triangle. Deformed the triangle to look more "alive", then subtract it from the circle. Draw a light green ray (using duplicate, rotated the clone and duplicate to get more rotated parts). Now all light green striped had a black outline. So I duplicate the circle and intersect it with the rays. Grouping them with the circle and removing the border for that group gave me a lily with no border at all. Then I used the duplicated circle as an overlay with transparent filling and black border.

I'm not much of a graphics guy, so I did not bother to animate the lily. I'll talk about animations, later.

Afterwards I copied all together on a blue/lightblue scene to get an icon and a title screen.

Next I needed the title font. I chose Indiana Jones one, because it was late and it should be some sort of "Adventure" game. Googled for a font that suits my needs and also that gradient colours. Easy.

11:30pm - oh my, it's late. Now hurry with the code.

I made a TYPE with the lines to print and calculate:
Code: GLBasic [Select]
TYPE Tline
        fac1% // factor 1
        fac2% // factor 2

        icorrect% // what result is the correct one? (Note to self: only one solution must be right!)
        results%[4] // results to print

        string$ // question (equation) string
ENDTYPE
 

And a type for the frog data
Code: GLBasic [Select]
TYPE Tfrog
        iline% = -1 // what line am I on?
        ipad%  = 2 // what pad (x-direction) am I on?
        jumpto%= 2  // pad to jump to. -1 -> wait for input

        correct%=TRUE // was the last answer correct? If not -> jump back

        jump#=0.1 // 0..1 -> jumping animation. >1 -> landed on new pad. Set jumpto% to -1

        col_on_map% = 0x0000ff // colour on the map to see who's heading

        is_computer%  =FALSE // is this player computer controlled? (Wait for a second, then jump)

        cpu_counter% = 4000 // gettimerall until next move of computer player
ENDTYPE
 

OK, then I had to fill the math equations for the lines. That ended out pretty hard to do properly, because I didn't want duplicates nor numbers from 1xN or 10xN. My method was just to check against these cases and if so, just GOTO and retry with new RND values. For such ideas it's important to see you're not running into a dead-end loop!
Next was to create fake-results. That was hard, because they should be sort of "neighbor" results.
So, I took this:

Code: GLBasic [Select]
        FOR i=0 TO nof_tasks%-1
                LOCAL li AS Tline
                @again:
                li.fac1 = RND(7) // 2..9 (1x and 10x is stupid)
                li.fac2 = RND(7)

                // don't repeat the same equation
                FOREACH ol IN gLines[]
                        IF  (ol.fac1 = li.fac1+1 AND ol.fac2=li.fac2+1) OR _
                                (ol.fac2 = li.fac1+1 AND ol.fac1=li.fac2+1) THEN GOTO again
                NEXT

                LOCAL fake1% = MOD(MAX(li.fac1%, li.fac2) + 8 - (1+RND(1) ), 8)
                LOCAL fake2% = MOD(MIN(li.fac1%, li.fac2) + 1 + RND(1)     , 8)

                INC li.fac1, 2
                INC li.fac2, 2
                INC fake1
                INC fake2
   DIMPUSH gLines[], li
   ...
   NEXT
 

I chose a RND from 0 to 7, and will add 2 later. So I have 2...9 for fac1 and fac2. Fine.
The "fake" results are just a RND of 1..2 offset. Once offset to the positive:
MOD(MIN(li.fac1%, li.fac2) + 1 + RND(1)     , 8)
which is basically:
fake1 = min(fac1, fac2) + 1 + RND(1)
if fake1 > 7 then fake1 = fake1-7 // limit to 0..7

The lower part is a bit trickier, because a negative number returns a nagative MOD, thus I added the mod value before to get a positive value:
MOD(  0 - RND(10) + 10, 10) <- RND gets negative. Adding 10 will make it positive again, mode 10 will cut overflow.

Next I made a function to display the whole "pond" on a given screen size, because I wanted it to be slightly scaleable. I made a sprite (ID=100) and used that sprite with CREATESCREEN to draw the whole "game" onto it.
That way I was able to draw and rotate the whole playfield for each player.
I could use polyvector to do that, or ROTOSPRITE, but that would have caused headache for the touch positions of the 2nd player, later.

And it was already 0:15am.
So I thought really hard and found, that you can use SETROTATION within a SHOWSCREEN loop to render both frog parts.
Yippieh!
My render loop was like:
Code: GLBasic [Select]
        WHILE TRUE
                FOR ifrog% = 0 TO LEN(gFrogs[])-1
                        ALIAS frog AS gFrogs[ifrog]

                        // silly trick to handle rotated screens
                        // current frog is at bottom of screen
                        SELECT ifrog
                        CASE 0
                                SETORIENTATION 0
                        CASE 1
                                SETORIENTATION 2
                        ENDSELECT

                        IF frog.jumpto >= 0
                                IF frog.correct // jump further if already jumping
                                        INC frog.jump, 0.02
                                ELSE // jump further, but slower
                                        INC frog.jump, 0.01
                                ENDIF
                                IF frog.jump>=1.0 // reached end of jump
                                        frog.jump=0.0 // reset jump
                                        IF frog.correct% // was fine?
                                                INC frog.iline // move to that line
                                                frog.ipad = frog.jumpto // set to that pad

                                                // check for game over...
                                        ENDIF
                                        frog.jumpto = -1 // able to touch new pad
                                ENDIF

                        ELSE // can jump
                                FOR im% = 0 TO GETMOUSECOUNT()-1 // multitouch
                                        SETACTIVEMOUSE im
                                        LOCAL mx%, my%, b1%, b2%
                                        MOUSESTATE mx, my, b1, b2 // rotated mouse coords - YAY!

                                        // computer player - play stupid random
                                        IF frog.is_computer
                                                b1=FALSE
                                                IF frog.cpu_counter% < GETTIMERALL() AND frog.iline < LEN(gLines[])-1 // time to jump
                                                        mx = gLines[frog.iline+1].icorrect% * w/5 + w/10 + 4 // tap position for correct result
                                                        my=h  + 200 // below center of screen
                                                        b1=TRUE
                                                        SELECT gDifficulty% // pick time for next jump action
                                                                CASE 0
                                                                        frog.cpu_counter = GETTIMERALL() + 4000
                                                                CASE 1
                                                                        frog.cpu_counter = GETTIMERALL() + 2110
                                                                CASE 2
                                                                        frog.cpu_counter = GETTIMERALL() + 1000
                                                        ENDSELECT
                                                ENDIF
                                        ENDIF

                                        IF b1 // tapped
                                                IF my > h // bottom of screen (where this frog is)

                                                        LOCAL isel% = (mx-w/10)*5 / w   // selected pad

                                                        // valid point
                                                        IF isel>=0 AND isel<=3
                                                                ALIAS line AS gLines[frog.iline+1]
                                                                IF isel = line.icorrect%
                                                                        frog.correct=TRUE
                                                                ELSE
                                                                        frog.correct=FALSE
                                                                ENDIF
                                                                frog.jumpto = isel // pad to jump to
                                                        ENDIF
                                                ENDIF
                                        ENDIF
                                NEXT
                                SETACTIVEMOUSE 0
                        ENDIF

                        // draw offscreen game playfield to sprite 100
                        MakePlayfield(ifrog)

                        // paste game for player
                        DRAWSPRITE 100+ifrog, 0, h

                        // shade out computer player
                        IF frog.is_computer
                                ALPHAMODE -0.4
                                DRAWRECT 0,h,w,h,0
                                ALPHAMODE 0
                        ENDIF
                NEXT
 

The drawing code is as simple as:
Code: GLBasic [Select]
FUNCTION MakePlayfield%: ifrog%
LOCAL w%, h%
        GETSPRITESIZE 100, w%, h% // get viewport size - render to sprite

LOCAL yblock# = h/2 // distance of each line to draw
        ALIAS frog AS gFrogs[ifrog]

        USESCREEN 0
                LOCAL ianim = 0
                LOCAL jump = 0
                LOCAL xfrog = X_of_pad(frog.ipad, w) // get x pixel position of pad(i)

                IF frog.jumpto <> -1 // frog is jumping
                        jump = frog.jump
                        IF frog.correct% // direct jump to next pad
                                jump = SIN(jump*90)
                                ianim=1
                                LOCAL xf2 = X_of_pad(frog.jumpto, w)
                                xfrog = xf2 * jump + xfrog*(1.0-jump)
                        ELSE // jump to next pad - then jump back
                                IF jump < 0.5 // to pad
                                        ianim=1
                                        jump = SIN(jump*180)*0.8 // jump to water
                                ELSE // and back
                                        ianim=2
                                        jump = (1. - jump) * 2* 0.8 // move back to old pad
                                ENDIF
                                LOCAL xf2 = X_of_pad(frog.jumpto, w)
                                xfrog = xf2 * jump + xfrog*(1.0-jump)
                        ENDIF
                ENDIF

                LOCAL dx = (64-w/4)/2 - 64/2// offset of water lily to draw position


                LOCAL ty% = (frog.iline + jump) * (yblock*2) + SIN(GETTIMERALL()/10) * 8 // make the water move slightly
                LOCAL tx% = COS(GETTIMERALL()/25) * 16

                STARTPOLY 1, 2 // fill background with water seamless texture
                POLYVECTOR 0,0, tx, ty,   0xffffff
                POLYVECTOR 0,h, tx, ty+h, 0xffffff
                POLYVECTOR w,0, w+tx, ty,   0xffffff
                POLYVECTOR w,h, w+tx, ty+h, 0xffffff
                ENDPOLY

                FOR y% = 0 TO LEN(gLines[])-1 // draw the lines to jump to
                        ALIAS line AS gLines[y%]

                        LOCAL yd% = (y - frog.iline - jump)*yblock + 48 // where to draw that line
                        IF yd+64 < 0 THEN CONTINUE // upper bounds of screen
                        IF yd > h THEN BREAK // lower bounds of screen - ignore all below that

                        FOR i%=0 TO 3 // 4 pads
                                LOCAL xd% = X_of_pad(i, w)
                                DRAWSPRITE 2, xd, yd // draw lily
                                IF yd > 48 AND y>0 THEN Center( line.results[i], xd+32, yd+28, TRUE) // print result option
                        NEXT
                        IF yd > 48 AND y>0 THEN Center(line.string$, w/2, yd-24, TRUE) // question
                NEXT

                LOCAL yfrog = 48 - SIN(jump*180)*60
                IF gGameover% AND frog.iline <> LEN(gLines[]) -1 THEN ianim = 2 // sad face
                DRAWANIM 10, ianim, xfrog, yfrog

        USESCREEN -1
ENDFUNCTION
 

As you can see, this is already the final drawing routine. I started with drawing blue water background, the lilys and the frog. Then added the frog-jump-animations and movements. I'm quite a fan of sin/cos, so I used that for the jumping curve. There's a tutorial about how to use that on the forums as well: http://www.glbasic.com/forum/index.php?topic=1389.0
When the game was working I started to beatify the output with e.g. the sin/cos movement of the background water.
As said, I'm not much of a graphics guy. But when you start a game usually looks very static. Making some objects shake in little ellipses often helps to get a game vital visual very easily. Just add dx, dy to your x,y, positions: dx=sin(gettimerall()/10)*max_amplitude; dy=cos(gettimerall()/10)*max_amplitude. If you make the "/10" for sin and cos different, it shakes in a S-shape. Just play with it.

OK, it was 0:50 when the program run fine. I tried to install on my iPhone, but failed because of the code sign process. 1:30, tired to the grave, I picked the Palm Pre2. Copy, run - but... the frog2 acted as if it was frog1. GAH!

Too late to try further - I got some sleep.

Next day, painting the carport the idea came - the bitmap of the offscreen surface must be wrong. After investigating I found out, that using USESCREEN X, then -1 to dar and then X again, it would not work - it's on my TODO list. To fix that, I used a unique texture for each frog's display. Phew.
1:30pm on Saturday I got me some paper for quick bug notes and feature ideas and we had the first match. I lost. Obviously. How was I to think I could beat a 3rd grade kid in maths :/

All in all, I was quite reassured that GLBasic was a good idea to start, because I don't think I would have made it in this time with any other tool. I also see, that when GLBasic has a bug (offscreen can't be used twice in one loop e.g.) it really sucks for the developers. I'm really sorry for any bugs you find and report and will do my best to get them fixed ASAP also for future releases.
















[attachment deleted by admin]
« Last Edit: 2011-Jul-08 by Kitty Hello »

MrTAToad

  • Guest
Re: How to make a "game" in 3.5 hours
« Reply #1 on: 2011-Jul-08 »
Very good - would be good to link Twitter & Facebook to this - people could find it useful!

Offline Ian Price

  • Administrator
  • Prof. Inline
  • *******
  • Posts: 4147
  • On the shoulders of giants.
    • View Profile
    • My Apps
Re: How to make a "game" in 3.5 hours
« Reply #2 on: 2011-Jul-08 »
A really nice walk over. Cheers :)
I came. I saw. I played.

Offline Hark0

  • Prof. Inline
  • *****
  • Posts: 1020
  • Geek Developer
    • View Profile
    • LitioPixel - Desarrollo de videojuegos con GLBasic | Videogame development with GLBasic
Re: How to make a "game" in 3.5 hours
« Reply #3 on: 2011-Jul-08 »
Very good!

I post a new + link to this topic in my blog...
 :good:
http://litiopixel.blogspot.com
litiopixel.blogspot.com - Desarrollo videojuegos Indie · Pixel-Art · Retroinformática · Electrónica Development Indie Videogames · Pixel-Art · Retrocomputing · Electronic

Offline bigsofty

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 2628
    • View Profile
Re: How to make a "game" in 3.5 hours
« Reply #4 on: 2011-Jul-08 »
Yep, I have to agree, very nice dev log of your mini game.  :good:
Cheers,

Ian.

“It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration.”
(E. W. Dijkstra)

Offline Schranz0r

  • Premium User :)
  • Administrator
  • Prof. Inline
  • *******
  • Posts: 5022
  • O Rly?
    • View Profile
Re: How to make a "game" in 3.5 hours
« Reply #5 on: 2011-Jul-09 »
And another 3.5 h to write this tutorial :)
I <3 DGArray's :D

PC:
AMD Ryzen 7 1700 @3.9GHz, 16GB HyperX Fury 3000MHz Ram, ASUS ROG GTX 1060 STRIX 6GB, Windows 10 Pro 64Bit, MSi Tomahawk B350 Mainboard

Offline Moru

  • Administrator
  • Prof. Inline
  • *******
  • Posts: 1775
    • View Profile
    • Homepage
Re: How to make a "game" in 3.5 hours
« Reply #6 on: 2011-Jul-09 »
Documentation is important :-)

Offline matchy

  • Prof. Inline
  • *****
  • Posts: 1543
    • View Profile
Re: How to make a "game" in 3.5 hours
« Reply #7 on: 2011-Jul-09 »
It is cool to make a quick game, like in an evening, day or even weekend. The competitions are good motivations for this also and I wish there's another one coming soon. ;)

Usually, I find myself creating prototypes rather than a fully commercial product and sometimes it could just be colored squares to be filled in later with sprites later as they are not required initally. Of course, it also really depends on the planning, for example; in this case with the frog, it could be all be drawn and saved on to a bitmap within glb (polys). The advantage, for example, would be that user frog skin colours can be customized.

Offline Slydog

  • Prof. Inline
  • *****
  • Posts: 930
  • KodeSource
    • View Profile
    • KodeSource
Re: How to make a "game" in 3.5 hours
« Reply #8 on: 2011-Jul-11 »
matchy: "The competitions are good motivations for this also and I wish there's another one coming soon."

I second that!
I need a diversion from my game!

Ha, how about a "Program a game in 3.5 hours" competition?!  :D
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

Offline XanthorXIII

  • Mr. Polyvector
  • ***
  • Posts: 197
    • View Profile
Re: How to make a "game" in 3.5 hours
« Reply #9 on: 2011-Jul-11 »
Very nice Gernot. This is something I want to do for my nephew when I get the time to create something like this.
I like inspirational stuff. Keeps me going and using GLBasic.
Owlcat has wise

Offline codegit

  • Dr. Type
  • ****
  • Posts: 270
    • View Profile
Re: How to make a "game" in 3.5 hours
« Reply #10 on: 2011-Jul-12 »
 8)
------------------------------------------
1 X Acer TravelMate 4270, laptop, XP PRO
1 X Dell Studio 17 laptop, Windows 7
1 X MacBook Pro 2,2 GHz Core 2 Duo, 2 GB RAM, 160 GB HDD, 9400M
2 X iTouch
1 X HTC Desire (Android 2.1)
iPad soon to be added