【Pygame Zero】簡単なゲーム10:animate( )で障害物を動かそう!
こんにちは!
「Pythonしよう!楽しく学べるプログラミング教室」の
ラッチ先生です


スックです。よろしくね!
BGM提供:DOVA-SYNDROME
https://dova-s.jp/
・ 「Spirit」by TECHNOTRAIN
効果音提供:Chisato’s Website
https://chisatosound.sakura.ne.jp/index.html
・ 「Accent. Brilliant [02] (Low)」:アクセント
・ 「Bump. Low-Bit [03]」: アクションゲーム
・ 「Kiga Nukeru [01] (Long)」:アニメ

基礎プログラムと 画像を入れた
「簡単なゲーム10: 宇宙へ GO!」zipフォルダを
ダウンロードしてください


今回は、スクリーンの色を “aqua” したよ
「原色大事典」サイトには、URL:https://www.colordic.org/
pygame zeroで使える色が載っています


このゲームで使用している音声ファイルは、
以下のサイトからお借りしています。
各自でダウンロードして、該当するフォルダに入れてください!
BGM提供:DOVA-SYNDROME
https://dova-s.jp/
・ 「Spirit」by TECHNOTRAIN
効果音提供:Chisato’s Website
https://chisatosound.sakura.ne.jp/index.html
・ 「Accent. Brilliant [02] (Low)」:アクセント
・ 「Bump. Low-Bit [03]」: アクションゲーム
・ 「Kiga Nukeru [01] (Long)」:アニメ

学習の流れ
左右キーで 動かす
雲が 下へ 流れる
ゲーム クリア
ゲームオーバー
プログラムを 実行してみよう
プログラミングの仕方を説明します
モジュールを 用意する

今回の「宇宙へ GO!」では、
・ boonが、雲や風船に 触れる
・ 雲の大きさを 変える
・ 雲や風船をランダムに選んだ場所へ 動かす
プログラムがあります。
そこで、2つのモジュールを 用意しました



モジュールとは、
関数やプログラムが書かれているファイルのことだよ

Actor( )クラスが入っている 変数boon には、
オブジェクトを動かす属性(データ)やメソッド(命令)が
あります。
今回のプログラミングのポイント

今回のプログラミングには、ポイントが 3つあります


リストclouds を作成して、
cleate_cloud関数で 作成された雲を 入れるよ

これで、雲を 好きな時に止められるね


クリアとゲームオーバーのプログラムを まとめたよ
boonを 左右キーで 動かす

boonオブジェクトに プロパティ:on を 追加します。
boonの動きと止めるのスイッチにします。

boon.on = True #1 プロパティ:onに Trueを 設定する
def boon_move(): #2 boon_move()関数を 定義する
if boon.on: #3 もし プロパティonが Trueなら
boon.y -= 1 #4 boonのy座標を 1ずつ減らす
if boon.top < 0: #5 もし プロパティtopが 0未満になったら
boon.on = False #6 プロパティ:onを Falseにする
def update():
boon_move() #7 boon_move()関数を 実行する- プロパティ:top とは?
-


おおっ! 止まった

次に、左右キーを押したら boonが左右に動くよう
boon_move( ) 関数を 定義するよ
min( ), max( ) 関数を使って
左右の端で止まるようにしました。

def boon_move():
if boon.on:
boon.y -= 1
if keyboard.right: #1 もし 右キーが 押されたら
boon.x = min(boon.x + 5, 700) #2 boonのx座標に x座標+5と700 の最小値を設定する
elif keyboard.left: #3 もし 左キーが 押されたら
boon.x = max(boon.x - 5, 100) #3 boonのx座標に x座標-5と100 の最大値を設定する
if boon.top < 0:
boon.on = False- keyboard オブジェクトとは?
-


これで、boonの動ける範囲が できたね
雲が 下へ 流れる

それでは、cleate_cloud()関数を作って 雲を 発生させましょう
雲を量産させるので、リストcloudsを 作るよ

clouds = [] #1 リストcloudsに 空リストを代入する
def create_cloud(): #2 create_cloud()関数を 定義する
x = random.randint(0, 800) #3 x座標に 0~800からランダムに選んだ数字を 代入する
cloud = Actor("cloud", (x, 0)) #4 変数cloudに cloudオブジェクトを代入する
cloud.scale = random.uniform(0.8, 2.0) #5 プロパティscaleに 0.8~2.0からランダムに選んだ数値を 設定する
clouds.append(cloud) #6 リストcloudsに cloudオブジェクトを追加する
clock.schedule(create_cloud, random.uniform(0.2, 1.5)) #7 0.2~1.5からランダムに選んだ時間後に create_cloud関数を 実行する
clock.schedule(create_cloud, random.uniform(0.2, 1.5)) #8 0.2~1.5からランダムに選んだ時間後に create_cloud関数を 実行するdef draw():
screen.fill("aqua")
balloon.draw()
boon.draw()
for cloud in clouds: #9 リストcloudsから cloudオブジェクトを取り出す
cloud.draw() #10 cloudオブジェクトを 表示する- プロパティ:scale とは?
-

- uniform( )関数 とは?
-

- schedule( )メソッド とは


雲が どんどん現れてくるね

animate( )関数を 使って雲を下へ 動かしていくよ
cleate_cloud( )関数の中に 追加します。

def create_cloud():
x = random.randint(0, 800)
cloud = Actor("cloud", (x, 0))
cloud.scale = random.uniform(0.8, 2.0)
target_x = random.randint(0, 800) #1 変数target_xに 0~800からランダムに選んだ数字を 代入する
dur_time = random.uniform(1, 4) #2 変数dur_timeに 1~4からランダムに選んだ 小数値を代入する
cloud.anim = animate(cloud, pos = (target_x, 600), duration= dur_time, on_finished=lambda:clouds.remove(cloud)) #3 プロパティanimに アニメーションを 代入する
clouds.append(cloud)
clock.schedule(create_cloud, random.uniform(0.2, 1.5))
lambda って、なあに?
★ 雲が 下へ動く プログラムの解説 ★
このプログラムは、一つ一つ解説していきます。
まず、雲の動きを animate( )関数で 作りました。
目的地と 間隔に ランダムな数字を入れれば、雲の動きが おもしろくなります。

ほんとだ! 動きがどれも違って いいねぇ
○到着する地点のx座標(target_x)を ランダムに 決めます
32. target_x = random.randint( 0 , 800 )
○ 雲が動く時間を 変数dur_time に代入します。
33. dur_time = random.uniform(1, 4)
dur: durationの略語
○雲の動きを アニメーションさせます
34. animate( cloud, pos=(target_x, 600), duration= dur_time)

雲が 下で 止まっちゃったね
○ 雲が 目的地に着いたら 削除する
animate( )関数には、
アニメーション終了後の動作を 引数on_finishedで 指定できます。
remove( )メソッドを使って、
下に到着した雲を リストcloudsから削除して 消しましょう
34. animate( cloud, ・・・, on_finished = clouds.remove(cloud) )

しかし、これでは、エラーが出てしまします

あっ!
もしかして、remove( )メソッドに ( )が あるから…

そうなんです。
関数に( )が あると、今すぐ実行するということ。
アニメーションしたと同時に雲を削除しようとするから
エラーが出るんだ
そこで、lambda(ラムダ)を 使います。

★ lambda : ○○を 使う★
34. animate(cloud, …, on_finished = lambda:clouds.remove(cloud))
lambda: clouds.remove(cloud) という関数になって
on_finished引数に渡り、アニメーションが 終了後 実行されます。

lambda :によって、
remove(cloud)に ( )が あっても すぐ実行されなくなったね
○ 雲のアニメーションを プロパティに 代入する
34. cloud.anim = animate(cloud,
pos = (target_x, 600),
duration= dur_time,
on_finished=lambda:clouds.remove(cloud))
これで、雲の動きを 途中で止めることが できます。

これで、雲の動きのプログラムは O.K!
ゲームクリア

まずは、ゲームを終了するgame_end( )関数を 定義します・

boon.on = True
boon.bg = None #2 プロパティbgに None(無し)を設定するdef game_end(bg, costume, sounds): #5 game_end()関数を 定義する
boon.on = False #6 プロパティ:onを Falseに設定する
boon.bg = bg #7 プロパティbgに 引数bgを設定する
boon.image = costume #8 プロパティimageに 引数costumeを設定する
sounds.play() #9 変数soundを 再生する
music.stop() #10 BGMを 止めるdef draw():
screen.fill("aqua")
if boon.bg: #3 もし プロパティbgに データが入ったら
screen.blit(boon.bg, (0, 0)) #4 プロパティbgのデータを 表示する
balloon.draw()
boon.draw()
for cloud in clouds:
cloud.draw()
music.play("spirit") #1 BGMを 流す- music モジュールとは

60. music.play(“spirit”)
BGM提供:DOVA-SYNDROME
https://dova-s.jp/
・ 「Spirit 」 by TECHNOTRAIN曲名の大文字は、小文字にしないとエラーが出るよ
- blit( )メソッドとは
-

53. if boon.bg:
54. screen.blit(boon.bg, (0, 0))game_end( bg, …)関数で、引数bgに 画像が設定されたら
53 if boon.bgで Trueに なります。

これで、game_end( )関数が 定義できました。
bg は、backgroundの 略語だよ

それでは、boonが 上まで行ったら
game_end( )関数を 実行させて、ゲームクリアにします
def boon_move():
if boon.on:
boon.y -= 1
if keyboard.right:
boon.x = min(boon.x + 5, 700) if boon.top < 0:
game_end("bg_clear", "boon_ok", sounds.accent_brilliant_02_low) #1 game_end()関数を 実行する
あれっ? クリアになっても 雲が 動いているよ

unschedule( )メソッドで、cleate_cloud関数を止めましょう。
それで、雲は 新しく作り出されません。
また、今動いている雲は、プロパティ:anim に
stop()メソッドを設定して 止めます。
最後に、clear()メソッドを使って
リストcloudsを空にすれば、雲は一斉に 消えるよ
def game_end(bg, costume, sounds):
boon.on = False
boon.bg = bg
boon.image = costume
sounds.play()
music.stop()
clock.unschedule(create_cloud) #1 create_cloudスケージュールを中止
for cloud in clouds: #2 リストcloudsから 雲を取り出す
cloud.anim.stop() #3 アニメーションを 中止にする
clouds.clear() #4 リストcloudsの中身を 削除する- unschedule( )メソッドとは

- clear( )メソッドとは
-


おおっ! 雲が なくなったね

つぎは、boonsが 雲に当たったら、下へ 移動させるよ
boonsに プロパティ:hit を追加して、
雲に当たったらどうか わかるようにしよう

1. boon の画像を ”boon_ouch“に する
2. 効果音を 鳴らす (bump_lowbit_03)
3. boonのy座標を 600にする

boon.on = True
boon.bg = None
boon.hit = False #1 プロパティhitに Falseを設定する
def boon_move():
if boon.on:
boon.y -= 1
if keyboard.right:
boon.x = min(boon.x + 5, 700) if boon.top < 0:
game_end("bg_clear", "boon_ok", sounds.accent_brilliant_02_low)
for cloud in clouds: #2 リストcloudsから 雲を取り出す
if cloud.collide_pixel(boon) and not boon.hit: #3 もし boonに 触れて そして プロパティhitが Falseだったら
boon.hit = True #4 プロパティhitを Trueに設定する
boon.image = "boon_ouch" #5 画像を ”boon_ouch"にする
sounds.bump_lowbit_03.play() #6 効果音を 鳴らす
boon.y = 600 #7 boonのy座標を 600に設定する
break #8 繰り返しを 抜ける- collide_pixel( )メソッドとは


あれっ? 1回だけだね…

おおっぉぉぉ…!
これだと、プロパティhitが Trueのままだから
雲に衝突しても 反応しないぞ
それでは、boonが上昇してx座標が550以下になったら
プロパティhitを Falseにして 元にもどそう!
def boon_move():
if boon.on:
boon.y -= 1
if keyboard.right:
boon.x = min(boon.x + 5, 700) for cloud in clouds:
if cloud.collide_pixel(boon) and not boon.hit:
boon.hit = True if boon.y < 550 and boon.on: #1 y座標が 550未満 かつ プロパティonが Trueなら
boon.hit = False #2 プロパティhitを Falseに設定する
boon.image = "boon" #3 画像を "boon"に設定する
見事クリア!
ゲームオーバー

風船に 触れると ゲームオーバーに なるんだね

そうです。
それでは、風船を animate( )関数を使って、
風船を動かす balloon_move()関数を 定義しましょう
・ target_x : 次の到着地点のx座標
・ target_y : 次の到着地点のy座標
・ dur_time: 次の到着地点へ動く時間
この3点を ランダムに決めます。

def game_end(bg, costume, sounds):
boon.on = False
boon.bg = bg def balloon_move(): #1 balloon_move()関数を 定義する
target_x = random.randint(0, 800) #2 変数target_xに 0~800からランダムに選んだ数値を 設定する
target_y = random.randint(100, 400) #3 変数target_yに 100~400からランダムに選んだ数値を 設定する
dur_time = random.uniform(1, 2) #4 変数dur_timeに 1~2からランダムに選んだ小数を 設定する
animate(balloon, pos = (target_x, target_y), duration = dur_time, on_finished = balloon_move) #5 風船の動きアニメーションを作る
balloon_move() #6 balloon_move()関数を実行する- uniform( )関数 とは?
-

- animate( )関数 とは
-


あれっ? boonが クリアにしても まだ 風船が 動いているよ

おぉぉぉ…と いけない!
風船のアニメーションも プロパティanimに 代入しましょう
そうすれば、stop( )メソッドで ゲームクリアした時に
止めることが できますね
def game_end(bg, costume, sounds):
boon.on = False
boon.bg = bg clouds.clear()
balloon.anim.stop() #2 風船のアニメーションを 止める
def balloon_move():
target_x = random.randint(0, 800)
target_y = random.randint(100, 400)
dur_time = random.uniform(1, 2)
balloon.anim = animate(balloon, pos = (target_x, target_y), duration = dur_time, on_finished = balloon_move) #1 プロパティanimに アニメーションを代入する
いいねぇ! クリアしたら 止まったね

それでは、最後に collide_pixel()メソッドを使って
風船に 触れたら ゲームオーバーに するよ

def boon_move():
if boon.on:
boon.y -= 1
if keyboard.right:
boon.x = min(boon.x + 5, 700) if boon.collide_pixel(balloon): #1 風船に 触ったら
game_end("bg_over", "boon_ouch", sounds.kiga_nukeru_01_long) #2 game_end( )関数を 実行する- collide_pixel( )メソッド とは?
-


今回の学習は、これで 終了! おつかれさま
まとめ

今回は、
animate( )関数を使って雲や風船をランダムに動かすプログラムを作りました。
import pgzrun
from pgzhelper import *
import random
WIDTH = 800
HEIGHT = 600
boon= Actor("boon", (400, 600))
balloon = Actor("balloon", (400, 100))
boon.on = True
boon.bg = None
boon.hit = False
def boon_move():
if boon.on:
boon.y -= 1
if keyboard.right:
boon.x = min(boon.x + 5, 700)
elif keyboard.left:
boon.x = max(boon.x - 5, 100)
if boon.top <0:
game_end("bg_clear",
"boon_ok",
sounds.accent_brilliant_02_low)
for cloud in clouds:
if cloud.collide_pixel(boon) and not boon.hit:
boon.hit = True
boon.image = "boon_ouch"
sounds.bump_lowbit_03.play()
boon.y = 600
break
if boon.y < 550 and boon.on:
boon.hit = False
boon.image = "boon"
if boon.collide_pixel(balloon):
game_end("bg_over", "boon_ouch", sounds.kiga_nukeru_01_long)
clouds = []
def create_cloud():
x = random.randint(0, 800)
cloud = Actor("cloud", (x, 0))
cloud.scale = random.uniform(0.8, 2.0)
target_x = random.randint(0, 800)
dur_time = random.uniform(1, 4)
cloud.anim = animate(cloud,
pos = (target_x, 600),
duration= dur_time,
on_finished=lambda:clouds.remove(cloud))
clouds.append(cloud)
clock.schedule(create_cloud, random.uniform(0.2, 1.5))
clock.schedule(create_cloud, random.uniform(0.2, 1.5))
def game_end(bg, costume, sounds):
boon.on = False
boon.bg = bg
boon.image = costume
sounds.play()
music.stop()
clock.unschedule(create_cloud)
for cloud in clouds:
cloud.anim.stop()
clouds.clear()
balloon.anim.stop()
def balloon_move():
target_x = random.randint(0, 800)
target_y = random.randint(100, 400)
dur_time = random.uniform(1, 2)
balloon.anim = animate(balloon,
pos = (target_x, target_y),
duration = dur_time,
on_finished = balloon_move)
balloon_move()
def update():
boon_move()
def draw():
screen.fill("aqua")
if boon.bg:
screen.blit(boon.bg, (0, 0))
balloon.draw()
boon.draw()
for cloud in clouds:
cloud.draw()
music.play("spirit")
pgzrun.go()
animate( )関数で作ったアニメーションは、
最後まで 実行されてしまいます。
アニメーションの途中で止めたい時は、
変数やプロパティに animateオブジェクトとして 代入しておこう

stop() メソッドで止められちゃう。便利だね
それじゃ、またね!











