工学じじいの縁側日記

工学じじいの縁側日記

引退間際の工学じじいがきままに、プログラミングやデバイス、工学について呟きます。

【簡単?】初心者がpython3とpygameでヒット&ブローゲーム作ってみる #03 画面作るよ【習作?】

初心者がpython3とpygameでヒット&ブローゲーム作ってみる  #03 画面作るよ

f:id:gomta777:20191115111505p:plain

前回から

【簡単?】初心者がpython3とpygameでヒット&ブローゲーム作ってみる【習作?】 - 工学じじいの縁側日記
【簡単?】初心者がpython3とpygameでヒット&ブローゲーム作ってみる #02【習作?】 - 工学じじいの縁側日記


前回は、テキストベースで作ったヒット&ブローゲームにpygameのガワをかぶせるべく、簡易的な状態遷移の処理を書いてみました。
今回は、画面の構成を作っていきます。




画面構成

完成イメージは、こんな感じです。

f:id:gomta777:20191119001414p:plain
図 完成イメージ

左側上部に、答えの入力欄が数字4桁で、画面左側下部に0~9の数字が並んでいて、押した順に上部の入力欄に入力されます。
左側には、現在までの、解答の履歴と、その時のヒット数、ブロー数が一覧で並んでいます。
15回×4ページ分の履歴を保存できるようにします。それで答えられなかったらゲームオーバーです。
まず、入力欄から作っていきます。

画面の作成 その1 入力欄

今回使う画像の一覧(次に使うのも入ってます。。。)は、以下のようになっています。
Webのフリー素材でもいいですし、適当にofficeの作図機能で作ってもいいと思います。
githubにもあるので、僕の作ったので良ければどうぞ。

f:id:gomta777:20191119001954p:plain
図 画像一覧

初めに読み込む画像は、数字とアスタリスクの画像 [00.png, 11.png, 22,png ~ 99.png, kome.png] です。
読み込みやすいように、連番のファイル名にしておいたので、このファイル名が並んだリストを作ります。
画像は、プロジェクトフォルダ配下の「images」フォルダに全て納めてあります。

    qfilestr = [] # 画像ファイル名を生成するためのリスト
    for i in range(10): # 問題の方の画像ファイルは 00.png~99.pngの名前になっている。
        qfilestr.append(str(i)+str(i) + ".png")  # 画像ファイル名を生成

次に、画面上での、解答欄の貼り付け位置を表す矩形(pygame.Rect型)を用意します。

f:id:gomta777:20191119010305p:plain
図 画面構成

数字1個当たりの画像の大きさは(幅、高さ)=(100,100)です。これを、図のように並べていきます。

    margin = [100, 100] # *を表示する位置までのマージン
    imagesize = [100,100] # 画像1個当たりの大きさ

    qbuttonrect = [] # *を表示するための矩形を作成 *のサイズは(100,100)
    for i in range(4): #marginの位置から4つ横に画像を並べるためのもの
        qbuttonrect.append(Rect(margin[0] + i * imagesize[1], margin[1], *imagesize))

これらを使って、いつものpygame.image.loadで読み込んでいきます。
ここまでの部分をまとめると以下のようになります。

    margin = [100, 100] # *を表示する位置までのマージン
    imagesize = [100,100] # 画像1個当たりの大きさ

    qfilestr = [] # 画像ファイル名を生成するためのリスト
    qimagelist = []  # 出題用の画像を保存しておくリスト0~9+*の画像が収まっている。
    for i in range(10): # 問題の方の画像ファイルは 00.png~99.pngの名前になっている。
        qfilestr.append(str(i)+str(i) + ".png")  # 画像ファイル名を生成
    for i in range(10): # 画像の読み込み
        qimagelist.append(pygame.image.load("./images/" + qfilestr[i]))
    #[00.png, 11.png, 22.png … 99.png, kome.png]
    qimagelist.append(pygame.image.load("./images/kome.png")) # *の画像の読み込み
    qbuttonrect = [] # *を表示するための矩形を作成 *のサイズは(100,100)
    for i in range(4): #marginの位置から4つ横に画像を並べるためのもの
        qbuttonrect.append(Rect(margin[0] + i * imagesize[1], margin[1], *imagesize))


最後、連番の名前ではない、アスタリスクの画像(kome.png)を別口でロードしてリストに追加しているところに注意してください。
リストの中身は、[00.png, 11.png, 22.png … 99.png, kome.png] の順に並んでいます。(qimagelist[10] がkome.pngです)

入力欄 描画処理を書く

先ほどの読み込んだ画像のリスト(qimagelist)と、それを貼り付けるための位置を表す矩形(qbuttonrect)をつかって、ゲーム画面に画像を貼り付けます。
処理自体は、qimagelist[10]のアスタリスク画像を、qbuttonrect[0]~qbuttonrect[3]の位置に貼り付けるだけですので簡単です。

 for i in range(4):
                screen.blit(qimagelist[10], qbuttonrect[i])

問題はこれをどこのタイミングで描画するか、です。

前回やったシーン切り替えのことを考えると、スタートボタンが押されたら、ゲームが始まったほうがそれっぽいですよね。
ということで、
gamescene==1のとき、すなわちゲーム中の時に入力欄を表示するのが自然な気がします。
すなわち、前回のボタンを表示した部分で、以下の様に描画処理を書きます。

        if gamescene == 0:
            screen.blit(gamebutton[0], gamebuttonrect)
        elif gamescene == 1:
            for i in range(4):
                screen.blit(qimagelist[10], qbuttonrect[i])
            screen.blit(gamebutton[1], gamebuttonrect)
        elif gamescene ==2:
            screen.blit(gamebutton[2], gamebuttonrect)
        else:
            print("error")

これで、ゲームスタートを押すと****と、アスタリスクが4つ表示されるはずです。

f:id:gomta777:20191119014843p:plain
図 入力欄の表示

入力用ボタンとなる数字画像の表示

入力用のボタンとなる数字画像(画面左側下部)を読み込んで表示します。
処理の内容は先ほどと変わらないので、コードのみ掲載しておきます。

nmargin = [50, 350] # 入力ボタンのためのマージン

    filestr  = [] # 画像ファイル名を収めるリスト
    for i in range(10): # 読み込む画像ファイル名のリストを作る
        filestr.append(str(i) + ".png")
    imagelist = [] # 読み込んだ画像のリスト
    for i in range(10):
        imagelist.append(pygame.image.load("./images/" + filestr[i])) #ファイルの読み込み
    buttonrect = [] # 画像ファイルの貼り付け位置が収められたリスト(Rect型)
    for i in range(5): # 0~4の画像の位置
        buttonrect.append(Rect(nmargin[0] + i * imagesize[0], nmargin[1], *imagesize))
    for i in range(5, 10): # 5~9の画像の位置
        buttonrect.append(Rect(nmargin[0] + (i - 5) * imagesize[0], nmargin[1]+imagesize[1], *imagesize))
入力用数字の画像を描画するコードの追加

これも同様に
gamescene==1の時に、描画されるように、追加します。

        if gamescene == 0:
            screen.blit(gamebutton[0], gamebuttonrect)
        elif gamescene == 1:
            for i in range(4): # 入力された数字を表示する領域
                screen.blit(qimagelist[10], qbuttonrect[i])
            for i in range(5): # 入力のためのボタンとなる画像0~4
                screen.blit(imagelist[i], buttonrect[i])
            for i in range(5, 10): # 入力のためのボタンとなる画像5~9
                screen.blit(imagelist[i], buttonrect[i])
            screen.blit(gamebutton[1], gamebuttonrect)
        elif gamescene ==2:
            screen.blit(gamebutton[2], gamebuttonrect)
        else:
            print("error")

実行してみます。
game start ボタンを押すと、以下の画面が出れば成功です。


f:id:gomta777:20191119014708p:plain
図 入力欄と入力ボタンの描画

なんとなく、どんな感じのゲームになるか見えてきましたね。

ここまでのソースコード一覧です。

from pygame.locals import *
import pygame
import sys
import random

def main():
    gamescene = 0  # 0 タイトル、1 ゲーム中、2 ゲームオーバー、-1 エラー
    pygame.init()  # Pygameを初期化
    screen = pygame.display.set_mode((900, 600))  # 画面を作成
    pygame.display.set_caption("Hit & Blow game")

    gamebutton = []
    gamebuttonrect = Rect(200, 250, 200, 50) # 画像の表示位置を表す矩形
    gamebutton.append(pygame.image.load("./images/pushstart.png"))
    gamebutton.append(pygame.image.load("./images/judge.png"))
    gamebutton.append(pygame.image.load("./images/gameover.png"))

    margin = [100, 100] # *を表示する位置までのマージン
    imagesize = [100,100] # 画像1個当たりの大きさ

    qfilestr = [] # 画像ファイル名を生成するためのリスト
    qimagelist = []  # 出題用の画像を保存しておくリスト0~9+*の画像が収まっている。
    for i in range(10): # 問題の方の画像ファイルは 00.png~99.pngの名前になっている。
        qfilestr.append(str(i)+str(i) + ".png")  # 画像ファイル名を生成
    for i in range(10): # 画像の読み込み
        qimagelist.append(pygame.image.load("./images/" + qfilestr[i]))
    qimagelist.append(pygame.image.load("./images/kome.png")) # *の画像の読み込み
    #[00.png, 11.png, 22.png … 99.png, kome.png]
    qbuttonrect = [] # *を表示するための矩形を作成 *のサイズは(100,100)
    for i in range(4): #marginの位置から4つ横に画像を並べるためのもの
        qbuttonrect.append(Rect(margin[0] + i * imagesize[1], margin[1], *imagesize))

    nmargin = [50, 350] # 入力ボタンのためのマージン

    filestr  = [] # 画像ファイル名を収めるリスト
    for i in range(10): # 読み込む画像ファイル名のリストを作る
        filestr.append(str(i) + ".png")
    imagelist = [] # 読み込んだ画像のリスト
    for i in range(10):
        imagelist.append(pygame.image.load("./images/" + filestr[i])) #ファイルの読み込み
    buttonrect = [] # 画像ファイルの貼り付け位置が収められたリスト(Rect型)
    for i in range(5): # 0~4の画像の位置
        buttonrect.append(Rect(nmargin[0] + i * imagesize[0], nmargin[1], *imagesize))
    for i in range(5, 10): # 5~9の画像の位置
        buttonrect.append(Rect(nmargin[0] + (i - 5) * imagesize[0], nmargin[1]+imagesize[1], *imagesize))


    running = True
    # メインループ

    while running:
        screen.fill((100, 100, 100))  # 背景色で塗る
        # この時点では、 screen.blit(gamebutton[gamescene], gamebuttonrect) で済むけど、
        # とりあえずは、if文でわけておく
        if gamescene == 0:
            screen.blit(gamebutton[0], gamebuttonrect)
        elif gamescene == 1:
            for i in range(4): # 入力された数字を表示する領域
                screen.blit(qimagelist[10], qbuttonrect[i])
            for i in range(5): # 入力のためのボタンとなる画像0~4
                screen.blit(imagelist[i], buttonrect[i])
            for i in range(5, 10): # 入力のためのボタンとなる画像5~9
                screen.blit(imagelist[i], buttonrect[i])
            screen.blit(gamebutton[1], gamebuttonrect)
        elif gamescene ==2:
            screen.blit(gamebutton[2], gamebuttonrect)
        else:
            print("error")

        for event in pygame.event.get():
            if event.type == QUIT:  # 終了イベント
                running = False
                pygame.quit()  # pygameのウィンドウを閉じる
                sys.exit()  # システム終了
            if (event.type == pygame.MOUSEBUTTONUP) and (event.button == 1):
                if gamebuttonrect.collidepoint(event.pos):
                    if gamescene == 0:
                        gamescene = 1
                    elif gamescene == 1:
                        gamescene = 2
                    elif gamescene == 2:
                        gamescene = 0
                    else:
                        gamescene = -1

        pygame.display.update()  # 描画処理を実行


if __name__ == "__main__":
    main()

次回

今回のソースコード一式はgithubにもあります。
https://github.com/gomta777/hitandblow/
のcreategamescreenが今回のものです。

次回は、入力の処理を仕上げていきたいと思います。


よかったら、ブログ村↓のクリックお願いします。