Možná si všimneš, zvlášť jestli jsi už nějakou verzi hada hrála, že ovládání tvé nové hry je trošku frustrující. A možná není úplně jednoduché přijít na to, proč.
Můžou za to (hlavně) dva důvody:
Pojďme je vyřešit.
Když zmáčkneš dvě šipky rychle za sebou, v dalším „tahu“ hada se projeví jen ta druhá.
Z pohledu programu to chování dává smysl – po stisknutí šipky se uloží její směr, a při „tahu“ hada se použije poslední uložený směr. S tímhle chováním je ale složité hada rychle otáčet: hráč si musí pohlídat, aby pro každý „tah“ hada nezmáčkl víc než jednu šipku. Lepší by bylo, kdyby se ukládaly všechny stisknuté klávesy, a had by v každém tahu reagoval maximálně jednu. Další by si „schoval“ na další tahy.
Takovou „frontu“ stisků kláves lze uchovávat v seznamu.
Přidej si na to do stavu hry seznam (v metodě __init__
):
self.queued_directions = []
Tuhle frontu plň po každém stisku klávesy, metodou append
.
Je potřeba změnit většinu funkce on_key_press
– místo změny
atributu se nový směr přidá do seznamu.
Abys nemusela psát čtyřikrát append
,
můžeš uložit nový směr do pomocné proměnné:
@window.event
def on_key_press(key_code, modifier):
if key_code == pyglet.window.key.LEFT:
new_direction = -1, 0
if key_code == pyglet.window.key.RIGHT:
new_direction = 1, 0
if key_code == pyglet.window.key.DOWN:
new_direction = 0, -1
if key_code == pyglet.window.key.UP:
new_direction = 0, 1
state.queued_directions.append(new_direction)
A zpátky k logice. V metodě move
místo
dir_x, dir_y = self.snake_direction
z fronty vyber první nepoužitý prvek.
Nezapomeň ho pak z fronty smazat, ať se dostane i na další:
if self.queued_directions:
new_direction = self.queued_directions[0]
del self.queued_directions[0]
self.snake_direction = new_direction
Zkontroluj, že to funguje.
Když hráč zmáčkne šipku opačného směru, než se had právě plazí, had se otočí a hlavou si narazí do krku.
Z pohledu programu to opět dává smysl: políčko napravo od hlavy je plné, had na něj tedy nemůže vstoupit a hráč prohrává. Z pohledu hry (a biologie!) ale narážení do krku moc smyslu nedává. Lepší by bylo obrácení směru úplně ignorovat.
A jak poznat opačný směr?
Když se had plazí doprava, (1, 0)
, tak je opačný směr doleva, (-1, 0)
.
Když se plazí dolů, (0, -1)
, tak naopak je nahoru, (0, 1)
.
Obecně, k (x, y) je opačný směr
(-x, -y).
Zatím ale pracujeme s celými n-ticemi, je potřeba obě na x a y „rozbalit“. Kód tedy bude vypadat takto:
old_x, old_y = self.snake_direction
new_x, new_y = new_direction
if (old_x, old_y) != (-new_x, -new_y):
self.snake_direction = new_direction
Dej ho místo puvodního self.snake_direction = new_direction
.
{ "data": { "sessionMaterial": { "id": "session-material:2019/brno-podzim-snake:extensions:0", "title": "Vylepšené ovládání", "html": "\n \n \n\n <h1>Vylepšení ovládání Hada</h1>\n<p>Možná si všimneš, zvlášť jestli jsi už nějakou verzi hada hrála,\nže ovládání tvé nové hry je trošku frustrující.\nA možná není úplně jednoduché přijít na to, proč.</p>\n<p>Můžou za to (hlavně) dva důvody:</p>\n<ol>\n<li>Když zmáčkneš dvě šipky rychle za sebou, v dalším „tahu“\nhada se projeví jen ta druhá.</li>\n<li>Když se had plazí doleva a hráč zmáčkne šipku doprava,\nhad se otočí a hlavou si narazí do krku.</li>\n</ol>\n<p>Pojďme je vyřešit.</p>\n<h2>Fronta pokynů</h2>\n<p>Když zmáčkneš dvě šipky rychle za sebou, v dalším „tahu“ hada se projeví jen\nta druhá.</p>\n<p>Z pohledu programu to chování dává smysl – po stisknutí šipky se uloží\njejí směr, a při „tahu“ hada se použije poslední uložený směr.\nS tímhle chováním je ale složité hada rychle otáčet: hráč si musí pohlídat,\naby pro každý „tah“ hada nezmáčkl víc než jednu šipku.\nLepší by bylo, kdyby se ukládaly <em>všechny</em> stisknuté klávesy, a had by\nv každém tahu reagoval maximálně jednu.\nDalší by si „schoval“ na další tahy.</p>\n<p>Takovou „frontu“ stisků kláves lze uchovávat v seznamu.\nPřidej si na to do stavu hry seznam (v metodě <code>__init__</code>):</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">queued_directions</span> <span class=\"o\">=</span> <span class=\"p\">[]</span>\n</pre></div><p>Tuhle frontu plň po každém stisku klávesy, metodou <code>append</code>.\nJe potřeba změnit většinu funkce <code>on_key_press</code> – místo změny\natributu se nový směr přidá do seznamu.\nAbys nemusela psát čtyřikrát <code>append</code>,\nmůžeš uložit nový směr do pomocné proměnné:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"nd\">@window.event</span>\n<span class=\"k\">def</span> <span class=\"nf\">on_key_press</span><span class=\"p\">(</span><span class=\"n\">key_code</span><span class=\"p\">,</span> <span class=\"n\">modifier</span><span class=\"p\">):</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">LEFT</span><span class=\"p\">:</span>\n <span class=\"n\">new_direction</span> <span class=\"o\">=</span> <span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">RIGHT</span><span class=\"p\">:</span>\n <span class=\"n\">new_direction</span> <span class=\"o\">=</span> <span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">DOWN</span><span class=\"p\">:</span>\n <span class=\"n\">new_direction</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"o\">-</span><span class=\"mi\">1</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">UP</span><span class=\"p\">:</span>\n <span class=\"n\">new_direction</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">1</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">queued_directions</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">new_direction</span><span class=\"p\">)</span>\n</pre></div><p>A zpátky k logice. V metodě <code>move</code> místo\n<code>dir_x, dir_y = self.snake_direction</code> z fronty vyber první nepoužitý prvek.\nNezapomeň ho pak z fronty smazat, ať se dostane i na další:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"k\">if</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">queued_directions</span><span class=\"p\">:</span>\n <span class=\"n\">new_direction</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">queued_directions</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"k\">del</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">queued_directions</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"n\">new_direction</span>\n</pre></div><p>Zkontroluj, že to funguje.</p>\n<h3>Zpátky ni krok</h3>\n<p>Když hráč zmáčkne šipku opačného směru, než se had právě plazí, had se otočí a \nhlavou si narazí do krku.</p>\n<p>Z pohledu programu to opět dává smysl: políčko napravo od hlavy je plné,\nhad na něj tedy nemůže vstoupit a hráč prohrává.\nZ pohledu hry (a biologie!) ale narážení do krku moc smyslu nedává.\nLepší by bylo obrácení směru úplně ignorovat.</p>\n<p>A jak poznat opačný směr?\nKdyž se had plazí doprava, <code>(1, 0)</code>, tak je opačný směr doleva, <code>(-1, 0)</code>.\nKdyž se plazí dolů, <code>(0, -1)</code>, tak naopak je nahoru, <code>(0, 1)</code>.\nObecně, k (<var>x</var>, <var>y</var>) je opačný směr\n(-<var>x</var>, -<var>y</var>).</p>\n<p>Zatím ale pracujeme s celými <var>n</var>-ticemi, je potřeba obě\nna <var>x</var> a <var>y</var> „rozbalit“.\nKód tedy bude vypadat takto:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"n\">old_x</span><span class=\"p\">,</span> <span class=\"n\">old_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span>\n <span class=\"n\">new_x</span><span class=\"p\">,</span> <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">new_direction</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">old_x</span><span class=\"p\">,</span> <span class=\"n\">old_y</span><span class=\"p\">)</span> <span class=\"o\">!=</span> <span class=\"p\">(</span><span class=\"o\">-</span><span class=\"n\">new_x</span><span class=\"p\">,</span> <span class=\"o\">-</span><span class=\"n\">new_y</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"n\">new_direction</span>\n</pre></div><p>Dej ho místo puvodního <code>self.snake_direction = new_direction</code>.</p>\n\n\n " } } }