-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
831 lines (742 loc) · 51.5 KB
/
index.html
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
<!DOCTYPE html>
<html>
<head>
<title>Pong Tutorial</title>
<link rel="stylesheet" href="style.css">
<link href="prism/prism.css" rel="stylesheet"/>
</head>
<body>
<div id="navbar">
<h2>Einführung</h2>
<a href="#intro">Einführung</a>
<a href="#vorwissen">Vorwissen</a>
<a href="#tip">Ein Tipp</a>
<br>
<h2>Schritten</h2>
<a href="#indexhtml">HTML-Seite erstellen</a>
<a href="#dercanvas">HTML-Canvas</a>
<a href="#stylecss">CSS</a>
<a href="#scriptjs">JavaScript</a>
<a href="#paddle">Schläger</a>
<a href="#gameloop">Spielschleife</a>
<a href="#controller">Controller</a>
<a href="#ball">Ball</a>
<a href="#style">Text und Linie</a>
<a href="#paddle2">Schläger Nr. 2</a>
<h2>Fazit</h2>
<a href="#conclusion">Fazit</a>
</div>
<div id="main">
<h1 id="intro" class="title"><u>Lass uns ein Spiel programmieren!</u></h2>
<p>Dies ist ein Step-by-Step Tutorial, der Ihnen zeigen wird, wie Sie das Spiel "Pong" mit HTML und JavaScript erstellen können. Scrolle weiter und nach eine oder zwei Stunden werden Sie ein komplettes, selbst-gemachtes Pong-Spiel haben! Nachdem Sie fertig sind, können Sie diesen neuen Wissen benutzen, um an diesen Spiel weiterzuarbeiten oder sogar eine neuen selber erstellen.</p>
<p><strong>Warum Pong?</strong> Pong ist ein klassischen Retro-Spiel. Es ist sehr simpel, die Spielmechanicken sind nicht komplex. Für Pong brauchen wir nur die Grundlagen, jedoch genug um in der Welt der Spielentwicklung einzusteigen.</p>
<p>Für jeden Objekt (Spieler-Schläger, Komputer-Schläger, Ball) definiere ich in diesem Tutorial individuelle Variablen für die Positionen, Geschwindigkeiten, Größen, usw.. Ich konnte natürlich JavaScript-Objekte oder Klassen benutzen, um den Code besser zu strukturieren, aber da das hier Anfängerfreundlich sein soll, benutze ich keine, da Variablen vielleicht für manchen einfacher zu benutzen und verstehen sind. Falls Sie doch zu viele Variablen chaotisch finden, können Sie natürlich auch die obengenannte Strukturen benutzen. Ich empfehle aber, als erstes diesen Code zu benutzen, und danach können Sie es ändern, so wie Sie es haben wollen.</p>
<br>
<p class="game-caption"><strong>Das Endergebenis (nutze Pfeiltasten um zu spielen)</strong></p>
<canvas id="pong" width="800" height="500"></canvas>
<script src="pong.js"></script>
<h1 id="vorwissen" class="title">Vorwissen</h2>
<hr>
<p>Wir Programmieren das Spiel mit JavaScript und ein wenig HTML und CSS. Obwohl ich versucht habe, diesen Tutorial Anfängerfreundlich zu machen, ist es empfohlen, mindestens die Grundlagen von JavaScript und das Programmieren allgemein zu haben. <strong>Falls Sie irgendetwas nicht verstehen, empfehle ich Ihnen, dass Sie darüber googeln und nachlesen.</strong></p>
<p>Zwei weitere Voraussetzungen sind ein Browser und eine IDE. IDE steht für "Integrated Development Environment", also Integrierte Entwicklungsumbegung. Das ist das Programm, wo wir unser Code schreiben. Sie können benutzen, was Sie wollen, ich aber empfehle VSCode (Visual Studio Code). Die können Sie <a href="https://code.visualstudio.com/download" target="_blank">hier</a> kostenlos installieren. Ich gehe in meinem Tutorial davon aus, dass Sie wissen, wie man mit einer IDE umgeht und wie man eine HTML-Seite in einem Browser öffnet.</p>
<h1 id="tip" class="title">Ein Tipp</h2>
<hr>
<p>Falls Sie bei irgendeinem Schritt irgendetwas falsch gemacht haben, öffnen Sie das Spiel in ihrem Browser, clicken Sie auf die rechte Maustaste, gehen Sie auf "Untersuchen" (auf Englisch - "Inspect").</p>
<img src="images/screenshot3.png" alt="Clicken Sie auf Untersuchen">
<p>Eine Art Fenster soll auf der rechte Seite geöffnet werden sein. Gehen Sie darauf und clicken Sie auf <strong>"Console"</strong>. <strong>Hier werden Fehlermeldungen gezeigt, was beim Programmieren SEHR hilfreich sein kann.</strong></p>
<img src="images/screenshot4.png" alt="Ein Fenster wird rechts geöffnet. Clicken Sie auf Console, dort werden Fehlermeldungen angezeigt.">
<h1 id="indexhtml" class="title">Schritt 1: HTML-Seite erstellen</h1>
<hr>
<p>Als erstes brauchen wir die Grund-HTML-Struktur. Damit erstellen wir eine leere Seite namens "Pong". Kopiere diesen Quellcode in eine Datei namens <u>index.html</u>; speichere sie und öffne sie in deinem belibten Browser.</p>
<div class="code-block">
<pre><code class="language-html"><!DOCTYPE html>
<html>
<head>
<title>Pong</title>
</head>
<body>
</body>
</html></code></pre>
</div>
<h1 id="dercanvas" class="title">Schritt 2: Der HTML-Canvas</h1>
<hr>
<p>Danach brauchen wir einen <a href="https://www.w3schools.com/html/html5_canvas.asp" target="blank">Canvas</a>. Der Canvas ist ein besonderer Object, der uns erlaubt, Grafiken darauf zu zeichnen. Auf dem Canvas kann man vielfältige Objekte zeichnen, z.B.: Vierecke, was ist genau das, was wir benutzen werden.<br><br>Erstelle einen Canvas im <body> bereich so:</p>
<div class="code-block">
<pre><code class="language-html"><canvas id="mein-spiel" width=800 height=500></canvas></code></pre>
</div>
<p>So erstellen wir einen Canvas mit einer id von "mein-spiel" Die "width" und "height" Parameter (Engl. Breite/Höhe) kontrollieren jeweils die Breite und die Höhe vom Canvas.</p>
<h1 id="stylecss" class="title">Schritt 3: CSS</h1>
<hr>
<p>Wenn Sie nun ihre Seite speichern und öffnen, ist es wahrscheinlich, dass Sie keine Änderungen sehen werden. Das liegt daran, dass der Canvas die selbe Farbe hat wie der Hintergrund. Lass uns dann eine CSS Datei erstellen, damit wir die Farbe ändern können.<br><br>Erstellen Sie eine neue Datei namens <u>style.css</u> und ändern Sie die Farbe des Canvas Elements:</u></p>
<div class="code-block">
<pre><code class="language-css">canvas {
background-color: black;
}</code></pre>
</div>
<p>Momentan werden Sie wieder nichts neues sehen. Wir mussen die CSS-Datei mit unseren HTML-Seite linken. Im <head> Bereich fügen Sie dies ein:</p>
<div class="code-block">
<pre><code class="language-html"><link rel="stylesheet" href="style.css"/></code></pre>
</div>
<p>Ihr HTML Quellcode soll nun so aussehen:</p>
<div class="code-block">
<pre><code class="language-html"><!DOCTYPE html>
<html>
<head>
<title>Pong</title>
<link rel="stylesheet" href="style.css"/>
</head>
<body>
<canvas id="mein-spiel" width=800 height=500></canvas>
</body>
</html></code></pre>
</div>
<h1 id="scriptjs" class="title">Schritt 4: JavaScript</h1>
<hr>
<p>Nun haben wir einen schwarzen, leeren Canvas. Wir brauchen aber erstmal zwei Schläger, womit wir spielen werden.<br><br>Erstmal brauchen wir einen Skript. Dies schreiben wir mit JavaScript. Erstellen Sie als erstes eine weitere Datei namens <u>script.js</u> mit folgendem Quellcode:</p>
<div class="code-block">
<pre><code class="language-js">const spielCanvas = document.getElementById('mein-spiel') // Referenz zur Canvas
const kontext = spielCanvas.getContext('2d') // 2D Kontext von unserer Canvas</code></pre>
</div>
<p>Was macht dieses Code denn wohl?<br><br>Die erste Zeile erstellt einen Objekt namens "spielCanvas", die einen HTML Element mit einer ID "mein-spiel" <a target="_blank", href="https://www.w3schools.com/jsref/met_document_getelementbyid.asp">referenziert</a>. Das ist tatsächlich diesselbe ID, die unserer Canvas hat. <strong>Also ist dieser Objekt nun eine Referenz an unser HTML Canvas</strong>. Dies brauchen wir später.<br><br>Die zweite Zeile ist viel interresanter. Wir definieren ein Objekt "kontext". Dies ist der 2D Kontext von unserer Canvas.<strong> Die 2D Kontext brauchen wir, um 2D Graphiken auf unser Canvas zeichnen zu können.</strong></p>
<br><br>
<p><strong>Lass uns einen Rechteck auf unser Canvas zeichnen.</strong> Fügen Sie dieses Code unter den ersten zwei Zeilen ein:</p>
<div class="code-block">
<pre><code class="language-js">kontext.fillStyle = 'white' // Weiße Farbe. Unser Rechteck wird mit dieser Farbe gefüllt.
// Rechteck zeichnen
kontext.beginPath() // Der Browser soll anfangen, eine Form zu zeichnen (Position und Größe werden unten definiert)
kontext.rect(100, 100, 50, 150) // Ein Rechteck bei der Position x=100, y=100 mit einer Breite von 50 Pixels und Höhe von 150
kontext.fill() // Den Rechteck mit weißer Farbe füllen (Farbe wurde Oben definiert mit "kontext.fillStyle")</code></pre>
</div>
<p><strong>Sie werden momentan kein Rechteck sehen, wenn sie die HTML Seite öffnen, da <u>der Skript überhaupt nicht läuft.</u></strong> Das ist aber leicht zu fixen. Unter unserem <canvas> Element<br>in index.html, fügen sie das ein:</p>
<div class="code-block">
<pre><code class="language-html"><script src="script.js"></script></code></pre>
</div>
<p>Mit der <script> Tag wird unser JavaScript-Code ausgeführt.</p>
<h1 id="paddle" class="title">Schritt 5: Schläger</h1>
<hr>
<p>Wir definieren eine Funktion, die einen weißen Rechteck zeichnet (damit unser Code nicht unlesbar wird, da wir mehrere Rechtecke zeichnen werden müssen). Dafür nehmen wir unsere Code, den wir im letzten Schritt geschrieben haben und fügen sie in eine Funktiondefinition ein:</p>
<div class="code-block">
<pre><code class="language-js">const spielCanvas = document.getElementById('mein-spiel') // Referenz zur Canvas
const kontext = spielCanvas.getContext('2d') // 2D Kontext von unserer Canvas
kontext.fillStyle = 'white' // Wir werden alle Figuren weiß zeichnen, also mussen wir die Farbe nur einmal einstellen
// Eine Funktion, die einen weißen Rechteck zeichnet
function zeichneRechteck(xPosition, yPosition, breite, hoehe) {
kontext.beginPath()
kontext.rect(xPosition, yPosition, breite, hoehe)
kontext.fill()
}
zeichneRechteck(100, 200, 50, 200) // Zeichne einen Rechnet bei x=100, y=200 mit der Breite 50 und die Höhe 200</code></pre>
</div>
<p>So haben wir eine Funktion names <u>zeichneRechteck</u> definiert mit der Parametern <u>xPosition</u>, <u>yPosition</u>, <u>breite</u>, und <u>hoehe</u> (Höhe).<br><br>Um zu sehen, ob der Code funktioniert, rufen sie die Funktion mit verschiedenen Parametern auf, wie oben auf der unterste Zeile dargestellt wird.</p>
<br><br>
<p>Damit der Schläger sich bewegen kann, mussen wir seine Position auf dem Canvas mit Variablen beschreiben. <strong>Definieren Sie zwei Variablen, jeweils eine für die X und Y position des Schlägers</strong> (definieren Sie die Variablen am Besten in der obersten Zeilen im Skript-Datei, jedoch unter den spielCanvas und kontext).</p>
<div class="code-block">
<pre><code class="language-js">let schlaegerXPosition = 100
let schlaegerYPosition = 100</code></pre>
</div>
<p>Auch hilfreich wäre es Variablen zu haben, die die Größe unseres Schlägers beschreiben (und auch die Größe vom zukünftigen Computer-Schläger). Diese definieren Sie bitte über schlaegerXPosition und schlaegerYPosition:</p>
<div class="code-block">
<pre><code class="language-js">const schlaegerBreite = 20
const schlaegerHoehe = 100</code></pre>
</div>
<p>Aktualisieren Sie nun den Code für das Zeichnen des Schlägers:</p>
<div class="code-block">
<pre><code class="language-js">zeichneRechteck(schlaegerXPosition, schlaegerYPosition, schlaegerBreite, schlaegerHoehe)</code></pre>
</div>
<br><br><br><br>
<p>Damit der Schläger in der Mitte der Canvas strartet, sollen wir die neue Position ausrechnen. Aktualisieren Sie die Definition für <u>schlaegerYPosition</u>. Die Y-Position des Sclägers wird auf die Hälfte von der Höhe der Canvas eingestellt, <strong>also den Mittelpunkt davon.</strong></p>
<div class="code-block">
<pre><code class="language-js">let schlaegerYPosition = spielCanvas.height / 2</code></pre>
</div>
<p><strong>WICHTIG! Da wir die Höhe vom Canvas in dieser Rechnung benutzen, mussen wir DIESE VARIABLE UNTER DER DEFINITION VON <u>spielCanvas</u> DEFINIEREN!</strong></p>
<br><br><br><br>
<p>Sie haben wahrscheinlich bemerkt, dass der Schläger ein wenig <strong>unter</strong> der Mitte von der Canvas gezeichnet wird. Das liegt daran, dass die Position des Rechteckes in wirklichkeit die Position von der oben-linkerer Ecke beschreibt.</p>
<img src="images/screenshot1.png" alt="Screenshot: der Schläger wird unter der Mitte der Canvas gezeichnet.">
<p>Damit der Schläger wirklich in der Mitte gezeichnet wird, müssen wir die Hälfte der Höhe vom Schläger von der Hälfte der Höhe von der Canvas subtrahieren:</p>
<div class="code-block">
<pre><code class="language-js">let schlaegerYPosition = (spielCanvas.height / 2) - (schlaegerHoehe / 2)</code></pre>
</div>
<br><br><br><br>
<p>Die JavaScript-Datei soll nun ungefähr so aussehen:</p>
<div class="code-block">
<pre><code class="language-js">const spielCanvas = document.getElementById('mein-spiel') // Referenz zur Canvas
const kontext = spielCanvas.getContext('2d') // 2D Kontext von unserer Canvas
// Breite bzw. Höhe der Schläger
const schlaegerBreite = 20
const schlaegerHoehe = 100
// Position vom Schläger (Spieler)
let schlaegerXPosition = 100
let schlaegerYPosition = (spielCanvas.height / 2) - (schlaegerHoehe / 2)
kontext.fillStyle = 'white' // Wir werden alle Figuren weiß zeichnen, also mussen wir die Farbe nur einmal einstellen
// Eine Funktion, die einen weißen Rechteck zeichnet
function zeichneRechteck(xPosition, yPosition, breite, hoehe) {
kontext.beginPath()
kontext.rect(xPosition, yPosition, breite, hoehe)
kontext.fill()
}
// Zeichne den Schläger des Spielers
zeichneRechteck(schlaegerXPosition, schlaegerYPosition, schlaegerBreite, schlaegerHoehe)</code></pre>
</div>
<h1 id="gameloop" class="title">Schritt 6: Spielschleife</h1>
<hr>
<p>Videospiele benutzen eine sogennannte "Spielschleife" um sie interaktiv zu machen. Die Spielschleife aktualisiert Dinge wie z.B. die Positionen von Objekten (in unserem Fall - die Schläger und den Ball) und zeichnet sie. Die Schleife macht dies Dutzende Male pro Sekunde (normalerweise 60 mal pro Sekunde, dies hängt aber davon ab, auf welche Bildwiederholfrequenz euer Rechner eingestellt ist). Professionelle Spiele benutzen oft mehrere Schleifen (z.B. eine für das Zeichnen der Objekte, eine, die die Positionen der Objekte aktualisiert, usw.), unser Spiel ist aber sehr klein und simpel. Wir werden also nur eine benutzen.</p>
<br>
<p>Wie machen wir das? Eine Option ist es, eine endlose <a href="https://www.w3schools.com/js/js_loop_while.asp" target="_blank">while</a> Schleife zu schreiben, das ist aber wegen mehreren Grunden keine gute Idee. Eine um vielfach bessere Lösung ist es, eine Funktion names "spielschleife" zu definieren, die <a href="https://www.w3schools.com/jsref/met_win_requestanimationframe.asp" target="_blank">window.requestAnimationFrame()</a> aufruft. window.requestAnimationFrame() ist eine einegbaute Methode genau für unser zweck bestimmt. Wie es im Name steht, stellt requestAnimationFrame() eine Anfrage an den Browser, die Bildschirm zu aktualisieren. Die Funktiondefinition sieht so aus:</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
window.requestAnimationFrame(spielschleife) // Anfrage für das nächste Bild
}
spielschleife() // Startet die Spielschleife</code></pre>
</div>
<p>Diesen Code-Auszug sollen Sie in die unterste Zeile in ihrem Skript-Datei einfügen.</p>
<br><br>
<p>Nun können wir den Funktionaufruf zeichneRechteck in unsere neue Spielschleife verschieben:</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
// Zeichne den Schläger des Spielers
zeichneRechteck(schlaegerXPosition, schlaegerYPosition, schlaegerBreite, schlaegerHoehe)
window.requestAnimationFrame(spielschleife) // Anfrage für das nächste Bild
}
spielschleife() // Startet die Spielschleife</code></pre>
</div>
<p>Lass uns Testen, ob die Schleife tatsächlich funktioniert. Wir schreiben eine Codezeile, die den Schläger nach jeder Bildschirmaktualisierung nach oben um ein Pixel verschiebt. Die Spielschleife soll danach so aussehen:</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
zeichneRechteck(schlaegerXPosition, schlaegerYPosition, schlaegerBreite, schlaegerHoehe) // Zeichne den Schläger des Spielers
schlaegerYPosition -= 1 // Subtrahiere 1 von der Y-Position des Schlägers (bewegt ihn 1 Pixel nach oben nach jeder Bildschirmaktualisierung)
window.requestAnimationFrame(spielschleife) // Anfrage für das nächste Bild
}
spielschleife() // Startet die Spielschleife</code></pre>
</div>
<p>Sie werden aber sehen, dass der Schläger wirk sich nicht zu bewegen, sonder wird es immer länger. Das liegt daran, dass wir einen neuen Rechteck jedes mal zeichnen, ohne den Alten zu löschen.</p>
<img src="images/screenshot2.png" alt="Screenshot: der Schläger wirkt sich nicht zu bewegen, sondern länger werden.">
<p>Damit alles richtig funktioniert, soll das Programm jedes mal die ganze Canvas löschen und alle Objekte darauf wieder neuzeichnen. Das machen wir mit der <a href="https://www.w3schools.com/jsref/canvas_clearrect.asp" target="_blank">clearRect</a> Methode so:</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
kontext.clearRect(0, 0, spielCanvas.width, spielCanvas.height); // Lösche die Canvas bevor andere Objekte gezeichnet werden können
zeichneRechteck(schlaegerXPosition, schlaegerYPosition, schlaegerBreite, schlaegerHoehe) // Zeichne den Schläger des Spielers
schlaegerYPosition -= 1 // Subtrahiere 1 von der Y-Position des Schlägers (bewegt ihn 1 Pixel nach oben nach jeder Bildschirmaktualisierung)
window.requestAnimationFrame(spielschleife) // Anfrage für das nächste Bild
}
spielschleife() // Startet die Spielschleife</code></pre>
</div>
<p>Jetzt soll der Schläger ohne Probleme nach oben schweben.</p>
<h1 id="controller" class="title">Schritt 7: Controller</h1>
<hr>
<p>Jetzt können wir endlich unser Spiel interaktiv machen. Wir programmieren den "Controller" für unser Schläger. Der Schläger soll sich bewegen, wenn der Spieler eine Pfeiltasten drückt. Als ersten definieren wir Variablen für die Key-Codes für Pfeiltasten (damit es mehr lesbar wird). Key-Codes sind einfach Nummern, die uns sagen, welche Taste gedrückt wird. In JavaScript steht die Nummer 38 für die Pfeiltaste nach oben und die Nummer 40 für die Pfeiltaste nach unten. Auch definieren wir die Geschwindigkeit für unser Schläger.</p>
<div class="code-block">
<pre><code class="language-js">const pfeiltasteOben = 38
const pfeiltasteUnten = 40
const schlaegerBewegungsgeschwindigkeit = 5</code></pre>
</div>
<p>Jetzt definieren wir eine <a href="https://www.geeksforgeeks.org/javascript-anonymous-functions/" target="_blank">anonyme Funktion</a>, die den Schläger nach oben/unten verschiebt, wenn die jeweilige Pfeiltasten gedrückt werden. Die Seite wartet darauf, dass eine Taste gedrückt wird. Wenn eine gedrückt wird, dann wird dieser Funktion aufgeruft. <strong>Achtung!</strong> Diese Funktion soll <strong>NICHT</strong> in die Spielschleife definiert werden!</p>
<div class="code-block">
<pre><code class="language-js">// Controller für den Schläger
document.onkeydown = function(event) {
if(event.keyCode === pfeiltasteOben) {
schlaegerYPosition -= schlaegerBewegungsgeschwindigkeit
} else if(event.keyCode === pfeiltasteUnten) {
schlaegerYPosition += schlaegerBewegungsgeschwindigkeit
}
}</code></pre>
</div>
<p>Wenn Sie nun das Spiel austesten, merken Sie wahrscheinlich, dass der Schläger sich ziemlich komisch bewegt. Es bewegt sich ein wenig, danach stoppt es, und dann geht es wieder. Um das zu fixen, definieren wir noch eine Variable, die für die Geschwindigkeit des Schlägers zuständig ist.</p>
<div class="code-block">
<pre><code class="language-js">let schlaegerYGeschwindigkeit = 0</code></pre>
</div>
<p>Aktualisieren Sie die Controller-Code:</p>
<div class="code-block">
<pre><code class="language-js">// Controller für den Schläger
document.onkeydown = function(event) {
if(event.keyCode === pfeiltasteOben) {
schlaegerYGeschwindigkeit = -schlaegerBewegungsgeschwindigkeit // Negative Geschwindigkeit = nach oben gehen
} else if(event.keyCode === pfeiltasteUnten) {
schlaegerYGeschwindigkeit = schlaegerBewegungsgeschwindigkeit
}
}</code></pre>
</div>
<p>Da wir nun nicht die eigentliche Position des Schlägers aktualisieren, sondern nur die Y-Geschwindigkeit, sollen wir eine Code-Zeile einfügen, die die Position aktualisiert. Wir addieren einfach die zurzeitige Position und die Geschwindigkeit. Fügen Sie diese Zeile in die Spielschleife-Funktion ein:</p>
<div class="code-block">
<pre><code class="language-js">// Aktualisiere die Position des Schlägers in Abhängigkeit von seiner Geschwindigkeit
schlaegerYPosition += schlaegerYGeschwindigkeit</code></pre>
</div>
<p>Damit der Schläger Stoppt, wenn der Spieler auf <strong>KEINE</strong> Pfeiltaste drückt, sollen wir eine weitere Funktion dafür definieren. Diesmal wird es ausgeführt, wenn der Spieler eine Taste loslässt. Auch diese soll <strong>AUßER</strong> der Spielschleife-Funktion definiert werden.</p>
<div class="code-block">
<pre><code class="language-js">// Wenn der Spieler eine Taste loslässt, dann soll der Schläger aufhören, sich zu bewegen
document.onkeyup = function(event) {
schlaegerYGeschwindigkeit = 0
}</code></pre>
</div>
<p>Ein letztes Problem, das wir noch haben ist, dass <strong>der Schläger aus der Sicht verschwinden kann, wenn er zu weit verschoben wird</strong>. Das fixen wir, indem wir jedes mal überprüfen, ob der Schläger schon am Rand des Sichtfeldes ist, wenn der Schläger sich nach oben oder unten bewegen muss. Wenn das so ist, bewegen wir ihn einfach nicht weiter. <strong>Wir fügen die Zeile, die die Position des Schlägers aktualisiert in eine if-Answeisung ein</strong>. Diese Anweisung wird jedes mal überprüfen, ob der Schläger schon am Rand der Canvas ist, bevor es seine Position aktualisiert:</p>
<div class="code-block">
<pre><code class="language-js">// Verschiebe den Schläger, WENN er sich nach OBEN bewegt UND NICHT am OBEREN
// RAND ist, ODER WENN es sich nach UNTEN bewegt UND NICHT am UNTEREN RAND ist.
if( ( schlaegerYGeschwindigkeit < 0 && schlaegerYPosition > 0 ) || ( schlaegerYGeschwindigkeit > 0 && (schlaegerYPosition + schlaegerHoehe) < spielCanvas.height ) ) {
// Aktualisiere die Position des Schlägers in Abhängigkeit von seiner Geschwindigkeit
schlaegerYPosition += schlaegerYGeschwindigkeit
}</code></pre>
</div>
<br><br><br><br>
<p><strong>Lass uns alles zusammenfassen, was wir bis jetzt geschrieben haben.</strong></p>
<p>Bis jetzt haben wir einen Schläger, der sich frei auf der Y-Achse bewegen kann, jedoch nicht weiter als die Rände des Sichtfeldes.</p>
<div class="code-block">
<pre><code class="language-js">const spielCanvas = document.getElementById('mein-spiel') // Referenz zur Canvas
const kontext = spielCanvas.getContext('2d') // 2D Kontext von unserer Canvas
// Breite bzw. Höhe der Schläger
const schlaegerBreite = 20
const schlaegerHoehe = 100
// KeyCodes für die Pfeiltasten
const pfeiltasteOben = 38
const pfeiltasteUnten = 40
// Schläger
let schlaegerXPosition = 100
let schlaegerYPosition = (spielCanvas.height / 2) - (schlaegerHoehe / 2)
let schlaegerYGeschwindigkeit = 0
const schlaegerBewegungsgeschwindigkeit = 5 // 5 Pixeln pro Frame
kontext.fillStyle = 'white' // Wir werden alle Figuren weiß zeichnen, also mussen wir die Farbe nur einmal einstellen
// Eine Funktion, die einen weißen Rechteck zeichnet
function zeichneRechteck(xPosition, yPosition, breite, hoehe) {
kontext.beginPath()
kontext.rect(xPosition, yPosition, breite, hoehe)
kontext.fill()
}
// Controller für den Schläger
document.onkeydown = function(event) {
if(event.keyCode === pfeiltasteOben) {
schlaegerYGeschwindigkeit = -schlaegerBewegungsgeschwindigkeit // Negative Geschwindigkeit = nach oben gehen
} else if(event.keyCode === pfeiltasteUnten) {
schlaegerYGeschwindigkeit = schlaegerBewegungsgeschwindigkeit
}
}
// Wenn der Spieler eine Taste loslässt, dann soll der Schläger aufhören, sich zu bewegen
document.onkeyup = function(event) {
schlaegerYGeschwindigkeit = 0
}
function spielschleife() {
// Verschiebe den Schläger, WENN er sich nach OBEN bewegt UND NICHT am OBEREN
// RAND ist, ODER WENN es sich nach UNTEN bewegt UND NICHT am UNTEREN RAND ist.
if( ( schlaegerYGeschwindigkeit < 0 && schlaegerYPosition > 0 ) || ( schlaegerYGeschwindigkeit > 0 && (schlaegerYPosition + schlaegerHoehe) < spielCanvas.height ) ) {
// Aktualisiere die Position des Schlägers in Abhängigkeit von seiner Geschwindigkeit
schlaegerYPosition += schlaegerYGeschwindigkeit
}
// Lösche die Canvas bevor andere Objekte gezeichnet werden können
kontext.clearRect(0, 0, spielCanvas.width, spielCanvas.height);
// Zeichne den Schläger des Spielers
zeichneRechteck(schlaegerXPosition, schlaegerYPosition, schlaegerBreite, schlaegerHoehe)
// Anfrage für das nächste Frame (Bild)
window.requestAnimationFrame(spielschleife)
}
spielschleife() // Startet die Spielschleife</code></pre>
</div>
<h1 id="ball" class="title">Schritt 8: Der Ball</h1>
<hr>
<p>Jetzt können wir endlich mal den Ball programmieren. Der Ball in Pong ist nichts außer einem weißem Quader, der mit konstanter Geschwindigkeit durch den Raum fliegt und ab und zu von Wände und Schläger abprallt. Zuerst definieren wir die benötigte Variablen für den Ball. Das wären die Größe, die X- und Y-Position, und die X- und Y-Geschwindigkeit.</p>
<div class="code-block">
<pre><code class="language-js">// Variablen für den Ball
const ballGroesse = 20 // Breite und Höhe des Balls (es ist ein Quader, die beide Werte sind gleich)
let ballXPosition = (spielCanvas.width / 2) - (ballGroesse / 2) // Mitte der Canvas (X-Achse)
let ballYPosition = (spielCanvas.height / 2) - (ballGroesse / 2) // Mitte der Canvas (Y-Achse)
let ballXGeschwindigkeit = -3
let ballYGeschwindigkeit = -3</code></pre>
</div>
<p>Wir fügen einen weiteren aufruf der zeichneRechteck Funktion ein, damit unser Ball gezeichnet werden kann. Fügen Sie diese Zeile in die Spielschleife-Funktion ein:</p>
<div class="code-block">
<pre><code class="language-js">// Zeichne den Ball
zeichneRechteck(ballXPosition, ballYPosition, ballGroesse, ballGroesse)</code></pre>
</div>
<p>Auch in der Spielschleife sollen wir die Position des Balles in Abhang von der Geschwindigkeit aktualisieren (sowie wir das mit dem Schläger gemacht haben).</p>
<div class="code-block">
<pre><code class="language-js">// Aktualisiere die Position des Balles in Abhängigkeit von seiner Geschwindigkeit
ballXPosition += ballXGeschwindigkeit
ballYPosition += ballYGeschwindigkeit</code></pre>
</div>
<p>Jetzt können wir die Abprall-Funktion programmieren. Es ist eigentlich ziemlich einfach. Alles, was wir machen mussen ist, die Y-Geschwindigkeit des Balles umkehren, wenn er einen Wand trifft.</p>
<img src="images/darstellung.png" alt="Wenn der Ball ab einer Wand abprallt, können wir seine Y-Geschwindigkeit umkehren (z.B. -3 wird zu 3).">
<p>Wir definieren eine Funktion namens "ballAbprall". Diese Funktion wird prüfen, ob der Ball an einem Rand der Canvas ist und wenn das stimmt, kehrt es seine Y-Geschwindikeit um.</p>
<p>Als erstes machen wir es nur für den oberen Rand:</p>
<div class="code-block">
<pre><code class="language-js">function ballAbprall() {
if(ballYPosition <= 0) {
// Abprall ab dem oberem Rand (soll jetzt nach oben gehen)
ballYGeschwindigkeit = Math.abs(ballYGeschwindigkeit)
}
}</code></pre>
</div>
<p>Hier wird die Y-Geschwindigkeit mit <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs" target="_blank">Math.abs()</a> positiv gemacht, weshalb es sich ab dem Abprall nach Unten bewegen wird.</p>
<p>Um es zu testen, fügen Sie die Funktionaufruf für ballAbprall() in die Spielschleife ein.</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
...
ballAbprall()
...
}</code></pre>
</div>
<p>Und nun für den unteren Canvas-Rand:</p>
<div class="code-block">
<pre><code class="language-js">function ballAbprall() {
if(ballYPosition <= 0) {
// Abprall ab dem oberem Rand (soll jetzt nach unten gehen)
ballYGeschwindigkeit = Math.abs(ballYGeschwindigkeit)
} else if((ballYPosition + ballGroesse) >= spielCanvas.height) {
// Abprall ab dem unterem Rand (soll jetzt nach oben gehen)
ballYGeschwindigkeit = -Math.abs(ballYGeschwindigkeit)
}
}</code></pre>
</div>
<p><strong>Falls Sie verwirrt sind:</strong> Wir benutzen Math.abs(), anstatt die Geschwindigkeit direkt umzukehren (z.B. mit ballYGeschwindigkeit = -ballYGeschwindigkeit), weil, wenn wir das so machen und der Ball aus irgendeinem Grund durch die Wand geht (das kann passieren, wenn die Geschwindigkeit hoch ist), wird das Programm jedes mall versuchen, ihn umzukehren, bis es rauß aus der Wand ist. Das heißt, der Ball wird nie heraßkommen können, weil die Geschwindigkeit mehrmals pro Sekunde von positiv zu negativ wechselt.</p>
<p>Dieses Problem taucht nicht auf, wenn das Programm genau weiß, ob die Geschwindikeit positiv oder negativ sein soll nach einem Abprall ab einer Wand.</p>
<p><strong>Aber warum Math.abs()?</strong> Wenn der Ball ab der oberen "Wand" abprallt, wird seine Geschwindigkeit auf den Absolutbetrag von dieser Geschwindigkeit gesetzt. Also ist es egal ob die Geschwindigkeit 3 oder -3 vorher war, wird es immer auf das positive Wert (+3) gesetzt. Beim Abprall von der unteren Wand passiert das Gegenteil. Die Geschwindigkeit wird auf das negative Absolutwert davon gesetzt, also ist der ergebenis immer das negative Wert davon, was es davor war.</p>
<br><br><br><br>
<p><strong>Jetzt mussen wir weitermachen.</strong> In Pong soll der Ball zurück zur Startposition gehen, wenn es den linken oder rechten Rand trifft, danach bekommt der Spieler auf der andere Seite einen Punkt. Als erstes definieren wir zwei Variablen für die Punkten.</p>
<div class="code-block">
<pre><code class="language-js">let menschPunkten = 0 // Punkten des Spielers
let computerPunkten = 0 // Punkte des Computers</code></pre>
</div>
<p>Danach definieren wir eine Funktion, die die Position und Geschwindigkeit des Balles zurückgesetzt.</p>
<div class="code-block">
<pre><code class="language-js">// Setzt den Ball zurück
function ballZurueckstzen() {
ballXPosition = (spielCanvas.width / 2) - (ballGroesse / 2) // Mitte der Canvas (X-Achse)
ballYPosition = (spielCanvas.height / 2) - (ballGroesse / 2) // Mitte der Canvas (Y-Achse)
ballXGeschwindigkeit = -3
ballYGeschwindigkeit = -3
}</code></pre>
</div>
<p>Endlich definieren wir eine Funktion namens "ballGetroffen()". Diese Funktion wird, ähnlich zur "ballAbprall()", prüfen ob der Ball an einem Rand ist. Wenn das so ist, wird seine Position zurückgesetzt und der Spieler (oder das Computer) bekommt einen Punkt.</p>
<div class="code-block">
<pre><code class="language-js">// Prüft, ob der Spieler oder das Computer getroffen hat
function ballGetroffen() {
if(ballXPosition < 0) {
computerPunkten++ // Der Computer hat getroffen!
ballZurueckstzen()
} else if(ballXPosition > spielCanvas.width) {
menschPunkten++ // Der Mensch hat getroffen!
ballZurueckstzen()
}
}</code></pre>
</div>
<p>Vergessen Sie nicht, ballGetroffen() in der Spielschleife aufzurufen!</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
...
ballAbprall()
ballGetroffen()
...
}</code></pre>
</div>
<br><br><br><br>
<p>Sie haben (hoffentlich) schon bemerkt, dass der Ball durch den Schläger geht. Das sollen wir jetzt fixen. Als ersten definieren wir eine Funktion "ballSchlaegerAbprall()". Diese funktion wird prüfen, ob der Ball einen Schläger getroffen hat.</p>
<div class="code-block">
<pre><code class="language-js">function ballSchlaegerAbprall() {
}</code></pre>
</div>
<p>Vergessen Sie nicht, die Funktion in die Spielschleife einzufügen!</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
...
ballAbprall()
ballGetroffen()
ballSchlaegerAbprall()
...
}</code></pre>
</div>
<p>Falls Sie nicht wissen wie wir prüfen werden, ob der Ball einen Schläger trifft, wir werden eine Hitbox programmieren. Hitboxen sind Rechtecken, und wenn zwei dieser Rechtecken sich überlappen, dann heißt das, dass sie sich berühren.</p>
<p>Als ersten prüfen wir, ob der linkerer Rand des Balles links vom rechten Rand des Schlägers:</p>
<div class="code-block">
<pre><code class="language-js">function ballSchlaegerAbprall() {
if(ballXPosition <= (schlaegerXPosition + schlaegerBreite)) {
ballXGeschwindigkeit = Math.abs(ballXGeschwindigkeit)
}
}</code></pre>
</div>
<p>Sie werden es nicht sehen, aber die Hitbox soll nun so assehen (das Grüne Feld stellt die Fläche dar, wo der Ball abprallen kann)</p>
<img src="images/darstellung2.png">
<p>Das ist ein bisshen zu viel, oder? Wir reduzieren die Fläche, indem wir prüfen, ob der ball sich unter dem Schläger befindet.</p>
<div class="code-block">
<pre><code class="language-js">function ballSchlaegerAbprall() {
if(
ballXPosition <= (schlaegerXPosition + schlaegerBreite)
&& (ballYPosition + ballGroesse) >= schlaegerYPosition) {
ballXGeschwindigkeit = Math.abs(ballXGeschwindigkeit)
}
}</code></pre>
</div>
<p>Die Kollisionsfläche sieht nun so aus:</p>
<img src="images/darstellung3.png">
<p>Nun prüfen wir auch ob der Ball nicht unter dem Schläger ist:</p>
<div class="code-block">
<pre><code class="language-js">function ballSchlaegerAbprall() {
if(
ballXPosition <= (schlaegerXPosition + schlaegerBreite)
&& (ballYPosition + ballGroesse) >= schlaegerYPosition
&& ballYPosition <= (schlaegerYPosition + schlaegerHoehe)) {
ballXGeschwindigkeit = Math.abs(ballXGeschwindigkeit)
}
}</code></pre>
</div>
<p>Fast fertig! Nun kann der ball nur hier abprallen:</p>
<img src="images/darstellung4.png">
<p>Damit der ball nicht abprallt, wenn es hinter dem Schläger ist, prüfen wir, ob die rechteste Seite des Balles linker ist als die linkeste Seite des Schlägers:</p>
<div class="code-block">
<pre><code class="language-js">function ballSchlaegerAbprall() {
if(
ballXPosition <= (schlaegerXPosition + schlaegerBreite)
&& (ballYPosition + ballGroesse) >= schlaegerYPosition
&& ballYPosition <= (schlaegerYPosition + schlaegerHoehe)
&& (ballXPosition + ballGroesse) >= schlaegerXPosition) {
ballXGeschwindigkeit = Math.abs(ballXGeschwindigkeit)
}
}</code></pre>
</div>
<p>Der Ball soll jetzt nur abprallen, wenn er den Schläger brührt.</p>
<p><strong>Falls Sie nicht verstanden haben, was hier abgeht:</strong> Wenn die Bedingung in der If-Anweisung Wahr ist, dann wird der ball abgeprallt. Die If-Answeisung prüft, ob der Ball <strong>NICHT über dem Schläger ist</strong> und <strong>NICHT darunter ist</strong> und <strong>NICHT zu weit rechts ist</strong> und <strong>NICHT zu weit links ist</strong>. Wenn alle 4 Bedingungen <strong>WAHR</strong> sind, dann ist die einzige andere Möglichkeit, dass der Ball den Schläger berührt.</p>
<br><br><br><br>
<p><strong>Ihrer Code soll jetzt ungefähr so aussehen:</strong></p>
<div class="code-block">
<pre><code class="language-js">const spielCanvas = document.getElementById('mein-spiel') // Referenz zur Canvas
const kontext = spielCanvas.getContext('2d') // 2D Kontext von unserer Canvas
// Breite bzw. Höhe der Schläger
const schlaegerBreite = 20
const schlaegerHoehe = 100
// KeyCodes für die Pfeiltasten
const pfeiltasteOben = 38
const pfeiltasteUnten = 40
// Schläger
let schlaegerXPosition = 100
let schlaegerYPosition = (spielCanvas.height / 2) - (schlaegerHoehe / 2)
let schlaegerYGeschwindigkeit = 0
const schlaegerBewegungsgeschwindigkeit = 5
// Variablen für den Ball
const ballGroesse = 20 // Breite und Höhe des Balls (es ist ein Quader, die beide Werte sind gleich)
let ballXPosition = (spielCanvas.width / 2) - (ballGroesse / 2) // Mitte der Canvas (X-Achse)
let ballYPosition = (spielCanvas.height / 2) - (ballGroesse / 2) // Mitte der Canvas (Y-Achse)
let ballXGeschwindigkeit = -3
let ballYGeschwindigkeit = -3
let menschPunkten = 0 // Punkten des Spielers
let computerPunkten = 0 // Punkte des Computers
kontext.fillStyle = 'white' // Wir werden alle Figuren weiß zeichnen, also mussen wir die Farbe nur einmal einstellen
// Eine Funktion, die einen weißen Rechteck zeichnet
function zeichneRechteck(xPosition, yPosition, breite, hoehe) {
kontext.beginPath()
kontext.rect(xPosition, yPosition, breite, hoehe)
kontext.fill()
}
// Controller für den Schläger
document.onkeydown = function(event) {
if(event.keyCode === pfeiltasteOben) {
schlaegerYGeschwindigkeit = -schlaegerBewegungsgeschwindigkeit // Negative Geschwindigkeit = nach oben gehen
} else if(event.keyCode === pfeiltasteUnten) {
schlaegerYGeschwindigkeit = schlaegerBewegungsgeschwindigkeit
}
}
// Wenn der Spieler eine Taste loslässt, dann soll der Schläger aufhören, sich zu bewegen
document.onkeyup = function(event) {
schlaegerYGeschwindigkeit = 0
}
function ballAbprall() {
if(ballYPosition <= 0) {
// Abprall ab dem oberem Rand (soll jetzt nach unten gehen)
ballYGeschwindigkeit = Math.abs(ballYGeschwindigkeit)
} else if((ballYPosition + ballGroesse) >= spielCanvas.height) {
// Abprall ab dem unterem Rand (soll jetzt nach oben gehen)
ballYGeschwindigkeit = -Math.abs(ballYGeschwindigkeit)
}
}
// Setzt den Ball zurück
function ballZurueckstzen() {
ballXPosition = (spielCanvas.width / 2) - (ballGroesse / 2) // Mitte der Canvas (X-Achse)
ballYPosition = (spielCanvas.height / 2) - (ballGroesse / 2) // Mitte der Canvas (Y-Achse)
ballXGeschwindigkeit = -3
ballYGeschwindigkeit = -3
}
// Prüft, ob der Spieler oder das Computer getroffen hat
function ballGetroffen() {
if(ballXPosition < 0) {
computerPunkten++ // Der Computer hat getroffen!
ballZurueckstzen()
} else if(ballXPosition > spielCanvas.width) {
menschPunkten++ // Der Mensch hat getroffen!
ballZurueckstzen()
}
}
function ballSchlaegerAbprall() {
if(
ballXPosition <= (schlaegerXPosition + schlaegerBreite)
&& (ballYPosition + ballGroesse) >= schlaegerYPosition
&& ballYPosition <= (schlaegerYPosition + schlaegerHoehe)
&& (ballXPosition + ballGroesse) >= schlaegerXPosition) {
ballXGeschwindigkeit = Math.abs(ballXGeschwindigkeit)
}
}
function spielschleife() {
// Verschiebe den Schläger, WENN er sich nach OBEN bewegt UND NICHT am OBEREN
// RAND ist, ODER WENN es sich nach UNTEN bewegt UND NICHT am UNTEREN RAND ist.
if( ( schlaegerYGeschwindigkeit < 0 && schlaegerYPosition > 0 ) || ( schlaegerYGeschwindigkeit > 0 && (schlaegerYPosition + schlaegerHoehe) < spielCanvas.height ) ) {
// Aktualisiere die Position des Schlägers in Abhängigkeit von seiner Geschwindigkeit
schlaegerYPosition += schlaegerYGeschwindigkeit
}
// Aktualisiere die Position des Balles in Abhängigkeit von seiner Geschwindigkeit
ballXPosition += ballXGeschwindigkeit
ballYPosition += ballYGeschwindigkeit
ballAbprall()
ballGetroffen()
ballSchlaegerAbprall()
// Lösche die Canvas bevor andere Objekte gezeichnet werden können
kontext.clearRect(0, 0, spielCanvas.width, spielCanvas.height)
// Zeichne den Schläger des Spielers
zeichneRechteck(schlaegerXPosition, schlaegerYPosition, schlaegerBreite, schlaegerHoehe)
// Zeichne den Ball
zeichneRechteck(ballXPosition, ballYPosition, ballGroesse, ballGroesse)
// Anfrage für das nächste Bild
window.requestAnimationFrame(spielschleife)
}
spielschleife() // Startet die Spielschleife</code></pre>
</div>
<h1 id="style" class="title">Text und Linie</h2>
<hr>
<p>Gleich machen wir das Spiel fertig, indem wir den zweiten Schläger programmieren. Aber zuerst machen wir was anderes. Im alten Pong-Spiel gab es mehr als nur Schläger und Bälle. Es gab Elemente wie die Nummern, die den Punktenzahl zeigen, und eine Trennlinie.</p>
<img src="images/pong.png" alt="Foto vom altem Pong-Spiel">
<p>Als erstes definieren wir die Farbe und Breite des Trennliniens.</p>
<div class="code-block">
<pre><code class="language-js">kontext.strokeStyle = 'white' // Weiße Farbe
kontext.lineWidth = 2 // Linienbreite</code></pre>
</div>
<p>Danach definieren wir eine Funktion, die eine Linie von A nach B zeichnet.</p>
<div class="code-block">
<pre><code class="language-js">// Zeichnet eine Linie von A nach B
function zeichneLinie(aX, aY, bX, bY) {
kontext.beginPath()
kontext.moveTo(aX, aY)
kontext.lineTo(bX, bY)
kontext.stroke()
}</code></pre>
</div>
<p>Den Aufruf fügen wir in die Spielschleife ein (Unter dem kontext.clearRect(...)-Aufruf!). Damit zeichnen wir eine Linie in der Mitte der Canvas.</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
...
// Zeichne Trennlinie
zeichneLinie(spielCanvas.width/2, 0, spielCanvas.width/2, spielCanvas.height)
...
}</code></pre>
</div>
<br><br><br><br>
<p>Um die Punktezahlen zu zeigen, setzen wir die Schriftart (außer der Spielschleife):</p>
<div class="code-block">
<pre><code class="language-js">kontext.font = "30px Arial"</code></pre>
</div>
<p>In der Spielschleife können wir nun die Punktezahlen mit der <a href="https://www.w3schools.com/graphics/canvas_text.asp">fillText()</a> Funktion zeigen.</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
...
// Punktezahlen zeigen
kontext.fillText(menschPunkten, spielCanvas.width/2 - 20, 50) // Spieler
kontext.fillText(computerPunkten, spielCanvas.width/2 + 20, 50) // Computer
...
}</code></pre>
</div>
<p>Das sieht aber nicht sehr gut aus und kann mit größeren Zahlen problematisch werden:</p>
<img src="images/screenshot5.png">
<p>Das können wir mit der textAlign Variable fixen:</p>
<div class="code-block">
<pre><code class="language-js">...
// Punktezahlen zeigen
kontext.textAlign = 'right'
kontext.fillText(menschPunkten, spielCanvas.width/2 - 20, 50) // Spieler
kontext.textAlign = 'left'
kontext.fillText(computerPunkten, spielCanvas.width/2 + 20, 50) // Computer
...</code></pre>
</div>
<h1 id="paddle2" class="title">Schläger Nr. 2</h2>
<hr>
<p>Wir programmieren endlich den zweiten Schläger. Dieser wird vom Computer Kontrolliert. Zuerst definieren wir die Variablen dafür:</p>
<div class="code-block">
<pre><code class="language-js">// Schläger 2 (Computer)
let schlaeger2XPosition = spielCanvas.width - 100
let schlaeger2YPosition = (spielCanvas.height / 2) - (schlaegerHoehe / 2)
let schlaeger2YGeschwindigkeit = 0</code></pre>
</div>
<p>Wir zeichnen ihn auf der Canvas genau so, wie wir den Spieler-Schläger zeichnen.</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
...
// Zeichne den Schläger der Computer
zeichneRechteck(schlaeger2XPosition, schlaeger2YPosition, schlaegerBreite, schlaegerHoehe)
...
}</code></pre>
</div>
<p>Der Computer-Schläger soll den Ball folgen. Also z.B. falls der Ball höher ist als er, soll der Schläger nach oben bewegen.</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
if((ballYPosition + ballGroesse / 2) < (schlaeger2YPosition + schlaegerHoehe / 2)) {
schlaeger2YGeschwindigkeit = -schlaegerBewegungsgeschwindigkeit // Bewege nach oben, wenn der ball höher ist
} else if((ballYPosition + ballGroesse / 2) > (schlaeger2YPosition + schlaegerHoehe / 2)) {
schlaeger2YGeschwindigkeit = schlaegerBewegungsgeschwindigkeit // Bewege nach unten, wenn der ball niedriger ist
}
...
}</code></pre>
</div>
<p>Die Position der Objekten beschreibt die Position des oben-linkeren Kantes, also addieren wir die Hälfte der Höhe des Schlägers und Balles jeweils zu ihren Y-Positionen (z.B.: ballYPosition + ballGroesse / 2), damit wir den Center Messen.</p>
<p>Damit dieser Schläger auch nicht durch die Wand geht, nehmen wir unseren Code für den Spieler-Schläger, der ihn Stoppt, wenn er einen Wand trifft. Wir ersetzen die Variablen und dann soll es funktionieren.</p>
<div class="code-block">
<pre><code class="language-js">function spielschleife() {
...
if((schlaeger2YGeschwindigkeit < 0 && schlaeger2YPosition > 0) || ( schlaeger2YGeschwindigkeit > 0 && (schlaeger2YPosition + schlaegerHoehe) < spielCanvas.height)) {
// Aktualisiere die Position des Schlägers in Abhängigkeit von seiner Geschwindigkeit
schlaeger2YPosition += schlaeger2YGeschwindigkeit
}
...
}</code></pre>
</div>
<p>Und endlich, damit der Ball abprallt, wenn er den Computer-Schläger trifft, aktualisieren wir ballSchlaegerAbprall(). Wir nehmen unseren alten Code und ändern ein Paar Teile.</p>
<div class="code-block">
<pre><code class="language-js">function ballSchlaegerAbprall() {
if(
ballXPosition <= (schlaegerXPosition + schlaegerBreite)
&& (ballYPosition + ballGroesse) >= schlaegerYPosition
&& ballYPosition <= (schlaegerYPosition + schlaegerHoehe)
&& (ballXPosition + ballGroesse) >= schlaegerXPosition) {
ballXGeschwindigkeit = Math.abs(ballXGeschwindigkeit)
} else if(
// Computer-Schläger
(ballXPosition + ballGroesse) >= schlaeger2XPosition
&& (ballYPosition + ballGroesse) >= schlaeger2YPosition
&& ballYPosition <= (schlaeger2YPosition + schlaegerHoehe)
&& ballXPosition <= (schlaeger2XPosition + schlaegerBreite)) {
ballXGeschwindigkeit = -Math.abs(ballXGeschwindigkeit)
}
}</code></pre>
</div>
<h1 id="conclusion" class="title">Fazit</h2>
<hr>
<p>Das Projekt ist endlich fertig! Hoffentlich haben Sie irgendetwas über JavaScript und/oder die Entwicklung von Videospielen gelernt.</p>
<p>Wir haben die Folgende Themen behandelt:</p>
<ul>
<li>HTML-Canvas</li>
<li>Spielschleifen</li>
<li>Controller (Tastatur-Eingabe)</li>
<li>Hitboxen</li>
<li>Canvas-Text mit JavaScript</li>
</ul>
<p>Das waren die Grundlagen der Spielentwicklung! Damit Sie besser lernen können, empfehle ich Ihnen, dieses Projekt nicht zu verlassen. Arbeiten Sie daran weiter! Verbessern Sie es. Es gibt viel, was man hier verbessern kann. <strong>Hier sind ein Paar Ideen:</strong></p>
<ul>
<li>Mit unterschiedlichen Größen (für die Schläger, Ball und Canvas) experimentieren.</li>
<li>Der Ball wird immer schneller, wenn einer punktet.</li>
<li>Die Richtung des Balles kann geändert werden, wenn der Schläger beim auftreffen sich bewegt.</li>
<li>Fügen Sie Effekten hinzu, z.B. Schatten.</li>
</ul>
</div>
<br><br><br><br>
<footer>
<div class="inlineblock">
<p><strong>Kontaktinfo:</strong></p>
<p id="contact">E-Mail Adresse: <a href="mailto:maxave404@gmail.com">maxave404@gmail.com</a><br>GitHub Konto: <a href="https://github.com/MaxAve" target="_blank">https://github.com/MaxAve</a></p>
</div>
<div class="inlineblock">
<p><strong>Quellcode:</strong></p>
<p id="contact">Quellcode für diese Seite: <a href="https://github.com/MaxAve/Pong-JS-Tutorial">https://github.com/MaxAve/Pong-JS-Tutorial</a><br> </p>
</div>
</footer>
<script src="prism/prism.js"></script>
</body>
</html>