【pythonゲーム作成】pythonでマインスイーパー作ってみるよ【その4】


【pythonゲーム作成】pythonでマインスイーパー作ってみるよ【その4】

今回はpythonでシンプルなマインスイーパーを作成してみたいと思います。
windowsに搭載されていた昔ながらのマインスイーパーを目指してみます!

おなじみのこんなやつ
↓↓↓

tkinterモジュールでは完全再現とまではいきませんが、なるべく似ているものを
作れるようにしていきたいと思います!

前回までのコード
↓↓↓

from tkinter import *

####メイン画面の生成####
root = Tk()
root.title("マインスイーパ")
root.resizable(0,0)     #サイズ変更不可にする

###メニュー作成###
#メニューオブジェクトを作る
menu_ROOT = Menu(root)
#メインウィンドウ(root)のmenuに作成したメニューオブジェクトを設定し更新
root.configure(menu = menu_ROOT)

#ゲームメニューを作る。menu_ROOTが親オブジェクト
menu_GAME = Menu(menu_ROOT, tearoff = False)

#「ゲーム」というラベルで親メニューをrootメニューに追加。
#サブメニュー付きなのでadd_cascadeメソッドを使う
menu_ROOT.add_cascade(label = 'ゲーム(G)', under = 4, menu = menu_GAME)
#「初級」「中級」「上級」ラベルでサブメニューを親メニュー(menu_GAME)に追加
menu_GAME.add_command(label = "初級(B)", under = 3)
menu_GAME.add_command(label = "中級(I)", under = 3)
menu_GAME.add_command(label = "上級(E)", under = 3)
#「終了」ラベルでメニューをrootメニューに追加
menu_ROOT.add_command(label = "終了(X)", under = 3)

###フレームオブジェクト作成###
#外枠のフレーム作成
root_frame = Frame(root, relief = 'groove', borderwidth = 5, bg = 'LightGray')
#上部ステータス画面のフレーム作成
status_frame = Frame(root_frame, width = 300, height = 50, relief = 'sunken', borderwidth = 3, bg = 'LightGray')
#下部ゲーム画面のフレーム作成
game_frame = Frame(root_frame, width = 300, height = 300, relief = 'sunken', borderwidth = 3, bg = 'LightGray')
#それぞれのフレームを配置する。ステータス画面とゲーム画面は上下左右それぞれ余白を少し設ける
root_frame.pack()
status_frame.pack(pady = 5, padx = 5)
game_frame.pack(pady = 5, padx = 5)

root.mainloop()

それでは、レッツスタート!!

【STEP4】マス目を配置する

次はマス目の配置です。マインスイーパーの初級では9×9マスの中に10コの地雷が入っています。
なので、まず画面上には81コのマス(=ボタン)が必要になりますね。
地雷はとりあえずおいておきましょう。

このマス目もフレームで作成していきます。また、クリック時のイベントも追加していきます。
クリック前は出っ張り感を出しておいて、クリックするとへっこむ感じにしたいと思います。
クリックイベントでreliefを変更して表現します。
出っ張りはreliefをraisedに、へっこみはreliefをridgeにします。
(reliefによるフレームの質感の違いは前回)を参照してください。
また、クリックすると数字をprintするようにもします。

それではコードを書いていきましょう。このコードも前回の続きに書いていくので、
上のコードの37行目と39行目の間に書いていきます。

####マス目の作成####
#左クリックした際のイベント関数を定義。これは下で(21行目で)フレームにクリックイベントを定義しているけども、
#それよりも先に書かないとダメ
def left_click(event):
    #event.widgetで該当のオブジェクト(ウィジェット=部品)を取得できる
    #クリックした感を出すので、relefをridgeに変更する
    event.widget.configure(relief = 'ridge', bd = '1')
    #またそのフレームのアトリビュートnumを表示する
    print(event.widget.num)

#各マス目に番号を振っておくといろいろ便利なのでそれ用の変数iを定義する
i = 0
#繰り返し作成したフレーム格納用リスト
frame_list = []
#for文の入れ子構造にして、9×9回繰り返す
for x in range(9):
    for y in range(9):
        #タテヨコ30pxの小さいフレームを量産。reliefをraisedにして出っ張り感を再現する
        frame = Frame(game_frame, width = 30, height = 30, bd = 3, relief = 'raised', bg = 'LightGray')
        #bindメソッドを使うと、そのオブジェクトにイベントを定義できる。
        #第一引数に<1>を指定すると左クリックした際のイベントとなる
        #第二引数には呼び出される関数(4行目から定義しているleft_click関数)を記述する
        frame.bind("<1>", left_click)
        #frameにnumアトリビュートを定義する
        frame.num = i
        #作成したフレームをフレームのリストに格納する。これでインデックス番号でアクセスすることで
        #各フレームを操作できる
        frame_list.append(frame)
        #gridを使ってフレームを配置する。packと違いgridを使うと、タテヨコ均等に9列x9列に配置できる
        #rowでヨコ、columnでタテを指定している
        frame.grid(row=x, column=y)
        i += 1

(コメントが多すぎる気がしますが…。)

4~9行目でフレームが左クリックされた際のイベントを定義しています。
7行目でフレーム押下でへこんだ質感に変更し、9行目で数字を表示します。

12行目~32行目でボタンを繰り返し作成・配置しています。
gridを使うことでタテヨコ均等に配置することができます。

25行目でnumアトリビュートを定義し、そこにiの値を代入しています。
そのため左上から0、横方向に1、2、3…、8と番号が付きます。
2列目は9、10、11…という感じになり、右下のフレームが最後の80となります。
配列のインデックスと合わせたいので0からの番号を振っています。

28行目では、作成したフレームをframe_listリストに追加していっています。
これで各マス目フレームをインデックス番号で操作できるようになります。
各マス目も左上からframe_list[0]、frame_list[1]…、となるので、うまいことnumと一致していますね。
このようにリストに格納しておくと、今後地雷を配置する際などに役に立ってきます。

さて、最後にもう1つ。この画像を見ると、ステータス画面の上部のフレームとマス目画面の幅が
ズレているのが分かります。

これは前回作成したステータス画面のフレーム幅が300px、今回のマス目の幅が30×9=270pxと
異なるためです。
これは今後作っていくことになる、中級・上級になってもずれが生じてしまいます。

そのため、ステータス画面の横幅は、300px固定決め打ちにするのではなく、
画面の横幅に合わせてあげるように修正しましょう。
前回のコード(一番上にあるコード)の、27行目~37行目を以下のように変更します。

###フレームオブジェクト作成###
#外枠のフレーム作成
root_frame = Frame(root, relief = 'groove', borderwidth = 5, bg = 'LightGray')
#上部ステータス画面のフレーム作成
status_frame = Frame(root_frame, height = 50, relief = 'sunken', borderwidth = 3, bg = 'LightGray')
#下部ゲーム画面のフレーム作成
game_frame = Frame(root_frame, relief = 'sunken', borderwidth = 3, bg = 'LightGray')
#それぞれのフレームを配置する。ステータス画面とゲーム画面は上下左右それぞれ余白を少し設ける
root_frame.pack()
status_frame.pack(pady = 5, padx = 5, fill = 'x')
game_frame.pack(pady = 5, padx = 5)

ステータス画面はwidthを指定しないで、heightだけ50px固定にします。
ゲーム画面は中に配置するマス目でサイズが勝手に変わるので、widthもheightも指定しません。

また、10行目でfill = ‘x’を指定することで、親オブジェクトの左右めいいっぱいにまで広げてくれます。

これでどんなサイズにでも対応できますね!

今回はこれで終わりにしたいと思います。次回は地雷をランダムに配置していきたいと思います。

  • このエントリーをはてなブックマークに追加

PAGE TOP