🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

4015閲覧

Pythonで落ちものパズルを作成しようと思っているのですが、マウスクリックで始まる想定のゲームがクリックを受け付けずゲームが進みません。

akiakane

総合スコア4

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2020/12/14 16:19

前提・実現したいこと

python勉強中のものです。
以下の構成で落ちものパズルを作りたいのですが実行後最初の起動画面でクリックしても次に進まず、エラーも出てこないため行き詰っております。
起こっている事象からしてgame_main関数内のindex0,index1に何か問題が起こっていそうなのですが問題解決までにいたりませんでした。。。
有識者の方でこのコードの不備をご指摘いただける方がいればご教授賜りたいです。
以上よろしくお願いします。

index 0 = タイトルの文字を表示しindex1の処理に移行
index 1 = ゲーム開始の入力を待つ、画面をクリックしたら最初に落ちてくるブロック(a)をセットしindex2に移行
index 2 = ゲーム中にブロック(a)を落下、すべてのブロック(a)が落下したらindex3の処理に移行
index 3 = ゲーム中にブロック(a)が三つ並んだかを調べ、揃ったブロックを別のブロック(b)に変える、index4の処理に移行
index 4 = ブロック(b)に変わったマスがあれば消してscoreの値を加算、index2に再び戻り落下処理をループ、ブロック(b)に変わったものはなく、マスの上限にまで達していなかったらindex5の処理へ、マスの上限まで積みあがった場合index6のゲームオーバー処理へ移行
index 5 = 入力待ち状態、マウスカーソルでマスを指定したらブロックを配置、その後index2の処理へ移行
index 6 = ゲームオーバー画面、変数で時間をカウント、5秒後にindex0の処理へ移動

発生している問題・エラーメッセージ

無し

該当のソースコード

import tkinter import random index = 0 timer = 0 score = 0 tsugi = 0 cursor_x = 0 cursor_y = 0 mouse_x = 0 mouse_y = 0 mouse_c = 0 def mouse_move(e): global mouse_x, mouse_y mouse_x = e.x mouse_y = e.y def mouse_press(e): global mouse_c mouse_c = 1 Board = [] Check = [] for i in range(10): Board.append([0,0,0,0,0,0,0,0]) Check.append([0,0,0,0,0,0,0,0]) def draw_block():#左上から順々に各マスの値を調べてマスに値が入っていたらブロックを表示させる関数 cvs.delete("BLOCK") for y in range(10): for x in range(8): if Board[y][x] > 0: cvs.create_image(x*72+60, y*72+60, image=img_Block[Board[y][x]], tag="BLOCK") def check_board(): for y in range(10): for x in range(8): Check[y][x] = Board[y][x] for y in range(1, 9):#Checkリストの上下のマスを調べて3つを揃っていたら肉球ブロックに変える処理 for x in range(8): if Check[y][x] > 0: if Check[y-1][x] == Check[y][x] and Check[y+1][x] == Check[y][x]: Board[y-1][x] = 7 Board[y][x] = 7 Board[y+1][x] = 7 for y in range(10):#Checkリストの左右のマスを調べて3つを揃っていたら肉球ブロックに変える処理 for x in range(1, 7): if Check[y][x] > 0: if Check[y][x-1] == Check[y][x] and Check[y][x+1] == Check[y][x]: Board[y][x-1] = 7 Board[y][x] = 7 Board[y][x+1] = 7 for y in range(1, 9):#Checkリストの斜めのマスを調べて3つを揃っていたら肉球ブロックに変える処理 for x in range(1, 7): if Check[y][x] > 0: if Check[y-1][x-1] == Check[y][x] and Check[y+1][x+1] == Check[y][x]: Board[y-1][x-1] = 7 Board[y][x] = 7 Board[y+1][x+1] = 7 if Check[y+1][x-1] == Check[y][x] and Check[y-1][x+1] == Check[y][x]: Board[y+1][x-1] = 7 Board[y][x] = 7 Board[y-1][x+1] = 7 def sweep_block():#揃ったブロックを消す関数 num = 0 for y in range(10): for x in range(8): if Board[y][x] == 7: Board[y][x] = 0 num = num + 1 return num def drop_block():#ブロックを一段ずつ落としていく関数 flg = False for y in range(8, -1, -1): for x in range(8): if Board[y][x] != 0 and Board[y+1][x] == 0: Board[y+1][x] = Board[y][x] Board[y][x] = 0 flg = True return flg def over_block():#最上階にブロックが達したか調べる関数 for x in range(8): if Board[0][x] > 0: return True return False def set_block():#最上階にブロックをセットする関数 for x in range(8): Board[0][x] = random.randint(0, 6) def draw_txt(txt, x, y, siz, col, tg):#影付きの文字列を表示する関数 fnt = ("Times New Roman", siz, "bold") cvs.create_text(x+2, y+2, text=txt, fill="black", font=fnt, tag=tg)#2ドットずらし黒い色で文字列を表示(影) cvs.create_text(x, y, text=txt, fill=col, font=fnt, tag=tg)#指定した色で文字列を表示 def game_main():#リアルタイム処理の実行 global index, timer, score, tsugi global cursor_x, cursor_y, mouse_c if index == 0:#ゲーム起動時のタイトルロゴの表示(index 0) draw_txt("ねこねこ", 312, 240, 100, "violet", "TITLE") draw_txt("Click to start.", 312, 560, 50, "orange", "TITLE") index = 1#indexの値を更新 mouse_c = 0 elif index == 1:#Borad上の情報を全てリセット(index 1) if mouse_c == 1:#マウスボタンをクリックしたとき実行 for y in range(10): for x in range(8): Board[x][y] = 0#マスの情報を全て消す mouse_c = 0#クリックしたフラグを解除 score = 0#scoreの値を初期化 tsugi = 0#配置するブロックを初期化 cursor_x = 0#カーソルの位置を左上に配置 cursor_y = 0#ji set_block()#最上段にブロックをセット draw_block()#ブロックの表示 cvs.delete("TITLE")#タイトルの文字を削除 index = 2#indexの値を更新 elif index == 2:#落下処理(index 2) if drop_block() == False:#drop_blockのflgのboolean型で判定、落ちたブロックがないのならindexの値を3に更新 index = 3#indexの値を更新 draw_block() elif index == 3:#ブロック判定処理(index 3) check_board() draw_block() index = 4#indexの値を更新 elif index == 4:#index3でブロックが変わったんなら削除する関数を実行 sc = sweep_block()#消したブロックの数を変数scに代入 score = score + sc*10#消したブロックの数を10倍してscoreに加算 if sc > 0:#消したBlockがあるならindex2に移動(再び落下処理) index = 2#indexの値を更新 else: if over_block() == False:#Blockが上限に達していなければindex5(ブロック再配置処理に移動) tsugi = random.randint(1, 6)#配置するブロックをランダムに決める index = 5#indexの値を更新 else: index = 6#indexの値を更新 timer = 0#timerの値を初期化 draw_block() elif index == 5:#マウスポインタの座標上と最上段のx軸すべてにブロックを配置する処理(index 5) if 24 <= mouse_x and mouse_x < 24+72*8 and 24 <= mouse_y and mouse_y < 24+72*10: cursor_x = int((mouse_x-24)/72)#ポインタのx軸からカーソルの横の位置を計算 cursor_y = int((mouse_y-24)/72)#ポインタのy軸からカーソルの縦の位置を計算 if mouse_c == 1:#マウスをクリックしたら mouse_c = 0#mouse_cのフラグ解除 set_block()#最上段にブロックを配置し Board[cursor_y][cursor_x] = tsugi#カーソルで指定したマスにもブロックを配置 tsugi = 0#次に指定するブロックを初期化し index = 2#indexの値を更新 cvs.delete("CURSOR")#カーソルを削除して cvs.create_image(cursor_x*72+60, cursor_y*72+60, image=cursor, tag="CURSOR")#新しい位置にカーソルの作成 draw_block()#その後ブロックを配置 elif index == 6:#ゲームオーバー処理の実行 timer = timer + 1#timerの値を1ずつ更新 if timer == 1:#timerの値が1なら draw_txt("GAME OVER", 312, 348, 60, "red", "OVER")#赤いGAME OVERの文字を出力 if timer == 50:#timerの値が50まで更新されていたら cvs.delete("OVER")#GAME OVERの文字を消し index = 0#indexの値を0に更新 cvs.delete("INFO")#いったんスコアの表示を消して draw_txt("SCORE "+str(score), 160, 60, 32, "blue", "INFO")#スコアの値を再表示 if tsugi > 0:#tsugiの中に次回表示するブロックが入っていれば cvs.create_image(752, 128, image=img_Block[tsugi], tag="INFO")#そのブロックの表示 root.after(100, game_main)#0.1秒後に再びメイン処理実行 root = tkinter.Tk() root.title("オチものパズル") root.resizable(False, False) root.bind("<Motion>", mouse_move) root.bind("<ButtonPress>", mouse_press) cvs = tkinter.Canvas(root, width=912, height=768) cvs.pack() bg = tkinter.PhotoImage(file="neko_bg.png") cursor = tkinter.PhotoImage(file="neko_cursor.png") img_Block = [ None, tkinter.PhotoImage(file="neko1.png"), tkinter.PhotoImage(file="neko2.png"), tkinter.PhotoImage(file="neko3.png"), tkinter.PhotoImage(file="neko4.png"), tkinter.PhotoImage(file="neko5.png"), tkinter.PhotoImage(file="neko6.png"), tkinter.PhotoImage(file="neko_niku.png") ] cvs.create_image(456, 384, image=bg) game_main() root.mainloop()

試したこと

参考書のソースコードをベースにしているため参考書の見直し(変数名などを自分なりに変更しています)、サンプルコードとのwinmarge比較、googleからkeysym、モジュール、類似事象についての検索。

補足情報(FW/ツールのバージョンなど)

なるべく理解して進みたく、丸々写経では理解する点に関しては難しいと判断し変数名やリスト名などをかえてしまっています。
こちらに投稿しても解決が難しかったら変更点を修正し先に進みたいと思います。。。。簡単ですが以下に想定動作のGIFも送付致しました。
参考書籍
【pythonで作るゲーム開発 入門講座】
ベースにしたサンプルコードを貼り比較などでもしてもらえたら楽なのでしょうが、著作権上の都合でお載せすることは難しそうなため参考書籍名のみ記載させていただきました。

開発環境
windowsOS 10
IDE vscode
python3.8.3

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

argparse

2020/12/14 17:22

`mouse_press` 関数の最後で `game_main()` 呼び出しを入れる等しなければ何も起こらないのでは? 関数は呼び出さなければ実行されません。「リアルタイム処理」というコメントからも、基本的な処理の流れに勘違いが有るように見受けられるので、「イベントが発生すると `bind` した関数が呼ばれる」というモデルについて再度御確認下さい。
akiakane

2020/12/15 10:05

回答ありがとうございます。 game_main()呼び出しは最後の行から数えて二番目に呼び出しているのですが、mouse_press関数の中にも呼び出し処理を行わないと動作しないという意味でしょうか? 一応mouse_press最後の行に'game_main()'を入れて実行しましたがプログラムは動きませんでした。。。
YT0014

2020/12/15 13:19

game_main()呼出し>index:0から1へ、tsugi:0のまま、なので、root.after()が呼ばれず、以後、game_main()が呼ばれない状態なのでは?
akiakane

2020/12/15 18:08

YT0014さん アドバイスありがとうございます。 index1番のtsugiが初期値のままで更新されていないからroot.afterがでの繰り返しがされないという認識で正しいでしょうか? tsugiの変数はindex4のrandomモジュールで1~6の数値を乱数で代入するようにしていて各indexの値は1→2→3と順番に進むようにしていますので最初の初期値は0にしています。 (tsugiの変数は次回の処理に落とすブロックの種類をランダムに選ぶ変数にしています) 送付したGIFが逆に事象を分かりにくくしていたら訂正したいのですがGIFは想定動作としてのGIFで実際はここまで進んでおらず最初のタイトル画面から進んでいないのが現状です。。。 (一応tsugiのに数値を入れてみたのですが事象はかわりませんでした。) 自分が見当違いの解釈をしているようならご指摘いただけると助かります。
YT0014

2020/12/16 03:25

現状の確認のため、game_main()の最初(変数宣言の直後)に、index,mouse_c,tsugiの値をコンソールなどに出力されることをお勧めします。 併せて、mouse_press()にも、呼ばれたことを確認する為の出力を。 game_main()が呼ばれていないのか、mouse_press()が動作していないのか、変数の遷移がおかしいのか、確認できるかと思います。
akiakane

2020/12/16 18:53

原因切り分け方法の提案ありがとうございます。助かります。(漠然としていた原因が明確になりました。。。) 結果としては以下でした。 game_main()の最初(index 0でのif分の最後)の変数index、mouse_cその他global変数をprintで出力したところコンソールに1と0が返ってきたのでgame_main事体は呼び出せており、尚且つ最初のindex 0の処理までは進んでいるようです。 次にindexが1の場合に分岐する処理内の変数宣言を全てprintしてみたところ出力はなかったためindex1で詰まっていると思われます。(elif直後に現在のindexも出力してみましたが出力されておりませんでした。。。) ですので、提案いただいた通りmousePress関数がうまく実行できていないという懸念もありましたのでmouse_press()の最後にmouse_cをprintしたところマウスクリックできちんと変数も1に更新されていました。。。
argparse

2020/12/17 14:24

見当外れのコメントで混乱させてしまい申し訳御座いません。斜め読みしすぎました。
akiakane

2020/12/17 15:36

argparseさん いえ、自分の質問にコメントいただけて大変感謝しております。 自分のコードが汚いばかりにこちらこそ申し訳ないです
guest

回答2

0

ベストアンサー

game_main()が継続して呼ばれていないのが原因だと思います。

Python

1 if tsugi > 0:#tsugiの中に次回表示するブロックが入っていれば 2 cvs.create_image(752, 128, image=img_Block[tsugi], tag="INFO")#そのブロックの表示 3 root.after(100, game_main)#0.1秒後に再びメイン処理実行 4

Python

1 if tsugi > 0:#tsugiの中に次回表示するブロックが入っていれば 2 cvs.create_image(752, 128, image=img_Block[tsugi], tag="INFO")#そのブロックの表示 3 root.after(100, game_main)#0.1秒後に再びメイン処理実行 4

に修正し、常に0.1秒間隔で呼ばれるようにしてください。

投稿2020/12/16 23:46

YT0014

総合スコア1748

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

akiakane

2020/12/17 15:45

動きました。。。 ありがとうございました。。 最後までお付き合いいただき大変助かりました。。
guest

0

回答は出てるようなので、書籍のコードを写す際に役立つ情報

ベースにしたサンプルコードを貼り比較

diff (compare) という差分比較ツールのおすすめです。
専用ソフトをインストールしなくても、ブラウザで使えるものもありますし、
大抵の IDE やエディタには標準の機能としてついてます。(勿論、Visual Studio Code にも)

常に回答となるコードがあるとは限らないので、個人的には
汎用的な解決策として応用が効く、デバッグによる問題解決がおすすめですが、
どうしても見落としがわからない…といった場合、参考にしてみてください。

投稿2020/12/17 05:26

teamikl

総合スコア8715

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

akiakane

2020/12/17 15:47

ありがとうございます! diffのソフトはwinmargeしか使ったことなかったので今後の参考にしていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問