floppy is a micro game engine for beginners written in javascript.
- it has basic primitives for drawing (rectangles, circles, lines, text)
- it includes interaction (click, tap)
- it includes sound (generated)
- it comes with a (simple) web editor online and ...
- ... with a tutorial to write your first game (simple, but complete)
- Step 0
- Step 1 - Coloring Background
- Step 2 - Drawing Pad
- Step 3 - Drawing Ball
- Step 4 - Moving Ball
- Step 5 - Bounching Right
- Step 6 - Bounching Left
- Step 7 - Bounching Top
- Step 8 - Moving Pad
- Step 9 - Bounching Pad
- Step 10 - Restarting Ball
- Step 11 - Adding Score
- Step 12 - Adding Lifes
- Step 13 - Adding Challenge
- Step 14 - Adding Sound
- Step 15 - Publishing Online
In this tutorial we will create a squash like game.
When opening the web editor default code include two functions (update and render) with empty body.
functionupdate(){}functionrender(){}What it is inside those functions it is executed continuosly (about 60 times each second). First the update function, then the render function.
In order to color the whole game window with a light blue, we add a Rect command to the render function.
functionupdate(){}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");// NEW}WIDTH is a constant = 320 = the whole width of game window
HEIGHT is a constant = 480 = the whole height of game window
To draw the pad, first we define four new variables (padX, padY, padW, padH) and then add a Rect command (using those variables).
padX=100;// NEWpadY=430;// NEW padW=120;// NEWpadH=15;// NEWfunctionupdate(){}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");// NEW}To draw the ball, first we define its variables (ballX, ballY, ballR) and then add a Circle command (using those variables).
padX=100;padY=430;padW=120;padH=15;ballX=160;// NEWballY=70;// NEWballR=16;// NEWfunctionupdate(){}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");// NEW}To move the ball we update its position (ballX, ballY).
Three new variables are defined in order to manage the update (dirX, dirY, speed).
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;// NEWdirY=1;// NEWspeed=2;// NEWfunctionupdate(){ballX=ballX+dirX*speed;// NEWballY=ballY+dirY*speed;// NEW}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");// NEW}When the ball reaches the right border of game window we set dirX to -1 in order to have the ball bounching.
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;functionupdate(){if(ballX+ballR>WIDTH){// NEWdirX=-1;// NEW}// NEW ballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");}When the ball reaches the left border of game window we set dirX to 1 in order to have the ball bounching.
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;functionupdate(){if(ballX+ballR>WIDTH){dirX=-1;}if(ballX<ballR){// NEWdirX=1;// NEW }// NEWballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");}When the ball reaches the top border of game window we set dirY to 1 in order to have the ball bounching.
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;functionupdate(){if(ballX+ballR>WIDTH){dirX=-1;}if(ballX<ballR){dirX=1;}if(ballY<ballR){// NEWdirY=1;// NEW}// NEWballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");}To move the pad, when TAPPED is true, we set the position of the pad taking the x position of our tapping (TAPX).
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;functionupdate(){if(ballX+ballR>WIDTH){dirX=-1;}if(ballX<ballR){dirX=1;}if(ballY<ballR){dirY=1;}if(TAPPED){// NEWpadX=TAPX-padW/2;// NEW}// NEWballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");}When the ball reaches the pad we set dirY to -1 in order to have the ball bounching on the pad.
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;functionupdate(){if(ballX+ballR>WIDTH){dirX=-1;}if(ballX<ballR){dirX=1;}if(ballY<ballR){dirY=1;}if(TAPPED){padX=TAPX-padW/2;}if((ballX>padX)&&(ballX<padX+padW)&&(ballY+ballR>padY)){// NEWdirY=-1;// NEW}// NEWballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");}If the ball crosses the pad games is restarted from initial position.
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;functionupdate(){if(ballX+ballR>WIDTH){dirX=-1;}if(ballX<ballR){dirX=1;}if(ballY<ballR){dirY=1;}if(TAPPED){padX=TAPX-padW/2;}if((ballX>padX)&&(ballX<padX+padW)&&(ballY+ballR>padY)){dirY=-1;}if(ballY+ballR>padY+padH){// NEWballX=160;// NEWballY=70;// NEW}ballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");}In order to manage and show the score, we add a new variable (score) and we use the Text command in the render function.
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;score=0;// NEWfunctionupdate(){if(ballX+ballR>WIDTH){dirX=-1;}if(ballX<ballR){dirX=1;}if(ballY<ballR){dirY=1;}if(TAPPED){padX=TAPX-padW/2;}if((ballX>padX)&&(ballX<padX+padW)&&(ballY+ballR>padY)){dirY=-1;score=score+10;// NEW}if(ballY+ballR>padY+padH){ballX=160;ballY=70;}ballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");Text(score,50,50,30,"green");// NEW}To manage multiple lifes, we add a new variable (lifes). We set it initially to 3 and we subtract 1 every time we lose a ball.
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;score=0;lifes=3;// NEWfunctionupdate(){if(ballX+ballR>WIDTH){dirX=-1;}if(ballX<ballR){dirX=1;}if(ballY<ballR){dirY=1;}if(TAPPED){padX=TAPX-padW/2;}if((ballX>padX)&&(ballX<padX+padW)&&(ballY+ballR>padY)){dirY=-1;score=score+10;}if(ballY+ballR>padY+padH){ballX=160;ballY=70;lifes=lifes-1;// NEW}if(lifes==0){// NEWspeed=0;// NEW}// NEWballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");Text(score,50,50,30,"green");Text(lifes,250,50,30,"red");// NEW}To add a challenge in the game, we increase the ball speed by 0.2 at each bounch on the pad.
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;score=0;lifes=3;functionupdate(){if(ballX+ballR>WIDTH){dirX=-1;}if(ballX<ballR){dirX=1;}if(ballY<ballR){dirY=1;}if(TAPPED){padX=TAPX-padW/2;}if((ballX>padX)&&(ballX<padX+padW)&&(ballY+ballR>padY)){dirY=-1;score=score+10;speed=speed+0.2;// NEW}if(ballY+ballR>padY+padH){ballX=160;ballY=70;lifes=lifes-1;}if(lifes==0){speed=0;}ballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");Text(score,50,50,30,"green");Text(lifes,250,50,30,"red");}Last enrichment. We add a sound (generic) when bounching using the SoundEffect command.
padX=100;padY=430;padW=120;padH=15;ballX=160;ballY=70;ballR=16;dirX=1;dirY=1;speed=2;score=0;lifes=3;functionupdate(){if(ballX+ballR>WIDTH){dirX=-1;SoundEffect();//}if(ballX<ballR){dirX=1;SoundEffect();//}if(ballY<ballR){dirY=1;SoundEffect();//}if(TAPPED){padX=TAPX-padW/2;}if((ballX>padX)&&(ballX<padX+padW)&&(ballY+ballR>padY)){dirY=-1;score=score+10;speed=speed+0.2;SoundEffect();//}if(ballY+ballR>padY+padH){ballX=160;ballY=70;lifes=lifes-1;}if(lifes==0){speed=0;}ballX=ballX+dirX*speed;ballY=ballY+dirY*speed;}functionrender(){Rect(0,0,WIDTH,HEIGHT,"lightblue");Rect(padX,padY,padW,padH,"green");Circle(ballX,ballY,ballR,"red");Text(score,50,50,30,"green");Text(lifes,250,50,30,"red");}We can finally publish the game using HTML Pasta.
Here the game published online. Game.
functionupdate(){};functionrender(){};Rect(x,y,w,h,"color");Circle(x,y,r,"color");Text("text",x,y,size,"color");Line(x_init,y_init,x_end,y_end,width,"color");WIDTHHEIGHTTAPPEDTAPXTAPYSoundEffect(frequencyValue,attack,decay,type,volumeValue,panValue,wait,pitchBendAmount,reverse,randomValue,dissonance,echo,reverb,timeout);more information @: https://github.com/kittykatattack/sound.js#generating-sound-effects-and-music
Random(min,max);// Random integer from min to maxPitchToFrequency(octave,semitone);// Frequency from octave (integer), semitone (integer)with:
- live preview
- syntax warnings
- saving game
- exporting game
- using offline
For game engine: How To Design A Mobile Game With HTML5
For approach: pico-8
For sound: sound.js







