ゲームをつくる
キーボード,マウス,当たり判定.ここまでの全部を使って,遊べるものを作ります.
ここまでのプログラムは,どれもこちらから一方的に命令するものでした. 実行すれば決まった絵が出て,決まったところで終わります. 今回は,人の操作に反応するプログラムを作ります. キーを押すと自機が動き,敵に当たると点が入り,失敗が続くと終わる — ゲームです.
新しく覚えることは多くありません.
「キーが押されたら,この関数を呼んでください」と登録する命令と,
「2 つのかめが近いかどうか」を調べる命令.この 2 つだけです.
あとは第 10 章の ontimer と def,第 5 章の if,
第 3 章の変数 — これまで習ったものの組み合わせです.
def と global は,第 10 章と同じく
決まり文句のままで作れます(仕組みは第 13 章でくわしく習います).
この章で作るもの.最後に作るのは,次のような画面のシューティングゲームです.
← → で下の自機を動かし,上から落ちてくる玉を受け止めます.
取れば左上のスコアが増え,3 回にがすとゲームオーバーです.
12.7 節で,これを5 段階に分けて少しずつ作ります.
- ゲームの骨組み(準備 → 毎フレームの処理 → 終了判定)を自分で書ける
onkeypressでキーボードから,onscreenclickやonclickでマウスから操作を受け取れるontimerでゲームループを作れる(while Trueは使えないことがわかる)distanceや座標の範囲で当たり判定を書ける- スコアを画面に出し,条件を満たしたらゲームを終われる
- 自分で考えた小さなゲームを,最後まで組み立てられる
- キーが効かないときは,一度タートル画面をクリックしてください.コード欄に文字カーソルがあると,キーはコード欄に取られます.
- 止めたいときは「■ 停止」を押します.押さないと動き続けます.
- 速さの設定は,今回のように
ontimerで動かすプログラムには効きません.速さを変えたいときはontimerのミリ秒の数を変えます.
12.1 ゲームの骨組み
どんなゲームも,中身は次の 3 つに分かれます. シューティングも,パズルも,レースゲームも,大きさが違うだけで形は同じです.
- 準備 — 自機や敵を作り,位置と色を決める.得点を 0 にする.1 回だけ行う.
- 毎フレームの処理 — 少し動かす,入力を見る,当たったか調べる,得点を書き直す.1 秒に何十回も繰り返す.
- 終了判定 — 続けるか,終わるかを決める.終わるなら繰り返しをやめる.
12.1.1 なぜ while True: が使えないのか
紙の教科書に載っているゲームのプログラムは,たいてい次のような形をしています.
「ずっと繰り返す」という意味の while True: です.
while True: # ← このページでは使えません
# 少し待つ
# 少し動かす
# 画面を描き直す
while True: で待ち続けると,絵は出ず,キーも効かず,
ページ全体が固まってしまいます.
この教材では,しばらく待っても終わらないプログラムは
実行中断 という表示を出して自動的に止めます.
12.1.2 ontimer でループを作る(第 10 章の復習)
かわりに使うのが ontimer(関数, ミリ秒) です.
「指定したミリ秒だけ経ったら,この関数を 1 回呼んでください」と
ブラウザにお願いする命令です.お願いしたらプログラムはいったん終わります.
ブラウザは空いた時間で画面を描き直し,キー入力を受け取り,時間が来たら関数を呼びます.
1 回しか呼ばれないので,呼ばれた関数の最後で,もう一度 ontimer を書く
のがコツです.これで「少し動く → 少し待つ → また少し動く」が続きます.
# ontimer でループを作る(第 10 章の復習)
from turtle import *
setup(620, 460)
t = Turtle()
t.shape("turtle")
t.color("seagreen")
t.pu()
t.goto(-280, 0)
# 1 コマぶんの処理
def tick():
t.fd(6)
if t.xcor() > 280:
t.goto(-280, 0)
ontimer(tick, 30) # ← 次のコマを予約する(これが「くり返し」)
ontimer(tick, 30) # ← 最初の 1 回
done()
ontimer(tick, 30) の 30 はミリ秒です.
1 秒 = 1000 ミリ秒なので,1 秒に約 33 回呼ばれます.
数を小さくすると速く,大きくするとゆっくりになります.
30 前後にしておくと,たいていのゲームでちょうどよい速さになります.
ontimer(tick, 30) の tick にかっこが付いていない
ことに注意してください.tick() と書くと「いま呼べ」という意味になり,
tick と書くと「この関数そのもの」を指します.
ここで渡したいのは関数そのものです.
12.2 キーボードで動かす
12.2.1 onkeypress と listen
キーボードも同じ考え方です.「このキーが押されたら,この関数を呼んでください」と
登録しておきます.登録する命令が onkeypress(関数, キーの名前) です.
| 書き方 | 意味 |
|---|---|
onkeypress(go_left, "Left") | ← キーが押されたら go_left() を呼ぶ |
onkey(stop_it, "Left") | ← キーが離されたら stop_it() を呼ぶ |
listen() | 「キーボードを聞く準備をしてください」の合図.登録をぜんぶ済ませたあとに 1 回書く |
# 矢印キーでかめを動かす
from turtle import *
setup(620, 460)
t = Turtle()
t.shape("turtle")
t.color("royalblue")
t.pu()
def go_left():
t.setx(t.xcor() - 20)
def go_right():
t.setx(t.xcor() + 20)
def go_up():
t.sety(t.ycor() + 20)
def go_down():
t.sety(t.ycor() - 20)
# キーと関数を結び付ける
onkeypress(go_left, "Left")
onkeypress(go_right, "Right")
onkeypress(go_up, "Up")
onkeypress(go_down, "Down")
listen()
done()
setx(x) は x 座標だけを変える命令,sety(y) は y 座標だけを変える命令です.
t.xcor() で「いまの x 座標」が取れるので,
t.setx(t.xcor() - 20) は「いまより 20 だけ左へ」という意味になります.
12.2.2 使えるキーの名前
キーの名前は文字列で書きます.大文字・小文字の区別があるので気をつけてください.
| キー | 書き方 | 備考 |
|---|---|---|
| ↑ ↓ ← → | "Up" "Down" "Left" "Right" | 先頭だけ大文字 |
| スペース | "space" | すべて小文字.弾を撃つのによく使う |
| ふつうの文字 | "a" "z" "1" | 小文字で書く."A" は Shift を押したときの扱いになる |
| Enter | "Return" | |
| Esc | "Escape" | ゲームを止めるのに使える |
"Left" のように名前で書けば,そのまま通じます.
12.2.3 押しっぱなしになめらかに動かす
12.2.1 のプログラムは,キーを押しっぱなしにしても 「トン … トトトトト」とぎこちなく動きます. これはキーボードの「リピート」の仕組みのせいで,最初の 1 回と 2 回目の間に間が空くからです.
なめらかに動かす定石は,キーで直接動かさないことです.
キーが押されたら「いま右向きに進みたい」という気持ちだけを変数に覚えさせ,
実際に動かすのは ontimer の中でやります.
離されたら気持ちを 0 に戻します.キーが離されたことは onkey で受け取れます.
# 押しっぱなしでなめらかに動かす
from turtle import *
setup(620, 460)
t = Turtle()
t.shape("turtle")
t.color("royalblue")
t.pu()
dx = 0 # 横方向の速さ
dy = 0 # たて方向の速さ
def press_left():
global dx
dx = -6
def press_right():
global dx
dx = 6
def press_up():
global dy
dy = 6
def press_down():
global dy
dy = -6
def release_x():
global dx
dx = 0
def release_y():
global dy
dy = 0
def tick():
t.goto(t.xcor() + dx, t.ycor() + dy)
ontimer(tick, 25)
onkeypress(press_left, "Left")
onkeypress(press_right, "Right")
onkeypress(press_up, "Up")
onkeypress(press_down, "Down")
onkey(release_x, "Left")
onkey(release_x, "Right")
onkey(release_y, "Up")
onkey(release_y, "Down")
listen()
ontimer(tick, 25)
done()
global 変数名 と書きます.これを忘れると,
関数の中だけの別の変数ができてしまい,外の dx は 0 のままです.
読むだけなら global は要りません(tick の中の
dx がそうです).書き換えるときだけ必要です.
第 10 章と同じ決まり文句です.
なぜ関数の中と外で変数が別ものになるのかは,第 13 章でくわしく習います.
dx = -6 と dy = 6 の両方が立ち,
ななめに動きます.
矢印キーの左右で動き,スペースキーを押すと色が変わるプログラムです. ____ を埋めて完成させてください. スペースキーの名前は表のとおりです.関数を渡すときはかっこを付けません.
from turtle import *
setup(620, 460)
t = Turtle()
t.shape("circle")
t.color("royalblue")
t.pu()
def go_left():
t.setx(t.xcor() - 20)
def go_right():
t.setx(t.xcor() + 20)
def change_color():
t.color("orange")
onkeypress(go_left, "____")
onkeypress(____, "Right")
onkeypress(change_color, "____")
____()
done()
12.3 マウスを使う
12.3.1 クリックした場所を受け取る
onscreenclick(関数) と登録しておくと,画面がクリックされたときに
その関数が呼ばれます.このとき,クリックされた場所の座標が 2 つ渡ってきます.
受け取る側は,def のかっこの中に
名前を 2 つ書いておきます.
こう書いておくと,呼ばれたときにその名前へ値が入ってきます. かっこの中に並べるこの名前を引数といいます. いまは決まり文句で,仕組みは第 13 章でくわしく習います.
def jump(x, y): # ← x と y の 2 つを受け取る約束
t.goto(x, y)
onscreenclick(jump) # ← ここでもかっこは付けない
# クリックした所へかめが飛ぶ
from turtle import *
setup(620, 460)
t = Turtle()
t.shape("circle")
t.color("tomato")
t.pu()
def jump(x, y):
t.goto(x, y)
print("クリック", round(x), round(y))
onscreenclick(jump)
done()
12.3.2 かめ自身をクリックする
画面全体ではなく,そのかめが押されたときだけ反応させたいこともあります.
もぐらたたきや,ボタンのような使い方です.そのときは
かめ.onclick(関数) と,かめに直接登録します.
# 押されたかめの色が変わる(もぐらたたき風)
from turtle import *
setup(620, 460)
a = Turtle()
a.shape("circle")
a.color("orange")
a.pu()
a.goto(-90, 0)
b = Turtle()
b.shape("square")
b.color("mediumpurple")
b.pu()
b.goto(90, 0)
def hit_a(x, y):
a.color("gray")
print("まるが押された")
def hit_b(x, y):
b.color("gray")
print("しかくが押された")
a.onclick(hit_a)
b.onclick(hit_b)
done()
shapesize で大きく見せても,押せる範囲は変わりません.
小さくて押しにくいときは,onscreenclick で座標を受け取り,
かめ.distance(x, y) < 30 のように自分で判定してください
(12.4 節でやります).
クリックするたびに,その場所に色つきの点を打つプログラムです.
____ を埋めてください.
onscreenclick が呼ぶ関数は,引数を 2 つ受け取る約束でした.
from turtle import *
setup(620, 460)
pen = Turtle()
pen.ht()
pen.pu()
pen.color("mediumseagreen")
def put(____, ____):
pen.goto(x, y)
pen.dot(24)
onscreenclick(____)
done()
12.3.3 ドラッグのかわり:つかんで,置く
マウスで絵をドラッグして動かす—— 「ボタンを押したままマウスを動かす」という操作を考えてみます. ところがこの教材のランタイムは, クリックされた瞬間しか受け取れません. 「いまボタンが押されているか」も「いまマウスがどこにいるか」も分からないので, ドラッグはそのままでは作れません.
そこで「1 回目のクリックでつかむ → 2 回目のクリックで置く」方式に置き換えます. 操作は 2 回のクリックに分かれますが, できることはドラッグと同じ(好きなものを好きな所へ動かす)です.
やり方は,つかんでいる最中かどうかを変数に覚えさせるだけです. この変数をフラグ(旗)と呼びます.12.6 節でも同じ考え方が出てきます.
# ドラッグのかわり:1 回目のクリックでつかみ,2 回目のクリックで置く
from turtle import *
setup(620, 460)
box = Turtle()
box.shape("square")
box.color("mediumpurple")
box.shapesize(2, 2)
box.pu()
box.goto(-120, 0)
msg = Turtle()
msg.ht()
msg.pu()
msg.color("dimgray")
msg.goto(0, 160)
holding = False # つかんでいる最中かどうか(フラグ)
# text に,表示したい文字が入ってくる(引数.第 13 章でくわしく習う)
def show(text):
msg.clear()
msg.write(text, align="center", font=("Arial", 16, "normal"))
def click(x, y):
global holding
if holding:
box.goto(x, y) # 2 回目:ここに置く
box.color("mediumpurple")
holding = False
show("置きました.つかむには四角をクリック")
elif box.distance(x, y) < 30: # 四角の近くが押された
box.color("crimson") # つかんだ印に色を変える
holding = True
show("つかみました.置きたい所をクリック")
show("四角をクリックしてつかむ")
onscreenclick(click)
done()
holding が True か False かで,
同じクリックの意味が変わるのがこの書き方のポイントです.
if の順番にも意味があります.
先に「つかんでいるか」を調べるので,つかんでいる間はどこを押しても「置く」になります.
12.4 当たり判定
ゲームらしさの正体は当たり判定です. 弾が敵に当たった,自機が壁にぶつかった,アイテムを拾った — すべて 「2 つのものが近いかどうか」を調べているだけです.
12.4.1 距離で判定する
t.distance(u) と書くと,かめ t とかめ u の
中心どうしの距離が返ってきます.
これがある値より小さければ「当たった」とみなします.
if t.distance(u) < 30:
print("当たった!")
判定に使う数(上の図の r)は,見た目の大きさに合わせて自分で決めます. 小さくすると「かすっても当たらない」きびしいゲームに, 大きくすると「近づけば当たる」やさしいゲームになります. 20〜30 くらいから試して,遊びながら調整してください.
# クリックで丸を動かし,四角に近づくと色が変わる
from turtle import *
setup(620, 460)
a = Turtle()
a.shape("circle")
a.color("royalblue")
a.pu()
a.goto(-150, -80)
b = Turtle()
b.shape("square")
b.color("seagreen")
b.pu()
b.goto(100, 40)
def move(x, y):
a.goto(x, y)
if a.distance(b) < 30:
b.color("crimson")
print("当たった! 距離", round(a.distance(b)))
else:
b.color("seagreen")
print("まだ遠い.距離", round(a.distance(b)))
onscreenclick(move)
done()
t.distance(x, y) のように座標を直接わたすこともできます.
onscreenclick で受け取った座標がかめに当たっているかを調べるときに使います.
12.4.2 座標の範囲で判定する
四角い的や,床・ゴールのような広い範囲を判定したいときは,
距離より「x がいくつからいくつの間で,かつ y がいくつからいくつの間」と書くほうが自然です.
2 つの条件を and でつなぎます.
x = t.xcor()
y = t.ycor()
if -100 < x and x < 100 and 50 < y and y < 150:
print("四角いゾーンの中にいます")
-100 < x < 100 とまとめて書くこともできます.
数学の書き方そのままです.上の if は
if -100 < x < 100 and 50 < y < 150: と書けます.
12.4.3 画面の端の判定と跳ね返り
画面からはみ出さないようにする,あるいは端でボールを跳ね返す — これも当たり判定です. 620 × 460 の画面なら,画面の端そのものは x が ±310,y が ±230 です. かめには大きさがあるので,少し内側の ±300・±215 あたりで折り返すと ちょうどよく見えます.
跳ね返らせる方法はとても簡単で,
進む向きの符号を反対にするだけです.
右へ 7 ずつ進んでいた(dx = 7)なら,dx = -dx とすれば
左へ 7 ずつ進むようになります.
# 画面の端ではね返るボール
from turtle import *
setup(620, 460)
bgcolor("#101820")
ball = Turtle()
ball.shape("circle")
ball.color("gold")
ball.pu()
dx = 7 # 横に進む量
dy = 5 # たてに進む量
def tick():
global dx, dy
ball.goto(ball.xcor() + dx, ball.ycor() + dy)
# 左右の壁
if ball.xcor() > 300 or ball.xcor() < -300:
dx = -dx
# 上下の壁
if ball.ycor() > 215 or ball.ycor() < -215:
dy = -dy
ontimer(tick, 25)
ontimer(tick, 25)
done()
or は「どちらか一方でも成り立てば」という意味です.
ball.xcor() > 300 or ball.xcor() < -300 は
「右の壁を越えたか,左の壁を越えたか」を 1 行で調べています.
左右のキーで動かせますが,このままでは画面の外まで行ってしまいます. x が −290 より小さくならず,290 より大きくならないようにしてください. ____ を埋めましょう.
if の後は「行きすぎていたら,行きすぎない位置に戻す」と考えます.
左へ行きすぎたら x を -290 に,右へ行きすぎたら x を 290 にします.
from turtle import *
setup(620, 460)
t = Turtle()
t.shape("turtle")
t.color("royalblue")
t.pu()
def go_left():
t.setx(t.xcor() - 20)
if t.xcor() < ____:
t.setx(____)
def go_right():
t.setx(t.xcor() + 20)
if t.xcor() ____ 290:
t.setx(290)
onkeypress(go_left, "Left")
onkeypress(go_right, "Right")
listen()
done()
12.5 スコアを表示する
点数を画面に出すには,スコア専用のかめを 1 匹用意します.
このかめは絵を描かず,文字を書くためだけに使います.
姿は ht() で消し,ペンは pu() で上げておきます.
点が増えたら,clear() で前の文字を消して,
write() で新しい文字を書きます.この 2 行がセットです.
board = Turtle()
board.ht() # 姿を消す
board.pu() # 線を引かない
board.goto(-290, 190) # 左上に置く
def show_score():
board.clear() # 前の文字を消す
board.write("スコア " + str(score),
font=("Arial", 18, "normal")) # 新しく書く
write に渡せるのは文字列です.
score は数なので,str(score) で文字列に直してから
+ でつなぎます."スコア " + str(3) は
"スコア 3" になります.
# スコア板:スペースキーを押すと点が増える
from turtle import *
setup(620, 460)
board = Turtle()
board.ht()
board.pu()
board.color("navy")
board.goto(-290, 190)
score = 0
def show_score():
board.clear()
board.write("スコア " + str(score), font=("Arial", 20, "normal"))
def plus():
global score
score = score + 1
show_score()
show_score()
onkeypress(plus, "space")
listen()
done()
clear() は,画面に描かれた線と文字をすべて消します.
かめ 1 匹ぶんだけを消すのではありません.
ですからゲームでは,自機も敵も pu() で動かして線を残さない
ようにしておきます.そうすれば,画面に残っている絵はスコアの文字だけになり,
clear() しても困りません.
スペースキーを押すたびに,残りが 1 ずつ減っていく表示を作ります.
____ を埋めてください.
書き換えの 2 行(消す・書く)と,global を忘れずに.
from turtle import *
setup(620, 460)
board = Turtle()
board.ht()
board.pu()
board.color("crimson")
board.goto(-290, 190)
rest = 10
def show_rest():
board.____()
board.write("のこり " + ____(rest), font=("Arial", 20, "normal"))
def minus():
____ rest
rest = rest - 1
show_rest()
show_rest()
onkeypress(minus, "space")
listen()
done()
12.6 ゲームオーバーのつくり方
ゲームを止めるとは,次の ontimer を予約しないことです.
予約しなければ,それ以上 tick は呼ばれず,動きが止まります.
def tick():
global miss
# …ここで敵を動かしたり当たり判定をしたり…
if miss >= 3: # 終わりの条件
board.goto(-90, 0)
board.write("ゲームオーバー", font=("Arial", 26, "bold"))
return # ← 予約せずに関数を抜ける
ontimer(tick, 30) # ← 続けるときだけ予約する
return は「この関数をここで終わりにする」という命令です.
return より下は実行されないので,ontimer の予約も行われません.
return にはもう 1 つ「計算した答えを持ち帰る」という大事な働きがあり,
そちらは第 14 章でくわしく習います.
ここでは「関数をここで打ち切る」という使い方だけを,
決まり文句として覚えてください.
もうひとつ,フラグ変数を使う書き方もあります.
「遊んでいる最中かどうか」を playing という変数に覚えさせ,
キーの関数でもそれを見るようにします.
こうすると,ゲームオーバーのあとにキーを押しても自機が動きません.
playing = True # True なら遊んでいる最中
def go_left():
if playing: # 遊んでいる最中だけ動く
player.setx(player.xcor() - 20)
def tick():
global playing
# …
if miss >= 3:
playing = False # 旗を下ろす
return
ontimer(tick, 30)
True(真)と False(偽)は,
「はい・いいえ」を表す Python の値です.先頭が大文字であることに注意してください.
if playing: は if playing == True: と同じ意味ですが,
前者のほうがよく使われます.
12.7 シューティングゲームを 5 段階で作る
いよいよゲームを作ります.いきなり全部を書こうとしてはいけません. 動くものを少しずつ大きく育てていくのが,ゲーム作りの正しい進め方です. ここでは 5 段階に分けます.1 段階ごとに必ず実行して, 思ったとおりに動くことを確かめてから次へ進んでください. ⑤ まで進めば完成です.⑥ は,弾を撃てるようにする発展編です.
| 段階 | できること | 使うもの |
|---|---|---|
| ① | 自機を左右に動かす | onkeypress,listen |
| ② | 敵が上から落ちてくる | ontimer,random |
| ③ | 当たったら敵が出直す | distance |
| ④ | 取った数をスコアに出す | スコア用のかめ,clear・write |
| ⑤ | 3 回取りそこねたら終わり | フラグ変数,return |
| ⑥ | (発展)弾を撃って敵に当てる | フラグ変数,ht・st |
12.7.1 ① 自機を動かす
まず自機だけ.画面の下のほうに置いて,左右のキーで動かします.
はみ出さないように max と min で止めています.
max(-290, x) は「−290 と x の大きいほう」という意味で,
x が −290 より小さくなったら −290 になります.
# シューティング ① 自機を動かす
from turtle import *
setup(620, 460)
bgcolor("#0d1117")
# --- 自機 ---
player = Turtle()
player.shape("triangle")
player.color("cyan")
player.pu()
player.goto(0, -170)
def go_left():
player.setx(max(-290, player.xcor() - 20))
def go_right():
player.setx(min(290, player.xcor() + 20))
onkeypress(go_left, "Left")
onkeypress(go_right, "Right")
listen()
done()
12.7.2 ② 敵を落とす
敵を 1 匹追加し,ontimer で少しずつ下げます.
下まで行ったら,また上のどこかに戻します.
横の位置は random.randint(-270, 270) で毎回変えると,
それだけでゲームらしくなります.
# シューティング ② 敵が落ちてくる
from turtle import *
import random
setup(620, 460)
bgcolor("#0d1117")
# --- 自機 ---
player = Turtle()
player.shape("triangle")
player.color("cyan")
player.pu()
player.goto(0, -170)
# --- 敵 ---
enemy = Turtle()
enemy.shape("circle")
enemy.color("orange")
enemy.pu()
enemy.goto(random.randint(-270, 270), 200)
def new_enemy():
enemy.goto(random.randint(-270, 270), 200)
def go_left():
player.setx(max(-290, player.xcor() - 20))
def go_right():
player.setx(min(290, player.xcor() + 20))
def tick():
enemy.sety(enemy.ycor() - 7) # 敵を下げる
if enemy.ycor() < -210: # 下まで落ちたら
new_enemy() # 上から出直す
ontimer(tick, 30)
onkeypress(go_left, "Left")
onkeypress(go_right, "Right")
listen()
ontimer(tick, 30)
done()
12.7.3 ③ 当たり判定を入れる
tick の中で,自機と敵の距離を調べます.
近ければ「取った」ことにして,敵を上から出直させます.
ここでゲームとして成立します.必ず実行して,取れることを確かめてください.
# シューティング ③ 当たり判定
from turtle import *
import random
setup(620, 460)
bgcolor("#0d1117")
player = Turtle()
player.shape("triangle")
player.color("cyan")
player.pu()
player.goto(0, -170)
enemy = Turtle()
enemy.shape("circle")
enemy.color("orange")
enemy.pu()
enemy.goto(random.randint(-270, 270), 200)
def new_enemy():
enemy.goto(random.randint(-270, 270), 200)
def go_left():
player.setx(max(-290, player.xcor() - 20))
def go_right():
player.setx(min(290, player.xcor() + 20))
def tick():
enemy.sety(enemy.ycor() - 7)
if player.distance(enemy) < 26: # 取った
print("取った!")
new_enemy()
elif enemy.ycor() < -210: # 取りそこねた
print("にがした…")
new_enemy()
ontimer(tick, 30)
onkeypress(go_left, "Left")
onkeypress(go_right, "Right")
listen()
ontimer(tick, 30)
done()
if と elif にしているのは,
「取った」と「にがした」のどちらか一方しか起きないことを,
読む人にもはっきり示すためです.
「取った」ときは new_enemy() で敵が上に戻るので,
両方 if で書いても結果は同じですが,
elif にしておけば,1 回の tick で両方を数えることが決して起きないと,
中身を追わなくても読むだけで分かります.
12.7.4 ④ スコアを付ける
スコア用のかめを足し,取った数を数えます.
tick の中で score を書き換えるので
global score が要ります.
# シューティング ④ スコア
from turtle import *
import random
setup(620, 460)
bgcolor("#0d1117")
player = Turtle()
player.shape("triangle")
player.color("cyan")
player.pu()
player.goto(0, -170)
enemy = Turtle()
enemy.shape("circle")
enemy.color("orange")
enemy.pu()
enemy.goto(random.randint(-270, 270), 200)
# --- スコア板 ---
board = Turtle()
board.ht()
board.pu()
board.color("white")
board.goto(-290, 190)
score = 0
miss = 0
def show_score():
board.clear()
board.write("スコア " + str(score) + " ミス " + str(miss),
font=("Arial", 18, "normal"))
def new_enemy():
enemy.goto(random.randint(-270, 270), 200)
def go_left():
player.setx(max(-290, player.xcor() - 20))
def go_right():
player.setx(min(290, player.xcor() + 20))
def tick():
global score, miss
enemy.sety(enemy.ycor() - 7)
if player.distance(enemy) < 26:
score = score + 1
new_enemy()
elif enemy.ycor() < -210:
miss = miss + 1
new_enemy()
show_score()
ontimer(tick, 30)
show_score()
onkeypress(go_left, "Left")
onkeypress(go_right, "Right")
listen()
ontimer(tick, 30)
done()
12.7.5 ⑤ ゲームオーバーを付ける
最後に終わりを付けます.ミスが 3 回たまったら,
大きな文字で「ゲームオーバー」と出して return で止めます.
フラグ playing を下ろしておくと,そのあとキーを押しても自機は動きません.
これで完成です.
# シューティング ⑤ 完成版
# 遊び方:← → で動かし,落ちてくる玉を取る.3 回にがすと終わり.
from turtle import *
import random
setup(620, 460)
bgcolor("#0d1117")
player = Turtle()
player.shape("triangle")
player.color("cyan")
player.pu()
player.goto(0, -170)
enemy = Turtle()
enemy.shape("circle")
enemy.color("orange")
enemy.pu()
enemy.goto(random.randint(-270, 270), 200)
board = Turtle()
board.ht()
board.pu()
board.color("white")
board.goto(-290, 190)
score = 0
miss = 0
playing = True
def show_score():
board.clear()
board.write("スコア " + str(score) + " ミス " + str(miss),
font=("Arial", 18, "normal"))
def new_enemy():
enemy.goto(random.randint(-270, 270), 200)
def go_left():
if playing:
player.setx(max(-290, player.xcor() - 20))
def go_right():
if playing:
player.setx(min(290, player.xcor() + 20))
def tick():
global score, miss, playing
enemy.sety(enemy.ycor() - 7)
if player.distance(enemy) < 26:
score = score + 1
new_enemy()
elif enemy.ycor() < -210:
miss = miss + 1
new_enemy()
show_score()
if miss >= 3: # 終わりの条件
playing = False
board.goto(-120, 0)
board.color("crimson")
board.write("ゲームオーバー", font=("Arial", 28, "bold"))
return # 予約せずに抜ける → 止まる
ontimer(tick, 30)
show_score()
onkeypress(go_left, "Left")
onkeypress(go_right, "Right")
listen()
ontimer(tick, 30)
done()
shape と color を変えて見た目を作り込む —
1 つ変えるたびに実行することを守れば,必ず動くものができます.
12.7.6 もう一歩:弾を撃てるようにする
ここまでの 5 段階は,落ちてくる敵を受け止めるゲームでした. 6 段階目として,敵が横に動き,自機から弾を撃って当てる シューティングも作ってみましょう.新しく覚えることはありません. いままで使ったものの組み合わせです.
- 敵は横に動く —
enemy.setx(enemy.xcor() + 4).右端まで行ったら左端から出直す - 弾は 1 発だけ — 飛んでいる最中かどうかを
flyingというフラグに覚えさせ,飛んでいないときだけ撃てるようにする - 弾は上へ飛ぶ —
bullet.sety(bullet.ycor() + 18) - 当たり判定は弾と敵の間 —
bullet.distance(enemy) < 26 - 使わない弾は消しておく —
bullet.ht()で姿を消し,撃つときにbullet.st()で出す
# シューティング ⑥ 弾を撃つ
# 遊び方:← → で動かし,スペースキーで弾を撃つ.横に動く敵に当てる.
from turtle import *
import random
setup(620, 460)
bgcolor("#0d1117")
player = Turtle()
player.shape("triangle")
player.color("cyan")
player.pu()
player.goto(0, -170)
enemy = Turtle()
enemy.shape("circle")
enemy.color("orange")
enemy.pu()
enemy.goto(-250, 150)
bullet = Turtle()
bullet.shape("circle")
bullet.color("gold")
bullet.shapesize(0.5, 0.5)
bullet.pu()
bullet.ht() # 撃つまでは見えない
board = Turtle()
board.ht()
board.pu()
board.color("white")
board.goto(-290, 190)
score = 0
flying = False # 弾が飛んでいる最中かどうか
def show_score():
board.clear()
board.write("スコア " + str(score), font=("Arial", 18, "normal"))
def go_left():
player.setx(max(-290, player.xcor() - 20))
def go_right():
player.setx(min(290, player.xcor() + 20))
def shoot():
global flying
if not flying: # 1 発ずつしか撃てない
bullet.goto(player.xcor(), player.ycor() + 20)
bullet.st()
flying = True
def tick():
global score, flying
# 敵は右へ.右端まで行ったら左端から出直す
enemy.setx(enemy.xcor() + 4)
if enemy.xcor() > 300:
enemy.goto(-300, random.randint(60, 190))
# 弾は上へ飛ぶ
if flying:
bullet.sety(bullet.ycor() + 18)
if bullet.distance(enemy) < 26: # 当たった
score = score + 1
flying = False
bullet.ht()
enemy.goto(-300, random.randint(60, 190))
elif bullet.ycor() > 230: # 画面の外へ出た
flying = False
bullet.ht()
show_score()
ontimer(tick, 30)
show_score()
onkeypress(go_left, "Left")
onkeypress(go_right, "Right")
onkeypress(shoot, "space")
listen()
ontimer(tick, 30)
done()
flying のようなフラグを使わずに書くと,
スペースキーを押しっぱなしにしたときに弾が毎コマ出直してしまい,
弾がその場に止まって見えます.
「いま飛んでいるか」を覚えておくことが大事です.
12.8 ゲームで役に立つ文法
ゲームを作っていると出てくる,ここまでで説明しそびれた書き方を 4 つ紹介します. どれも短いものです.読めるようになっておけば十分です.
12.8.1 break — 繰り返しを途中でやめる
for の繰り返しの途中で break を実行すると,
その場で繰り返しから抜けます.「見つかったらもう探さなくてよい」ときに使います.
for i in range(10):
if i == 4:
break # ここで for から抜ける
print(i) # 0 1 2 3 だけが出る
break は「入口ひとつ・出口ひとつ」というプログラムの作法をくずすので,
使いすぎないほうがよいと言われます.
playing のようなフラグ変数で制御すれば,
break を使わずに同じことが書けます(12.6 節でやったとおりです).
12.8.2 continue — この回だけ飛ばす
continue は,繰り返しを抜けるのではなく
その回の残りを飛ばして次の回へ進みます.
for i in range(6):
if i % 2 == 0:
continue # 偶数のときは下を飛ばす
print(i) # 1 3 5 だけが出る
12.8.3 not — 真偽をひっくり返す
not は「〜でなければ」という意味です.
not True は False,not False は True になります.
playing = False
if not playing: # 「遊んでいる最中でなければ」
print("ゲームは止まっています")
not playing は playing == False と同じ意味です.
どちらでもかまいませんが,not のほうが短く,英語の文として読めます.
12.8.4 and と or — 条件を組み合わせる
条件は組み合わせられます.
and は「両方とも成り立つ」,
or は「どちらか一方でも成り立つ」です.
| 書き方 | 意味 | ゲームでの使いどころ |
|---|---|---|
A and B | A も B も成り立つとき | 「四角いゾーンの中」「自機が生きていて,かつ当たった」 |
A or B | A か B の少なくとも一方が成り立つとき | 「左の壁か右の壁に当たった」 |
not A | A が成り立たないとき | 「ゲーム中でなければ操作を受け付けない」 |
# break / continue / not / and / or をまとめて試す
# break:4 になったら繰り返しをやめる
for i in range(10):
if i == 4:
break
print("break の例", i)
print("---")
# continue:偶数の回は飛ばす
for i in range(6):
if i % 2 == 0:
continue
print("continue の例", i)
print("---")
# not:真偽をひっくり返す
playing = False
if not playing:
print("ゲームは止まっています")
print("---")
# and と or
x = 120
y = 40
if x > 100 and y > 30:
print("and:2 つとも成り立つ")
if x > 500 or y > 30:
print("or:どちらか一方でも成り立つ")
ゲームが止まっているときは自機が動かないようにします.
____ を埋めてください.
スペースキーで止める/再開する仕組みです.
「止まっていれば再開,動いていれば停止」は not で書けます.
from turtle import *
setup(620, 460)
t = Turtle()
t.shape("turtle")
t.color("royalblue")
t.pu()
playing = True
def go_left():
if ____: # 遊んでいる最中だけ動く
t.setx(t.xcor() - 20)
def go_right():
if playing:
t.setx(t.xcor() + 20)
def toggle():
____ playing
playing = ____ playing # True と False を入れかえる
print("playing =", playing)
onkeypress(go_left, "Left")
onkeypress(go_right, "Right")
onkeypress(toggle, "____")
listen()
done()
12.9 この章で出てきた命令
| 命令 | 意味 |
|---|---|
onkeypress(関数, "Left") | そのキーが押されたときに関数を呼ぶ.かっこを付けずに関数名だけ書く |
onkey(関数, "Left") | そのキーが離されたときに関数を呼ぶ |
listen() | キーボードを聞く準備をする.登録のあとに 1 回書く |
onscreenclick(関数) | 画面がクリックされたら関数を呼ぶ.関数は (x, y) を受け取る |
かめ.onclick(関数) | そのかめがクリックされたら関数を呼ぶ.押せる範囲は中心から半径 14 ほど |
ontimer(関数, ミリ秒) | 指定した時間のあとに関数を 1 回呼ぶ.関数の最後で書き直すとループになる |
かめ.distance(相手) | 相手のかめまでの距離.< 30 などで当たり判定に使う |
かめ.distance(x, y) | 座標までの距離 |
かめ.setx(x) / かめ.sety(y) | x 座標だけ/y 座標だけを変える |
かめ.xcor() / かめ.ycor() | いまの x 座標/y 座標 |
かめ.clear() | 描いた線と文字を消す(画面ぜんぶが対象) |
かめ.write(文字, font=(…)) | その位置に文字を書く |
かめ.shape("circle") | 姿を変える.classic arrow square triangle circle turtle blank |
bgcolor(色) | 画面の背景色を変える |
setup(横, たて) | 画面の大きさを決める |
random.randint(a, b) | a 以上 b 以下の整数をでたらめに 1 つ.import random が要る |
global 変数名 | 関数の中から,外の変数を書き換えるときに書く |
return | 関数をその場で終わりにする |
not / and / or | 条件をひっくり返す/両方/どちらか |
break / continue | 繰り返しを抜ける/その回の残りを飛ばす |
while True:は使えません.ループはontimerで作ります.- 画像を貼り付けた見た目は使えません.
shapeとcolorとshapesizeで作ります. - 音は出せません.
- マウスの「いまの位置」やドラッグは取れません.クリックされたときだけ座標が届きます.
- 右クリック・ダブルクリックの区別もできません.クリックは 1 種類だけです.
演習
以下の演習には,出発点となるプログラムが入っています. 「▶ この演習を開く」で右側に読み込み,それを発展させて作品にしてください. できあがったら「⤓ 保存」でファイルに保存して,手元に残しておきましょう.
矢印キーで上下左右に 20 ずつ動かせるプログラムを与えます.
これをもとに,次の要件を満たすお絵かきソフトにしてください.
線が見やすいように,shapesize(2, 2) で自機を大きくし,
pensize(3) で線を太くしておきます(ここは準備です).
- 画面からはみ出さないようにする.x は −290〜290,y は −210〜210 の中で止まるようにします(12.4.3 節)
onkeypress(関数, "space")を足し,スペースキーに働きを付ける- これもやりましょう:スペースキーを押すたびに,ペンの上げ下げが交互に切りかわるようにする.ペンを下ろしている間は色を
"crimson"にして動いた跡を線で残し,上げている間は元の"royalblue"に戻して線を残さない.最初はペンを上げた状態です.いまペンを下ろしているかどうかをTrue/Falseの変数(フラグ)で覚えておき,notで切りかえます
必須(すべて満たして正しく動けば 8)
- 矢印キー 4 つで,これまでどおり上下左右に 20 ずつ動く
- 上下左右のどの向きでも画面からはみ出さない(x は −290〜290,y は −210〜210 の中で止まる)
onkeypress(関数, "space")でスペースキーに関数を結び付けている- スペースキーを押すたびに,ペンを下ろす(赤で線が残る)と上げる(青で線が残らない)が交互に切りかわる(フラグと
not,global)
発展(必須に加えて 1 つ以上で 9,学んだ範囲を大きく超える作り込みで 10)
- 12.2.3 節の書き方で,押しっぱなしでなめらかに動くようにする(キーでは速さの変数だけを変え,動かすのは
ontimerの中) - 止まる範囲の端(290 と 210)を変数にまとめ,1 か所を変えるだけで動ける範囲が変わるようにする
- 別のキー(たとえば
c)で線を全部消し,そのあともフラグとペンの状態・色が食い違わないようにする
必須が 1〜2 つ足りない → 7 / 動かない・未着手が複数 → 6
図形をマウスで動かすプログラムを与えます. このランタイムはドラッグに対応していない(ボタンを押したままの状態も, いまのマウスの位置も取れない)ので, 1 回目のクリックでつかみ,2 回目のクリックで置く方式にしてあります. できることはドラッグと同じです.これを発展させて,次の要件を満たす片付けゲームにしてください.
- つかんでいる間は,つかんだ図形を
shapesize(2.6, 2.6)に大きくし,置いたらshapesize(2, 2)に戻す(いまつかんでいることが分かる印) - 画面の右上に,置き場所のわくを描く.わくは左下の角が (60, 60),横 200,縦 140 の四角(x は 60〜260,y は 60〜200)です.線を描くかめはスコア用とは別にします
- 図形を置くたびに,わくの中にある図形の数を座標の範囲(
60 < a.xcor() < 260 and 60 < a.ycor() < 200のような条件)で数え直す - その数を,(−290, 190) に置いたスコア用のかめで「わくの中 ○ 個」と表示し,置くたびに
clear()→write()で書き直す(最初は「わくの中 0 個」) - これもやりましょう:3 つともわくに入ったら,スコアの下 (−290, 150) に「クリア!」と表示する.1 つでもわくから出したら「クリア!」は消えるようにします
held にはかめそのものを入れています.
何もつかんでいないことを表すのに None(「なし」を表す Python の値)を使い,
if held is not None: で「何かつかんでいるか」を調べています.
必須(すべて満たして正しく動けば 8)
- つかんでいる間は,つかんだ図形が 2.6 倍に大きくなり,置くと 2 倍に戻る
- x が 60〜260,y が 60〜200 のわくが画面に描かれている
- 置くたびに,わくの中にある図形の数を座標の範囲(
andか< x <)で数え,「わくの中 ○ 個」をclear()→write()で書き直す(最初は「わくの中 0 個」) - 3 つともわくに入ると「クリア!」と出て,1 つ出すと消える
発展(必須に加えて 1 つ以上で 9,学んだ範囲を大きく超える作り込みで 10)
- わくを 2 つにし,図形ごとに入れるべきわくを決めて,正しいわくに入った数だけを数える(条件の組み合わせ)
ontimerで,つかんでいないものだけが勝手に動くようにする(heldで動きを分ける)- クリアまでにクリックした回数を数えて表示し,クリアしたあとは数えないようにする
必須が 1〜2 つ足りない → 7 / 動かない・未着手が複数 → 6
アイテムを拾うと点が増えるプログラムを与えます. 自機を動かしてアイテムに触れると,アイテムが別の場所に現れ,スコアが 1 増えます. これを発展させて,次の要件を満たす30 秒の制限時間つきゲームにしてください.
- 残り時間を表す変数
rest = 30を作り,ontimer(tick, 1000)で 1 秒ごとに呼ばれる関数tickの中で 1 ずつ減らす - スコアの表示を「スコア ○ のこり ○ 秒」に変え,アイテムを取ったときと 1 秒たつごとに書き直す
- 残りが 0 になったら,画面の中央付近 (−110, 0) に「おしまい」と大きく表示し,次の
ontimerを予約せずにreturnで止める - 終わったあとは,矢印キーを押しても自機が動かないようにする(フラグ変数
playingをTrueで始め,終わったらFalseにする) - これもやりましょう:終わりぎわを盛り上げるため,残り時間が 10 秒以下(
restが 10 のときを含む)になったら,金色のアイテムを取ったときの点を 2 点にする.残りが 11 秒以上のあいだは,これまでどおり 1 点です
必須(すべて満たして正しく動けば 8)
ontimer(tick, 1000)で 1 秒ごとに残り時間が 1 ずつ減り,「スコア ○ のこり ○ 秒」が画面に表示される- 残りが 0 になると「おしまい」と表示され,
returnでtickが止まる - 終わったあとは,キーを押しても自機が動かない(フラグ変数
playing) - 残りが 11 秒以上なら 1 点,10 秒以下(10 秒ちょうどを含む)なら 2 点入る
発展(必須に加えて 1 つ以上で 9,学んだ範囲を大きく超える作り込みで 10)
- スコアに応じて自機の進む量やアイテムの動きを変え,取るほど難しくなるようにする
- 終わったときに,スコアの範囲(境目を決めて
if/elif)で「たいへんよくできました」などのことばを変える - 取ると点が減る「はずれ」のアイテムを足し,スコアが 0 より小さくならないようにする
必須が 1〜2 つ足りない → 7 / 動かない・未着手が複数 → 6
こんどは取るのではなくよけるゲームです. 落ちてくる岩に当たったら終わり,よけた数がスコアになります. 出発点を与えるので,次の要件を満たすように完成させてください.
出発点には当たり判定とゲームオーバーがまだ書かれていません.
# ここに「岩に当たったら終わり」を書く の場所に,自分で足してください.
player.distance(rock)が 26 より小さくなったら「当たった」とする- 当たったら,画面の中央付近 (−120, 0) に「ゲームオーバー」と大きく表示し,次の
ontimerを予約せずにreturnで止める - 止まったあとは,矢印キーを押しても自機が動かないようにする(フラグ変数
playingをTrueで始め,当たったらFalseにする) - これもやりましょう:だんだん難しくする.岩が下まで落ちて「よけた数」が 1 増えるたびに,落ちる速さ
speed_yを 1 ずつ大きくする(最初は 8).ただしspeed_yは 20 より大きくしない(20 になったら,それ以上よけても 20 のまま)
必須(すべて満たして正しく動けば 8)
player.distance(rock)が 26 未満になると当たったと判定する- 当たったら「ゲームオーバー」と画面に表示し,
returnで次のontimerを予約せずに止める - 止まったあとは,キーを押しても自機が動かない(フラグ変数
playing) - よけるたびに
speed_yが 1 ずつ大きくなり,20 で止まる(それ以上速くならない)
発展(必須に加えて 1 つ以上で 9,学んだ範囲を大きく超える作り込みで 10)
- 速さの増え方(1)と上限(20)を変数にまとめ,1 か所を変えるだけで難しさを調整できるようにする
- よけた数が決めた数になったら「クリア」と出して止める(終わり方を 2 通りにする)
- 自機が左右の端を越えたら反対側から出てくるようにする(端の判定を自分で考える)
必須が 1〜2 つ足りない → 7 / 動かない・未着手が複数 → 6
キーを分ければ,1 台のパソコンで 2 人遊べます.
左のプレイヤーは a と z,右のプレイヤーは
↑ と ↓ で動かす「テニス」の出発点を与えます.
ボールがラケットに当たると跳ね返り,後ろの壁に当たると相手の得点です.
これを発展させて,次の要件を満たす試合にしてください.
- どちらかが 5 点取ったら,画面の中央付近 (−90, 0) に「左の勝ち」か「右の勝ち」を大きく表示し,
returnで止める - 止まったあとは,キーを押してもラケットが動かないようにする(フラグ変数
playingをTrueで始め,終わったらFalseにする) - ラケットではね返るたびに,ボールの横の速さを 1 ずつ大きくする(左のラケットでは
dx = -dx + 1,右のラケットではdx = -dx - 1).点が入ってボールを (0, 0) に戻すときは,速さも元に戻す(右の得点ならdx = 6,左の得点ならdx = -6) - これもやりましょう:速くなりすぎないように,横の速さの大きさを15 までにする.左のラケットではね返ったあとの
dxは 15 より大きくせず,右のラケットではね返ったあとのdxは −15 より小さくしない(min・maxかifを使う)
必須(すべて満たして正しく動けば 8)
- どちらかが 5 点取ったら「左の勝ち」か「右の勝ち」を画面に表示し,
returnで止める - 止まったあとは,キーを押してもラケットが動かない(フラグ変数
playing) - ラケットではね返るたびに,ボールの横の速さ
dxの大きさが 1 ずつ大きくなり,点が入ると 6 に戻る - 何度はね返っても
dxの大きさが 15 を超えない(左では 15,右では −15 で止まる)
発展(必須に加えて 1 つ以上で 9,学んだ範囲を大きく超える作り込みで 10)
- ラケットの長さを 1 つの変数で決め,見た目(
shapesize)と当たり判定の数が一緒に変わるようにする - スペースキーで,終わったあとに得点・速さ・フラグを元に戻し,もう一度遊べるようにする
- ボールが当たったラケットの位置で,はね返る向き(
dy)を変える
必須が 1〜2 つ足りない → 7 / 動かない・未着手が複数 → 6
自由な発想で,自分のゲームを作ってください. 12.7 節の 5 段階をそのまま真似て,①から順に少しずつ育ててください. いきなり完成形を書こうとすると,まず動きません. 弾を撃つゲームにしたいときは,12.7.6 節の ⑥ が出発点になります.
必須(すべて満たして正しく動けば 8)
- 操作できる —
onkeypressかonscreenclickで,遊ぶ人が何かを動かせる - 当たり判定がある —
distanceか座標の範囲で,ものとものが当たったかを判定している - スコアと終了がある — 点数を画面に出し,条件を満たしたら
returnで止まる - 先頭のコメントに題名・氏名・日付と遊び方を書いている
発展(必須に加えて 1 つ以上で 9,学んだ範囲を大きく超える作り込みで 10)
- スコアや時間に応じて速さなどを変え,だんだん難しくなる(増え方・上限を変数で決める)
- 12.7.6 節のように弾を撃てる(フラグで「飛んでいる最中」を管理する),2 人で対戦できる
- 終わり方を複数にして結果の表示を分ける,やり直しで変数をすべて元に戻すなど,ゲームの流れを状態で組み立てる
必須が 1〜2 つ足りない → 7 / 動かない・未着手が複数 → 6
まとめ
- ゲームは準備 → 毎フレームの処理 → 終了判定の 3 つでできている.
- ブラウザでは
while True:で待てない.ループはontimer(関数, ミリ秒)を関数の最後で書き直して作る. - キーは
onkeypress(関数, "Left")で登録し,最後にlisten().なめらかに動かしたいときは,キーで速さの変数だけ変えて,動かすのはontimerの中でやる. - マウスは
onscreenclick(関数)で座標を受け取る.かめ自身を押させたいときはかめ.onclick(関数).ドラッグは使えないので,つかむ → 置くの 2 回クリックに置き換える. - 当たり判定は
distanceによる円か,x と y の範囲.端で跳ね返らせるには進む量の符号を反対にする. - スコアは専用のかめを 1 匹用意し,
clear()→write()で書き直す. - 止めるときは,次の
ontimerを予約しない.returnとフラグ変数を使う. - 関数の中から外の変数を書き換えるときは
globalが要る(第 10 章からの決まり文句.仕組みは第 13 章). - 作るときは少しずつ.1 つ足したら必ず実行.
ここまでの 3 回で,def と global を
決まり文句として使い続けてきました.
tick・go_left・draw_board —
気がつけば,ずいぶんたくさんの「名前の付いた中身」を書いています.
次の回では,その def の正体を習います.
かっこの中に名前を並べておくと何ができるのか(引数),
なぜ関数の中と外で変数が別ものになるのか(そして global は何をしているのか).
決まり文句の理由が分かると,今回のゲームの長いプログラムを
短く,読みやすく書き直せるようになります.
使える命令の一覧は付録「タートル命令リファレンス」(第 4 章)に, 白紙のエディタは付録「じゆうに書く」にあります. 思いついたことを,いつでも試してみてください.
復習:虫食いで確かめる
コードの ____ を埋めてください.実行前に結果を予測し,実行後に問題文の条件と照合しましょう.assert は条件が成り立つかを調べ,成り立たないとエラーになります.
右矢印を離すたびに20進むようにします.関数の名前とキーを補い,実行後にキー操作で確認してください.
from turtle import *
def move_right():
forward(20)
onkey(____, ____)
listen()
done()