2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PythonのTkinterで某パズルゲーム(のようなもの)をつくってみました

Posted at

プログラミング歴数ヶ月の初心者です。
ゲームが作りたかったので、比較的敷居の低そうな落ちものパズルをつくってみました
Qiita初投稿です。

こんな感じのゲームです(gif30秒くらいになってしまった。長い)
ezgif-3-3eee2fbd7703.gif

説明はコードの方にコメントしてあります。(ミス等多そうですが)

panepon.py
import tkinter
import random
import math

phase = 0           #ゲーム進行を管理するための変数
cursor_x = 2        #カーソルのx座標(初期)
cursor_y = 11       #カーソルのy座標(初期)
cursor_dx = 1       #カーソルの移動量x
cursor_dy = 1       #カーソルの移動量y
block_width = 64    #ブロック幅
block_height = 64   #ブロック高さ

def right(e):                       #右キーを押した時の処理
    global cursor_x, cursor_dx      #グローバル変数を書き換えるためにグローバル宣言
    cursor_x += cursor_dx
    if cursor_x > 4:                #カーソルがゲーム枠より右に行かないための処理
        cursor_x = 4

def left(e):                        #左キーを押した時の処理
    global cursor_x, cursor_dx
    cursor_x -= cursor_dx
    if cursor_x < 0:                #カーソルがゲーム枠より左に行かないための処理
        cursor_x = 0

def up(e):                          #上キーを押した時の処理
    global cursor_y, cursor_dy
    cursor_y -= cursor_dy
    if cursor_y < 0:                #カーソルがゲーム枠より上に行かないための処理
        cursor_y = 0

def down(e):                        #下キーを押した時の処理
    global cursor_y, cursor_dy
    cursor_y += cursor_dy
    if cursor_y > 11:               #カーソルがゲーム枠より下に行かないための処理
        cursor_y = 11

def press_a(e):                     #Aキーを押した時の処理
    global a                        #グローバル変数aの定義
    a = 1                           #Aキーが押されたことを管理

def press_c(e):                     #Cキーを押した時の処理
    global c
    c = 1

def press_r(e):                     #Rキーを押した時の処理
    global r
    r = 1

block = []  #ブロックの配置を管理する配列
check = []  #チェック用の配列

for i in range(17):
    block.append([0,0,0,0,0,0])
    check.append([0,0,0,0,0,0])

def draw_block():                   #ブロック描画用関数
    cvs.delete("BLOCK")             #今まで描画していたブロックを削除
    for y in range(17):
        for x in range(6):
            if block[y][x] > 0:     #ブロックが存在する場合
                cvs.create_image(x * block_width + 35, y * block_height - block_height * 4 - 35, image=img_block[block[y][x]], tag="BLOCK")
                                    #ブロック画像の描画                                                   ブロック画像用配列

def check_block():                  #ブロックをチェックする関数
    for y in range(17):
        for x in range(6):
            check[y][x] = block[y][x]  #現在のブロック配置をcheck配列に代入

    for y in range(1,16):
        for x in range(6):
            if check[y][x] > 0:      #ブロックが存在する場合
                if check[y-1][x] == check[y][x] and check[y+1][x] == check[y][x]:  #同じ色のブロックが縦に3列以上並んでいる場合
                    if check[y][x] == 1:  #並んだブロックが赤である場合ハートに変える
                        block[y-1][x] = 6
                        block[y][x] = 6
                        block[y+1][x] = 6
                    if check[y][x] == 2:  #並んだブロックが青である場合イルカに変える
                        block[y-1][x] = 7
                        block[y][x] = 7
                        block[y+1][x] = 7
                    if check[y][x] == 3:  #並んだブロックが緑である場合クローバーに変える
                        block[y-1][x] = 8
                        block[y][x] = 8
                        block[y+1][x] = 8
                    if check[y][x] == 4:  #並んだブロックが黄色である場合ビールに変える
                        block[y-1][x] = 9
                        block[y][x] = 9
                        block[y+1][x] = 9
                    if check[y][x] == 5:  #並んだブロックが紫である場合ワインに変える
                        block[y-1][x] = 10
                        block[y][x] = 10
                        block[y+1][x] = 10
                        

    for y in range(17):
        for x in range(1,5):
            if check[y][x] > 0:
                if check[y][x-1] == check[y][x] and check[y][x+1] == check[y][x]:  #同じ色のブロックが横に3列以上並んでいる場合
                    if check[y][x] == 1:
                        block[y][x-1] = 6
                        block[y][x] = 6
                        block[y][x+1] = 6
                    if check[y][x] == 2:
                        block[y][x-1] = 7
                        block[y][x] = 7
                        block[y][x+1] = 7
                    if check[y][x] == 3:
                        block[y][x-1] = 8
                        block[y][x] = 8
                        block[y][x+1] = 8
                    if check[y][x] == 4:
                        block[y][x-1] = 9
                        block[y][x] = 9
                        block[y][x+1] = 9
                    if check[y][x] == 5:
                        block[y][x-1] = 10
                        block[y][x] = 10
                        block[y][x+1] = 10
    

def sweep_block():                  #ブロックを消す関数
    num = 0                         #ブロックが消えた個数のカウント
    for y in range(17):
        for x in range(6):
            if block[y][x] == 6:
                block[y][x] = 0
                num += 1
            if block[y][x] == 7:
                block[y][x] = 0
                num += 1
            if block[y][x] == 8:
                block[y][x] = 0
                num += 1
            if block[y][x] == 9:
                block[y][x] = 0
                num += 1
            if block[y][x] == 10:
                block[y][x] = 0
                num += 1
    return num                                          #ブロックが消えた個数を呼び出し元に返す

def drop_block():                                       #ブロックを落とす関数
    flg = False                                         #phase移行のためのチェック
    for y in range(15, -1, -1):                         #yを下から2段目の列から調べる
        for x in range(6):
            if block[y][x] != 0 and block[y+1][x] == 0: #調べている箇所にブロックがある、かつ、1マス下にブロックがない場合
                block[y+1][x] = block[y][x]             #ブロックを下に写す
                block[y][x] = 0                         #写した元のブロックを消す
                flg = True
    return flg                                          #flgを呼び出し元に戻す

def over_block():           #ゲームオーバー処理用の関数
    for x in range(6):
        if block[5][x] > 0: #ブロックがゲーム枠最上段に存在する場合
            return True
    return False

def set_up():                               #ブロックを1段上に移動する関数
    for y in range(5, 16):
        for x in range(6):
            block[y][x] = block[y + 1][x]

def set_block():                            #ブロックを最下段に出す関数
    for x in range(6):
        block[16][x] = random.randint(1, 5) #最下段に、ブロック画像用配列の1~5をランダムに配置

def ojama_block(ojama_num):                     #上段にランダムにブロックを配置する関数
    for y in range(0, ojama_num):
        for x in range(6):
            block[y][x] = random.randint(1, 5)

def cross_block():                                                          #カーソル内の左右を入れ替える関数
    if c == 1:                                                              #Cキーが押されたら
        source = block[cursor_y + 5][cursor_x]                              #変数sourceにカーソル内左側のブロックを代入
        block[cursor_y + 5][cursor_x] = block[cursor_y + 5][cursor_x + 1]   #カーソル内左側のブロックを右側のブロックと同じものにする
        block[cursor_y + 5][cursor_x + 1] = source                          #カーソル内右側のブロックをsourceと同じにする

def draw_text(text, x, y, size, col, tag):                         #テキストを表示する関数
    font = ("Times New Roman", size, "bold")                       #フォント、フォントサイズ、文字太さを先に入れておく
    cvs.create_text(x, y, text=text, fill=col, font=font, tag=tag) #テキストを表示する座標、引数で持ってきた情報を元にテキストを表示

def game_main():                #メインの処理
    global phase, a, c, r, score, combo, ojama_num, over, force, cursor_x, cursor_y
    if phase == 0:              #phaseが初期の場合(タイトル画面)
        cvs.delete("OVER")      #リスタート時のための削除処理ここから
        cvs.delete("CHARA")
        cvs.delete("RESTART")
        for y in range(17):
            for x in range(6):
                block[y][x] = 0
                draw_block()    #リスタート時のための削除処理ここまで
        draw_text("パネ◯ン", 384, 334, 100,"violet","TITLE") #テキストの描画
        draw_text(
        """
        Aキーで
        ゲームを
        始めるよ!
        
        """, 425, 590, 25, "blue", "TITLE")
        cvs.create_image(576, 650, image=chara, tag="CHARA")
        phase = 1       #phase1に移行
        score = 0       #スコア表示用の数値
        combo = 1       #コンボ数管理の変数
        ojama_num = 0   #上からブロックを降らすための数値
        force = 1       #上からブロックを降らすための数値
        over = 0        #ゲームオーバー管理用の変数
        a = 0           #Aキーが押されていない状態
        c = 0
        r = 0
    elif phase == 1:                 #phaseが1の場合
        if a == 1:                   #Aキーを押した場合
            for y in range(15):
                for x in range(6):
                    block[y][x] = 0
                    a = 0               #Aキーが押されていない状態に戻す
                    draw_block()        #ブロックの描画
                    cvs.delete("TITLE") #タイトル(テキスト)の削除
                    draw_text("ゲーム説明", 576, 30, 40, "blue", "READ")
                    draw_text("↑ ↓ ← → キーでカーソルが動くよ!", 576, 130, 20, "blue", "READ")
                    draw_text("Aキーで最下段にブロックを出すよ!", 576, 180, 20, "blue", "READ")
                    draw_text("Cキーで左右を入れ替えるよ!", 576, 230, 20, "blue", "READ")
                    draw_text("縦か横に3つ以上並んだら消えるよ!", 576, 280, 20, "blue", "READ")
                    draw_text("一定回数消すと上からブロックが降るよ!", 576, 330, 20, "blue", "READ")
                    draw_text("最上段にブロックが到達したらダメ!", 576, 380, 20, "blue", "READ")
                    phase = 2
    elif phase == 2:
        if c == 1:
            cross_block()
            c = 0
        if drop_block() == False:  #flgがFalseの場合
            phase = 3
        draw_block()
    elif phase == 3:
        check_block()
        draw_block()
        phase = 4
    elif phase == 4:
        sc = sweep_block()  #ブロックが消えた個数numをscに代入
        if sc > 9:        #消えた個数によってスコアの上昇量scを管理ここから
            sc += 100
        elif sc > 8:
            sc += 50
        elif sc > 7:
            sc += 30
        elif sc > 6:
            sc += 15
        elif sc > 5:
            sc += 7
        elif sc > 4:
            sc += 3
        elif sc > 3:
            sc += 1                 #消えた個数によってスコアの上昇量scを管理ここまで
        sc *= combo ** 4            #コンボ量の4乗をスコアの上昇量scに掛ける
        score += sc                 #スコアに上昇量を足す
        if sc > 0:                  #ブロックが消されていた場合
            combo += 1              #コンボ数をプラス1
            if combo > 2:
                cvs.delete("COMBO") #今まで表示されていたコンボ表示を削除
                draw_text(str(combo - 1)+"連鎖!", 480, 550, 50, "blue", "COMBO")
                                    #コンボ数を表示するテキスト
            ojama_num += 0.4        #上からブロックを降らす量を0.4増やす
            force += 0.05           #ブロックを消すたびに、ojama_numに掛ける数値が増える
            phase = 2
        else:
            if over_block() == False:
                phase = 5
            else:
                over = 1            #ゲームオーバー処理へ
        draw_block()
    elif phase == 5:
        combo = 1                   #コンボ数を元に戻す
        cvs.delete("COMBO")         #コンボ数表示を消す
        if ojama_num >= 1:          #ojama_numが1以上の場合
            ojama_num = math.floor(ojama_num * (force))  #ojama_numにforce掛けてを少数切り捨てする
            ojama_block(ojama_num)  #ojama_numの数値と同じ数(列)、ブロックを上にセットする
            ojama_num = 0
        if c == 1:
            cross_block()
            c = 0
            phase = 2
        if a == 1:
            a = 0
            set_up()
            set_block()
            score += 10
            phase = 2
    cvs.delete("INFO")  #スコア表示を消す
    if phase > 1:
        draw_text("SCORE", 576, 450, 40, "black", "INFO")
        draw_text(score * 10, 576, 490, 40, "black", "INFO") #スコア表示をする
    draw_block()
    if over == 1:           #ゲームオーバー処理
        cvs.delete("CHARA")                                    #今まで描画していたキャラクターを消す
        cvs.create_image(576, 650, image=chara2, tag="CHARA")  #キャラクター画像を別のものに変える
        draw_text("GAME OVER", 384, 384, 100, "black", "OVER")
        cvs.delete("READ")  #ルール説明を消す
        cvs.delete("COMBO") #コンボ表示を消す
        draw_text(
        """
        Rキーで
        タイトルに
        戻るよ...
        
        """, 425, 590, 25, "blue", "RESTART")
        if r == 1:
            phase = 0
    cvs.delete("CURSOR")  #カーソル画像を消す
    cvs.create_image(cursor_x * block_width + 67, cursor_y * block_height + 29, image=cursor, tag="CURSOR")
                          #カーソル画像を描画する
    root.after(100,game_main)  #0.1秒後にgame_main関数を呼び出す

root = tkinter.Tk()          #ウィンドウを作る
root.title("パネ◯ン")         #タイトルの設定
root.resizable(False,False)  #ウィンドウサイズを変更できないようにしている
root.bind("<Right>",right)   #Rightキーを押した時、right関数が呼ばれるようにしている
root.bind("<Left>",left)
root.bind("<Up>",up)
root.bind("<Down>",down)
root.bind("<a>",press_a)
root.bind("<c>",press_c)
root.bind("<r>",press_r)
cvs = tkinter.Canvas(root,width=768, height=768)  #キャンバスのサイズを768x768ピクセルにする
cvs.pack()                                        #キャンバスを配置する

bg = tkinter.PhotoImage(file="bg.png")  #bgに画像を代入

img_block = [
  None,
  tkinter.PhotoImage(file="red.png"),
  tkinter.PhotoImage(file="blue.png"),
  tkinter.PhotoImage(file="green.png"),
  tkinter.PhotoImage(file="yellow.png"),
  tkinter.PhotoImage(file="purple.png"),
  tkinter.PhotoImage(file="heart.png"),
  tkinter.PhotoImage(file="dolphin.png"),
  tkinter.PhotoImage(file="clover.png"),
  tkinter.PhotoImage(file="ale.png"),
  tkinter.PhotoImage(file="wine.png")
]   #ブロック画像用配列

cursor = tkinter.PhotoImage(file="cursor.png")
chara = tkinter.PhotoImage(file="chara.png")
chara2 = tkinter.PhotoImage(file="chara2.png")
cvs.create_image(384,384,image=bg)
game_main()
root.mainloop()  #GUIを表示

こうして見ると、マジックナンバー多いですね。気をつけたい

次はRPG作りたいなーって思ってます。(ちょっと勉強したけどめちゃくちゃ難しい。。)

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?