├── 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 |
--------------------------------------------------------------------------------