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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

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

Python

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

Q&A

解決済

1回答

1913閲覧

インスタンス変数の正しい参照の仕方がわからない

mas_555

総合スコア13

Python 3.x

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

Python

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

0グッド

0クリップ

投稿2019/06/19 15:32

実現したいこと

勉強のためpythonとtkinterでブロック崩しゲームを作成しています。
第一ステップとして、枠の中でボールが跳ね返り続けるプログラムを作成しています。
クラス定義を使ったプログラムにチャレンジしようとしています。
不明点があるので教えてください。

問題点

ボールが動かない

下記コードだと、ボールが動きません。

python

1import tkinter as tk 2 3win_width = 600 4win_height = 400 5 6root = tk.Tk() 7root.title("ブロック崩し") 8cv = tk.Canvas(root, width = win_width, height = win_height) 9cv.pack() 10 11class Ball: 12 def __init__(self, x = 250, y = 250, w = 10, dx = 5, dy = 5, color = "red"): 13 self.x = x # ボールの中心座標x,y 14 self.y = y 15 self.w = 10 # ボールの幅 16 self.dx = dx # ボールの移動量dx,dy 17 self.dy = dy 18 self.color = color 19 self.x1 = self.x - self.w # ボールの左端 20 self.x2 = self.x + self.w # ボールの上端 21 self.y1 = self.y + self.w # ボールの右端 22 self.y2 = self.y - self.w # ボールの下端 23 24 25 26 def draw(self): 27 cv.delete("ball") 28 cv.create_oval(self.x1, self.y1, self.x2, self.y2, fill = self.color, tag = "ball") # ボールを描画 29 30 def move(self): 31 if self.x1 < 0 or self.x2 > win_width: # もしボールの左右端が枠にぶつかったら移動方向を反転 32 self.dx *= -1 33 if self.y1 < 0 or self.y2 > win_height: # もしボールの上下端が枠にぶつかったら移動方向を反転 34 self.dy *= -1 35 36 self.x += self.dx #移動 37 self.y += self.dy #移動 38 39 40 41ball = Ball() 42 43def gameloop(): 44 ball.move() 45 ball.draw() 46 root.after(50, gameloop) 47 48gameloop() 49root.mainloop() 50

ボールが動く

ボールの上下左右端の変数設定コードを、ボールを動かすメソッドmove()
内に書いただけで動きます。

python

1import tkinter as tk 2 3win_width = 600 4win_height = 400 5 6root = tk.Tk() 7root.title("ブロック崩し") 8cv = tk.Canvas(root, width = win_width, height = win_height) 9cv.pack() 10 11class Ball: 12 def __init__(self, x = 250, y = 250, w = 10, dx = 5, dy = 5, color = "red"): 13 self.x = x # ボールの中心座標x,y 14 self.y = y 15 self.w = 10 # ボールの幅 16 self.dx = dx # ボールの移動量dx,dy 17 self.dy = dy 18 self.color = color 19 # self.x1 = self.x - self.w # 20 # self.x2 = self.x + self.w # 21 # self.y1 = self.y + self.w # 22 # self.y2 = self.y - self.w # move()内に移動 23 24 25 26 def draw(self): 27 cv.delete("ball") 28 cv.create_oval(self.x1, self.y1, self.x2, self.y2, fill = self.color, tag = "ball") # ボールを描画 29 30 def move(self): 31 32 self.x1 = self.x - self.w # ボールの左端 33 self.x2 = self.x + self.w # ボールの上端 34 self.y1 = self.y + self.w # ボールの右端 35 self.y2 = self.y - self.w # ボールの下端 36 37 38 39 if self.x1 < 0 or self.x2 > win_width: # もしボールの左右端が枠にぶつかったら移動方向を反転 40 self.dx *= -1 41 if self.y1 < 0 or self.y2 > win_height: # もしボールの上下端が枠にぶつかったら移動方向を反転 42 self.dy *= -1 43 44 self.x += self.dx #移動 45 self.y += self.dy #移動 46 47 48 49ball = Ball() 50 51def gameloop(): 52 ball.move() 53 ball.draw() 54 root.after(50, gameloop) 55 56gameloop() 57root.mainloop() 58

なぜ前者は動かないの

前者のプログラムは、メソッド move()内で、
self.x += self.dx #移動
self.y += self.dy #移動
の部分によって、ボールの中心位置座標が書き換えられています。
それによってself.x1~self.y2も、self.x, self.y を使って計算していますので、値が変わっているはずではないかと考えています。
ボールが動かないのはなぜでしょうか?
最近クラスについて勉強しだしたので、あまりよくわかっていません。
教えていただけると嬉しいです。
よろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

それによってself.x1~self.y2も、self.x, self.y を使って計算していますので、値が変わっているはずではないかと考えています。

ここの認識が間違っています。前者のコードで計算しているのは __init__ の中であり、これは Ball クラスのインスタンスが生成されたときにした呼び出されません。
実行時の計算順としてより正確には、 x1,y1,x2,y2 は描画するために必要な値となるはずなので、

python

1 def draw(self): 2 3 self.x1 = self.x - self.w # ボールの左端 4 self.x2 = self.x + self.w # ボールの上端 5 self.y1 = self.y + self.w # ボールの右端 6 self.y2 = self.y - self.w # ボールの下端 7 8 cv.delete("ball") 9 cv.create_oval(self.x1, self.y1, self.x2, self.y2, fill = self.color, tag = "ball") # ボールを描画 10 11 12 def move(self): 13 if self.x1 < 0 or self.x2 > win_width: # もしボールの左右端が枠にぶつかったら移動方向を反転 14 self.dx *= -1 15 if self.y1 < 0 or self.y2 > win_height: # もしボールの上下端が枠にぶつかったら移動方向を反転 16 self.dy *= -1 17 18 self.x += self.dx #移動 19 self.y += self.dy #移動

であるべきかと思います。
ここまでくると実は self.x1 のようにインスタンス変数にする必要がなく、描画時のローカル変数で十分であることもわかります。

投稿2019/06/19 17:11

mather

総合スコア6753

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

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

mas_555

2019/06/20 14:21 編集

__init__ は、ball = Ball()した時(インスタンス生成したとき)一度しか処理されないから、ball.draw() も ball.move() もインスタンス生成時にself.x1に代入された値260(x初期値:250 + w定数:10) を参照し続けているということですね。  self.xがmove()によっていくら更新されようと、self.x1にはインスタンス生成時の処理時に計算された値が残りつづけているということですね。(すみません、ご回答と同じような意味の文章を繰り返している気がしますが、自分の理解の確認のために書いています。) 「ここまでくると実は self.x1 のようにインスタンス変数にする必要がなく、描画時のローカル変数で十分であることもわかります。」 ボールの上下左右端をローカル変数で記述する場合は、各メソッドごとに毎度変数を作成するような感じでしょうか?(変数を使わないで計算式べた書き(self.x - self.w)だと、書き間違いミスが増えそうです) 続けて質問恐れ入りますが、ご教示いただけると嬉しいです。
mather

2019/06/20 16:15 編集

いえ、 self.x1 = self.x - self.w # ボールの左端 ではなく、 x1 = self.x - self.w # ボールの左端 でいいということです。 draw関数の中でしか使いませんよね?
mas_555

2019/06/23 07:27

ボールの上下左右端の値は、draw以外でも、moveでウィンドウの枠との衝突判定にも使用しています。 また、今後はブロックや操作パドルとの衝突判定にも使用するつもりであります。 関数内でx1 = self.x - self.wとする場合は、他の関数でもボール上下左右端の値を使用する場合に、同じ式を書く必要があると考えました。 ご指摘いただいた通り、クラスを使う目的をはっきりさせないまま使おうとしていたかもしれません。 クラスについて、もっと勉強しようと思います。 ご丁寧に教えてくださりありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問