【Pygame Zero】簡単なゲーム18:ビーム連射!シューティングゲーム
こんにちは!
「Pythonしよう!楽しく学べるプログラミング教室」の
ラッチ先生です


スックです。よろしくね!
BGM提供: DOVA-SYNDROME
https://dova-s.jp/
・ 「Time Paradox 」 written by 風人
効果音提供:効果音ラボ
https://soundeffect-lab.info/
・ 「スイッチを押す」: アニメ
・ 「ステータス治療1」: 戦闘[1]
・ 「爆発1」:戦闘[2]

基礎プログラムと 画像を入れた
「簡単なゲーム18: シューティングゲーム!」zipフォルダを
ダウンロードしてください



このゲームで使用している音声ファイルは、
以下のサイトからお借りしています。
各自でダウンロードして、該当するフォルダに入れてください!
BGM提供: DOVA-SYNDROME
https://dova-s.jp/
・ 「Time Paradox 」 written by 風人
効果音提供:効果音ラボ
https://soundeffect-lab.info/
・ 「スイッチを押す」: アニメ
・ 「ステータス治療1」: 戦闘[1]
・ 「爆発1」:戦闘[2]

学習の流れ
戦闘機を 左右キーで 動かす
敵が 襲来する
結果発表

プログラムを 実行してみよう
プログラミングの仕方を説明します
モジュールを 用意する

今回の「シューティングゲーム」では、
・ビームと 敵、戦闘機と敵 が 衝突する
・ 敵が現れる場所や 敵の動く速さを ランダムに決める
プログラムがあります。
そこで、2つのモジュールを 用意しました



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

Actor()クラスが入っている変数 jet には、
jet を動かす属性(データ)やメソッド(命令)が あります。


ビームや敵も 同じだよ
そして、属性(データ)やメソッド(命令)は、
『 . (ドット)』を付ければ、使えるよ
今回のプログラミングのポイント

今回のプログラミングのポイントが こちらです

update()関数の引数dt を使って 敵を削除します
戦闘機を 左右キーで 動かす

最初に、
戦闘機を左右に動かす jet_move関数を 定義しましょう
def jet_move(): #1 jet_move関数を定義する
if jet.active: #2 プロパティactiveが Trueなら
if keyboard.right: #3 もし 右キーが 押されたら
jet.x += 5 #4 x座標を 5pxずつ増える
if keyboard.left: #5 もし 左キーが 押されたら
jet.x -= 5 #6 x座標を 5pxずつ減らす
jet.x = max(35, min(WIDTH-35, jet.x)) #7 x座標に 35~765を代入する
def update():
jet_move() #8 jet_move()関数を 実行する- プロパティ: active とは?
-
- keyboardオブジェクト とは?

- max( ), min()関数 とは?
-


クリアやゲームオーバーになったら
プロパティ:active を False にして 戦闘機の動きを止めるよ

次は、戦闘機から 出す beamを作っていきましょう
beams = [] #1 空リストbeamsを 作成する
def create_beam(): #2 create_beam関数を 定義する
beam = Actor("beam", (jet.x, jet.y + 50)) #3 ビームを 生成する
beams.append(beam) #4 リストbeamsに ビームを追加する
clock.schedule_interval(create_beam, 0.2) #5 create_beam関数を 0.2秒ごと実行する
def jet_move(): def draw():
screen.blit(bg_image, (0, 0))
jet.draw()
for beam in beams: #6 リストbeamsから beamを取り出す
beam.draw() #7 beamを 表示する- append( )メソッド とは?

- schedule_interval( )メソッド とは?


beamは 戦闘機の上に 表示されるのね!

beamを動かすbeam_move( )関数を定義しましょう
clock.schedule_interval(create_beam, 0.2)
def beam_move(): #1 beam_move()関数を 定義する
for beam in beams[:]: #2 コピーリストbeamsから beamを取り出す
beam.y -= 10 #3 beamのy座標を 10pxずつ減らす
if beam.y < 0: #4 もし beamのy座標が 0未満になったら
beams.remove(beam) #5 リストbeamsから beamを削除するdef update():
jet_move()
beam_move() #6 beam_move()関数を 実行する- remove( )メソッド とは?


あれっ? beams [ : ] に なってるよ

beamは、上の端へ行ったら 削除します。
リストbeams から beamを 取り出すと エラーが出る場合があります。
こういう場合は、リストbeams のコピーから 取り出しましょう

戦闘機のプログラムは、これで完成だよ
敵が 襲来する

次は、
敵を 生成するcleate_enemy 関数を 定義しましょう
敵のオブジェクトに プロパティを追加します
・ speed : 敵の動く速さ
・ hit : ビームの当たり判定 True / False
・ hit_timer : ビームに当たってからのタイマー
beams = []
enemies = [] #1 空リストenemiesを 作成する
def create_enemy(): #2 create_enemy関数を 定義する
x = random.randint(35, WIDTH-35) #3 x座標を 35~765からランダムに選んで設定する
enemy = Actor("enemy", (x, 0)) #4 敵を 生成する
enemy.speed = random.randint(3, 10) #5 プロパティspeedを 3~10から選んだ数値を設定する
enemy.hit = False #6 プロパティhitを Falseに設定する
enemy.hit_timer = 0 #7 プロパティhit_timerを 0に設定する
enemies.append(enemy) #8 リストenemiesに enemyを追加する
clock.schedule_interval(create_enemy, 0.5) #9 0.5秒ごとに create_enemy関数を 実行するdef draw():
screen.blit(bg_image, (0, 0))
jet.draw()
for beam in beams:
beam.draw()
for enemy in enemies: #10 リストenemiesから 敵を取り出す
enemy.draw() #11 敵を 表示する- randint( )メソッド とは?

- append( )メソッド とは?

- schedule_interval( )メソッド とは?


敵が どんどん出現しているね

enemy_move関数で
敵を動かすプログラムを 定義しましょう
プロパティspeedに入っている数値が ランダムなので
いろいろな速さで 敵が襲ってくるよ
clock.schedule_interval(create_enemy, 0.5)
def enemy_move(): #1 enemy_move関数を 定義する
for enemy in enemies[:]: #2 コピーリストenemiesから 敵を取り出す
enemy.y += enemy.speed # 3 y座標が プロパティspeedごと増える
if enemy.y > HEIGHT: #4 もし y座標が 800より大きくなったら
enemies.remove(enemy) #5 リストenemiesから 敵を削除するdef update():
jet_move()
beam_move()
enemy_move() #6 enemy_move関数を 実行する
おおっ!いいねぇ

ビームに当たったら、プロパティhitを Trueにして
敵の動きを 止めるよ
def enemy_move():
for enemy in enemies[:]:
if enemy.hit: #7 もし プロパティhitが Trueなら
continue #8 下のプログラムを 飛ばす
enemy.y += enemy.speed
for beam in beams: #1 リストbeamsから beamを取り出す
if beam.collide_pixel(enemy): #2 もし 敵に当たっていたら
enemy.hit = True #3 プロパティhitを Trueに設定する
sounds.スイッチを押す.play() #4 効果音を 鳴らす
enemy.image = "enemy_over" #5 画像を "enemy_over"
break #6 抜ける
if enemy.y > HEIGHT:
enemies.remove(enemy) 効果音提供:効果音ラボ
https://soundeffect-lab.info/
・ 「スイッチを押す」: アニメ
- continue とは?

- collide_pixel( )メソッド とは?

- 効果音について


効果音ラボサイトからの音源は、MP3形式です。
しかし、pygame zeroプログラムでは、MP3形式は使えません。WAV形式に 変換して フォルダsoundsに 入れてください

プロパティhit が True になったら 止まったね

update()関数の引数 dt(デルタタイム)を使って
ビームに当たり 0.2秒 経ったら消えるようにします。
def enemy_move(dt): #3 enemy_move関数を 定義する
for enemy in enemies[:]:
if enemy.hit:
enemy.hit_timer += dt #4 プロパティhit_timerを dtずつ増やす
if enemy.hit_timer >= 0.2: #5 もし プロパティhit_timerが 0.2秒以上になったら
enemies.remove(enemy) #6 リストenemiesから 敵を削除する
continue def update(dt): #1 引数dtで デルタタイムを受け取る
jet_move()
beam_move()
enemy_move(dt) #2 引数dtに 渡す 効果音提供:効果音ラボ
https://soundeffect-lab.info/
・ 「スイッチを押す」: アニメ
- remove( )メソッド とは?


0.2秒だと短くて、プロパティhit_timerの動きが わからないので
2秒以上になったら 削除したよ
結果発表


set_result( )関数で 結果発表を定義しましょう

bg_image = "bg"
result = None #1 変数resultを宣言 初期値:None(無し)
beams = []
enemies = []
def set_result(result_val,
image,
sound,
keep_enemies): #3 set_result関数を 定義する
global result, bg_image #4 グローバル変数result, bg_image
music.stop() #5 BGMを 止める
jet.active = False #6 戦闘機:プロパティactiveに Falseを設定する
clock.unschedule(create_beam) #7 create_beam関数を 止める
clock.unschedule(create_enemy) #8 create_enemy関数を 止める
result = result_val #9 変数resultに 引数imageを 代入する
bg_image = image #10 変数bg_imageに 引数imageを 代入する
sound.play() #11 効果音を 鳴らす
enemies[:] = keep_enemies #12 リストコピーenemiesに 引数keep_enemiesを 代入する
music.play("time_paradox") #2 BGMを 流す
pgzrun.go()- musicモジュールとは?

- unschedule() メソッドとは?


クリアとゲームオーバーの共通項目です。
・ BGMを 止める
・ 戦闘機の動きを 止める
・ cleate_enemy関数を 止める
・ cleate_beam関数を 止める

最初に、
敵が 戦闘機に当たったら ゲームオーバーのプログラムを作りましょう
def enemy_move(dt):
if result: #1 もし 変数resultに 値(over または clear)が入れば
return #2 返す ★つまり、ここから下のプログラムは やりません!
for enemy in enemies[:]:
if enemy.hit:
enemy.hit_timer += dt
if enemy.hit_timer >= 0.2:
enemies.remove(enemy)
continue
enemy.y += enemy.speed
if enemy.collide_pixel(jet): #3 もし 戦闘機に 当たったら
jet.image = "jet_over" #4 戦闘機の画像を jet_overに設定する
set_result("over",
"bg_over",
sounds.爆発1,
[enemy]) #5 set_result関数を 実行する
return #6 返す
for beam in beams: BGM提供: DOVA-SYNDROME
https://dova-s.jp/
・ 「Time Paradox 」 written by 風人
効果音提供:効果音ラボ
https://soundeffect-lab.info/
・ 「スイッチを押す」: アニメ
・ 「爆発1」:戦闘[2]

おおっ!すべてが止まったね

敵を 20機爆破したら ゲームクリアにします。
変数hit を作って、そこに敵を爆破した数値を入れましょう
bg_image = "bg"
result = None
hit = 0 #1 変数hitを宣言 初期値: 0
def enemy_move(dt):
global hit #2 グローバル変数hit
if result:
return
for enemy in enemies[:]:
if enemy.hit:
enemy.hit_timer += dt for beam in beams:
if beam.collide_pixel(enemy):
enemy.hit = True
sounds.スイッチを押す.play()
enemy.image = "enemy_over"
hit += 1 #3 変数hitを 1ずつ増やす
if hit >= 20: #4 もし 変数hitが 20以上なら
set_result("clear",
"bg_clear",
sounds.ステータス治療1,
[]) #5 set_result関数を 実行する
return #6 返す
break
if enemy.y > HEIGHT: def draw():
screen.blit(bg_image, (0, 0))
screen.draw.text(f"HIT: {hit}",
(30, 20),
fontsize=70,
color="orange") #7 表示する
jet.draw()BGM提供: DOVA-SYNDROME
https://dova-s.jp/
・ 「Time Paradox 」 written by 風人
効果音提供:効果音ラボ
https://soundeffect-lab.info/
・ 「スイッチを押す」: アニメ
・ 「ステータス治療1」: 戦闘[1]
・ 「爆発1」:戦闘[2]
- screen.drawモジュールとは?

- f 文字列とは?


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

今回は、
シューティングゲームを作りました。
import pgzrun
import random
from pgzhelper import *
WIDTH = 800
HEIGHT = 600
jet = Actor("jet", (400, 500))
jet.active = True
bg_image = "bg"
result = None
hit = 0
beams = []
enemies = []
def set_result(result_val,
image,
sound,
keep_enemies):
global result, bg_image
music.stop()
jet.active = False
clock.unschedule(create_beam)
clock.unschedule(create_enemy)
result = result_val
bg_image = image
sound.play()
enemies[:] = keep_enemies
def create_enemy():
x = random.randint(35, WIDTH-35)
enemy = Actor("enemy", (x, 0))
enemy.speed = random.randint(3, 10)
enemy.hit = False
enemy.hit_timer = 0
enemies.append(enemy)
clock.schedule_interval(create_enemy, 0.5)
def enemy_move(dt):
global hit
if result:
return
for enemy in enemies[:]:
if enemy.hit:
enemy.hit_timer += dt
if enemy.hit_timer > 0.2:
enemies.remove(enemy)
continue
enemy.y += enemy.speed
if enemy.collide_pixel(jet):
jet.image = "jet_over"
set_result("over",
"bg_over",
sounds.爆発1,
[enemy])
return
for beam in beams:
if beam.collide_pixel(enemy):
enemy.hit = True
sounds.スイッチを押す.play()
enemy.image = "enemy_over"
hit += 1
if hit >= 20:
set_result("clear",
"bg_clear",
sounds.ステータス治療1,
[])
return
break
if enemy.y > HEIGHT:
enemies.remove(enemy)
def create_beam():
beam = Actor("beam", (jet.x, jet.y-50))
beams.append(beam)
clock.schedule_interval(create_beam, 0.2)
def beam_move():
for beam in beams[:]:
beam.y -= 10
if beam.y < 0:
beams.remove(beam)
def jet_move():
if jet.active:
if keyboard.right:
jet.x += 5
elif keyboard.left:
jet.x -= 5
jet.x = max(35, min(jet.x, WIDTH-35))
def update(dt):
jet_move()
beam_move()
enemy_move(dt)
def draw():
screen.blit(bg_image, (0, 0))
screen.draw.text(f"HIT: {hit}",
(30, 20),
fontsize=70,
color="orange")
jet.draw()
for beam in beams:
beam.draw()
for enemy in enemies:
enemy.draw()
music.play("time_paradox")
pgzrun.go()
たくさんいる敵に、それぞれタイマーをつける時は、
dt (デルタタイム)を 使いましょう
update関数の引数dt から簡単に読み込むことができます

みなさんも、 シューティングゲームを作ってみよう!
それじゃあ、またね!















