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

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

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

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

Python 3.x

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

Tkinter

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

Python

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

Q&A

解決済

1回答

1478閲覧

【Python】画像をマウスで追尾させる(ゲームプログラム)

SkeletonFarts

総合スコア3

canvas

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

Python 3.x

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

Tkinter

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

Python

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

0グッド

0クリップ

投稿2020/12/08 01:46

前提・実現したいこと

Pythonのtkinterを用い、インベーダーゲームをベースに勉強がてら改良をしています。

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

現在の動作では"<Button1-Motion>"のイベントシーケンス部分にて、マウスをクリックしたままで自機をx軸で移動する動作ですが、
マウスを動かすだけでx座標を動かす動作にしたいです。

def bind(self): #canvas内で動作させる canvas.tag_bind(self.id, "<Button1-Motion>", self.dragged) #自機の移動

全体のソースコード

Python

1import tkinter as tk 2from tkinter import messagebox 3import random 4from PIL import Image, ImageTk 5import os 6 7WINDOW_HEIGHT = 600 # ウィンドウの高さ 8WINDOW_WIDTH = 600 # ウィンドウの幅 9 10CANNON_Y = 550 # 自機のy座標 11 12ENEMY_SPACE_X = 100 # 敵の間隔(x座標) 13ENEMY_SPACE_Y = 60 # 敵の間隔(y座標) 14ENEMY_MOVE_SPACE_X = 2 # 敵の移動間隔(x座標) 15ENEMY_MOVE_SPEED = 15 # 敵の移動スピード(2000 ms) 16NUMBER_OF_ENEMY = 2 # 敵の数 17ENEMY_SHOOT_INTERVAL = 10 # 敵がランダムに弾を打ってくる間隔 18 19COLLISION_DETECTION = 300 # 当たり判定 20 21BULLET_HEIGHT = 10 # 弾の縦幅 22BULLET_WIDTH = 2 # 弾の横幅 23BULLET_SPEED = 50 # 弾のスピード(100 ms) 24 25TEXT_GOOD_SIZE = 10 # goodのサイズ 26TEXT_CONGRATULATIONS_SIZE = 50 # congratularionsのサイズ 27TEXT_GAMECLEAR_SIZE = 60 # gameclearのサイズ 28TEXT_GAMEOVER_SIZE = 90 # gameoverのサイズ 29 30 31class Cannon: # 自機 32 33 def __init__(self, x, y=CANNON_Y): #x座標は中心に固定, y座標は"CANNON_Y"で変更 34 self.x = x 35 self.y = y 36 self.draw() 37 self.bind() 38 39 def draw(self): #自機の画像を配置(完了) 40 self.id = canvas.create_image( 41 self.x, self.y, image=cannon_tkimg, tag="cannon") 42 43 def bind(self): #canvas内で動作させる 44 canvas.bind("<space>", self.pressed) 45 canvas.tag_bind(self.id, "<Button1-Motion>", self.dragged) #自機の移動 46 47 def pressed(self, event=None): #弾 48 mybullet = MyBullet(event.x, self.y) 49 mybullet.draw() 50 mybullet.shoot() 51 52 def dragged(self, event): 53 dx = event.x - self.x 54 self.x, self.y = canvas.coords(self.id) 55 canvas.coords(self.id, self.x+dx, self.y) 56 self.x = event.x 57 58 def destroy(self): 59 canvas.delete(self.id) 60 61 62class MyBullet: # 自分の弾 63 64 def __init__(self, x, y): 65 self.x = x 66 self.y = y 67 68 def draw(self): #弾の描写 69 self.id = canvas.create_rectangle( 70 self.x-BULLET_WIDTH, self.y+BULLET_HEIGHT, self.x+BULLET_WIDTH, self.y-BULLET_HEIGHT, fill="blue") #fill関数で弾の色を変更する 71 72 def shoot(self): 73 if self.y >= 0: 74 canvas.move(self.id, 0, -BULLET_HEIGHT) 75 self.y -= BULLET_HEIGHT 76 self.defeat() 77 root.after(BULLET_SPEED, self.shoot) 78 79 def defeat(self): 80 for enemy in enemies: 81 if ((self.x-enemy.x)**2+(self.y-enemy.y)**2) < COLLISION_DETECTION: 82 enemy.exist = False 83 enemy.destroy() 84 canvas.create_text(enemy.x, enemy.y, text="good!", fill="cyan", font=( 85 "System", TEXT_GOOD_SIZE), tag="good") 86 87 def destroy(self): 88 canvas.delete(self.id) 89 90 91class Enemy: # 敵 92 93 def __init__(self, x, y): 94 self.x = x % WINDOW_WIDTH 95 self.y = y+x//WINDOW_WIDTH*ENEMY_SPACE_Y 96 self.exist = True 97 self.draw() 98 self.move() 99 100 def draw(self): 101 self.id = canvas.create_image( 102 self.x, self.y, image=crab_tkimg, tag="enemy") 103 104 def enemy_shoot(self): 105 if self.exist: 106 enemybullet = EnemyBullet(self.x, self.y) 107 enemybullet.draw() 108 enemybullet.shoot() 109 110 def move(self): 111 if self.exist: 112 if self.x > WINDOW_WIDTH: 113 self.x -= ENEMY_MOVE_SPACE_X 114 self.y += ENEMY_SPACE_Y 115 elif self.x < 0: 116 self.x += ENEMY_MOVE_SPACE_X 117 self.y += ENEMY_SPACE_Y 118 if self.y % (ENEMY_SPACE_Y*2) == ENEMY_SPACE_Y: 119 self.x += ENEMY_MOVE_SPACE_X 120 else: 121 self.x -= ENEMY_MOVE_SPACE_X 122 canvas.coords(self.id, self.x, self.y) 123 root.after(ENEMY_MOVE_SPEED, self.move) 124 125 def destroy(self): 126 canvas.delete(self.id) 127 128 129class EnemyBullet: # 敵の弾 130 131 def __init__(self, x, y): 132 self.x = x 133 self.y = y 134 135 def draw(self): 136 self.id = canvas.create_rectangle( 137 self.x-BULLET_WIDTH, self.y+BULLET_HEIGHT, self.x+BULLET_WIDTH, self.y-BULLET_HEIGHT, fill="red") 138 139 def shoot(self): 140 if self.y <= WINDOW_HEIGHT: 141 canvas.move(self.id, 0, BULLET_HEIGHT) 142 self.y += BULLET_HEIGHT 143 self.collision() 144 root.after(BULLET_SPEED, self.shoot) 145 146 def collision(self): 147 if ((self.x-cannon.x)**2+(self.y-cannon.y)**2) < COLLISION_DETECTION: 148 gameover() 149 150 def destroy(self): 151 canvas.delete(self.id) 152 153 154def gameclear(): # ゲームクリア判定 155 winflag = 0 156 for enemy in enemies: 157 if enemy.exist == False: 158 winflag += 1 159 if winflag == NUMBER_OF_ENEMY: 160 canvas.delete("good") 161 canvas.create_text(WINDOW_WIDTH//2, WINDOW_HEIGHT//2-80, text="Congratulations!", 162 fill="lime", font=("System", TEXT_CONGRATULATIONS_SIZE)) 163 canvas.create_text(WINDOW_WIDTH//2, WINDOW_HEIGHT//2+20, text="GAME CLEAR!", 164 fill="lime", font=("System", TEXT_GAMECLEAR_SIZE)) 165 root.after(1000, gameclear) 166 167 168def gameover(): # ゲームオーバー判定 169 canvas.delete("cannon", "good") 170 canvas.create_text(WINDOW_WIDTH//2, WINDOW_HEIGHT//2, text="GAME OVER", 171 fill="red", font=("System", TEXT_GAMEOVER_SIZE)) 172 173 174def enemy_randomshoot(): # ランダムに敵の弾が発射 175 enemy = random.choice(enemies) 176 enemy.enemy_shoot() 177 root.after(ENEMY_SHOOT_INTERVAL, enemy_randomshoot) 178 179def comming_soon(): 180 messagebox.showinfo("準備中です!", "只今リセットボタン実装に向けて準備中です!") 181 182if __name__ == "__main__": 183 # 初期描画 184 root = tk.Tk() 185 root.title("invader") 186 canvas = tk.Canvas(root, width=WINDOW_WIDTH, height=WINDOW_HEIGHT, bg="black") 187 canvas.focus_set() 188 canvas.pack() 189 190 #canvasの指定座標に文字列を表示する 191 canvas.create_text(540, 15, text="インベーダーゲーム Ver0.1", fill="red") 192 193 # 画像の読み込み 194 cannon_img = Image.open("data/cannon.jpeg") 195 cannon_tkimg = ImageTk.PhotoImage(cannon_img) 196 crab_img = Image.open("data/crab.jpeg") 197 crab_tkimg = ImageTk.PhotoImage(crab_img) 198 199 # メニューバー 200 menubar = tk.Menu(root) 201 root.configure(menu=menubar) 202 menubar.add_command(label="QUIT", underline=0, command=root.quit) #QUITメニューの配置 203 menubar.add_command(label="RESET", command=comming_soon) #RESETメニューの配置 204 205 # インスタンス生成 206 cannon = Cannon(WINDOW_WIDTH//2, CANNON_Y) 207 enemies = [] 208 for i in range(NUMBER_OF_ENEMY): 209 enemy_i = Enemy(i*ENEMY_SPACE_X+50, ENEMY_SPACE_Y) 210 enemies.append(enemy_i) 211 212 enemy_randomshoot() 213 214 gameclear() 215 216 root.mainloop()

ご迷惑をおかけしますが、宜しくお願い致します。

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

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

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

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

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

guest

回答1

0

ベストアンサー

クリックなしのイベントは<Motion> を用います。

イベントを補足する対象が「画像」なのか「キャンバス」で若干変わりますが、
操作性を考えるとキャンバスが良さそうなので後者かな

python

1canvas.tag_bind(self.id, "<Motion>", self.dragged) 2canvas.bind("<Motion>", self.dragged)

追記: ゲームオーバー時にエラーが出るので、エラーに対応するには
gameover() でタグを消去した後は、self.id を利用しないようにする対応が別途必要です。
若しくは、自機の移動をゲーム中のみにする等。
(開始時に bind して、gameover 時に dragged イベントハンドラを unbind で解除)

投稿2020/12/08 05:56

編集2020/12/08 06:03
teamikl

総合スコア8664

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問