-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCh7_Asteroids.html
More file actions
1688 lines (1662 loc) · 90.7 KB
/
Ch7_Asteroids.html
File metadata and controls
1688 lines (1662 loc) · 90.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<title>JavaScript: Ch7 Asteroids</title>
<meta name="title" content="Variations on a Theme: JavaScript: Ch7 Asteroids">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="UTF-8">
<meta name="description" content="An object-oriented Introduction">
<meta name="keywords" content="JavaScript,object orientation,introduction">
<meta name="author" content="Ralph P. Lano">
<meta name="robots" content="index,follow">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="book.css">
</head>
<body>
<center>
<div id="wrap">
<ul class="sidenav">
<p><a href="../index.html">Variations on a Theme</a><a href="index.html">JavaScript</a></p>
<li><a href="Ch1_Karel.html">Karel</a></li>
<li><a href="Ch2_Graphics.html">Graphics</a></li>
<li><a href="Ch3_Console.html">Console</a></li>
<li><a href="Ch4_Agrar.html">Agrar</a></li>
<li><a href="Ch5_MindReader.html">MindReader</a></li>
<li><a href="Ch6_Swing.html">Swing</a></li>
<li><a href="Ch7_Asteroids.html">Asteroids</a></li>
<li><a href="Ch8_Stocks.html">Stocks</a></li>
<li><a href="index.html"> </a></li>
<li><a href="AppA_Primer.html">Primer</a></li>
<li><a href="AppB_Libraries.html">Libraries</a></li>
<li><a href="AppC_Ideas.html">Ideas</a></li>
</ul>
<div class="content">
<p>
<img alt="" src="img/e271affc-a756-4bfd-b50e-7429e71932e8.png" style="display: block; margin-left: auto; margin-right: auto;width: 285px; height: 294px;" /></p>
<h1>
Asteroids</h1>
<p>
This chapter is again about graphics and games. But before we get started we first need to learn about arrays. This then allows us to manipulate images, which are nothing but two-dimensional arrays. Next we spend some more time on object orientation, namely inheritance and composition. And we will also learn how to work with key events.</p>
<p>
.</p>
<h2>
<img alt="" src="img/eierKarton.jpg" style="width: 316px; height: 256px; float: right;" />Arrays</h2>
<p>
What are arrays? An egg carton is an array. It's an array for ten eggs. That does not necessarily mean that there are always ten eggs in there, sometimes there are only three eggs in it.</p>
<p>
Obviously, arrays are quite practical and therefore we will take a closer look at arrays in JavaScript. Suppose we wanted to create an empty array, then we would write:</p>
<pre style="margin-left: 40px;">
let eggs = [];
</pre>
<p>
The square brackets say that we declared an empty array. We can also create an array that has stuff in it, like numbers:</p>
<pre style="margin-left: 40px;">
let eggs = [1, 2, 3, 4];</pre>
<p>
In this case, the array has four numbers in it. We can create arrays of any data types, e.g. we could also create an array with four GOvals:</p>
<pre style="margin-left: 40px;">
let circles = [];
circles[0] = new GOval(100, 66, 50, 50);
circles[1] = new GOval(100, 116, 50, 50);
circles[2] = new GOval(150, 66, 50, 50);
circles[3] = new GOval(150, 116, 50, 50);</pre>
<p>
Arrays have the important property that they are ordered, i.e. they are numbered consecutively starting with 0.</p>
<p>
.</p>
<h2>
Working with Arrays</h2>
<p>
After we have declared and created an array, we have to fill it with values. We can do this by hand,</p>
<pre style="margin-left: 40px;">
let eggs = [];
eggs[0] = 0;
eggs[1] = 2;
eggs[2] = 4;
...
eggs[9] = 18;</pre>
<p>
Meaning we assign the first element in the array (element number 0) the value 0, the second element the value 2 etc. We can also do the assignment with a loop:</p>
<pre style="margin-left: 40px;">
let eggs = [];
for (let i=0; i<5; i++) {
eggs[i] = await readInt("?");
}
</pre>
<p>
or as we have seen above:</p>
<pre style="margin-left: 40px;">
let eggs = [ 2, 4, 6, 8 ];
</pre>
<p>
If we want to access an element, we have to enter its house number. So we access the third element with:</p>
<pre style="margin-left: 40px;">
println( eggs[2] );</pre>
<p>
If we want to output all elements, it is best done with a loop:</p>
<pre style="margin-left: 40px;">
for (let i=0; i<eggs.length; i++) {
println( eggs[i] );
}
</pre>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Ch7_Asteroids/monthName" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/monthName.png" style="width: 200px; height: 113px; display: block;" />Try it</a></div>
<h2>
Exercise: MonthName</h2>
<p>
A useful example is the conversion of the month as a number, e.g. 12, into the months name, e.g. December. You could do this with a lengthy if or switch condition, but you can also do it very elegantly with arrays:</p>
<pre style="margin-left: 40px;">
const monthName = ["January", "February", "March", "April",
"May", "June", "July", "August", "September", "October",
"November", "December"];
async function setup() {
createConsole();
let monthNr = await readInt("Enter number of month (1=January): ");
println(monthName[monthNr - 1]);
}</pre>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Ch7_Asteroids/fourGOvals" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/fourGOvals.png" style="width: 200px; height: 201px; display: block;" />Try it</a></div>
<h2>
Exercise: Array of Objects</h2>
<p>
Arrays are not only useful with numbers and strings, but also with objects. Let's say we want to create an array with four ovals in it, The following code would do that:</p>
<pre style="margin-left: 40px;">
function setup() {
createCanvas(300, 300);
frameRate(5);
let circles = [];
circles[0] = new GOval(100, 66, 50, 50);
circles[1] = new GOval(100, 116, 50, 50);
circles[2] = new GOval(150, 66, 50, 50);
circles[3] = new GOval(150, 116, 50, 50);
add(circles[0]);
add(circles[1]);
add(circles[2]);
add(circles[3]);
}
function draw() {
update();
}
</pre>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Ch7_Asteroids/chess" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/chess.png" style="width: 200px; height: 158px; display: block;" />Try it</a></div>
<h2>
Multidimensional Arrays</h2>
<p>
One-dimensional arrays are fun and save us a lot of paperwork. But two-dimensional arrays are even cooler. We start quite simple with the game of chess, which can be represented by an 8 by 8 array of chars:</p>
<pre style="margin-left: 40px;">
const chess = [
['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']];
</pre>
<p>
Lowercase letters stand for black, and uppercase letters for white. If we want to display the playing field, then we could do that with two nested for loops:</p>
<pre style="margin-left: 40px;">
function printChessBoard() {
for (let i = 0; i < 8; i++) {
for (let j = 0; j < 8; j++) {
print(chess[i][j]);
}
println();
}
}</pre>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Ch7_Asteroids/grayImage" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/grayImage.png" style="width: 200px; height: 100px; display: block;" />Try it</a></div>
<h2>
Exercise: GrayImage</h2>
<p>
Images are just two-dimensional arrays. As a little exercise we want to convert a color image into a gray image. First we load the image using the GImage class:</p>
<pre style="margin-left: 40px;">
let image = new GImage("Ch7_Asteroids/Taj_Mahal_(Edited).jpeg");</pre>
<p>
Next, we need to get to the pixels. This can be done with the method <em>getPixelArray()</em> of the class GImage:</p>
<pre style="margin-left: 40px;">
let pixels = <span style="color:#0000ff;">await</span> image.getPixelArray();
let width = image.width;
let height = image.height;</pre>
<p>
This gives us a two-dimensional array of numbers. Notice the <em>await</em>, which is neccessary because sometimes it takes a little longer for an image to load, and we should not access the pixels of an image before it is loaded.</p>
<p>
One pixel consists of four numbers: the first for the red color, second for the green color, third blue color and the fourth is the alpha value of a pixel, that is its transparency. Let's say we want to access a pixel at position x=5 and y=22, then the following formula gives us the position of that pixel:</p>
<pre style="margin-left: 40px;">
let i = (y * width + x) * 4;</pre>
<p>
</p>
<p>
To get to the color values, we use this index:</p>
<pre style="margin-left: 40px;">
let red = pixels[i + 0];
let green = pixels[i + 1];
let blue = pixels[i + 2];
let alpha = pixels[i + 3];</pre>
<p>
This is how we read the pixels. But we want to create a new gray image. Hence, first we need to create a new empty pixel array:</p>
<pre style="margin-left: 40px;">
let pixelsGray = [];</pre>
<p>
Then we use the formula that Gimp uses to calculate the gray value of a pixel [2]:</p>
<pre style="margin-left: 40px;">
let lum = Math.trunc(0.21 * red + 0.72 * green + 0.07 * blue);</pre>
<p>
and then we put these values into the new pixel array:</p>
<pre style="margin-left: 40px;">
pixelsGray[i + 0] = lum;
pixelsGray[i + 1] = lum;
pixelsGray[i + 2] = lum;
pixelsGray[i + 3] = alpha;</pre>
<p>
This we do for all the pixels in the array using a nested for-loop:</p>
<pre style="margin-left: 40px;">
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
...
}
}
</pre>
<p>
Finally, we turn this array back into a new GImage:</p>
<pre style="margin-left: 40px;">
let gray = new GImage(width, height);
gray.setPixelArray(pixelsGray);
add(gray, 200, 0);</pre>
<p>
.</p>
<h2>
Object Orientation</h2>
<p>
In the second part of this chapter we want to deepen our understanding with regards to object orientation. The two big themes are inheritance ("is a" relationship) and composition ("has a" relationship). We start with a little game, the MarsLander.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Ch7_Asteroids/marsLander" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/marsLander.png" style="width: 200px; height: 200px; display: block;" />Try it</a></div>
<h2>
Exercise: MarsLander</h2>
<p>
In the next decade, Elon Musk wants to send the first humans to Mars. Karel volunteered and needs to practice landing. For this purpose, we need to write a MarsLander simulator. It's about landing a spaceship safely on Mars. The plan is to use the arrow keys (up and down) to slow down or accelerate our spaceship. If the touchdown speed is to high, Karel dies.</p>
<p>
We will use the top-down approach, starting with the draw() method:</p>
<pre style="margin-left: 40px;">
function draw() {
if (spaceShip != null) {
moveSpaceShip();
checkForCollision();
} else {
displayGameOver();
noLoop();
}
update();
}</pre>
<p>
As usual, there is also a setup() that creates the spaceship. The game loop looks exactly like our last animation, billiards. What's interesting now is that we no longer have an infinite loop, but a loop with the abort criterion: namely if there is no more SpaceShip, i.e. spaceShip == null, then the game should stop. The <em>noLoop()</em> statement stops the looping.</p>
<p>
We have three instance variables,</p>
<pre style="margin-left: 40px;">
let spaceShip;
let vy = 0;
let vx = 0;</pre>
<p>
i.e. the spaceShip, together with its velocities vx and vy.</p>
<p>
In the setup() the spaceShip is initialized. The code is identical to the one from the exercise in chapter two, nothing new here,</p>
<pre style="margin-left: 40px;">
function setup() {
createCanvas(APP_WIDTH, APP_HEIGHT);
frameRate(5);
spaceShip = new GPolygon();
spaceShip.addVertex(0, -SPACE_SHIP_SIZE);
spaceShip.addVertex(-2 * SPACE_SHIP_SIZE / 3, SPACE_SHIP_SIZE);
spaceShip.addVertex(0, SPACE_SHIP_SIZE / 2);
spaceShip.addVertex(2 * SPACE_SHIP_SIZE / 3, SPACE_SHIP_SIZE);
add(spaceShip, (APP_WIDTH - SPACE_SHIP_SIZE) / 2, SPACE_SHIP_SIZE);
}</pre>
<p>
The moveSpaceShip() method is absolutely trivial:</p>
<pre style="margin-left: 40px;">
function moveSpaceShip() {
vy += GRAVITY;
spaceShip.move(vx, vy);
}</pre>
<p>
The speed of our space ship increases with every step by the GRAVITY of Mars, and it is moved by its velocities.</p>
<p>
In the checkForCollision() method we check if we have already reached the surface of Mars:</p>
<pre style="margin-left: 40px;">
function checkForCollision() {
let y = spaceShip.getY();
if (y > (APP_HEIGHT - SPACE_SHIP_SIZE)) {
spaceShip = null;
}
}</pre>
<p>
If so, we simply set the spaceShip to null. Here "null" means: "not initialized" or "there is none" or "does not exist". We can set objects explicitly to null, which means that we delete the object. In our example, we use this to end our game loop.</p>
<p>
What remains are the key events: similar to the mousePressed() method, there is a keyPressed() method:</p>
<pre style="margin-left: 40px;">
function keyPressed() {
switch (keyCode) {
case 38: // up
vy--;
break;
case 40: // down
vy++;
break;
}
}</pre>
<p>
Of course we want to know which key was pressed and this we get from the keyCode. Each key has its own key code, and for the up key this is 38 and for the down key this is 40.</p>
<p>
Now we can play or rather train for our Mars mission.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Ch7_Asteroids/marsLander2" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/marsLander.png" style="width: 200px; height: 200px; display: block;" />Try it</a></div>
<h2>
Inheritance</h2>
<p>
What does MarsLander have to do with inheritance? Not much yet. But let's look at the code. What should bother us a little bit are the following three lines,</p>
<pre style="margin-left: 40px;">
let spaceShip;
let vy = 0;
let vx = 0;</pre>
<p>
because vx and vy are the speed of the spaceShip, meaning they actually belong to the spaceShip. Suppose we had several spaceShips, or we had a lot of asteroids moving around, then we would have a lot of vx's and vy's. And it's going to be very confusing and ugly.</p>
<p>
To prevent this we do the following: we declare a new class called GSpaceShip and we put everything that has to do with the spaceShip into this class:</p>
<pre style="margin-left: 40px;">
class GSpaceShip <span style="color:#0000ff;">extends GPolygon</span> {
constructor(x, y) {
<span style="color:#0000ff;">super(x, y);</span>
super.addVertex(0, -SPACE_SHIP_SIZE);
super.addVertex(-2 * SPACE_SHIP_SIZE / 3, SPACE_SHIP_SIZE);
super.addVertex(0, SPACE_SHIP_SIZE / 2);
super.addVertex(2 * SPACE_SHIP_SIZE / 3, SPACE_SHIP_SIZE);
this.vy = 0;
this.vx = 0;
}
move() {
this.vy += GRAVITY;
super.move(this.vx, this.vy);
}
}</pre>
<p>
First, we see that GSpaceShip is a GPolygon, because it says "GSpaceShip extends GPolygon". This means that GSpaceShip inherits all properties and methods from GPolygon. That is why inheritance is also said to be an "is a" relationship.</p>
<p>
Second, we see that the global variables vx and vy are now instance variables of the spaceShip, so they are where they belong.</p>
<p>
Third, we take a look at the constructor: there we see a "super()" in the first line. The method super() does nothing else but call the constructor of the superclass, the parent class. In our case, the GPolygon(). Then we see how we add vertices to ourselves (we are now a GPolygon). So in the constructor we create our spaceship appearance.</p>
<p>
Finally, we see that we have added a new method called move(). Since GSpaceShip now knows its own speed, it can also move itself.</p>
<p>
Inheritance therefore has many advantages: above all, it leads to classes becoming more independent and having fewer dependencies. It is also said that the class takes responsibility for its own attributes (variables) and behavior (methods). These lesser dependencies also lead to a smaller coupling, which makes our code less complicated.</p>
<p>
Let's have a look at the simplifications in MarsLander2. First, we only need one instance variable:</p>
<pre style="margin-left: 40px;">
let spaceShip;</pre>
<p>
and also the setup() and moveSpaceShip() methods become much cleaner:</p>
<pre style="margin-left: 40px;">
function setup() {
createCanvas(APP_WIDTH, APP_HEIGHT);
frameRate(5);
spaceShip = new GSpaceShip();
add(spaceShip, (APP_WIDTH - SPACE_SHIP_SIZE) / 2, SPACE_SHIP_SIZE);
}
function moveSpaceShip() {
spaceShip.move();
}</pre>
<p>
This is pretty cool. We will really come to appreciate these simplifications when it comes to programming the Asteroids game.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Ch7_Asteroids/marsLander3" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/marsLander3.png" style="width: 200px; height: 200px; display: block;" />Try it</a></div>
<h2>
Composition</h2>
<p>
The second important concept of object orientation is composition. As we have seen, you can create new classes (GSpaceShip) by inheritance from an existing class (GPolygon). But we can also create new classes by composing them from several existing classes. </p>
<p>
As an example we write a class GSmiley.</p>
<pre style="margin-left: 40px;">
class GSmiley extends GCompound {
constructor(SIZE) {
super();
let face = new GOval(SIZE, SIZE);
face.setFilled(true);
face.setFillColor(Color.YELLOW);
this.add(face);
let leftEye = new GOval(SIZE / 10, SIZE / 10);
leftEye.setColor(Color.GREEN);
this.add(leftEye, SIZE / 4, SIZE / 4);
let rightEye = new GOval(SIZE / 10, SIZE / 10);
rightEye.setColor(Color.RED);
this.add(rightEye, 3 * SIZE / 4, SIZE / 4);
let mouth = new GArc(SIZE / 2, SIZE / 2, 225, 90);
this.add(mouth, 0.3 * SIZE, 0.3 * SIZE);
}
}</pre>
<p>
Our GSmiley consists of different components, so it has a face, a leftEye, a rightEye and a mouth. In the constructor we create a new object from several old objects. That's composition. That's also why we say that composition is a "has a" relationship, because GSmiley has a face, a leftEye, a rightEye and a mouth.</p>
<p>
.</p>
<h2>
GCompound</h2>
<p>
We can also mix inheritance and composition. If we add "extends GCompound" to the class declaration for the GSmiley example, then we can also use GSmiley in our MarsLander. If we simply replace "GPolygon" with "GSmiley" in our first version of the MarsLander, the program works like before, only our spaceship now looks like a smiley.</p>
<p>
.</p>
<h2>
<img alt="" src="img/8aff1c4e-4764-43d9-9651-6f407d47f9fe.png" style="width: 150px; height: 121px; float: right;" />Inheritance vs Composition</h2>
<p>
When should we use inheritance and when composition? A rule of thumb is, if possible, use composition. This has to do with the fact that there is no multiple inheritance in JavaScript. So a class can't have two parents. This restriction does not apply to composition, in principle a class can consist of any number of components.</p>
<p>
.</p>
<p>
.</p>
<p>
<img alt="" src="img/9a6ee258-1e5b-4941-a675-2be6f1216447.png" style="width: 136px; height: 155px; float: right;" />A small note: by multiple inheritance we mean that a class has several parent classes. That's not allowed. But it is quite possible that a class has a parent class, and this parent class again has a parent class, so to speak the grandparent class of the original. For example, GObject is the grandparent class of the GSpaceShip class. That's allowed.</p>
<p>
.</p>
<p>
.</p>
<hr />
<h1>
Review</h1>
<p>
With the principles of inheritance and composition we have reached and cracked the core of object orientation. We have learned how to give an existing class additional properties through inheritance. A GPolygon cannot move independently because it has no speed. The class GSpaceShip, which is actually also a GPolygon, knows its own speed and can move by itself. We have also seen that we can assemble a new class from several existing classes via composition. Both are very useful, as we will see.</p>
<p>
In addition, we have learned a few other useful things, such as</p>
<ul>
<li>
arrays,</li>
<li>
multidimensional arrays,</li>
<li>
image processing,</li>
<li>
key events,</li>
<li>
and we used the class GCompound.</li>
</ul>
<p>
.</p>
<hr />
<h1>
Projects</h1>
<p>
The projects in this chapter are fun projects. Here we go.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/swap" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/swap.png" style="width: 200px; height: 113px; display: block;" />Try it</a></div>
<h2>
Swap</h2>
<p>
In this project we want to swap two elements in an array. In the array</p>
<pre style="margin-left: 40px;">
let arr = [0, 2, 4, 6];</pre>
<p>
we want to swap the element at the second position (the "2") with the element at the third position (the "4"). We want to do this with a method <em>swap(arr)</em>, which has an array as parameter.</p>
<p>
Two things we want to learn in this exercise: First, in arrays we always start counting from 0. Second, arrays are passed as reference, i.e. if we pass an array as a parameter to a method, then we pass the original, not a copy. All changes we make to it in the method are permanent, i.e. change the original array.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/examStatistics" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/examStatistics.png" style="width: 200px; height: 113px; display: block;" />Try it</a></div>
<h2>
ExamStatistics</h2>
<p>
As another example for the use of arrays let us store the grades of an exam in an array. Since we do not yet know exactly how many students will take the exam, but it is very unlikely that there will be more than 100, we will create an array for 100 grades:</p>
<pre style="margin-left: 40px;">
let scores = [];</pre>
<p>
Then we ask the user to enter the grades. For this the loop-and-a-half is ideally suited. To know when we are done, we agree that entering a "-1" (the sentinel) means that all grades have been entered. </p>
<p>
Once we have all the grades in the array, we want to calculate some statistical data about the grades like total number of students who took the exam, the average grade, the lowest grade and the highest grade.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/pianoConsole" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/pianoConsole.png" style="width: 200px; height: 113px; display: block;" />Try it</a></div>
<h2>
PianoConsole</h2>
<p>
As our first application for arrays we write a small music program. Before being able to play sound, we need to load a library that allows us to do that. Therefore, at the beginning of your code, place the following include() statement:</p>
<pre style="margin-left: 40px;">
include("./libraries/p5.sound.min.js");</pre>
<p>
(Also, sound may not work on mobile devices, I had problems with an iPad playing any sound.)</p>
<p>
Once we have that, we can start by storing our melody in an array,</p>
<pre style="margin-left: 40px;">
const tune = [0, 1, 2, 3, 4, 4, 5, 5, 5, 5, 4];</pre>
<p>
where 0 refers to C4, 1 to D4, etc. But before playing, we must load the sound files. Those must be preloaded:</p>
<pre style="margin-left: 40px;">
const songFileNames = "CDEFGAB";
function preload() {
for (let i = 0; i < songFileNames.length; i++) {
let soundName = songFileNames.charAt(i) + '4';
let fileName = 'Pr7_Asteroids/music/' + soundName + '.wav';
soundFiles[i] = loadSound(fileName);
}
}</pre>
<p>
Preloading is neccessary, because sometimes the internet is a little slow, and we want to make certain that our program only starts after all the sound files have loaded.</p>
<p>
Next we initialize our program. Since console programs usually have no loop, we must turn the loop on, and we also declare and initialize the <em>currentSong</em> variable.</p>
<pre style="margin-left: 40px;">
let currentSong;
function setup() {
createConsole();
<span style="color:#0000ff;">loop();</span>
frameRate(5);
currentSong = soundFiles[0];
}</pre>
<p>
Now we are ready to play our tune:</p>
<pre style="margin-left: 40px;">
function draw() {
if (!currentSong.isPlaying() && counter < tune.length) {
let tun = tune[counter];
print(songFileNames.charAt(tun) + ",");
currentSong = soundFiles[tun];
currentSong.play();
counter++;
}
}</pre>
<p>
We wait until a sound file has finished playing, before we move to the next. Our counter helps us to keep track on which sound to play. Obviously, you can play other melodies, add different and more sound files, etc.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/piano" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/piano.png" style="width: 200px; height: 100px; display: block;" />Try it</a></div>
<h2>
Piano</h2>
<p>
Console applications are always a little more boring, and honestly, who would spend any money on them? But we already wrote a UI for our piano in the second chapter. Of course we want to control our piano with the mouse.</p>
<p>
The question that arises is how do we know which key was pressed? Interestingly, we can use the getElementAt() method for this:</p>
<pre style="margin-left: 40px;">
function mousePressed() {
let x = mouseX;
let y = mouseY;
let obj = getElementAt(x, y);
if (obj !== undefined) {
...
}
}</pre>
<p>
This gives us the GRect that was pressed. Now there are three alternatives to go on:</p>
<ol>
<li>
If the GRect had a name, then we would know which tone to play. We can do this with inheritance: we define a new class GKey, which is a GRect and has an additional attribute for the name.</li>
<li>
We memorize somewhere the x-coordinate of the keys. With obj.getX() we can get it, and viola we know which key was pressed.</li>
<li>
Or we keep references to all keys in an array.</li>
</ol>
<p>
Let us take a closer look at this third possibility. For this we need an array as global variable:</p>
<pre style="margin-left: 40px;">
let keys = [];</pre>
<p>
When we create the keys, we store them in our global array:</p>
<pre style="margin-left: 40px;">
let keyCounter = 0;
// draw 8 white keys
for (let i = 0; i < 7; i++) {
<span style="color:#0000ff;">keys[keyCounter]</span> = new GRect(WIDTH / 7, HEIGHT - HEIGHT_OFFSET);
add(keys[keyCounter], i * WIDTH / 7, 0);
keyCounter++;
}
</pre>
<p>
And now we can simply test for equality in our mousePressed() method:</p>
<pre style="margin-left: 40px;">
function mousePressed() {
let x = mouseX;
let y = mouseY;
let obj = getElementAt(x, y);
if (obj !== undefined) {
for (let i = 0; i < songFileNames.length; i++) {
if (obj == keys[i]) {
print(songFileNames.charAt(i) + ",");
currentSong = soundFiles[i];
currentSong.play();
}
}
}
}</pre>
<p>
If we could get this program to work on our mobile phones, we'd be rich! (Next year...)</p>
<p>
BTW, only worry about the white keys. The black keys are a little tricky, in a little while we learn even simpler ways to do this.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/flippedImage" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/flippedImage.png" style="width: 200px; height: 100px; display: block;" />Try it</a></div>
<h2>
FlippedImage</h2>
<p>
When manipulating images we usually use arrays. We have already seen how we can access the pixels of an image. In this example we want to mirror a given image. This can be done horizontally or vertically. To do this, we create a new array for the pixels</p>
<pre style="margin-left: 40px;">
let pixelsFlipped = [];</pre>
<p>
and use two nested loops</p>
<pre style="margin-left: 40px;">
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let i = (y * width + x) * 4;
let j = ((height - y) * width + x) * 4;
pixelsFlipped[j + 0] = pixels[i + 0];
pixelsFlipped[j + 1] = pixels[i + 1];
pixelsFlipped[j + 2] = pixels[i + 2];
pixelsFlipped[j + 3] = pixels[i + 3];
}
}</pre>
<p>
to swap the pixels. From the new array we create a new image via</p>
<pre style="margin-left: 40px;">
let flipped = new GImage(width, height);
flipped.setPixelArray(pixelsFlipped);
return flipped;</pre>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/grayImageXOR" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/grayImageXOR.png" style="width: 200px; height: 200px; display: block;" />Try it</a></div>
<h2>
GrayImageXOR</h2>
<p>
Steganography is the art of hidden transmission of information [3]. It is interesting that this can be done quite simply with the XOR function, i.e. the Exclusive Or. How do we do that? Let's assume we have two images and their pixel arrays:</p>
<pre style="margin-left: 40px;">
let pixels1 = await grayImage1.getPixelArray();
let pixels2 = await grayImage2.getPixelArray();</pre>
<p>
then we get the red value for each pixel, for example:</p>
<pre style="margin-left: 40px;">
let i1 = pixels1[i];
let i2 = pixels2[i];</pre>
<p>
And just as we could add these two values, we could also use the XOR function:</p>
<pre style="margin-left: 40px;">
let xr = i1 ^ i2;</pre>
<p>
We make a bit-wise XOR of the bits of r1 with those of r2. To test this, we take the Taj and the Mona Lisa as images, and 'XOR' them. The result is a funny mixture, where you can recognize neither one nor the other, or both depending. The interesting thing happens, when we take the pixels of that mixed image and let the XOR function run over it again: then the original image appears again. Interestingly, exactly the opposite. This can also be used for encryption.</p>
<p>
The RAID-5 system is based on the same principle and is used to provide failure safety for hard disks [4].</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/colorImage" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/colorImage.png" style="width: 200px; height: 100px; display: block;" />Try it</a></div>
<h2>
ColorImage</h2>
<p>
A common use of image manipulation is to reduce the number of colors in an image. This is a nice application for rounding to integers.</p>
<pre style="margin-left: 40px;">
let r = pixels[i + 0];
r = Math.trunc(r / FACTOR) * FACTOR;</pre>
<p>
Originally r can take values between 0 and 255. If we divide this number by let's say 64, then we only have values between 0 and 3. If we multiply this again by 64, we only have the values 0, 64, 128 and 192. So there are only four red values left.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/imageFilterSimple" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/imageFilterSimple.png" style="width: 200px; height: 100px; display: block;" />Try it</a></div>
<h2>
ImageFilterSimple</h2>
<p>
You can also perform many other image manipulations. For example, you can simply subtract adjacent pixels:</p>
<pre style="margin-left: 40px;">
let n = (y * w + x - 1) * 4;
r[0][1] = pixels[n + 0];
n = (y * w + x) * 4;
r[1][1] = pixels[n + 0];
...
let xx = r[1][1] - r[0][1];
xx *= 10;
...
let nn = (y * w + x) * 4;
pixelsGray[nn + 0] = xx;</pre>
<p>
The result corresponds to a simple edge detection.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/imageFilterMatrix" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/imageFilterMatrix.png" style="width: 200px; height: 100px; display: block;" />Try it</a></div>
<h2>
ImageFilterMatrix</h2>
<p>
Much more interesting image manipulations become possible if we realize that arrays are also matrices. You may want to keep this to yourself, or you might get burned [6]. But once you know that, you can do cool things with images. The filters sharpen, blur, edgeEnhance, edgeDetect, or emboss, as they are known from any image processing program, are actually only the application of a convolution matrix on an given image [5]. For example, to sharpen an image, the following matrix will do the trick:</p>
<pre style="margin-left: 40px;">
let currentFilter = [
[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]
];
let currentFactor = 1;</pre>
<p>
Executing matrix multiplication (that is, applying the filter to the image) is performed in the following method:</p>
<pre style="margin-left: 40px;">
function applyFilterToPixel(x, y, width) {
let r = 0;
let g = 0;
let b = 0;
for (let i = 0; i <= 2; i++) {
for (let j = 0; j <= 2; j++) {
let n = ((x + i) * width + (y + j)) * 4;
r += array[n + 0] * currentFilter[j][i];
g += array[n + 1] * currentFilter[j][i];
b += array[n + 2] * currentFilter[j][i];
}
}
let nn = (x * width + y) * 4;
array2[nn + 0] = checkBounds(r / currentFactor);
array2[nn + 1] = checkBounds(g / currentFactor);
array2[nn + 2] = checkBounds(b / currentFactor);
array2[nn + 3] = 255;
}</pre>
<p>
This must be called for each pixel of the original image. Notice, that we made the two pixel arrays global variables:</p>
<pre style="margin-left: 40px;">
let array = [];
let array2 = [];</pre>
<p>
This is actually a pretty advanced program.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr6_Swing/calculator" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/calculatorGUI.png" style="width: 200px; height: 100px; display: block;" />Try it</a></div>
<h2>
Calculator</h2>
<p>
Applications for arrays are really diverse. Very often they save you from having to write a lot of code. A nice example is the calculator from the last chapter. You can of course create the buttons, i.e. JSButtons, all individually, but you can also make it a bit more effective:</p>
<pre style="margin-left: 40px;">
const btnNames = ["7", "8", "9", "/", "4", "5", "6", "*",
"1", "2", "3", "-", ".", "0", "=", "+"];
function setup() {
...
let pnl = new JSPanel('grid', 4);
addWidget(pnl, 'CENTER');
for (let i = 0; i < btnNames.length; i++) {
let btn = new JSButton(btnNames[i]);
pnl.add(btn);
}
}</pre>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr4_Agrar/ticTacToe" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/ticTacToe.png" style="width: 200px; height: 200px; display: block;" />Try it</a></div>
<h2>
TicTacToeLogic</h2>
<p>
Arrays can also be useful for games. In the fourth chapter we already wrote the UI for the TicTacToe game. Now we are ready to understand the logic part. The playing field can be understood as a two-dimensional array, which is an instance variable that we initialize in the constructor:</p>
<pre style="margin-left: 40px;">
class TicTacToeLogic {
constructor() {
this.board = [[0,0,0],[0,0,0],[0,0,0]];
}
...
}
</pre>
<p>
Originally all values of the playing field are set to 0. If we now mark the squares that player one has occupied with a 1 and the squares that player two has occupied with a 2, then this is a perfect description of the current state of the game.</p>
<p>
If we want to test if a certain move is allowed, then we only have to check if the value of the playing field at that position is 0:</p>
<pre style="margin-left: 40px;">
isMoveAllowed(player, i, j) {
if (this.board[i][j] == 0) {
this.board[i][j] = player;
return true;
}
return false;
}</pre>
<p>
If we want to test if a player has won, we have to check if one of the players has occupied a whole vertical, horizontal or diagonal row. For the vertical rows the following will do the trick:</p>
<pre style="margin-left: 40px;">
checkVerticals() {
for (let i = 0; i < 3; i++) {
if ((this.board[i][0] == 1) && (this.board[i][1] == 1)
&& (this.board[i][2] == 1)) {
return true;
}
}
for (let i = 0; i < 3; i++) {
if ((this.board[i][0] == 2) && (this.board[i][1] == 2)
&& (this.board[i][2] == 2)) {
return true;
}
}
return false;
}</pre>
<p>
We just go through one column after the other (for-loop) and check if all three values are set to 1 (for player 1) or 2 (for player 2).</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/battleShip" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/battleShip.png" style="width: 200px; height: 200px; display: block;" />Try it</a></div>
<h2>
BattleShip</h2>
<p>
Battleship [7] is another classic game for which arrays are very useful. Our BattleShip game should become a game human versus computer, that means the computer distributes its boats on the playing field and we have to find them.</p>
<p>
Just like TicTacToe, we use an array of integers for the playing field:</p>
<pre style="margin-left: 40px;">
let board = makeArray(BOARD_SIZE, BOARD_SIZE);</pre>
<p>
where we dynamically create the array with the makeArray() function:</p>
<pre style="margin-left: 40px;">
function makeArray(d1, d2) {
var arr = [];
for (let i = 0; i < d2; i++) {
arr.push(new Array(d1));
}
return arr;
}</pre>
<p>
The ships themselves are represented by numbers: 5 stands for an AircraftCarrier, 4 for a Battleship, 3 for a Submarine or a Destroyer and 2 for a PatrolBoat. To determine how many of each type of ship there are, we can use another array:</p>
<pre style="margin-left: 40px;">
const SHIP_SIZES = [5, 4, 3, 3, 2];</pre>
<p>
That means, if we want to have some more PatrolBoats, just add some more 2's.</p>
<p>
In the setup() method,</p>
<pre style="margin-left: 40px;">
function setup() {
...
drawLines();
initBoard();
}</pre>
<p>
we draw the playing field and initialize the boats. In the initBoard() method we go through the list of ships (SHIP_SIZES) and add one by one using the placeShip(int shipNr, int shipSize) method. This method can be quite simple, if we place the ships just next to each other. However, then the game becomes boring quickly. On the other hand, we can also place the ships randomly, then things get quite complicated, because the ships are not supposed to 'collide'. For us the simple version is fine.</p>
<p>
What remains to be implemented is the mousePressed(). We'll use our integer division trick again:</p>
<pre style="margin-left: 40px;">
function mousePressed() {
let i = Math.trunc(mouseX / STEP);
let j = Math.trunc(mouseY / STEP);
showLabelAt(i, j);
}</pre>
<p>
Next, we implement the showLabelAt(int i, int j) method: it checks the board array to see if there is a ship at the location the user selected:</p>
<pre style="margin-left: 40px;">
function showLabelAt(i, j) {
let lbl = new GLabel("" + board[i][j]);
if (board[i][j] === undefined) {
lbl = new GLabel(".");
}
lbl.setFont("SansSerif-bold-24");
let x = i * STEP + 7;
let y = j * STEP + 24;
add(lbl, x, y);
}</pre>
<p>
This is another trick that can save you lots of unnecessary code. What's left to do is to draw the label at the position where the mouse was clicked. And that's it.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/cityAtNight" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/cityAtNight.png" style="width: 200px; height: 150px; display: block;" />Try it</a></div>
<h2>
CityAtNight</h2>
<p>
Reuse is a very central concept of object orientation. This can be achieved with inheritance as well as with composition. Let's start with an example for composition. Let's remember chapter 2, where we programmed a skyscraper. If we want to draw a whole city now, it would be very practical if we could reuse our skyscrapers:</p>
<pre style="margin-left: 40px;">
const rgen = new RandomGenerator();
function setup() {
...
for (let i = 0; i < 8; i++) {
let cols = rgen.nextInt(4, 6);
let rows = rgen.nextInt(4, 8);
let x = rgen.nextInt(-40, APP_WIDTH - 40); // 120
let y = rgen.nextInt(APP_HEIGHT / 4, APP_HEIGHT / 2); // 30
let h = <span style="color:#0000ff;">new GSkyscraper(rows, cols);</span>
add(h, x, y);
}
}</pre>
<p>
So we would need a class GSkyscraper to draw a skyscraper. Since a skyscraper consists of several GRects, it makes sense, analogous to the GSmiley, to use a GCompound:</p>
<pre style="margin-left: 40px;">
class GSkyscraper extends <span style="color:#0000ff;">GCompound</span> {
...
}</pre>
<p>
As with any class, we need a constructor</p>
<pre style="margin-left: 40px;">
constructor(rows, cols) { {
...
}</pre>
<p>
in which we pass the number of window rows and columns we want the skyscraper to have. Depending on whether all skyscrapers should look the same or differently we have to add some randomness to the addWindow() method.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/sevenSegmentDisplayProgram" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/sevenSegmentDisplayProgram.png" style="width: 200px; height: 100px; display: block;" />Try it</a></div>
<h2>
SevenSegmentDisplay</h2>
<p>
Another nice example of reuse by means of composition is the seven-segment display. Let's remember chapter 2, where we programmed a seven-segment display. If we now need several of these seven-segment displays, e.g. for a counter, a calculator or a clock, then it would be practical if there was a SevenSegmentDisplay class that we could simply use several times, similar to a GRect.</p>
<p>
Since a seven-segment display consists of several GRects, it makes sense, similar to the GSmiley, to again use a GCompound:</p>
<pre style="margin-left: 40px;">
class SevenSegmentDisplay extends <span style="color:#0000ff;">GCompound</span> {
...
}</pre>
<p>
As with any class, we need a constructor</p>
<pre style="margin-left: 40px;">
constructor(width, height, ledWidth) {
...
}</pre>
<p>
in which we ideally specify the width and height of the display, as well as the width of the LEDs. The constructor should then construct the display from GRects.</p>
<p>
Really practical would be a displayNumber(char c) method,</p>
<pre style="margin-left: 40px;">
displayNumber(c) {
c = '' + c;
this.turnAllSegmentsOff();
switch (c) {
case '0':
const code0 = [1, 1, 1, 1, 1, 0, 1];
this.turnSegmentsOn(code0);
break;
case '1':
...
}
}</pre>
<p>
to which you simply pass a number, and which then displays it. The turnSegmentsOn() method could look like this:</p>
<pre style="margin-left: 40px;">
turnSegmentsOn(code) {
if (code[0] == 1) {
this.upperFrontVertical.setColor(this.colorOn);
}
...
}</pre>
<p>
The SevenSegmentDisplay can then be easily used in a graphics program:</p>
<pre style="margin-left: 40px;">
function setup() {
...
ssd1 = new SevenSegmentDisplay(40, 80, LED_WIDTH);
add(ssd1, X_OFFSET + LED_WIDTH, LED_WIDTH);
ssd2 = new SevenSegmentDisplay(40, 80, LED_WIDTH);
add(ssd2);
ssd2.move(X_OFFSET + 40 + 3 * LED_WIDTH, LED_WIDTH);
}</pre>
<p>
Extension: Instead of the JSTextField you could also use the SevenSegmentDisplay for the Calculator.</p>
<p>
.</p>
<div style="display:block; float: right; margin: 10px;">
<a href="./src/tryIt.html?name=Pr7_Asteroids/birdFlocking" style="display: block; text-align: center;" target="_blank"><img alt="" src="img/birdFlocking.png" style="width: 200px; height: 200px; display: block;" />Try it</a></div>
<h2>
BirdFlocking</h2>
<p>
Swarming behavior can be observed in fish, birds and many other animals. Interestingly, swarm behavior can be simulated relatively easily. The individuals in a swarm, sometimes called boids, only have to follow three simple rules [11]:</p>
<ul>
<li>
Separation: keep some distance from your neighbors if you get too close (short range repulsion).</li>
<li>
Alignment: move roughly in the direction of your neighbors.</li>
<li>
Cohesion: move roughly towards the common center of your neighbors (long range attraction).</li>
</ul>
<p>
The simulation is similar to the Planets project (later in this chapter), with the subtle difference that instead of Newton's gravity, the boid rules apply.</p>
<p>
.</p>
<p>