├── README.md └── images ├── circle.png ├── rect.png ├── step0.png ├── step1.png ├── step2.png ├── step3.png ├── step4.png ├── step5.png ├── step8.png └── test.md /README.md: -------------------------------------------------------------------------------- 1 | # :floppy_disk: floppy :floppy_disk: 2 | 3 | *floppy* is a micro game engine for beginners written in javascript. 4 | 5 | * it has basic primitives for drawing (rectangles, circles, lines, text) 6 | * it includes interaction (click, tap) 7 | * it includes sound (generated) 8 | * it comes with a (simple) web editor online and ... 9 | * ... with a tutorial to write your first game (simple, but complete) 10 | 11 | ## Index 12 | 13 | [Tutorial](#tutorial) 14 | * [Step 0](#step-0) 15 | * [Step 1 - Coloring Background](#step-1---coloring-background) 16 | * [Step 2 - Drawing Pad](#step-2---drawing-pad) 17 | * [Step 3 - Drawing Ball](#step-3---drawing-ball) 18 | * [Step 4 - Moving Ball](#step-4---moving-ball) 19 | * [Step 5 - Bounching Right](#step-5---bounching-right) 20 | * [Step 6 - Bounching Left](#step-6---bounching-left) 21 | * [Step 7 - Bounching Top](#step-7---bounching-top) 22 | * [Step 8 - Moving Pad](#step-8---moving-pad) 23 | * [Step 9 - Bounching Pad](#step-9---bounching-pad) 24 | * [Step 10 - Restarting Ball](#step-10---restarting-ball) 25 | * [Step 11 - Adding Score](#step-11---adding-score) 26 | * [Step 12 - Adding Lifes](#step-12---adding-lifes) 27 | * [Step 13 - Adding Challenge](#step-13---adding-challenge) 28 | * [Step 14 - Adding Sound](#step-14---adding-sound) 29 | * [Step 15 - Publishing Online](#step-15---publishing-online) 30 | 31 | [Commands](#commands) 32 | 33 | [Editor](#editor) 34 | 35 | [Thanks and Inspiration](#thanks-and-inspirations) 36 | 37 | ## Tutorial 38 | 39 | In this tutorial we will create a squash like game. 40 | 41 | ### Step 0 42 | 43 | When opening the web editor default code include two functions (update and render) with empty body. 44 | 45 | #### Code 46 | 47 | ```javascript 48 | function update() { 49 | 50 | } 51 | function render() { 52 | 53 | } 54 | ``` 55 | 56 | What it is inside those functions it is executed continuosly (about 60 times each second). 57 | First the update function, then the render function. 58 | 59 | #### Result 60 | 61 | 62 | 63 | ### Step 1 - Coloring Background 64 | 65 | In order to color the whole game window with a light blue, we add a [Rect](#rect) command to the render function. 66 | 67 | #### Code 68 | 69 | ```javascript 70 | function update() { 71 | 72 | } 73 | function render() { 74 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); // NEW 75 | } 76 | ``` 77 | 78 | WIDTH is a constant = 320 = the whole width of game window 79 | 80 | HEIGHT is a constant = 480 = the whole height of game window 81 | 82 | 83 | #### Result 84 | 85 | 86 | 87 | ### Step 2 - Drawing Pad 88 | 89 | To draw the pad, first we define four new variables (padX, padY, padW, padH) and then add a Rect command (using those variables). 90 | 91 | #### Code 92 | 93 | ```javascript 94 | padX=100; // NEW 95 | padY=430; // NEW 96 | padW=120; // NEW 97 | padH=15; // NEW 98 | function update() { 99 | 100 | } 101 | function render() { 102 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 103 | Rect(padX,padY,padW,padH,"green"); // NEW 104 | } 105 | ``` 106 | 107 | #### Result 108 | 109 | 110 | 111 | ### Step 3 - Drawing Ball 112 | 113 | To draw the ball, first we define its variables (ballX, ballY, ballR) and then add a [Circle](#circle) command (using those variables). 114 | 115 | #### Code 116 | 117 | ```javascript 118 | padX=100; 119 | padY=430; 120 | padW=120; 121 | padH=15; 122 | ballX=160; // NEW 123 | ballY=70; // NEW 124 | ballR=16; // NEW 125 | function update() { 126 | 127 | } 128 | function render() { 129 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 130 | Rect(padX,padY,padW,padH,"green"); 131 | Circle(ballX,ballY,ballR,"red"); // NEW 132 | } 133 | ``` 134 | 135 | #### Result 136 | 137 | 138 | 139 | ### Step 4 - Moving Ball 140 | 141 | To move the ball we update its position (ballX, ballY). 142 | 143 | Three new variables are defined in order to manage the update (dirX, dirY, speed). 144 | 145 | #### Code 146 | 147 | ```javascript 148 | padX=100; 149 | padY=430; 150 | padW=120; 151 | padH=15; 152 | ballX=160; 153 | ballY=70; 154 | ballR=16; 155 | dirX=1; // NEW 156 | dirY=1; // NEW 157 | speed=2; // NEW 158 | function update() { 159 | ballX=ballX+dirX*speed; // NEW 160 | ballY=ballY+dirY*speed; // NEW 161 | } 162 | function render() { 163 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 164 | Rect(padX,padY,padW,padH,"green"); 165 | Circle(ballX,ballY,ballR,"red"); // NEW 166 | } 167 | ``` 168 | 169 | #### Result 170 | 171 | 172 | 173 | ### Step 5 - Bounching Right 174 | 175 | When the ball reaches the right border of game window we set dirX to -1 in order to have the ball bounching. 176 | 177 | #### Code 178 | 179 | ```javascript 180 | padX=100; 181 | padY=430; 182 | padW=120; 183 | padH=15; 184 | ballX=160; 185 | ballY=70; 186 | ballR=16; 187 | dirX=1; 188 | dirY=1; 189 | speed=2; 190 | function update() { 191 | if (ballX + ballR > WIDTH) { // NEW 192 | dirX=-1; // NEW 193 | } // NEW 194 | ballX=ballX+dirX*speed; 195 | ballY=ballY+dirY*speed; 196 | } 197 | function render() { 198 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 199 | Rect(padX,padY,padW,padH,"green"); 200 | Circle(ballX,ballY,ballR,"red"); 201 | } 202 | ``` 203 | 204 | ### Step 6 - Bounching Left 205 | 206 | When the ball reaches the left border of game window we set dirX to 1 in order to have the ball bounching. 207 | 208 | #### Code 209 | 210 | ```javascript 211 | padX=100; 212 | padY=430; 213 | padW=120; 214 | padH=15; 215 | ballX=160; 216 | ballY=70; 217 | ballR=16; 218 | dirX=1; 219 | dirY=1; 220 | speed=2; 221 | function update() { 222 | if (ballX + ballR > WIDTH) { 223 | dirX=-1; 224 | } 225 | if (ballX < ballR) { // NEW 226 | dirX=1; // NEW 227 | } // NEW 228 | ballX=ballX+dirX*speed; 229 | ballY=ballY+dirY*speed; 230 | } 231 | function render() { 232 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 233 | Rect(padX,padY,padW,padH,"green"); 234 | Circle(ballX,ballY,ballR,"red"); 235 | } 236 | ``` 237 | 238 | ### Step 7 - Bounching Top 239 | 240 | When the ball reaches the top border of game window we set dirY to 1 in order to have the ball bounching. 241 | 242 | #### Code 243 | 244 | ```javascript 245 | padX=100; 246 | padY=430; 247 | padW=120; 248 | padH=15; 249 | ballX=160; 250 | ballY=70; 251 | ballR=16; 252 | dirX=1; 253 | dirY=1; 254 | speed=2; 255 | function update() { 256 | if (ballX + ballR > WIDTH) { 257 | dirX=-1; 258 | } 259 | if (ballX < ballR) { 260 | dirX=1; 261 | } 262 | if (ballY < ballR) { // NEW 263 | dirY=1; // NEW 264 | } // NEW 265 | ballX=ballX+dirX*speed; 266 | ballY=ballY+dirY*speed; 267 | } 268 | function render() { 269 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 270 | Rect(padX,padY,padW,padH,"green"); 271 | Circle(ballX,ballY,ballR,"red"); 272 | } 273 | ``` 274 | 275 | ### Step 8 - Moving Pad 276 | 277 | To move the pad, when TAPPED is true, we set the position of the pad taking the x position of our tapping (TAPX). 278 | 279 | #### Code 280 | 281 | ```javascript 282 | padX=100; 283 | padY=430; 284 | padW=120; 285 | padH=15; 286 | ballX=160; 287 | ballY=70; 288 | ballR=16; 289 | dirX=1; 290 | dirY=1; 291 | speed=2; 292 | function update() { 293 | if (ballX + ballR > WIDTH) { 294 | dirX=-1; 295 | } 296 | if (ballX < ballR) { 297 | dirX=1; 298 | } 299 | if (ballY < ballR) { 300 | dirY=1; 301 | } 302 | if (TAPPED) { // NEW 303 | padX=TAPX-padW/2; // NEW 304 | } // NEW 305 | ballX=ballX+dirX*speed; 306 | ballY=ballY+dirY*speed; 307 | } 308 | function render() { 309 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 310 | Rect(padX,padY,padW,padH,"green"); 311 | Circle(ballX,ballY,ballR,"red"); 312 | } 313 | ``` 314 | 315 | #### Result 316 | 317 | 318 | 319 | ### Step 9 - Bounching Pad 320 | 321 | When the ball reaches the pad we set dirY to -1 in order to have the ball bounching on the pad. 322 | 323 | #### Code 324 | 325 | ```javascript 326 | padX=100; 327 | padY=430; 328 | padW=120; 329 | padH=15; 330 | ballX=160; 331 | ballY=70; 332 | ballR=16; 333 | dirX=1; 334 | dirY=1; 335 | speed=2; 336 | function update() { 337 | if (ballX + ballR > WIDTH) { 338 | dirX=-1; 339 | } 340 | if (ballX < ballR) { 341 | dirX=1; 342 | } 343 | if (ballY < ballR) { 344 | dirY=1; 345 | } 346 | if (TAPPED) { 347 | padX=TAPX-padW/2; 348 | } 349 | if ( (ballX >padX) && (ballX padY) ) { // NEW 350 | dirY=-1; // NEW 351 | } // NEW 352 | ballX=ballX+dirX*speed; 353 | ballY=ballY+dirY*speed; 354 | } 355 | function render() { 356 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 357 | Rect(padX,padY,padW,padH,"green"); 358 | Circle(ballX,ballY,ballR,"red"); 359 | } 360 | ``` 361 | 362 | ### Step 10 - Restarting Ball 363 | 364 | If the ball crosses the pad games is restarted from initial position. 365 | 366 | #### Code 367 | 368 | ```javascript 369 | padX=100; 370 | padY=430; 371 | padW=120; 372 | padH=15; 373 | ballX=160; 374 | ballY=70; 375 | ballR=16; 376 | dirX=1; 377 | dirY=1; 378 | speed=2; 379 | function update() { 380 | if (ballX + ballR > WIDTH) { 381 | dirX=-1; 382 | } 383 | if (ballX < ballR) { 384 | dirX=1; 385 | } 386 | if (ballY < ballR) { 387 | dirY=1; 388 | } 389 | if (TAPPED) { 390 | padX=TAPX-padW/2; 391 | } 392 | if ( (ballX >padX) && (ballX padY) ) { 393 | dirY=-1; 394 | } 395 | if ( ballY+ballR > padY+padH ) { // NEW 396 | ballX=160; // NEW 397 | ballY=70; // NEW 398 | } 399 | ballX=ballX+dirX*speed; 400 | ballY=ballY+dirY*speed; 401 | } 402 | function render() { 403 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 404 | Rect(padX,padY,padW,padH,"green"); 405 | Circle(ballX,ballY,ballR,"red"); 406 | } 407 | ``` 408 | 409 | ### Step 11 - Adding Score 410 | 411 | In order to manage and show the score, we add a new variable (score) and we use the [Text](#text) command in the render function. 412 | 413 | #### Code 414 | 415 | ```javascript 416 | padX=100; 417 | padY=430; 418 | padW=120; 419 | padH=15; 420 | ballX=160; 421 | ballY=70; 422 | ballR=16; 423 | dirX=1; 424 | dirY=1; 425 | speed=2; 426 | score=0; // NEW 427 | function update() { 428 | if (ballX + ballR > WIDTH) { 429 | dirX=-1; 430 | } 431 | if (ballX < ballR) { 432 | dirX=1; 433 | } 434 | if (ballY < ballR) { 435 | dirY=1; 436 | } 437 | if (TAPPED) { 438 | padX=TAPX-padW/2; 439 | } 440 | if ( (ballX >padX) && (ballX padY) ) { 441 | dirY=-1; 442 | score=score+10; // NEW 443 | } 444 | if ( ballY+ballR > padY+padH ) { 445 | ballX=160; 446 | ballY=70; 447 | } 448 | ballX=ballX+dirX*speed; 449 | ballY=ballY+dirY*speed; 450 | } 451 | function render() { 452 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 453 | Rect(padX,padY,padW,padH,"green"); 454 | Circle(ballX,ballY,ballR,"red"); 455 | Text(score,50,50,30,"green"); // NEW 456 | } 457 | ``` 458 | 459 | ### Step 12 - Adding Lifes 460 | 461 | 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. 462 | 463 | #### Code 464 | 465 | ```javascript 466 | padX=100; 467 | padY=430; 468 | padW=120; 469 | padH=15; 470 | ballX=160; 471 | ballY=70; 472 | ballR=16; 473 | dirX=1; 474 | dirY=1; 475 | speed=2; 476 | score=0; 477 | lifes=3; // NEW 478 | function update() { 479 | if (ballX + ballR > WIDTH) { 480 | dirX=-1; 481 | } 482 | if (ballX < ballR) { 483 | dirX=1; 484 | } 485 | if (ballY < ballR) { 486 | dirY=1; 487 | } 488 | if (TAPPED) { 489 | padX=TAPX-padW/2; 490 | } 491 | if ( (ballX >padX) && (ballX padY) ) { 492 | dirY=-1; 493 | score=score+10; 494 | } 495 | if ( ballY+ballR > padY+padH ) { 496 | ballX=160; 497 | ballY=70; 498 | lifes=lifes-1; // NEW 499 | } 500 | if (lifes==0) { // NEW 501 | speed=0; // NEW 502 | } // NEW 503 | ballX=ballX+dirX*speed; 504 | ballY=ballY+dirY*speed; 505 | } 506 | function render() { 507 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 508 | Rect(padX,padY,padW,padH,"green"); 509 | Circle(ballX,ballY,ballR,"red"); 510 | Text(score,50,50,30,"green"); 511 | Text(lifes,250,50,30,"red"); // NEW 512 | } 513 | ``` 514 | 515 | ### Step 13 - Adding Challenge 516 | 517 | To add a challenge in the game, we increase the ball speed by 0.2 at each bounch on the pad. 518 | 519 | #### Code 520 | 521 | ```javascript 522 | padX=100; 523 | padY=430; 524 | padW=120; 525 | padH=15; 526 | ballX=160; 527 | ballY=70; 528 | ballR=16; 529 | dirX=1; 530 | dirY=1; 531 | speed=2; 532 | score=0; 533 | lifes=3; 534 | function update() { 535 | if (ballX + ballR > WIDTH) { 536 | dirX=-1; 537 | } 538 | if (ballX < ballR) { 539 | dirX=1; 540 | } 541 | if (ballY < ballR) { 542 | dirY=1; 543 | } 544 | if (TAPPED) { 545 | padX=TAPX-padW/2; 546 | } 547 | if ( (ballX >padX) && (ballX padY) ) { 548 | dirY=-1; 549 | score=score+10; 550 | speed=speed+0.2; // NEW 551 | } 552 | if ( ballY+ballR > padY+padH ) { 553 | ballX=160; 554 | ballY=70; 555 | lifes=lifes-1; 556 | } 557 | if (lifes==0) { 558 | speed=0; 559 | } 560 | ballX=ballX+dirX*speed; 561 | ballY=ballY+dirY*speed; 562 | } 563 | function render() { 564 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 565 | Rect(padX,padY,padW,padH,"green"); 566 | Circle(ballX,ballY,ballR,"red"); 567 | Text(score,50,50,30,"green"); 568 | Text(lifes,250,50,30,"red"); 569 | } 570 | ``` 571 | 572 | ### Step 14 - Adding Sound 573 | 574 | Last enrichment. We add a sound (generic) when bounching using the [SoundEffect](#sound) command. 575 | 576 | #### Code 577 | 578 | ```javascript 579 | padX=100; 580 | padY=430; 581 | padW=120; 582 | padH=15; 583 | ballX=160; 584 | ballY=70; 585 | ballR=16; 586 | dirX=1; 587 | dirY=1; 588 | speed=2; 589 | score=0; 590 | lifes=3; 591 | function update() { 592 | if (ballX + ballR > WIDTH) { 593 | dirX=-1; 594 | SoundEffect (); // 595 | } 596 | if (ballX < ballR) { 597 | dirX=1; 598 | SoundEffect (); // 599 | } 600 | if (ballY < ballR) { 601 | dirY=1; 602 | SoundEffect (); // 603 | } 604 | if (TAPPED) { 605 | padX=TAPX-padW/2; 606 | } 607 | if ( (ballX >padX) && (ballX padY) ) { 608 | dirY=-1; 609 | score=score+10; 610 | speed=speed+0.2; 611 | SoundEffect (); // 612 | } 613 | if ( ballY+ballR > padY+padH ) { 614 | ballX=160; 615 | ballY=70; 616 | lifes=lifes-1; 617 | } 618 | if (lifes==0) { 619 | speed=0; 620 | } 621 | ballX=ballX+dirX*speed; 622 | ballY=ballY+dirY*speed; 623 | } 624 | function render() { 625 | Rect(0,0,WIDTH,HEIGHT,"lightblue"); 626 | Rect(padX,padY,padW,padH,"green"); 627 | Circle(ballX,ballY,ballR,"red"); 628 | Text(score,50,50,30,"green"); 629 | Text(lifes,250,50,30,"red"); 630 | } 631 | ``` 632 | 633 | ### Step 15 - Publishing Online 634 | 635 | We can finally publish the game using HTML Pasta. 636 | 637 | Here the game published online. 638 | Game. 639 | 640 | ## Commands 641 | 642 | ### Game Loop 643 | 644 | ```javascript 645 | function update() {}; 646 | function render() {}; 647 | ``` 648 | 649 | ### Draw 650 | 651 | #### Rect 652 | 653 | ```javascript 654 | Rect(x, y, w, h, "color"); 655 | ``` 656 | 657 | 658 | #### Circle 659 | 660 | ```javascript 661 | Circle(x, y, r, "color"); 662 | ``` 663 | 664 | 665 | #### Text 666 | 667 | ```javascript 668 | Text("text", x, y, size, "color"); 669 | ``` 670 | 671 | #### Line 672 | 673 | ```javascript 674 | Line(x_init, y_init, x_end, y_end, width, "color"); 675 | ``` 676 | 677 | ### Constants 678 | 679 | ```javascript 680 | WIDTH 681 | HEIGHT 682 | ``` 683 | 684 | ### Interaction 685 | 686 | ```javascript 687 | TAPPED 688 | TAPX 689 | TAPY 690 | ``` 691 | 692 | ### Sound 693 | 694 | ```javascript 695 | SoundEffect(frequencyValue, attack, decay, type, volumeValue, panValue, wait, pitchBendAmount, reverse, randomValue, dissonance, echo, reverb, timeout); 696 | ``` 697 | more information @: 698 | https://github.com/kittykatattack/sound.js#generating-sound-effects-and-music 699 | 700 | ### Utils 701 | 702 | ```javascript 703 | Random(min, max); // Random integer from min to max 704 | PitchToFrequency(octave, semitone); // Frequency from octave (integer), semitone (integer) 705 | ``` 706 | 707 | ## Editor 708 | 709 | web editor 710 | 711 | with: 712 | 713 | * live preview 714 | * syntax warnings 715 | * saving game 716 | * exporting game 717 | * using offline 718 | 719 | ## Thanks and Inspirations 720 | 721 | For game engine: 722 | How To Design A Mobile Game With HTML5 723 | 724 | For approach: 725 | pico-8 726 | 727 | For sound: 728 | sound.js 729 | -------------------------------------------------------------------------------- /images/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpagg/floppy/f0a1f06ebf6ece64fcb140d6240808d7edb1bfe5/images/circle.png -------------------------------------------------------------------------------- /images/rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpagg/floppy/f0a1f06ebf6ece64fcb140d6240808d7edb1bfe5/images/rect.png -------------------------------------------------------------------------------- /images/step0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpagg/floppy/f0a1f06ebf6ece64fcb140d6240808d7edb1bfe5/images/step0.png -------------------------------------------------------------------------------- /images/step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpagg/floppy/f0a1f06ebf6ece64fcb140d6240808d7edb1bfe5/images/step1.png -------------------------------------------------------------------------------- /images/step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpagg/floppy/f0a1f06ebf6ece64fcb140d6240808d7edb1bfe5/images/step2.png -------------------------------------------------------------------------------- /images/step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpagg/floppy/f0a1f06ebf6ece64fcb140d6240808d7edb1bfe5/images/step3.png -------------------------------------------------------------------------------- /images/step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpagg/floppy/f0a1f06ebf6ece64fcb140d6240808d7edb1bfe5/images/step4.png -------------------------------------------------------------------------------- /images/step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpagg/floppy/f0a1f06ebf6ece64fcb140d6240808d7edb1bfe5/images/step5.png -------------------------------------------------------------------------------- /images/step8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpagg/floppy/f0a1f06ebf6ece64fcb140d6240808d7edb1bfe5/images/step8.png -------------------------------------------------------------------------------- /images/test.md: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------