Pythonでゲームプログラミング「ゆるインベーダー」

クラスを使ったプログラミング

今回のテーマは、クラスを利用したプログラミング

シューティングゲームを作りながら、Pythonのクラスについて理解が深まるような動画にしよう。

ということで・・・。

出現するたくさんのエイリアン。うん・・・エイリアンクラスを用意して・・・と。

あと・・・ユーザークラスと、ミサイルクラスと。

ミサイルクラスは、自分のミサイルと、エイリアンのミサイルとで、継承を使おうかしら?

まぁ・・・。

そんな感じで、一応少しだけ考えちゃいるんですわ。・・・はい。

詳しくは動画を見ていただくとして、ココには今回の動画で書いたコードを掲載しておきますね。

【前編】クラスの解説とユーザー系プログラミングまで

【後編】ゆるインベーダー仕上げ

今回のゲームでは、画像ファイル(png)をコード内で読み込んでいます。下のコードをコピペなどで試す際には、以下のpngファイル3つをダウンロードし、Pythonファイルと同一ディレクトリ(フォルダ内)に置いて実行してください。
ファイル名「ユーザー」star.png/「エイリアン」alien.png/「ボス」boss.png



尚、動画前編で解説していますが、このコード実行には外部モジュール「Pillow」のインストールが必要です。

import tkinter as tk
import random
from PIL import ImageTk

########## スタークラス ####################
class Star:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.draw()
        self.action()

    def draw(self):
        self.disp = can.create_image(self.x, self.y, image=starImg)

    def action(self):
        can.tag_bind(self.disp, "<Button1-Motion>", self.move)
        can.tag_bind(self.disp, "<Button-3>", self.bang)
    
    def move(self, event):
        if event.x<30 or event.x>770 or event.y>470 or event.y<300 :
            return
        can.coords(self.disp, event.x, event.y)
        self.x = event.x
        self.y = event.y
    
    def bang(self, event):
        Bullet(event.x, event.y, -10, "orange")

########## エイリアンクラス ####################
class Alien:
    def __init__(self):
        self.x = 30
        self.y = 30
        self.moveX = 20
        self.draw()
        self.move()

    def draw(self):
        self.disp = can.create_image(self.x, self.y, image=alienImg)
    
    def move(self):
        if self.x<0 or self.x>800 :
            self.moveX *= -1
            self.y += 50
        self.x += self.moveX
        can.coords(self.disp, self.x, self.y)
        r = random.randint(0, 30)
        if r==0 :
            self.bang()
        self.loop = win.after(100, self.move)
        self.judge()
    
    def judge(self):
        if ((self.x-star.x)**2+(self.y-star.y)**2)<2500 or self.y>470 :
            can.delete(self.disp)
            win.after_cancel(self.loop)
            gameOver()
        
    def bang(self):
        AlienBullet(self.x, self.y, 10, "blue")

########## ボスクラス ####################
class Boss():
    def __init__(self):
        self.life = 100
        self.x = 0
        self.y = 0
        self.moveX = 20
    
    def draw(self):
        self.disp = can.create_image(self.x, self.y, image=bossImg)
        self.dispLife = can.create_rectangle(self.x-30, self.y, self.x-30+self.life*0.8, self.y+10, fill="lime", outline="lime")
    
    def move(self):
        if self.x<100 or self.x>700 :
            self.moveX *= -1
        self.x += self.moveX
        can.coords(self.disp, self.x, self.y)
        can.coords(self.dispLife, self.x-30, self.y, self.x-30+self.life*0.8, self.y+10)
        r = random.randint(0, 4)
        if r==0 :
            self.bang()
        self.loop = win.after(100, self.move)
    
    def bang(self):
        AlienBullet(self.x, self.y, 10, "red")

########## 弾丸クラス ####################
class Bullet:
    def __init__(self, x, y, moveY, color):
        self.x = x
        self.y = y
        self.moveY = moveY
        self.color = color
        self.draw()
        self.move()
    
    def draw(self):
        self.disp = can.create_oval(self.x-6, self.y-10, self.x+6, self.y+10, fill=self.color)

    def move(self):
        if self.y<0 or self.y>500 :
            can.delete(self.disp)
            return
        can.move(self.disp, 0, self.moveY)
        self.y += self.moveY
        win.after(20, self.move)
        self.judge()
    
    def judge(self):
        for alien in arrayAlien :
            if ((self.x-alien.x)**2+(self.y-alien.y)**2)<600 :
                can.delete(alien.disp)
                win.after_cancel(alien.loop)
                arrayAlien.pop(arrayAlien.index(alien))
        if ((self.x-boss.x)**2+(self.y-boss.y)**2)<3000 :
            boss.life -= 1
            can.delete(self.disp)
            if boss.life<0 :
                can.delete(boss.disp, boss.dispLife)
                can.delete(self.disp)
                win.after_cancel(boss.loop)
                gameClear()

########## エイリアン弾丸クラス ####################
class AlienBullet(Bullet):
    def judge(self):
        if ((self.x-star.x)**2+(self.y-star.y)**2)<600 :
            can.delete(star.disp)
            gameOver()

########## ゲームオーバー&クリア ####################
def gameOver():
    can.create_text(400, 250, text="YOU LOSE!", fill="red", font=("", 100))
    can.delete(star.disp)

def gameClear():
    can.create_text(400, 250, text="YOU WIN!", fill="lime", font=("", 100))

########## メイン ####################
#ウィンドウ設定
win = tk.Tk()
win.title("ゆるインベーダー")
win.geometry("800x500+120+100")

#キャンバス
can = tk.Canvas(win, width=800, height=500, bg="cyan")
can.pack()

#画像の変換
starImg = ImageTk.PhotoImage(file="star.png")
alienImg = ImageTk.PhotoImage(file="alien.png")
bossImg = ImageTk.PhotoImage(file="boss.png")

#スターをインスタンス化
star = Star(375, 450)
#エイリアンをインスタンス化
arrayAlien = []
i = 0
def createAlien():
    global i
    i += 1
    alien = Alien()
    arrayAlien.append(alien)
    createAlienLoop = win.after(500, createAlien)
    if i==30 :
        win.after_cancel(createAlienLoop)
createAlien()
#ボスをインスタンス化
boss = Boss()
#ボス出現のタイミング
def timing():
    timingLoop = win.after(1000, timing)
    if len(arrayAlien)==0 :
        boss.x = 400
        boss.y = 100
        boss.draw()
        boss.move()
        win.after_cancel(timingLoop)
timing()

win.mainloop()