Skip to content
Snippets Groups Projects
Commit f9549687 authored by Robert Seimetz's avatar Robert Seimetz
Browse files

Create fallback html

parent f0407b28
No related branches found
No related tags found
No related merge requests found
<style type="text/css">
code { display: block; border: 2px solid black}
table, th, td { border: 2px solid black; border-collapse: collapse}
</style>
<h1 id="dokumentation-spiele-programmieren-mit-pygame">Dokumentation
Spiele programmieren mit PyGame</h1>
<h2 id="das-grundgerüst">Das Grundgerüst</h2>
<p>Zuerst brauchen wir einen Startpunkt. Heißt wir brauchen einen
Bildschirm, eine Welt und ein paar Charaktere und Objekte. So ein
Standard-Grundgerüst haben wir im main_template vorgegeben, dieses könnt
ihr benutzen. Das funktioniert so: 1. Ein <em>screen</em> ist der Rahmen
des Spiels und hat eine definierte Höhe und Breite (Wir benutzen
englische Begriffe daher -&gt; <em>width</em> und <em>height</em>). 2.
Als 2D-Welt wird eine <em>Tile-Map</em> geladen. Das ist eine Karte, die
wir mit Hilfe des Programmes <em>Tiled</em> erstellt haben. 3. Um
Objekte (<em>Sprites</em>) zusammenzufassen, werden
<em>Sprite-Gruppen</em> erstellt. So kann z.B. allgemein definiert
werden, was bei einer Kollision zwischen Sprites der Gruppe
<em>Player</em> und <em>Enemy</em> passiert, und muss dies nicht für
jeden einzelnen Sprite tun. 4. In der settings.py kann eingestellt
werden, ob es Scrolling geben soll (kein Autoscrolling), welche
Schriftart für Texte benutzt werden sollen und ob es Gravitation gibt
(bei Rennspielen braucht man beispielsweise keine).</p>
<h2 id="sprites">Sprites</h2>
<h3 id="python-wissen-klassen">Python-Wissen: Klassen</h3>
<p>In Python, und vielen anderen Programmiersprachen, gibt es Klassen.
Klassen beschreiben, welche Eigenschaften und Funktionen Objekte dieser
Klasse haben. Klassen sind quasi wie ein Bauplan aus dem man dann
mehrmals dasselbe Objekt herstellen kann. Von diesem Bauplan kann man
mit Hilfe von <em>Parametern</em> abweichen, zum Beispiel kann man jedes
mal ein anderes Bild einfügen. Gibt es mehrere Klassen, die viele
gemeinsame Eigenschaften haben, ist es sinnvoll, eine übergeordnete
Klasse (auch <em>Super-Klasse</em>) mit den allgemeinenen Eigenschaften
und Kinderklassen mit den speziellen Eigenschaften zu erstellen.</p>
<p>Für uns heißt das: Die Klasse <em>_Character</em> hat die Parameter
<em>location</em> (Spawnkoordinaten), <em>scale</em> (Größe) und
<em>image</em> (gewünschtes Bild). Die <em>Player</em> im Spiel, als
auch die <em>Enemies</em>, sind solche <em>Character</em>, daher haben sie
alles auch diese Attribute. Es gibt aber auch Unterschiede, z.B. lassen
sich <em>Player</em> steuern, <em>Enemies</em> nicht. Hier kommt das
Konzept der <em>Vererbung</em> ins Spiel. Die gemeinsamen Eigenschaften
sind in der Klasse <em>_Character</em> definiert. Darauf basierend gibt es die
Klassen <em>LinearPlayer</em> und <em>AnimatedCoin</em>, welche beide
von der Klasse <em>_Character</em> erben. Das bedeutet, dass sie deren
Eigenschaften ebenfalls haben, und zusätzlich neue Eigenschaften und
Funktionen definieren können. Am Ende übergibt man also der Klasse
<em>Player</em> die vier <em>Parameter</em>, die die <em>_Character</em>-Klasse
braucht und ein weiteres.</p>
<h3 id="wie-fügt-ich-selber-sprites-ein">Wie fügt ich selber Sprites
ein?</h3>
<p>Um selber einen Sprite hinzuzufügen, müssen wir erstmal eine passende
Klasse, also einen passenden Bauplan finden. Folgende gibt es schon
vorgefertigt:</p>
<img src=mermaid.png alt="Entscheidungshilfe um Funktionen auszuwaehlen" width=1116 height=753>
<p>Jedes Objekt braucht unterschiedliche Parameter, wie oben
beschrieben. Hier sind sie jeweils aufgelistet:</p>
<ul>
<li>Beide Spieler
<ol type="1">
<li>Spawnkoordinaten (x, y)</li>
<li>Gewünschte Größe in pixel (Breite, Höhe)</li>
<li>Dateiname des Bildes (mit Dateiendung)</li>
<li>Genutzte Karte (standardmäßig tilemap)</li>
<li>Spritegroup der Wände (standardmäßig platforms)</li>
</ol></li>
<li>Gegner
<ol type="1">
<li>Spawnkoordinaten (x, y)</li>
<li>Gewünschte Größe in pixel (Breite, Höhe)</li>
<li>Dateiname des Bildes (mit Dateiendung)</li>
<li>Genutzte Karte (standardmäßig tilemap)</li>
<li>Spritegroup der Wände (standardmäßig platforms)</li>
<li>Die Geschwindigkeit des Gegners</li>
<li>Entweder:</li>
</ol>
<ul>
<li>Die Richtung der Bewegung ([speedx, speedy]) (Bei LinearEnemy)</li>
<li>Den Spieler der verfolgt werden soll (meist player) (Bei
FollowingEnemy)</li>
<li>Wie oft die Richtung geändert werden soll (in Sekunden) (Bei
RandomEnemy)</li>
</ul></li>
<li>Objekte</li>
</ul>
<ol type="1">
<li>Spawnkoordinaten (x, y)</li>
<li>Größe des Bildes (Breite, Höhe)</li>
<li>Dateiname des Bildes (mit Dateiendung)</li>
<li>Bei Animationen: Größe des einzelnen Sprites</li>
</ol>
<ul>
<li>Projektile</li>
</ul>
<ol type="1">
<li>Spawnkoordinaten (bei schießender Figur: rect.topleft/topright der
Figur)</li>
<li>Variable in der Bewegung gespeichert ist (bei schießender Figur:
z.B. player.move oder enemy.move) oder Winkel des Pfeils (bei
AngleProjectile. Bei schießendem Spieler player.angle)</li>
<li>Größe des Bildes [x, y]</li>
<li>Dateiname des Bildes (mit Dateiendung)</li>
<li>Geschwindigkeit der Pfeile</li>
</ol>
<p>Wenn man diese Parameter übergibt sieht das etwa so aus:</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>player <span class="op">=</span> LinearPlayer((<span class="dv">32</span>,<span class="dv">32</span>),(<span class="dv">48</span>,<span class="dv">48</span>) ,<span class="st">&quot;yeti.png&quot;</span>, tilemap, platforms) <span class="co"># Hier wird der Spieler erstellt. Die Parameter sind:</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="co">(32,32) -&gt; Die Koordinaten</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co">(48,48) -&gt; Die Größe in Pixel</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;yeti.png&quot; -&gt; Der Dateiname</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="co">tilemap -&gt; Die Karte</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="co">platforms -&gt; Die Spritegruppe der Plattformen und Wände</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span></code></pre></div>
<p>Man kann aber auch etwas tricksen. In Tiled kann man nämlich auch
einfach ein Objekt auf der Objektebene hinzufügen, das entsprechend
bennenen und dann die Koordinaten und Größe des Objekts im Code
verwenden.</p>
<p>Dazu muss man folgendes machen:</p>
<ol type="1">
<li>Erstelle eine Klasse mit der entsprechenden übergeordneten
Klasse</li>
</ol>
<div class="sourceCode" id="cb3"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Coin(_Object):</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, location, size, image<span class="op">=</span><span class="st">&quot;Golden.png&quot;</span>):</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> _Object.<span class="fu">__init__</span>(<span class="va">self</span>, location, size, image)</span></code></pre></div>
<ol start="2" type="1">
<li><p>Erstelle Objekte in Tiled mit gleichen Namen</p></li>
<li><p>Erstelle eine Spritegruppe und nutze eine <em>for-Schleife</em>
um alle Objekte mit dem Namen der Gruppe hinzuzufügen. Dann kannst du
sie zeichnen</p></li>
</ol>
<div class="sourceCode" id="cb4"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>coins <span class="op">=</span> pygame.sprite.RenderClear() <span class="co"># Erstellen der Spritegruppe</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> objects <span class="kw">in</span> tilemap.data.objects: <span class="co"># Alle Objekte, die Tiled hat, werden durchgeprüft</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> objects.name <span class="op">==</span> <span class="st">&quot;coin&quot;</span>: <span class="co"># Wenn der Name des Objektes &quot;wall&quot; ist, dann...</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> coins.add(Coin(objects.x, objects.y, objects.width, objects.height)) <span class="co"># Erstelle ein Objekt aus der Klasse &quot;Coin&quot; mit exakt den Koordinaten und Größe und füge es der Spritegruppe &quot;coins&quot; hinzu.</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>...</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> update():</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> coins.draw(objects_surface) <span class="co"># Jetzt zeichne die gesamte Spritegruppe auf den Screen (siehe unten)</span></span></code></pre></div>
<h2 id="wie-zeichne-ich-auf-den-bildschirm">Wie zeichne ich auf den
Bildschirm?</h2>
<p>Ähnlich zu einem Film, der aus vielen Bildern (<em>Frames</em>)
besteht, funktioniert auch PyGame. Erst wird eine einfarbige Fläche
erstellt, auf die dann die Sprites vorgezeichnet werden und wenn alles
fertig wird, wird es auf einmal gemalt. Dabei gilt:</p>
<p>Der Code wird von oben nach unten gelesen. Daher muss sie immer
diesem Aufbau folgen:</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> update():</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> screen.fill((<span class="dv">0</span>,<span class="dv">0</span>,<span class="dv">0</span>)) <span class="co"># Das füllt den Bildschirm mit einer Farbe. Daher bitte immer zuerst.</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> screen.blit(tilemap_image, (<span class="dv">0</span>,<span class="dv">0</span>)) <span class="co"># Auf den schwarzen Bildschirm wird die Karte gemalt.</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="co"># Hier werden Charaktere und Objekte &quot;vorgezeichnet&quot;. Da ist die Reihenfolge relativ egal.</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> coins.draw(objects_surface)</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> enemies.draw(characters_surface) </span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> screen.blit(player.image, (player.rect.left, player.rect.top))</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a> <span class="co"># Zuletzt wird alles gemalt</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a> pygame.display.flip()</span></code></pre></div>
<p>Das findet für jeden Frame erneut statt und steht in der
<em>update-Funktion</em>, die immer wieder aufgerufen wird. Damit in dem
Spiel etwas passiert und nicht immer das gleiche Bild entsteht, muss
zwischen den Aufrufen der <em>update-Funktion</em> noch etwas geschehen:
Spiel-Logik.</p>
<h2 id="die-spiel-logik">Die Spiel-Logik</h2>
<p>Ein interaktives Spiel braucht eine Steuerung. Vor jedem
<em>update</em>-Aufruf wird geprüft, ob gerade eine Pfeiltaste gedrückt
ist. Wenn das so ist, wird die Position des Players verändert, sodass
die Figur im nächsten Frame eine andere Position hat.</p>
<div class="sourceCode" id="cb6"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> event <span class="kw">in</span> pygame.event.get(): <span class="co">#Jedes Event wird in jedem Frame geprüft</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> event.<span class="bu">type</span> <span class="op">==</span> pygame.QUIT: <span class="co"># Hier wird geprüft ob auf das X oben rechts gedrückt wurde.</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> exit(<span class="dv">0</span>)</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> event.<span class="bu">type</span> <span class="op">==</span> pygame.KEYDOWN: <span class="co"># Hier wird geprüft ob irgendeine Taste gedrückt wurde.</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> event.key <span class="op">==</span> pygame.K_LSHIFT: <span class="co"># Danach schaut man, *welche* Taste das ist.</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> player.running <span class="op">=</span> <span class="va">True</span> </span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> event.<span class="bu">type</span> <span class="op">==</span> pygame.KEYUP: <span class="co"># Hier wird geprüft ob irgendeine Taste losgelassen wurde.</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> event.key <span class="op">==</span> pygame.K_LSHIFT: <span class="co"># Danach schaut man wieder, welche Taste genau.</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> player.running <span class="op">=</span> <span class="va">False</span></span></code></pre></div>
<p>Auf die gleiche Weise funktionieren spielinterne Ereignisse wie
Kollisionen zwischen einem Player und einem Enemy oder Coin. Kollidieren
z.B. Player und Coin, kann der Punktestand erhöht werden.</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> (pygame.sprite.spritecollide(player, coins, <span class="va">True</span>)):</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> player.score <span class="op">+=</span><span class="dv">100</span></span></code></pre></div>
<h3 id="wie-ändere-ich-die-vorgegebenen-templates">Wie ändere ich die
vorgegebenen Templates?</h3>
<p>Ein großer Teil der Arbeit besteht darin, das was es schon gibt, auf
die eigenen Bedürfnisse anzupassen. Sagen wir mal, wir wollen, dass
Spieler nicht nur mit WASD steuerbar ist sondern auch mit den
Pfeiltasten. Dann tun wir folgendes:</p>
<ol type="1">
<li>Wir erstellen eine neue <em>Klasse</em> (kein Objekt!) mit der
Vorlage, die wir wollen (hier LinearPlayer)</li>
</ol>
<div class="sourceCode" id="cb8"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Player(LinearPlayer): <span class="co"># Wir erstellen einen neue Klasse, die von *LinearPlayer* erbt</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, location, size, image, tilemap, walls): </span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">super</span>().<span class="fu">__init__</span>(<span class="va">self</span>, location, size, image, tilemap, walls) <span class="co"># Diese Klasse hat exakt dieselben Eigenschaften wie die Vorlage (=Vererbung). Mit super() erhält man die Super-Klasse, also LinearPlayer</span></span></code></pre></div>
<p>Nun können wir die Klasse verändern oder neue Dinge hinzufügen.</p>
<ol start="2" type="1">
<li>Bestehendes Abändern</li>
</ol>
<div class="sourceCode" id="cb9"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Player(LinearPlayer): <span class="co"># Wir erstellen einen neue Klasse, die von <em>LinearPlayer</em> erbt</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, location, size, image, tilemap, walls): </span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">super</span>().<span class="fu">__init__</span>(<span class="va">self</span>, location, size, image, tilemap, walls) <span class="co"># Diese Klasse hat exakt dieselben Eigenschaften wie die Vorlage (=Vererbung)</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>.keys <span class="op">=</span> { <span class="co"># self.keys ist schon in der übergeordneten Klasse definiert. Das überschreiben wir hier.</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;up&quot;</span>: pygame.K_UP,</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;down&quot;</span>: pygame.K_DOWN,</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;left&quot;</span>: pygame.K_LEFT,</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;right&quot;</span>: pygame.K_RIGHT, <span class="co">#Hier standen vorher die Kasten W, A, S und D. Eine Übersicht über alle Tasten ist hier zu finden: https://www.pygame.org/docs/ref/event.html </span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;jump&quot;</span>: pygame.K_SPACE </span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a> </span></code></pre></div>
<ol start="3" type="1">
<li>Neues hinzufügen</li>
</ol>
<div class="sourceCode" id="cb10"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Player(LinearPlayer): <span class="co"># Wir erstellen einen neue Klasse, die von <em>LinearPlayer</em> erbt</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, location, size, image, tilemap, walls): </span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">super</span>().<span class="fu">__init__</span>(<span class="va">self</span>, location, size, image, tilemap, walls) <span class="co"># Diese Klasse hat exakt dieselben Eigenschaften wie die Vorlage (=Vererbung)</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>.keys <span class="op">=</span> { <span class="co"># self.keys ist schon in der übergeordneten Klasse definiert. Das überschreiben wir hier.</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;up&quot;</span>: pygame.K_UP,</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;down&quot;</span>: pygame.K_DOWN,</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;left&quot;</span>: pygame.K_LEFT,</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;right&quot;</span>: pygame.K_RIGHT, <span class="co">#Hier standen vorher die Kasten W, A, S und D. Eine Übersicht über alle Tasten ist hier zu finden: https://www.pygame.org/docs/ref/event.html </span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;jump&quot;</span>: pygame.K_SPACE,</span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;say_hi&quot;</span>: pygame.K_9</span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> say_hi(<span class="va">self</span>, sound): <span class="co"># Ab hier beginnt eine neue Funktion</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> event <span class="kw">in</span> events:</span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> event.<span class="bu">type</span> <span class="op">==</span> pygame.KEYDOWN:</span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> event.key <span class="op">==</span> <span class="va">self</span>.keys[<span class="st">&quot;say_hi&quot;</span>]:</span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a> hi <span class="op">=</span> pygame.mixer.Sound(<span class="st">&quot;./sound/&quot;</span> <span class="op">+</span> sound)</span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true" tabindex="-1"></a> hi.play()</span></code></pre></div>
<p>Am Ende hat man jetzt eine Vorlage mit neuen, eigenen Funktionen.</p>
<h1 id="einzelne-funktionen">Einzelne Funktionen</h1>
<p>Es gibt aber nicht nur ganze Klassen sondern auch einzelne
Funktionen. Also anstatt eines kompletten Bauplans (=einer Klasse) sind
Funktionen nur kleine Codesnippets, die an mehreren Stellen ausgeführt
werden müssen.</p>
<h2 id="timer">Timer</h2>
<p>Manchmal braucht man einen Timer, zum Beispiel um einen Gegner
spawnen zu lassen oder um die Zeit im Spiel anzuzeigen. Die Zeit in
Sekunden wird mithilfe der Funktion <b>time()</b> ausgegeben.</p>
<h2 id="musik-sound">Musik &amp; Sound</h2>
<p>Zwischen Musik und Sound gibt es ein paar feine Unterschiede.</p>
<table>
<colgroup>
<col style="width: 44%"/>
<col style="width: 55%"/>
</colgroup>
<thead>
<tr class="header">
<th>Musik</th>
<th>Sound</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Kann nur eine Datei abgespielt werden</td>
<td>Können mehrere gleichzeitig existieren</td>
</tr>
<tr class="even">
<td>Wiederholt sich</td>
<td>Wiederholt sich (eigentlich) nicht</td>
</tr>
<tr class="odd">
<td>Startet sofort</td>
<td>Muss erst definiert werden und dann abgespielt</td>
</tr>
</tbody>
</table>
<p>Musik wird mit der Funktion <em>add_music()</em> gestartet. Diese
benötigt die Parameter <em>filename</em> und wie oft es sich wiederholen
soll. -1 ist eine unendliche Wiederholung. Die Datei muss im Verzeichnis
./sound/ sein.</p>
<p>Sound muss erst definiert werden um ihn dann zu einem späteren
Zeitpunkt abzuspielen. Ein Sound kann auch öfter benutzt werden.</p>
<div class="sourceCode" id="cb11"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>filename <span class="op">=</span> <span class="st">&quot;example.mp3&quot;</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>sound <span class="op">=</span> pygame.mixer.Sound(<span class="st">&quot;./sound/&quot;</span> <span class="op">+</span> filename)</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>...</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>sound.play()</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>...</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>sound.play()</span></code></pre></div>
<h2 id="text">Text</h2>
<p>Oft muss man Text auf den Bildschirm spielen. Zum Beispiel um einen
Score auf dem Bildschirm zu zeigen.</p>
<div class="sourceCode" id="cb12"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> update():</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a> screen.blit(add_text(text, size, color, bold), (x, y))</span></code></pre></div>
<p>Was passiert hier genau? Zuerst wird der Text selbst erstellt. Dazu
braucht die <b>add_text</b>-Funktion den “Text”, die Schriftgröße,
die Farbe in RGB (R, G, B) und die Angabe, ob der Text fett sein soll.
Danach wird gesagt, <em>wo</em> der Text erscheinen soll.</p>
<div class="sourceCode" id="cb13"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> update():</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> screen.blit(add_text(<span class="st">&quot;Hi&quot;</span>, <span class="dv">32</span>, (<span class="dv">255</span>, <span class="dv">0</span>, <span class="dv">0</span>), <span class="va">False</span>), (<span class="dv">10</span>, <span class="dv">20</span>), )</span></code></pre></div>
<p>Man kann auch den Text mit Variablen kombinieren. Sollen die Leben
des Spielers (Hier gespeichert in player.lives) angezeigt werden, würde
man folgenden Text nutzen:</p>
<div class="sourceCode" id="cb14"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>(<span class="ss">f&quot;Lives: </span><span class="sc">{</span>player<span class="sc">.</span>lives<span class="sc">}</span><span class="ss">&quot;</span>)</span></code></pre></div>
<p>Die Schriftgröße kann in der settings.py editiert werden.</p>
<h2 id="bilder-spiegeln-und-rotiern">Bilder spiegeln und rotiern</h2>
<p>Während deiner Arbeit kann es passieren, dass du ein Bild aus
verschienden Gründen spiegeln möchtest. Dafür benutzt du die Funktion
<b>reflect_image()</b>.</p>
<p>Diese benutzt du so:</p>
<div class="sourceCode" id="cb15"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>reflect_image(<span class="st">&quot;Dateiname des Bildes&quot;</span>, (<span class="va">True</span><span class="op">/</span>Fales wenn du das Bild horizontal spieln willst), (<span class="va">True</span><span class="op">/</span><span class="va">False</span> wenn du dsa Bild vertikal gespiegelt werden soll))</span></code></pre></div>
<p>Wenn du aber ein Bild drehen willst benutzt du
<b>rotate_image</b>.</p>
<div class="sourceCode" id="cb16"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>rotate_image(<span class="st">&quot;Dateiname des Bildes&quot;</span>, (Zahl um wie viel Grad das Bild gedreht werden soll))</span></code></pre></div>
<h2 id="neustarten-des-programmes">Neustarten des Programmes</h2>
<p>Code immer wieder zu stoppen und zu starten kann nervig sein. Daher
kann die <b>retry()</b> ein Programm einfacher neustarten. Dabei
braucht sie immer <b>__file__</b> als <em>Parameter</em>. Das hat
einen ganz einfachen Grund: Die Funktion ruft einen neuen Prozess auf,
der die ganze Datei neustartet und daher den genauen Dateipfad braucht.
Deswegen ist es auch ganz wichtig, dass die Funktion nicht dazu benutzt
wird, um von einem Chekpoint neuzustarten, das wird nicht funktionieren.
Nur wenn man das ganze Programm neustarten will, ist das eine
Option.</p>
docs/mermaid.png

51.2 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment