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

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

詳細はこちら
Python 3.x

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

Tkinter

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

Python

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

Q&A

解決済

1回答

1427閲覧

tkinterにおける跳ね返り処理

tkr1205

総合スコア13

Python 3.x

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

Tkinter

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

Python

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

0グッド

0クリップ

投稿2019/11/29 09:11

前提・実現したいこと

Pythonを使ってBOIDモデルを実装しています.

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

Python標準のtkinterを使っているのですが,birdが複数いる時の跳ね返りの処理が上手く動作せず,右下の角に収束してしまいます.

該当のソースコード

Python

1import math 2import random 3from statistics import mean 4from tkinter import * 5 6 7class Coordinate(): 8 def __init__(self): 9 self.x = 0 10 self.y = 0 11 12 def reset(self): 13 self.x = 0 14 self.y = 0 15 16 17class Bird(): 18 """ 19 Bird Class 20 """ 21 def __init__(self, x, y, vx, vy, identifier, field_info, others, view): 22 """Bird Class Constructor 23 24 BOID用のエージェントです 25 :param x: x coordinate 26 :param y: y coordinate 27 :param vx: x方向の移動量 28 :param vy: y方向の移動量 29 :param id: 識別用id 30 :param field_info: (field_width, field_height, fish_radius, fish_speed) 31 :param others: other fish 32 :param view: 視野の広さ(半径) 33 """ 34 self.x = x 35 self.y = y 36 self.vx = vx 37 self.vy = vy 38 self.identifier = identifier 39 self.others = others 40 self.view = view 41 self.field_info = field_info 42 self.inverse_x = self.field_info[0] - self.x 43 self.inverse_y = self.field_info[1] - self.y 44 self.r1 = 2.0 # cohesion coefficient 45 self.r2 = 0.5 # separation coefficient 46 self.r3 = 1.0 # alignment coefficient 47 self.neighbors = [] # 近くにいるエージェント 48 49 self.v1 = Coordinate() 50 self.v2 = Coordinate() 51 self.v3 = Coordinate() 52 53 def set_others(self, others): 54 self.others = others 55 self.find_neighbors() 56 57 # 結合 58 def cohesion(self): 59 center_pull_coeff = 10 60 if len(self.neighbors) == 0: 61 return 62 self.v1.x = mean([agent.x for agent in self.neighbors if not agent.identifier == self.identifier]) 63 self.v1.y = mean([agent.y for agent in self.neighbors if not agent.identifier == self.identifier]) 64 self.v1.x = (self.v1.x - self.x) / center_pull_coeff 65 self.v1.y = (self.v1.y - self.y) / center_pull_coeff 66 67 # 離脱 68 def separation(self): 69 personal_space = 50 70 for agent in self.neighbors: 71 if self.identifier == agent.identifier: 72 continue 73 74 dist = self.calc_distance(agent, self) 75 if dist <= personal_space: 76 self.v2.x -= (agent.x - self.x) / dist 77 self.v2.y -= (agent.y - self.y) / dist 78 79 def _separation(self): 80 if len(self.neighbors) == 0: 81 return 82 self.v2.x = mean([agent.x for agent in self.neighbors if not agent.identifier == self.identifier]) - self.x 83 self.v2.y = mean([agent.y for agent in self.neighbors if not agent.identifier == self.identifier]) - self.y 84 85 # 距離の計算 86 def calc_distance(self, a, b): 87 return min(math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2), 88 math.sqrt((a.inverse_x - b.x) ** 2 + (a.inverse_y - b.y) ** 2)) 89 90 # 整列 91 def alignment(self): 92 if len(self.neighbors) == 0: 93 return 94 self.v3.x = mean([agent.x for agent in self.neighbors if not self.identifier == agent.identifier]) 95 self.v3.y = mean([agent.y for agent in self.neighbors if not self.identifier == agent.identifier]) 96 self.v3.x = (self.v3.x - self.vx) / 2 97 self.v3.y = (self.v3.y - self.vy) / 2 98 99 def _collision_detection(self): 100 if self.x - self.field_info[2] <= 0: 101 self.vx *= -1 102 self.x = self.field_info[2] 103 if self.x + self.field_info[2] >= self.field_info[0]: 104 self.vx *= -1 105 self.x = self.field_info[0] - self.field_info[2] 106 107 if self.y - self.field_info[2] <= 0: 108 self.vy *= -1 109 self.y = self.field_info[2] 110 if self.y + self.field_info[2] >= self.field_info[1]: 111 self.vy *= -1 112 self.y = self.field_info[1] - self.field_info[2] 113 114 def step(self): 115 self.cohesion() 116 self.separation() 117 self.alignment() 118 119 def find_neighbors(self): 120 self.neighbors = [neighbor for neighbor in self.others 121 if self.calc_distance(self, neighbor) <= self.view and 122 not self.identifier == neighbor.identifier] 123 124 def update(self): 125 self.vx += self.r1 * self.v1.x + self.r2 * self.v2.x + self.r3 * self.v3.x 126 self.vy += self.r1 * self.v1.y + self.r2 * self.v2.y + self.r3 * self.v3.y 127 128 distance = math.sqrt(self.vx ** 2 + self.vy ** 2) 129 if distance > self.field_info[3]: 130 self.vx = (self.vx / distance) * self.field_info[3] 131 self.vy = (self.vy / distance) * self.field_info[3] 132 133 self.x += int(self.vx) 134 self.y += int(self.vy) 135 136 self._collision_detection() 137 print(self.vx, self.vy) 138 139 self.inverse_x = self.field_info[0] - self.x 140 self.inverse_y = self.field_info[1] - self.y 141 142 def clear_movement(self): 143 self.v1.reset() 144 self.v2.reset() 145 self.v3.reset() 146 147 def draw(self): 148 self.clear_movement() 149 self.step() 150 self.update() 151 return self.x, self.y, self.vx, self.vy 152 153root = Tk() 154 155WIDTH = 1000 156HEIGHT = 1000 157 158fish_radian = 10 159bird_num = 3 160SPEED = 5 161VIEW = 50 162 163c = Canvas(root, width=WIDTH, height=HEIGHT) 164c.pack() 165 166 167fish_list = [Bird(random.randint(0, WIDTH), random.randint(0, HEIGHT), random.randint(-SPEED, SPEED), 168 random.randint(-SPEED, SPEED), i, (WIDTH, HEIGHT, fish_radian, SPEED), [], VIEW) for i in range(bird_num)] 169[fish.set_others(fish_list) for fish in fish_list] 170 171 172def animate(): 173 c.delete("all") 174 for fish in fish_list: 175 fish.draw() 176 c.create_oval(fish.x - fish_radian, fish.y - fish_radian, fish.x+fish_radian, fish.y+fish_radian) 177 c.create_line(fish.x, fish.y, fish.x + fish.vx * 3, fish.y + fish.vy * 3) 178 [fish.set_others(fish_list) for fish in fish_list] 179 root.after(20, animate) 180 181animate() 182root.mainloop() 183 184

試したこと

条件式を色々変えて試しました.
ほぼ全ての条件で同様にウィンドウの右下に収束してしまいました.

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

MacOS Catalina
Python 3.6.8

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

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

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

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

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

guest

回答1

0

ベストアンサー

python

1 self.vx += self.r1 * self.v1.x + self.r2 * self.v2.x + self.r3 * self.v3.x 2 self.vy += self.r1 * self.v1.y + self.r2 * self.v2.y + self.r3 * self.v3.y

self.vx, self.vy がどんどん変化して、どんどん早くなっていくと思うのですが、意図した動作ですか?

ソースの理解を兼ねてリファクタリングしてみました。
問題そうな部分も修正してみました。

python

1import random 2from statistics import mean 3from tkinter import * 4 5 6class Field: 7 WIDTH = 300 8 HEIGHT = 200 9 10 11class Coordinate(): 12 13 def __init__(self): 14 self.reset() 15 16 def reset(self): 17 self.x = 0 18 self.y = 0 19 20 21class Bird(): 22 NUM = 3 23 RADIAN = 10 24 SPEED = 5 25 VIEW = 50 26 27 @staticmethod 28 def setup(): 29 Bird.birds = [Bird() for _ in range(Bird.NUM)] 30 31 def __init__(self): 32 self.x = random.randint(0, Field.WIDTH) 33 self.y = random.randint(0, Field.HEIGHT) 34 self.vx = random.randint(-self.SPEED, self.SPEED) 35 self.vy = random.randint(-self.SPEED, self.SPEED) 36 self.view = self.VIEW 37 self.inverse_x = Field.WIDTH - self.x 38 self.inverse_y = Field.HEIGHT - self.y 39 self.r1 = 2.0 # cohesion coefficient 40 self.r2 = 0.5 # separation coefficient 41 self.r3 = 1.0 # alignment coefficient 42 43 self.v1 = Coordinate() 44 self.v2 = Coordinate() 45 self.v3 = Coordinate() 46 47 def neighbors(self): 48 return [agent for agent in self.birds if agent != self] 49 50 # 結合 51 def cohesion(self): 52 neighbors = self.neighbors() 53 if not neighbors: 54 return 55 center_pull_coeff = 10 56 self.v1.x = mean([agent.x for agent in neighbors]) 57 self.v1.y = mean([agent.y for agent in neighbors]) 58 self.v1.x = (self.v1.x - self.x) / center_pull_coeff 59 self.v1.y = (self.v1.y - self.y) / center_pull_coeff 60 61 # 離脱 62 def separation(self): 63 personal_space = 50 64 for agent in self.neighbors(): 65 dist = self.calc_distance(agent) 66 if dist and dist <= personal_space: 67 self.v2.x -= (agent.x - self.x) / dist 68 self.v2.y -= (agent.y - self.y) / dist 69 70 def _separation(self): 71 neighbors = self.neighbors() 72 if not neighbors: 73 return 74 self.v2.x = mean([agent.x for agent in neighbords]) - self.x 75 self.v2.y = mean([agent.y for agent in neighbords]) - self.y 76 77 # 距離の計算 78 def calc_distance(self, target): 79 return ((target.x - self.x) ** 2 + (target.y - self.y) ** 2) ** 0.5 80 81 # 整列 82 def alignment(self): 83 neighbors = self.neighbors() 84 if not neighbors: 85 return 86 self.v3.x = mean([agent.x for agent in neighbors]) 87 self.v3.y = mean([agent.y for agent in neighbors]) 88 self.v3.x = (self.v3.x - self.vx) / 2 89 self.v3.y = (self.v3.y - self.vy) / 2 90 91 def _collision_detection(self): 92 if self.x - self.RADIAN < 0: 93 self.vx *= -1 94 self.x = self.RADIAN 95 if self.x + self.RADIAN > Field.WIDTH: 96 self.vx *= -1 97 self.x = Field.WIDTH - self.RADIAN 98 99 if self.y - self.RADIAN < 0: 100 self.vy *= -1 101 self.y = self.RADIAN 102 if self.y + self.RADIAN > Field.HEIGHT: 103 self.vy *= -1 104 self.y = Field.HEIGHT - self.RADIAN 105 106 def step(self): 107 self.cohesion() 108 self.separation() 109 self.alignment() 110 111 def update(self): 112 dx = self.r1 * self.v1.x + self.r2 * self.v2.x + self.r3 * self.v3.x 113 dy = self.r1 * self.v1.y + self.r2 * self.v2.y + self.r3 * self.v3.y 114 self.vx += dx if self.vx >= 0 else -dx 115 self.vy += dy if self.vy >= 0 else -dy 116 117 distance = (self.vx ** 2 + self.vy ** 2) ** 0.5 118 if distance > self.SPEED: 119 self.vx = (self.vx / distance) * self.SPEED 120 self.vy = (self.vy / distance) * self.SPEED 121 122 self.x += int(self.vx) 123 self.y += int(self.vy) 124 125 self._collision_detection() 126 print(self.vx, self.vy) 127 128 self.inverse_x = Field.WIDTH - self.x 129 self.inverse_y = Field.HEIGHT - self.y 130 131 def clear_movement(self): 132 self.v1.reset() 133 self.v2.reset() 134 self.v3.reset() 135 136 def draw(self, drawer): 137 self.clear_movement() 138 self.step() 139 self.update() 140 drawer.create_oval(self.x - self.RADIAN, self.y - self.RADIAN, self.x+self.RADIAN, self.y+self.RADIAN) 141 drawer.create_line(self.x, self.y, self.x + self.vx * 3, self.y + self.vy * 3) 142 143def main(): 144 Bird.setup() 145 146 root = Tk() 147 148 canvas = Canvas(root, width=Field.WIDTH, height=Field.HEIGHT) 149 canvas.pack() 150 151 def animate(): 152 canvas.delete("all") 153 for bird in Bird.birds: 154 bird.draw(canvas) 155 root.after(20, animate) 156 157 animate() 158 root.mainloop() 159 160if __name__ == '__main__': 161 main()

投稿2019/12/01 11:59

編集2019/12/01 13:32
shiracamus

総合スコア5406

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

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

tkr1205

2019/12/01 12:47

shiracamusさん,ありがとうございます. https://qiita.com/takashi/items/9d684a6e3742a15e8726 こちらの記事を参考にして書いたコードなのですが,self.vx, self.vyに関しては,その下にあります条件式で,一定の値を超えた場合は調整するようにしております.
shiracamus

2019/12/01 13:18

それでピンときましたが、 dx = self.r1 * self.v1.x + self.r2 * self.v2.x + self.r3 * self.v3.x dy = self.r1 * self.v1.y + self.r2 * self.v2.y + self.r3 * self.v3.y self.vx += dx if self.vx >= 0 else -dx self.vy += dy if self.vy >= 0 else -dy でいかがでしょう? 回答欄のソースコードも修正してみました。
tkr1205

2019/12/01 13:37

上手く壁で跳ね返ってくれるようになりました. ありがとうございます! まだ,BOIDとしての動きには問題があるので修正しようと思います.ありがとうございます.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問