【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’を指定することで、親オブジェクトの左右めいいっぱいにまで広げてくれます。
これでどんなサイズにでも対応できますね!
今回はこれで終わりにしたいと思います。次回は地雷をランダムに配置していきたいと思います。