質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2808閲覧

python tkinter の描画処理に関する疑問

mas_555

総合スコア13

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/06/13 13:48

編集2019/06/13 14:17

前提・実現したいこと

勉強のためpythonとtkinterでブロック崩しゲームを作成しています。
参考にしたサイト
上記リンクを参考にしながら、ひとまず枠の中でボールが跳ね返り続けるコードを書いてみましたが、
不明点があるので教えてください。

【疑問】

下記コードの場合、ボールが壁(ウィンドウの枠)にぶつかった判定の時、ボールの半分が壁に沈み込む(見えなくなる)ような描写になるはずですが、
そうなっていません。(ボールの端が壁にぶつかったときに跳ね返っているように見える)
これはどうしてでしょうか?

python

1 2import tkinter as tk 3ball = {"dirx": 15, 4 "diry": -15, 5 "x": 350, 6 "y": 300, 7 "w": 10} 8 9win = tk.Tk() 10buff = tk.StringVar() 11buff.set("") 12label = tk.Label(win, textvariable = buff) 13label.pack() 14 15cv = tk.Canvas(win, width = 600, height = 400) 16cv.pack() 17 18def draw_ball(): 19 cv.delete("all") 20 cv.create_oval(ball["x"] - ball["w"], ball["y"] + ball["w"], ball["x"] + ball["w"], ball["y"] - ball["w"], fill = "red") 21 22def move_ball(): 23 bx = ball["x"] + ball["dirx"] 24 by = ball["y"] + ball["diry"] 25 26 buff.set("posi x = " + str(bx) + " posi y = " + str(by)) 27 28 if bx <= 0 or bx >= 600: 29 ball["dirx"] *= -1 30 31 if by <= 0 or by >= 400: 32 ball["diry"] *= -1 33 34 if 0 < bx < 600: 35 ball["x"] = bx 36 37 if 0 < by < 400: 38 ball["y"] = by 39 40def gameloop(): 41 draw_ball() 42 move_ball() 43 44 win.after(50, gameloop) 45 46gameloop() 47win.mainloop() 48

【疑問に思った理由】

ボールの描画は、Canvasのcreate_oval()で行っています。create_oval(円の左端, 円の上端, 円の右端, 円の下端)で描画されます。
今回のコードでは、create_ovalにボールの中心位置座標からの相対距離を入力しています。
ボールの中心位置座標を(x,y) , ボールの幅 = 10 として、
create_oval( x-10, y+10, x+10, y-10)で、ボールの中心をx,yとした直径20の円が描画されます。

ボールの移動・跳ね返りは、ボールの中心位置座標(x,y)の値を変更し、(x,y) が ウィンドウの端(0,0) (600,400)の位置に来た時に反射させています。
ということは、ボールの中心がウィンドウの端に来た時、ボールの半径10の部分はウィンドウの外に消えるような描写となっているべきです。
しかし、ボールの端がウィンドウの端とぶつかって反射しているように見えます。
(本当はボールの端がウィンドウの端とぶつかるように見えたほうがリアルで喜ばしいのですが、このコードでそうなるべきではないと考えています)
教えていただけるとうれしいです。

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

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

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

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

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

guest

回答1

0

ベストアンサー

下記コードの場合、ボールが壁(ウィンドウの枠)にぶつかった判定の時、ボールの半分が壁に沈み込む(見えなくなる)ような描写になるはずですが、そうなっていません。(ボールの端が壁にぶつかったときに跳ね返っているように見える)

これはどうしてでしょうか?

xy 方向の移動量 dirx, diry が15でボールの半径が10なので、多くの場合はボールがはみ出す位置にくる前に if bx <= 0 or bx >= 600 または if by <= 0 or by >= 400 の条件判定で移動方向が反転しているためです。
ただ必ずこの条件ではみ出さないかというと、はみ出してしまうことがあります。
実際、よく見てみると、左側の壁にぶつかって反転する際にはみ出しています。

イメージ説明

以下、コードに確認しやすいようにいくつか変更を加えましたので、動かしてみるとわかると思います。

    1. キャンバスの背景色を水色に変更

→ どこまでがキャンバスかわかりづらいため

    1. move_ball() と draw_ball() の順番を逆にした。

→ 移動した今の位置にボールを描画したほうがわかりやすいため

    1. 反転するタイミングでの円の4点の座標を出力し、3秒間止まるようにした。

3の反転するタイミングが壁に一番近づいたタイミングであり、このときの4点の座標がはみ出していなければ、円がキャンバスからはみ出していないことになります。

python

1import tkinter as tk 2import time 3 4ball = {"dirx": 15, 5 "diry": -15, 6 "x": 350, 7 "y": 300, 8 "w": 10} 9 10win = tk.Tk() 11buff = tk.StringVar() 12buff.set("") 13label = tk.Label(win, textvariable = buff) 14label.pack() 15 16cv = tk.Canvas(win, width = 600, height = 400) 17cv.pack() 18cv.config(background="lightblue") 19 20def draw_ball(): 21 cv.delete("all") 22 cv.create_oval(ball["x"] - ball["w"], ball["y"] + ball["w"], ball["x"] + ball["w"], ball["y"] - ball["w"], fill = "red") 23 24def move_ball(): 25 bx = ball["x"] + ball["dirx"] 26 by = ball["y"] + ball["diry"] 27 28 buff.set("posi x = " + str(bx) + " posi y = " + str(by)) 29 30 if bx <= 0 or bx >= 600: 31 ball["dirx"] *= -1 32 33 if by <= 0 or by >= 400: 34 ball["diry"] *= -1 35 36 if not (0 < bx < 600 and 0 < by < 400): 37 # デバッグ用 38 time.sleep(3) 39 print(ball["x"] - ball["w"], ball["y"] + ball["w"], ball["x"] + ball["w"], ball["y"] - ball["w"]) 40 41 if 0 < bx < 600: 42 ball["x"] = bx 43 44 if 0 < by < 400: 45 ball["y"] = by 46 47def gameloop(): 48 move_ball() 49 draw_ball() 50 51 win.after(50, gameloop) 52 53gameloop() 54win.mainloop() 55

投稿2019/06/13 15:23

編集2019/06/13 15:25
tiitoi

総合スコア21956

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

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

mas_555

2019/06/15 14:48

条件式とボールの幅と移動の刻み幅によるものということですね。 コードの変更した内容も非常に勉強になりました。 わかりやすく回答いただきありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問