我怎样才能让我的对象检测到它是否被点击?
How can I have my object detect if it's been clicked?
我正在尝试创建一个简单的点击游戏。我想随机创建 "bombs" 并且玩家需要在它爆炸前点击它。现在我真的很努力让我的程序可以注册点击坐标,并确定你是否点击了炸弹或者你是否错过了。有没有人 pointers/hints 继续前进?
这是我目前所知道的。
我的炸弹class:
from graphics import *
import time
class Bomb(object):
def __init__(self, location, radius, window):
self.circle = Circle(location, radius)
self.circle.draw(window)
self.circle.setFill("black")
self.start_time = time.time()
def update(self):
if time.time() - self.start_time > 3.0:
self.circle.setFill("blue")
def ready_to_explode(self):
if time.time() - self.start_time > 3.0:
return True
def is_clicked(self):
#use x y coordinates of click and determine if the distance between this point and center of circle is < or > than radius?
def explode(self):
self.circle.setFill("pink")
def defuse(self):
self.circle.setFill("green")
我的主程序:
from graphics import *
import time
import random
from bomb import Bomb
window = GraphWin("Click-click-BOOM! *", 400, 400)
event_text = Text(Point(100, 100), "events")
event_text.draw(window)
time_text = Text(Point(100, 200), "time info")
time_text.draw(window)
def keyboard_callback(event):
event_text.setText(event.char)
if "q" == event.char:
global quit
quit = True
def click_callback(event):
click_output = "button1 click at "
click_output += "<" + str(event.x) + ", " + str(event.y) + ">"
event_text.setText(click_output)
window.bind_all("<Key>", keyboard_callback)
window.bind_all("<Button-1>", click_callback)
start_time = time.time()
last_time = start_time
quit = False
bombs = []
bomb_to_add = Bomb(Point(random.randint(1, 400), random.randint(1, 400)), 25, window)
bombs.append(bomb_to_add)
frames = 0
while not quit:
for bomb in bombs:
bomb.update()
if bomb.is_clicked():
bomb.defuse()
window.close()
exit()
我相信你有两个问题需要解决。
首先,您正在呼叫 window.bind_all("<Button-1>", click_callback)
。这不是 graphics
方法,而是 Tkinter 方法。 (Tkinter 是 graphics
在幕后使用的 windows 和低级图形库;它是 Python 标准库的一部分。)
这 应该 意味着,每次用户在 window 中的任意位置单击鼠标时,都会调用 click_callback
函数。您可以通过在函数开头添加 print('click_callback got called')
来查看是否 被 调用。
但看起来您在此处 运行设置您自己的手动帧循环:
while not quit:
for bomb in bombs:
bomb.update()
if bomb.is_clicked():
bomb.defuse()
您可以使用 Tkinter 执行此操作,只要您从循环中驱动它的主循环。每一帧,你都想告诉它检查和处理事件一次,而不是永远,我认为它看起来像这样:
while not quit:
window.mainloop(1)
for bomb in bombs:
bomb.update()
if bomb.is_clicked():
bomb.defuse()
但这可能不对,主要文档中也没有提及,因此您可能需要进行一些搜索。
话虽这么说,如果你真的想要一个手动帧循环,你可能真的不想要 Tkinter
和 graphics
,你想要 PyGame
.
但是有一个不同的方法来解决这个问题:只要让 Tkinter 运行 它的事件循环(我认为 graphics
会神奇地为你处理这个,如果你不妨碍它的话),并要求 Tkinter 将您的内容安排到 运行。像这样:
def do_frame():
if quit:
window.quit() # may be wrong...
for bomb in bombs:
bomb.update()
if bomb.is_clicked():
bomb.defuse()
window.after(100, do_frame)
window.after(100, do_frame)
最后一行的意思是“从现在开始尽可能接近 100 毫秒,调用 do_frame
。
然后,在 do_frame
中,您通过循环执行每次执行的所有操作,最后要求 Tkinter 在另一个 100 毫秒后再次调用 do_frame
。
您唯一需要更改的另一件事是,当 quit
为真时,您不能直接退出程序末尾,因此您可能必须显式调用某些 quit
方法,可能在 window
.
这个不能给你非常准确的时间。所以,任何东西,比如说,移动一定数量的 pixels/frame 都会变得更快和更慢。如果你想让它移动一定数量的 pixels/second,你必须检查时间,看看自上次 do_frame
被调用以来已经过了多长时间并调整像素。
但对于 Tkinter 来说它更简单,也更惯用。
一旦你解决了这个问题,无论哪种方式,你的 click_callback
应该开始被调用。
但它在 window 上被调用,而不是圆形或其他图形小部件。
据我所知,graphics
库没有任何命中测试功能(确定鼠标所在的对象)。因此,您必须手动查看您的对象并查看谁应该获得点击,然后手动对该对象调用一些方法。 (此外,如果您想要对象的中心或角点坐标,而不是主要 window,您必须手动转换它们。)
使用漂亮的 sprite 库(同样,就像 PyGame 中的那个)会更容易,但 自己做起来并不难——尤其是如果你不用担心效率。 (在过去,这是一个难题,视频游戏机的速度大约是今天 phone 的 10000 倍。)
我正在尝试创建一个简单的点击游戏。我想随机创建 "bombs" 并且玩家需要在它爆炸前点击它。现在我真的很努力让我的程序可以注册点击坐标,并确定你是否点击了炸弹或者你是否错过了。有没有人 pointers/hints 继续前进?
这是我目前所知道的。
我的炸弹class:
from graphics import *
import time
class Bomb(object):
def __init__(self, location, radius, window):
self.circle = Circle(location, radius)
self.circle.draw(window)
self.circle.setFill("black")
self.start_time = time.time()
def update(self):
if time.time() - self.start_time > 3.0:
self.circle.setFill("blue")
def ready_to_explode(self):
if time.time() - self.start_time > 3.0:
return True
def is_clicked(self):
#use x y coordinates of click and determine if the distance between this point and center of circle is < or > than radius?
def explode(self):
self.circle.setFill("pink")
def defuse(self):
self.circle.setFill("green")
我的主程序:
from graphics import *
import time
import random
from bomb import Bomb
window = GraphWin("Click-click-BOOM! *", 400, 400)
event_text = Text(Point(100, 100), "events")
event_text.draw(window)
time_text = Text(Point(100, 200), "time info")
time_text.draw(window)
def keyboard_callback(event):
event_text.setText(event.char)
if "q" == event.char:
global quit
quit = True
def click_callback(event):
click_output = "button1 click at "
click_output += "<" + str(event.x) + ", " + str(event.y) + ">"
event_text.setText(click_output)
window.bind_all("<Key>", keyboard_callback)
window.bind_all("<Button-1>", click_callback)
start_time = time.time()
last_time = start_time
quit = False
bombs = []
bomb_to_add = Bomb(Point(random.randint(1, 400), random.randint(1, 400)), 25, window)
bombs.append(bomb_to_add)
frames = 0
while not quit:
for bomb in bombs:
bomb.update()
if bomb.is_clicked():
bomb.defuse()
window.close()
exit()
我相信你有两个问题需要解决。
首先,您正在呼叫 window.bind_all("<Button-1>", click_callback)
。这不是 graphics
方法,而是 Tkinter 方法。 (Tkinter 是 graphics
在幕后使用的 windows 和低级图形库;它是 Python 标准库的一部分。)
这 应该 意味着,每次用户在 window 中的任意位置单击鼠标时,都会调用 click_callback
函数。您可以通过在函数开头添加 print('click_callback got called')
来查看是否 被 调用。
但看起来您在此处 运行设置您自己的手动帧循环:
while not quit:
for bomb in bombs:
bomb.update()
if bomb.is_clicked():
bomb.defuse()
您可以使用 Tkinter 执行此操作,只要您从循环中驱动它的主循环。每一帧,你都想告诉它检查和处理事件一次,而不是永远,我认为它看起来像这样:
while not quit:
window.mainloop(1)
for bomb in bombs:
bomb.update()
if bomb.is_clicked():
bomb.defuse()
但这可能不对,主要文档中也没有提及,因此您可能需要进行一些搜索。
话虽这么说,如果你真的想要一个手动帧循环,你可能真的不想要 Tkinter
和 graphics
,你想要 PyGame
.
但是有一个不同的方法来解决这个问题:只要让 Tkinter 运行 它的事件循环(我认为 graphics
会神奇地为你处理这个,如果你不妨碍它的话),并要求 Tkinter 将您的内容安排到 运行。像这样:
def do_frame():
if quit:
window.quit() # may be wrong...
for bomb in bombs:
bomb.update()
if bomb.is_clicked():
bomb.defuse()
window.after(100, do_frame)
window.after(100, do_frame)
最后一行的意思是“从现在开始尽可能接近 100 毫秒,调用 do_frame
。
然后,在 do_frame
中,您通过循环执行每次执行的所有操作,最后要求 Tkinter 在另一个 100 毫秒后再次调用 do_frame
。
您唯一需要更改的另一件事是,当 quit
为真时,您不能直接退出程序末尾,因此您可能必须显式调用某些 quit
方法,可能在 window
.
这个不能给你非常准确的时间。所以,任何东西,比如说,移动一定数量的 pixels/frame 都会变得更快和更慢。如果你想让它移动一定数量的 pixels/second,你必须检查时间,看看自上次 do_frame
被调用以来已经过了多长时间并调整像素。
但对于 Tkinter 来说它更简单,也更惯用。
一旦你解决了这个问题,无论哪种方式,你的 click_callback
应该开始被调用。
但它在 window 上被调用,而不是圆形或其他图形小部件。
据我所知,graphics
库没有任何命中测试功能(确定鼠标所在的对象)。因此,您必须手动查看您的对象并查看谁应该获得点击,然后手动对该对象调用一些方法。 (此外,如果您想要对象的中心或角点坐标,而不是主要 window,您必须手动转换它们。)
使用漂亮的 sprite 库(同样,就像 PyGame 中的那个)会更容易,但 自己做起来并不难——尤其是如果你不用担心效率。 (在过去,这是一个难题,视频游戏机的速度大约是今天 phone 的 10000 倍。)