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:
fac1% // factor 1
fac2% // factor 2
icorrect% // what result is the correct one? (Note to self: only one solution must be right!)
results% // results to print
string$ // question (equation) string
And a type for the frog data
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
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:
FOR i=0 TO nof_tasks%-1
LOCAL li AS Tline
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
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
DIMPUSH gLines, li
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) ,
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.
My render loop was like:
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
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
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...
frog.jumpto = -1 // able to touch new pad
ELSE // can jump
FOR im% = 0 TO GETMOUSECOUNT()-1 // multitouch
LOCAL mx%, my%, b1%, b2%
MOUSESTATE mx, my, b1, b2 // rotated mouse coords - YAY!
// computer player - play stupid random
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
SELECT gDifficulty% // pick time for next jump action
frog.cpu_counter = GETTIMERALL() + 4000
frog.cpu_counter = GETTIMERALL() + 2110
frog.cpu_counter = GETTIMERALL() + 1000
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.jumpto = isel // pad to jump to
// draw offscreen game playfield to sprite 100
// paste game for player
DRAWSPRITE 100+ifrog, 0, h
// shade out computer player
The drawing code is as simple as:
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]
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)
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
jump = SIN(jump*180)*0.8 // jump to water
ELSE // and back
jump = (1. - jump) * 2* 0.8 // move back to old pad
LOCAL xf2 = X_of_pad(frog.jumpto, w)
xfrog = xf2 * jump + xfrog*(1.0-jump)
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
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
IF yd > 48 AND y>0 THEN Center(line.string$, w/2, yd-24, TRUE) // question
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
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.