工学じじいの縁側日記

工学じじいの縁側日記

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

【pygame】pythonでヒット&ブローゲーム作る #06 ログの表示をかっこよく【簡単】

pygamepythonでヒット&ブローゲーム作る #06 ログの表示をかっこよく【簡単】

f:id:gomta777:20191115111505p:plain

前回までのまとめ

【簡単?】初心者がpython3とpygameでヒット&ブローゲーム作ってみる【習作?】 - 工学じじいの縁側日記
【簡単?】初心者がpython3とpygameでヒット&ブローゲーム作ってみる #02【習作?】 - 工学じじいの縁側日記
【簡単?】初心者がpython3とpygameでヒット&ブローゲーム作ってみる #03 画面作るよ【習作?】 - 工学じじいの縁側日記
【pygame】pythonでヒット&ブローゲーム作る #04 数字の入力【簡単】 - 工学じじいの縁側日記
【python】ヒット&ブローゲーム作る #05 正誤判定、ログの表示【プログラミング】 - 工学じじいの縁側日記



前回までで、タイトル画面、ゲーム画面の状態遷移、

f:id:gomta777:20191119133131p:plainf:id:gomta777:20191119152220p:plainf:id:gomta777:20191122045331p:plain
図 状態遷移

問題の出題と、解答の入力とチェック

f:id:gomta777:20191119163750p:plainf:id:gomta777:20191122045323p:plainf:id:gomta777:20191122045327p:plain
図 解答の入力とチェック

最後に、ログの表示を(割と適当に)やりました。

f:id:gomta777:20191122051930p:plainf:id:gomta777:20191122052229p:plain
図 解答ログの表示

これで一通りのゲームにはなるのですが、ログが15、6回で画面買いに流れてしまって見えなくなってしまいます。
今回はこれを解決するべく、ログの表示をかっこ良いタブを使ったページ切り替えにしてみます!

それでは、作っていくー(笑)



ログの表示枠

まず初めに、ログの表示に枠を付けて、「それっぽく」してみます。

f:id:gomta777:20191119010305p:plain
図 オブジェクトの配置

グリッド表示関数を作成

この配置を元に、関数を作っていきます。
解説と異なるフォントを使った人は、少し枠の大きさの調整が必要です。
文字を描画するのに必要な領域のサイズは、
Font.size 命令で取得できます。(今回は高さに30必要だったので、30がたくさん出てきてます)

def draw_judgment_grid(surface):
    bias = 90
    pygame.draw.rect(surface, (255, 255, 255), (555, bias, 335, 30 * 15), 2)
    for i in range(14):
        pygame.draw.line(surface, (255, 255, 255), (555, bias + 30 * (i + 1)), (555 + 335, bias + 30 * (i + 1)), 2)
    pygame.draw.line(surface, (255, 255, 255), (630, bias), (630, 30 * 18), 2)
    pygame.draw.line(surface, (255, 255, 255), (728, bias), (728, 30 * 18), 2)
    pygame.draw.line(surface, (255, 255, 255), (800, bias), (800, 30 * 18), 2)def draw_judgment_grid(surface):
    bias = 90
    pagesize = 15
    baserect = Rect(555, 90, 335, 30)
    pygame.draw.rect(surface, (255, 255, 255), \
        (baserect.left, baserect.top, baserect.width, baserect.height * pagesize), 2)
    for i in range(pagesize):
        pygame.draw.line(surface, (255, 255, 255), \
            (baserect.left, baserect.top + baserect.height * (i + 1)), \
            (555 + 335, bias + 30 * (i + 1)), 2)
    pygame.draw.line(surface, (255, 255, 255),\
        (baserect.left + 75, baserect.top), (baserect.left + 75, baserect.top + baserect.height * pagesize), 2)
    pygame.draw.line(surface, (255, 255, 255), \
        (baserect.left + 173, baserect.top), (baserect.left + 173, baserect.top + baserect.height * pagesize), 2)
    pygame.draw.line(surface, (255, 255, 255), \
        (baserect.left + 245, baserect.top), (baserect.left + 245, baserect.top +baserect.height * pagesize), 2)

(省略)

   if gamescene == 0:
            screen.blit(title, titlepos)
            screen.blit(gamebutton[0], gamebuttonrect)
        elif gamescene == 1:
            draw_judgment_grid(screen)

(省略)

実行するとこんな感じになります。

f:id:gomta777:20191123233835p:plain
図 ロググリッドの表示

ページに合わせたデータ形式

これまでは、以下の様に「judgement」ボタンを押したときにリストanslogに、判断結果を順次追加していました。

judge = judgement(players_input, question)
anslog.append("%02d 回目 " % turn + str(players_input) + \
"  " + str(judge[0]) + " HIT  " + str(judge[1]) + " BLOW")
turn += 1

これを、タブボタンによるページ切り替えで表示します。
今回考えてるページは、

  • 1ページ15回分の記録
  • 4ページ
  • 上部のタブボタンでページ切り替え可能

f:id:gomta777:20191119001414p:plain
図 タブとページのイメージ

ページを表すリストを生成

15(行)x4(ページ)を表すリストを生成します。

    baserect = Rect(555, 90, 335, 30)  # ログの表示領域の一段分
    pagesize = 15 # ページ当たりのログ数
    pages = 4 #最大ページ数
    judgement_pages = [["" for i in range(pagesize)] for j in range(pages)]
    # 15x5ページ分のリストを用意します judgement_pages[ログ][ページ]
  pygame.draw.rect(surface, (0,0,0), \
        (baserect.left, baserect.top, baserect.width, baserect.height * pagesize))
    # ログ表示領域の背景を黒で初期化

最後の行は、表示領域を見やすいように黒で初期化しているだけです。
次に、1次元的に、順次保存されているログを保存しているリスト(anslog)をページ分けしてあげます。
15個以上あった時は、次のページに保存してあげればいいですね。

 if i*pagesize+j < len(logmsg):
  judgement_pages[i][j] = logmsg[i*pagesize+j]

の部分で、ページに割り振りされています。

# pages = 4
# pagesize = 15
# logmsgはanslogが実引数で渡されてくるはず
    for i in range(pages):
        for j in range(pagesize):
            if i*pagesize+j < len(logmsg):
                judgement_pages[i][j] = logmsg[i*pagesize+j]
    # ログを、ページに書き写す 1次元配列→2次元配列化
指定されたページを描画

データは準備できたので、指定されたページを描画します。
ここが、この処理の肝になる部分ですね。

for i in range(pagesize):
    if len(logmsg) != 0 and i == (len(logmsg) - 1) % pagesize:
    # ログが空じゃなくて、iが指定ページの表示できる範囲内なら描画
        pygame.draw.rect(surface, (100, 0, 0), \
            (baserect.left, baserect.top + baserect.height * i, baserect.width, baserect.height))
        # 一番新しいログは目立つように色を変える
    text1 = fnt.render(judgement_pages[pagenum][i], True, (255, 255, 255))
    surface.blit(text1, (baserect.left+5, baserect.height * i + baserect.top+10 - 3))
    # 描画テキストを生成し描画
# 4ページあるうちの指定されたページのログを描画する

前ページの中から指定されたページの15個のログを表示しているだけです。ちょっと面倒ですが、よく読むとそんなに複雑ではないことがわかると思います。

これで、指定されたページのログが表示できます。

ページ数を表すタブを表示

タブを表示します。
ここにきてまた新しい画像を読み込まなければならないですが、説明は最小限にします。

f:id:gomta777:20191124013620p:plain
図 タブ画像

ファイル名は、オレンジの方がtab1~tab4.png、みどりのほうがtab1a~tab4a.pngにしていますので、for文でループを作って読み込みやすくなっています。
処理に関しては、すでに説明済みのものばかりです。(数字ボタンの読み込みと表示の時と同じです)

tabimagelist = []
tabimagename = []
for i in range(4):
    tabimagename.append("tab"+str(i+1) + ".png")
for i in range(4):
    tabimagelist.append(pygame.image.load("./images/" + tabimagename[i]))
atabimagelist = []
atabimagename = []
for i in range(4):
    atabimagename.append("tab" + str(i + 1) + "a.png")
for i in range(4):
    atabimagelist.append(pygame.image.load("./images/" + atabimagename[i]))
tabimagerect = []
for i in range(4):
    tabimagerect.append(Rect(555 + i * 83, 90 - 39, 85, 40))
# baserect = Rect(555, 90, 335, 30) 描画領域から、タブの位置を逆算してます
# tab画像の大きさは 83x39で計算 描画は85x40で描画しています。
タブ表示関数の作成

次に、タブを描画する関数draw_tabsを作ります。

def draw_tabs(surface, tabnumber, tabimage, atabimage, tabrect):
    baserect = Rect(555, 90, 335, 30)  # ログの表示領域の一段分
    tabsize = [83, 39]
    for i in range(4):
        if i == tabnumber: # 指定されたタブならば、赤いタブ画像
            # surface.blit(tabimage[i], (baserect.left + i * tabsize[0], baserect.top - tabsize[1]))
            surface.blit(tabimage[i], tabrect[i])
        else: #それ以外は緑のタブ画像で表示
            # surface.blit(atabimage[i], (baserect.left+i*tabsize[0], baserect.top - tabsize[1]))
            surface.blit(atabimage[i], tabrect[i])

この辺は何度も出てきている描画処理の組み合わせなので、そんなには悩むところはないと思います。

読み込んだタブ画像を、実際に表示します。
現在表示しているタブは、オレンジで、その他のタブは緑で表示します。
結果の表示のあたりにこっそり入れてやります。
現在のタブは、まだ処理をしていないので、とりあえず変数current_tab(現在のページを表す)
を作っておいて0(1ページ目)を指定しておきます。

current_tab = 0
(省略)    
draw_judgement_state(screen, log_font, anslog, current_tab)
draw_tabs(screen, current_tab, tabimagelist, atabimagelist)
draw_judgment_grid(screen)

実行結果:

f:id:gomta777:20191124020049p:plain
図 タブの表示

ページ切り替えの実装

ページ切り替えの実装は、タブの表示領域を苦リクしたときに、押した画像の領域によってcurrent_tabの番号を0~3に切り替えて、ログ表示関数であるdraw_judgement_stateに私直す事で実現します。

for i in range(len(tabimagerect)):
    if gamescene == 1:
        if tabimagerect[i].collidepoint(event.pos):
            current_tab = i

また、「judgement」ボタンが正しく押されて、anslogが増えていくと自動的にcurrent_tabを切り替えます。
現在のページ数は、ログの数を15で割った整数となります。

 current_tab = (len(anslog) - 1) // 15

これでページの切り替えができます。

実行結果:

f:id:gomta777:20191124022734p:plainf:id:gomta777:20191124022737p:plainf:id:gomta777:20191124022741p:plainf:id:gomta777:20191124022730p:plain
図 タブの切り替え


1ページ目が埋まり、2ページ目にログが表示され、3,4ページ目はまだ空なのがわかります。

今回のまとめ

今回はログの表示をかっこよく、タブとページで実装してみました。
プログラム的にはちょっと面白いところだったのではないかなと思います。

次回

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

次回は、最終回です。
ゲームとしてはほとんど完成したと思いますので、最後の細かいところを修正して完成としたいと思います!



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