へっぽこ技術ブログです

【AdventCalendar】【Python】pygameを始めよう。というか、始めた

この記事は横浜医療情報専門学校プログラミングクラブのAdvent Calendar 12月18日分の記事です。というか、忘れてたので急いで書いてます。


adventar.org

 

最近は訳あってプログラミング言語として Pythonばかり使ってるんですが、プログラミング言語の教育やら啓蒙(大げさ)のためには、「上辺と底辺の2つの値を入力して台形の面積を計算するプログラム」とか興味の湧かない計算をネタに学ぶのではなく、「なんか知らんけどカーソルキーで絵が動く」とか面白っぽいネタが必要と考えています。(ちなみに前者は昔に某高校の非常勤講師を受けた時に高校の先生がやっていた内容です。それを全否定してカリキュラム作り直しちゃいましたけど...)

 

という訳で、PythonでもGUIアプリを作る方法を調べたのですが、最初に調べた限りではどこのサイトでもどの書籍でも tkinter というライブラリを使う方法が紹介されていたんですよね。

そこで tkinter を使う方法を試行錯誤して、作品としてアクションゲーム(っぽいやつ)を作ってみたのですが、たくさんの敵キャラを動かしてみたところそれはもうカクカクで実用的ではない。その時に出した結論は『Pythonってアクションゲーム作りには向かない」でした。

 

しかし、ひょんなことから日経ソフトウェアのバックナンバーを見る機会があってその中で書かれていた記事が「pygameライブラリを使ってPythonでゲームを作る」ってやつだったので試したところこれがかなりイケルやつでした。まあ pygameライブラリって10年以上前からあるらしいんですが、出会いってそういうものですよね。出会ってなければ間違った結論のままだったわけで、そういうことって多いんだろうなと改めて実感。

 

とりあえず作りかけのアクションゲーム(っぽいもの)を以下に置いておきます。あくまで作りかけなので悪しからず。こなれたコードになってないことも悪しからず。


# ばくだんくん

import pygame
import random

##### Global Variables #####
SCREEN_WIDTH = 452   #画面のサイズ
SCREEN_HEIGHT = 352  #
COLOR_BLACK = (0,0,0)  # 黒
COLOR_RED =(255,0,0)   # 赤
DX = [-1, 0, 1, 0]   # 向きごとの移動分
DY = [0, -1, 0, 1]   #
stageNumber = 1  # 現在のステージ番号
bombKazu = 1   # 持っている爆弾の数。初期数は1つ
bombLength = 1   # 爆弾の威力。初期は1つ先のマスまで
mapAll = open("./mapdata.txt", "r").readlines()

##### Class #####
class MyChar(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        
        self.muki = 0
        self.jyoutai = 0
        
        self.images = []
        for i in range(4):
            self.images.append(pygame.image.load("./img/my" + str(i) + ".png"))
        self.image = self.images[self.muki]
        colorkey = self.image.get_at((0,0))
        print(colorkey)
        self.image.set_colorkey(colorkey)
        self.rect = self.image.get_rect()

    def changeImage(self):
        self.image = self.images[self.muki]
        
        
class TekiChar(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        
        self.muki = 0
        self.jyoutai = 0
        
        self.images = []
        for i in range(3):
            self.images.append(pygame.image.load("./img/teki" + str(i) + ".png"))
        self.image = self.images[self.muki]
        self.rect = self.image.get_rect()

    def update(self):
        if self.jyoutai <= 0:
            self.muki = random.randrange(4)
            self.jyoutai = 32
            xnext = (self.rect.x - 100) // 32 + DX[self.muki]
            ynext = (self.rect.y      ) // 32 + DY[self.muki]
            if maps[ynext][xnext] != "0":
                self.jyoutai = 0
        else:
            self.image = self.images[(self.rect.x + self.rect.y) % 3]
            self.rect.x += DX[self.muki]
            self.rect.y += DY[self.muki]
            self.jyoutai -= 1



class Bomb(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        
        self.jyoutai = 0

##### SubRoutine #####
def gamenByouga():
    screen.fill(COLOR_BLACK)
    for y in range(11):
        for x in range(11):
            #pygame.draw.rect(screen, (255,255,255), (x * 32, y * 32, 32, 32))
            if maps[y][x] == "8":
                screen.blit(imgBlock, (x * 32 + 100, y * 32))
            elif maps[y][x] == "9":
                screen.blit(imgWall, (x * 32 + 100, y * 32))
            elif maps[y][x] == "G":
                screen.blit(imgDoor, (x * 32 + 100, y * 32))
            else:
                screen.blit(imgFloor, (x * 32 + 100, y * 32))

def mapsSetup():
    global tekiCharGroup
    tekiCharGroup = pygame.sprite.Group()
    global maps
    maps = []
    for y in range(11):
        maps.append(list(mapAll[(stageNumber - 1) * 12 + y]))
        for x in range(11):
            if maps[y][x] == "M":
                myChar.rect.x = 32 * x + 100
                myChar.rect.y = 32 * y
                maps[y][x] = "0"
            elif maps[y][x] == "E":
                tekiChar = TekiChar()
                tekiChar.rect.x = 32 * x + 100
                tekiChar.rect.y = 32 * y
                tekiCharGroup.add(tekiChar)
                maps[y][x] = "0"
    print(maps)
            
#######################################################
##### GAME START
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
gameclock = pygame.time.Clock()

### Charcter Generate
myCharGroup = pygame.sprite.Group()
myChar = MyChar()
myCharGroup.add(myChar)
tekiCharGroup = pygame.sprite.Group()
tekiChar = TekiChar()
tekiCharGroup.add(tekiChar)

##### Char Tile #####
imgFloor = pygame.image.load("./img/tileFloor.png").convert()
imgBlock = pygame.image.load("./img/tileBlock.png").convert()
imgWall = pygame.image.load("./img/tileWall.png").convert()
imgDoor = pygame.image.load("./img/tileDoor.png").convert()



##### Stage Start
mapsSetup()

for sprite in myCharGroup.sprites():
    print(sprite)
for sprite in tekiCharGroup.sprites():
    print(sprite)

endflag = 0

while endflag == 0:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            endflag = 1

    keypress = pygame.key.get_pressed()
    if myChar.jyoutai == 0:
        if (keypress[pygame.K_LEFT]):
            myChar.muki = 0
            myChar.jyoutai = 8
        elif (keypress[pygame.K_UP]):
            myChar.muki = 1
            myChar.jyoutai = 8
        elif (keypress[pygame.K_RIGHT]):
            myChar.muki = 2
            myChar.jyoutai = 8
        elif (keypress[pygame.K_DOWN]):
            myChar.muki = 3
            myChar.jyoutai = 8
        myChar.changeImage()
    else:
        myChar.rect.x += (DX[myChar.muki] * 4)
        myChar.rect.y += (DY[myChar.muki] * 4)
        myChar.jyoutai -= 1
            
    gamenByouga() # 背景となるマップや残り時間などを描画
    tekiCharGroup.update() # 敵の移動処理
    tekiCharGroup.draw(screen) #敵の描画
    myCharGroup.draw(screen) #自機の描画

    gameclock.tick(30)
    pygame.display.flip()

pygame.quit()

うん、pygameいいよ皆さん始めましょう。というか、やっつけな記事でごめんなー。


明日の記事は、みんなのアイドル 五段パイセンによる『在学生向けポエム「横浜医療情報専門学校を卒業したあと学校と在校生に対して思うこと」』です。お楽しみに〜〜!