第3回 ゼロ戦がかめはめ波
ゼロ戦の武器は機関銃と思うのが当たり前ですが、当たり前でつまらない。UFOを相手にするのだからもっと強力なもの。
そうだ<かめかめ波>にしよう!
動画はこれ。

今までは描画した画像は非表示になることはありませんでした。今回からかめはめ波に当たったUFOは爆発して消えます。 また、かめはめ波もUFOに当たったら非表示になります。

ソース

import pygame
from pygame import image
import pygame.sprite
import sys

SCREEN = pygame.Rect(0, 0, 640, 480) 

#UFOのクラス
class ufo(pygame.sprite.Sprite):
    def __init__(self, x,y, vx, vy, angle=0):
        pygame.sprite.Sprite.__init__(self)
        self.vx = vx
        self.vy = vy
        self.angle = angle
        self.image = pygame.image.load('ufo.png').convert_alpha()
        if angle != 0: 
            self.image = pygame.transform.rotate(self.image, angle)
        w = self.image.get_width()
        h = self.image.get_height()
        self.rect = pygame.Rect(x, y, w, h)
        self.attacked = False
        self.attackCnt = 4

    def update(self):
        self.rect.move_ip(self.vx, self.vy)
        self.rect = self.rect.clamp(SCREEN)

#かめはめ波のクラス
class kamehame(pygame.sprite.Sprite):
    def __init__(self, x, y , vy):
        pygame.sprite.Sprite.__init__(self)
        self.vy = vy
        self.image = pygame.image.load('kame.png').convert_alpha()
        w = self.image.get_width()
        h = self.image.get_height()
        self.rect = pygame.Rect(x, y, w, h)
        self.attacking = True

    def update(self):
        self.rect.move_ip(0, self.vy)

  

def main():
    pygame.init()
    pygame.display.set_caption("Pygame インベーダー ゼロ戦がかめはめ波")
    screen = pygame.display.set_mode(SCREEN.size)
    img_zero = pygame.image.load('zerosen.png')
    img_kame = pygame.image.load('kame.png')
    clock = pygame.time.Clock()
   #スプライトグループの作成
    group = pygame.sprite.RenderUpdates()
    # スプライトの追加
    #横8列、縦4行
    x = 16
    y = 16
    x_jet = 0
    y_jet = SCREEN.height - img_zero.get_height()
    for i in range(4):
        for j in range(8):
            ufo1= ufo(x,y, 2, 0, 0)
            group.add(ufo1) 
            x += 64
        y +=50
        x = 16

    #かめはめ波グループ
    groupKame = pygame.sprite.RenderUpdates()

    while True:
        keyName =''
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                # 終了ボタンで終了
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                keyName = pygame.key.name(event.key)
       #キーの取得
        key = pygame.key.get_pressed()
        if key[pygame.K_LEFT] == True:
            x_jet -= 4
        elif key[pygame.K_RIGHT] == True:
            x_jet += 4
        if x_jet >= SCREEN.right - img_zero.get_width():
            x_jet = SCREEN.right - img_zero.get_width()
        elif x_jet < SCREEN.left:
             x_jet = SCREEN.left

        # かめかめ波発射
        if keyName == 'space':
            if len(groupKame) <= 4:
                beam1 = kamehame(x_jet + img_zero.get_width() / 2 - img_kame.get_width() / 2, 
                     y_jet - img_kame.get_height(), -10)
                groupKame.add(beam1)

        #かめはめ波の処理
        for km in   groupKame:
            if km.rect.bottom < 0:
                km.remove(groupKame)
                continue

            for uf in group:
                headX = km.rect.left + (km.rect.right - km.rect.left) / 2
                if km.rect.top <= uf.rect.bottom and \
                    headX >=  uf.rect.left and headX <  uf.rect.right:
                    uf.attacked = True
                    uf.image = pygame.image.load('explode.png').convert_alpha()
                    km.remove(groupKame)
                    break

        for uf in group:
            if uf.attacked == True:
                uf.attackCnt -= 1
                if uf.attackCnt == 0:
                    uf.remove(group)

        screen.fill((128, 128, 255)) # 画面の背景色
        # スプライトグループを更新
        dir = 0
        for u  in group:
            if u.vx  + u.rect.right >= SCREEN.width:
                dir = 1
                break
            elif u.rect.left + u.vx < SCREEN.left:
                dir = -1
                break
        for u  in group:
            if dir == 1:
                u.vx = -abs(u.vx)
            elif  dir == -1:
                u.vx = abs(u.vx)

        group.update()
        groupKame.update()
        # スプライトを描画
        group.draw(screen)
        groupKame.draw(screen)
        screen.blit(img_zero, [x_jet,y_jet])

        pygame.display.update()
        clock.tick(50)

if __name__ == '__main__':
    main()
だいぶ長めのソースになりました。このバージョンではゼロ戦からUFOの攻撃はできますが、UFOからの攻撃はありません。

かめはめ波のクラス

#かめはめ波のクラス
class kamehame(pygame.sprite.Sprite):
    def __init__(self, x, y , vy):
        pygame.sprite.Sprite.__init__(self)
        self.vy = vy
        self.image = pygame.image.load('kame.png').convert_alpha()
        w = self.image.get_width()
        h = self.image.get_height()
        self.rect = pygame.Rect(x, y, w, h)
        self.attacking = True

    def update(self):
        self.rect.move_ip(0, self.vy)
ufoクラスに比べて縦方向に移動するだけなので簡単です。

インスタンスの作成
このクラスはスペースキーを押したときにインスタンスが作られます。

        if keyName == 'space':
            if len(groupKame) <= 4:
                beam1 = kamehame(x_jet + img_zero.get_width() / 2 - img_kame.get_width() / 2, 
                     y_jet - img_kame.get_height(), -10)
                groupKame.add(beam1)
x座標はゼロ戦の中央になるように計算します。
y座標はゼロ戦のy座標からかめかめ波の高さを引きます。
作成したインスタンスをかめはめ波グループに追加します。

UFOに命中しなかった時の処理

        for km in   groupKame:
            if km.rect.bottom < 0:
                km.remove(groupKame)
                continue
かめはめ波の下の位置が0より小さくなったら命中しなかったことになります。 そしてこのオブジェクトをかめはめ波グループから取り除くことにより、このオブジェクトは無効にあります。
 
かめはめ波がUFOに命中

            for uf in group:
                headX = km.rect.left + (km.rect.right - km.rect.left) / 2
                if km.rect.top <= uf.rect.bottom and \
                    headX >=  uf.rect.left and headX <  uf.rect.right:
                    uf.attacked = True
                    uf.image = pygame.image.load('explode.png').convert_alpha()
                    km.remove(groupKame)
                    break
headXはかめはめ波の中央の座標です。これがUFOの下座標より大きくかつ左右座標にあれば命中にします。
攻撃されたフラグをオンにします。
画像を爆発に変更します。
かめはめ波をグループから取り除きます。
これで爆発の画像が表示されます。

        for uf in group:
            if uf.attacked == True:
                uf.attackCnt -= 1
                if uf.attackCnt == 0:
                    uf.remove(group)
攻撃されたフラグがオンのとき、カウンターを減じて0になったらufoをグループから取り除きます。これで爆発の表示から無表示になります。

画像のダウンロード