如何在Python Kivy中实现对象碰撞?
我想为安卓手机和平板电脑制作一个Luckman风格的吃豆人游戏。但是我在处理迷宫的碰撞检测时遇到了问题。
我试着参考这些文件 https://drive.google.com/file/d/1pwCDpU1UPzB7V7TV7-WmbyDLkpIDODry/view。但是因为我的迷宫不是作为图片存在,而是作为在.kv文件中绘制的对象,所以没有成功。
这是我的程序代码:
main.pyfrom kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.widget import Widget
from kivy.uix.anchorlayout import AnchorLayout
from player import *
from kivy.clock import Clock
from kivy.core.window import Window
class GamePlay(Screen):
width = Window.width
height = Window.height
pacman = Player()
def __init__(self, **kwargs):
super(GamePlay,self).__init__(**kwargs)
def on_touch_down(self, touch):
self.start_pos = touch.pos
return True
def on_touch_move(self, touch):
# Calculate swipe distance and direction
swipe_distance_x = abs(touch.pos[0] - self.start_pos[0])
swipe_distance_y = abs(touch.pos[1] - self.start_pos[1])
swipe_threshold = 50 # Adjust threshold based on your needs
# Detect swipe directions
if swipe_distance_y > swipe_threshold:
if touch.pos[1] > self.start_pos[1]:
self.pacman.velocity = (0, 1)
self.pacman.number_update_pac_img = "up"
else:
self.pacman.velocity = (0, -1)
self.pacman.number_update_pac_img = "down"
elif swipe_distance_x > swipe_threshold:
if touch.pos[0] > self.start_pos[0]:
self.pacman.velocity = (1, 0)
self.pacman.number_update_pac_img = "right"
else:
self.pacman.velocity = (-1, 0)
self.pacman.number_update_pac_img = "left"
return True
def update(self, dt):
self.pacman.move()
class LuckmanApp(App):
def build(self):
game = GamePlay()
Clock.schedule_interval(game.update, 1.0/60.0)
return game
if __name__ == '__main__':
LuckmanApp().run()
luckman.kv
<Player>:
Image:
id: "pacman"
source: root.pac_img
allow_stretch: True
pos: root.pos
size: self.parent.width / 18, self.parent.height / 18
<GamePlay>:
canvas:
Rectangle:
source: "png/bg.png"
size: self.size
pacman: pacman_player
FloatLayout:
# Верхний вход
canvas:
Color:
rgb: [0, 0, 1]
# Верхний вход - правая стенка
Rectangle:
pos: self.width / 1.79, self.height
size: self.width / -43.2, self.height / -6
# Верхний вход - левая стенка
Rectangle:
pos: self.width / 2.274, self.height
size: self.width / 43.2, self.height / -6
# Верхний левый квадрат стенок
Rectangle:
pos: self.width / 2.274, self.height / 1.2
size: self.width / -15, self.height / 96
Rectangle:
pos: self.width / 2.52, self.height / 1.2
size: self.width / -43.2, self.height / 8
Rectangle:
pos: self.width / 2.52, self.height / 1.055
size: self.width / -2.85, self.height / 96
Rectangle:
pos: self.width / 15, self.height / 1.0444
size: self.width / -43.2, self.height / -4.7
Rectangle:
pos: self.width / 23.1, self.height / 1.34
size: self.width / 7.3, self.height / -96
Rectangle:
pos: self.width / 2.6, self.height / 1.34
size: self.width / -7.7, self.height / -96
Rectangle:
pos: self.width / 2.68, self.height / 1.359
size: self.width / 11, self.height / 16.2
# Верхний левый средний квадрат
Rectangle:
pos: self.width / 7.29, self.height / 1.0948
size: self.width / 22, self.height / -7.6
Rectangle:
pos: self.width / 3.92, self.height / 1.0948
size: self.width / 22, self.height / -7.6
# Верхний правый квадрат стенок
Rectangle:
pos: self.width / 1.8, self.height / 1.2
size: self.width / 15, self.height / 96
Rectangle:
pos: self.width / 1.631, self.height / 1.2
size: self.width / 43.2, self.height / 8
Rectangle:
pos: self.width / 1.631, self.height / 1.055
size: self.width / 2.85, self.height / 96
Rectangle:
pos: self.width / 1.06, self.height / 1.0444
size: self.width / 43.2, self.height / -4.7
Rectangle:
pos: self.width / 1.035, self.height / 1.34
size: self.width / -7.3, self.height / -96
Rectangle:
pos: self.width / 1.6043, self.height / 1.34
size: self.width / 7.7, self.height / -96
Rectangle:
pos: self.width / 1.57, self.height / 1.359
size: self.width / -10, self.height / 16.2
# Верхний правый средний квадрат
Rectangle:
pos: self.width / 1.207, self.height / 1.0948
size: self.width / 22, self.height / -7.6
Rectangle:
pos: self.width / 1.324, self.height / 1.0948
size: self.width / -22, self.height / -7.6
# Нижний вход - правая стенка
Rectangle:
pos: self.width / 1.79, self.height * 0
size: self.width / -43.2, self.height / 6
# Нижний вход - левая стенка
Rectangle:
pos: self.width / 2.274, self.height * 0
size: self.width / 43.2, self.height / 6
# Нижний левый квадрат стенок
Rectangle:
pos: self.width / 2.274, self.height / 6.4
size: self.width / -15, self.height / 96
Rectangle:
pos: self.width / 2.52, self.height / 6
size: self.width / -43.2, self.height / -8
Rectangle:
pos: self.width / 2.52, self.height / 24
size: self.width / -2.85, self.height / 96
Rectangle:
pos: self.width / 15, self.height / 24
size: self.width / -43.2, self.height / 4.7
Rectangle:
pos: self.width / 23.1, self.height / 3.8
size: self.width / 7.25, self.height / -96
Rectangle:
pos: self.width / 2.6, self.height / 3.8
size: self.width / -7.7, self.height / -96
Rectangle:
pos: self.width / 2.68, self.height / 3.8
size: self.width / 11, self.height / -16.2
#Нижний Левый средний квадрат
Rectangle:
pos: self.width / 7.29, self.height / 4.61
size: self.width / 22, self.height / -7.6
Rectangle:
pos: self.width / 3.92, self.height / 4.61
size: self.width / 22, self.height / -7.6
# Нижний правый квадрат стенок
Rectangle:
pos: self.width / 1.8, self.height / 6.4
size: self.width / 15, self.height / 96
Rectangle:
pos: self.width / 1.631, self.height / 6
size: self.width / 43.2, self.height / -8
Rectangle:
pos: self.width / 1.631, self.height / 24
size: self.width / 2.85, self.height / 96
Rectangle:
pos: self.width / 1.06, self.height / 24
size: self.width / 43.2, self.height / 4.7
Rectangle:
pos: self.width / 1.035, self.height / 3.8
size: self.width / -7.25, self.height / -96
Rectangle:
pos: self.width / 1.6043, self.height / 3.8
size: self.width / 7.6, self.height / -96
Rectangle:
id: stop
pos: self.width / 1.57, self.height / 3.8
size: self.width / -10, self.height / -16.2
#Нижний правый средний квадрат
Rectangle:
pos: self.width / 1.207, self.height / 4.61
size: self.width / 22, self.height / -7.6
Rectangle:
pos: self.width / 1.324, self.height / 4.61
size: self.width / -22, self.height / -7.6
#Центральные стороны
#Центральная левая горизонтальная стенка
Rectangle:
pos: self.width / 6.31, self.height / 1.34
size: self.width / 43.2, self.height / -2.05
#Центральная правая горизонтальная стенка
Rectangle:
pos: self.width / 1.208, self.height / 1.34
size: self.width / 43.2, self.height / -2.05
#Центальная верхняя стенка
Rectangle:
pos: self.width / 3.92, self.height / 1.43
size: self.width / 2., self.height / -96
#Центр спавна призраков
Rectangle:
pos: self.width / 2.68, self.height / 1.555
size: self.width / 11, self.height / 96
Rectangle:
pos: self.width / 1.57, self.height / 1.555
size: self.width / -10, self.height / 96
Rectangle:
pos: self.width / 2.68, self.height / 1.65
size: self.width / 43.2, self.height / 27
Rectangle:
pos: self.width / 1.57, self.height / 1.65
size: self.width / -43.2, self.height / 27
Rectangle:
pos: self.width / 2.68, self.height / 1.65
size: self.width / 3.79, self.height / -96
#Центальная средняя стенка
Rectangle:
pos: self.width / 3.92, self.height / 1.786
size: self.width / 2, self.height / -96
#Центральная правая вертекальная стенка
Rectangle:
pos: self.width / 1.324, self.height / 1.68
size: self.width / -22, self.height / 17.5
#Центральная левая вертекальная стенка
Rectangle:
pos: self.width / 3.92, self.height / 1.68
size: self.width / 22, self.height / 17.5
#Центальная левая нижняя стенка
Rectangle:
pos: self.width / 6.31, self.height / 1.945
size: self.width / 3.25, self.height / -96
#Центальняя правая нижняя стенка
Rectangle:
pos: self.width / 1.208, self.height / 1.945
size: self.width / -3.5, self.height / -96
#Центральний нижний квадрат
Rectangle:
pos: self.width / 3.92, self.height / 3.8
size: self.width / 2, self.height / 4.85
Player:
id: pacman_player
pos: self.width / 2.13, self.height / 5.7
player.py
from kivy.uix.widget import Widget
from kivy.properties import StringProperty, NumericProperty, ReferenceListProperty
from kivy.vector import Vector
class Player(Widget):
pac_img = StringProperty("png/pacman/pacman_down.gif")
number_update_pac_img = "None"
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
#update pacman gifts
if self.number_update_pac_img == "up":
self.pac_img = "png/pacman/pacman_up.gif"
if self.number_update_pac_img == "down":
self.pac_img = "png/pacman/pacman_down.gif"
if self.number_update_pac_img == "left":
self.pac_img = "png/pacman/pacman_left.gif"
if self.number_update_pac_img == "right":
self.pac_img = "png/pacman/pacman_right.gif"
1 个回答
0
如果你为迷宫的每个部分使用一个 Widget
,那么你可以利用 Widget
的 collide_widget()
方法来检测碰撞。首先,定义一个矩形的类:
class MyRectangle(Widget):
def on_size(self, *args):
# Make sure sizes are positive
if self.width < 0:
self.x += self.width
self.width = -self.width
if self.height < 0:
self.y += self.height
self.height = -self.height
这里的 on_size()
方法是为了确保大小大于0,因为你的画布上的 Rectangles
可能会有负值的大小。
接着,把你的 luckman.kv
文件改写成使用 MyRectangle
类,而不是直接用画布上的 Rectangles
,像这样:
<Player>:
size_hint: None, None
size: self.parent.width / 18, self.parent.height / 18
Image:
id: pacman
source: root.pac_img
allow_stretch: True
pos: root.pos
size: root.size
<GamePlay>:
canvas:
Rectangle:
source: "png/bg.png"
size: self.size
pacman: pacman_player
FloatLayout:
id: flt
# Верхний вход
# canvas:
# Color:
# rgb: [0, 0, 1]
# Верхний вход - правая стенка
MyRectangle:
pos: flt.width / 1.79, flt.height
size: flt.width / -43.2, flt.height / -6
# Верхний вход - левая стенка
MyRectangle:
pos: flt.width / 2.274, flt.height
size: flt.width / 43.2, flt.height / -6
# Верхний левый квадрат стенок
MyRectangle:
pos: flt.width / 2.274, flt.height / 1.2
size: flt.width / -15, flt.height / 96
MyRectangle:
pos: flt.width / 2.52, flt.height / 1.2
size: flt.width / -43.2, flt.height / 8
MyRectangle:
pos: flt.width / 2.52, flt.height / 1.055
size: flt.width / -2.85, flt.height / 96
MyRectangle:
pos: flt.width / 15, flt.height / 1.0444
size: flt.width / -43.2, flt.height / -4.7
MyRectangle:
pos: flt.width / 23.1, flt.height / 1.34
size: flt.width / 7.3, flt.height / -96
MyRectangle:
pos: flt.width / 2.6, flt.height / 1.34
size: flt.width / -7.7, flt.height / -96
MyRectangle:
pos: flt.width / 2.68, flt.height / 1.359
size: flt.width / 11, flt.height / 16.2
# Верхний левый средний квадрат
MyRectangle:
pos: flt.width / 7.29, flt.height / 1.0948
size: flt.width / 22, flt.height / -7.6
MyRectangle:
pos: flt.width / 3.92, flt.height / 1.0948
size: flt.width / 22, flt.height / -7.6
# Верхний правый квадрат стенок
MyRectangle:
pos: flt.width / 1.8, flt.height / 1.2
size: flt.width / 15, flt.height / 96
MyRectangle:
pos: flt.width / 1.631, flt.height / 1.2
size: flt.width / 43.2, flt.height / 8
MyRectangle:
pos: flt.width / 1.631, flt.height / 1.055
size: flt.width / 2.85, flt.height / 96
MyRectangle:
pos: flt.width / 1.06, flt.height / 1.0444
size: flt.width / 43.2, flt.height / -4.7
MyRectangle:
pos: flt.width / 1.035, flt.height / 1.34
size: flt.width / -7.3, flt.height / -96
MyRectangle:
pos: flt.width / 1.6043, flt.height / 1.34
size: flt.width / 7.7, flt.height / -96
MyRectangle:
pos: flt.width / 1.57, flt.height / 1.359
size: flt.width / -10, flt.height / 16.2
# Верхний правый средний квадрат
MyRectangle:
pos: flt.width / 1.207, flt.height / 1.0948
size: flt.width / 22, flt.height / -7.6
MyRectangle:
pos: flt.width / 1.324, flt.height / 1.0948
size: flt.width / -22, flt.height / -7.6
# Нижний вход - правая стенка
MyRectangle:
pos: flt.width / 1.79, flt.height * 0
size: flt.width / -43.2, flt.height / 6
# Нижний вход - левая стенка
MyRectangle:
pos: flt.width / 2.274, flt.height * 0
size: flt.width / 43.2, flt.height / 6
# Нижний левый квадрат стенок
MyRectangle:
pos: flt.width / 2.274, flt.height / 6.4
size: flt.width / -15, flt.height / 96
MyRectangle:
pos: flt.width / 2.52, flt.height / 6
size: flt.width / -43.2, flt.height / -8
MyRectangle:
pos: flt.width / 2.52, flt.height / 24
size: flt.width / -2.85, flt.height / 96
MyRectangle:
pos: flt.width / 15, flt.height / 24
size: flt.width / -43.2, flt.height / 4.7
MyRectangle:
pos: flt.width / 23.1, flt.height / 3.8
size: flt.width / 7.25, flt.height / -96
MyRectangle:
pos: flt.width / 2.6, flt.height / 3.8
size: flt.width / -7.7, flt.height / -96
MyRectangle:
pos: flt.width / 2.68, flt.height / 3.8
size: flt.width / 11, flt.height / -16.2
#Нижний Левый средний квадрат
MyRectangle:
pos: flt.width / 7.29, flt.height / 4.61
size: flt.width / 22, flt.height / -7.6
MyRectangle:
pos: flt.width / 3.92, flt.height / 4.61
size: flt.width / 22, flt.height / -7.6
# Нижний правый квадрат стенок
MyRectangle:
pos: flt.width / 1.8, flt.height / 6.4
size: flt.width / 15, flt.height / 96
MyRectangle:
pos: flt.width / 1.631, flt.height / 6
size: flt.width / 43.2, flt.height / -8
MyRectangle:
pos: flt.width / 1.631, flt.height / 24
size: flt.width / 2.85, flt.height / 96
MyRectangle:
pos: flt.width / 1.06, flt.height / 24
size: flt.width / 43.2, flt.height / 4.7
MyRectangle:
pos: flt.width / 1.035, flt.height / 3.8
size: flt.width / -7.25, flt.height / -96
MyRectangle:
pos: flt.width / 1.6043, flt.height / 3.8
size: flt.width / 7.6, flt.height / -96
MyRectangle:
id: stop
pos: flt.width / 1.57, flt.height / 3.8
size: flt.width / -10, flt.height / -16.2
#Нижний правый средний квадрат
MyRectangle:
pos: flt.width / 1.207, flt.height / 4.61
size: flt.width / 22, flt.height / -7.6
MyRectangle:
pos: flt.width / 1.324, flt.height / 4.61
size: flt.width / -22, flt.height / -7.6
#Центральные стороны
#Центральная левая горизонтальная стенка
MyRectangle:
pos: flt.width / 6.31, flt.height / 1.34
size: flt.width / 43.2, flt.height / -2.05
#Центральная правая горизонтальная стенка
MyRectangle:
pos: flt.width / 1.208, flt.height / 1.34
size: flt.width / 43.2, flt.height / -2.05
#Центальная верхняя стенка
MyRectangle:
pos: flt.width / 3.92, flt.height / 1.43
size: flt.width / 2., flt.height / -96
#Центр спавна призраков
MyRectangle:
pos: flt.width / 2.68, flt.height / 1.555
size: flt.width / 11, flt.height / 96
MyRectangle:
pos: flt.width / 1.57, flt.height / 1.555
size: flt.width / -10, flt.height / 96
MyRectangle:
pos: flt.width / 2.68, flt.height / 1.65
size: flt.width / 43.2, flt.height / 27
MyRectangle:
pos: flt.width / 1.57, flt.height / 1.65
size: flt.width / -43.2, flt.height / 27
MyRectangle:
pos: flt.width / 2.68, flt.height / 1.65
size: flt.width / 3.79, flt.height / -96
#Центальная средняя стенка
MyRectangle:
pos: flt.width / 3.92, flt.height / 1.786
size: flt.width / 2, flt.height / -96
#Центральная правая вертекальная стенка
MyRectangle:
pos: flt.width / 1.324, flt.height / 1.68
size: flt.width / -22, flt.height / 17.5
#Центральная левая вертекальная стенка
MyRectangle:
pos: flt.width / 3.92, flt.height / 1.68
size: flt.width / 22, flt.height / 17.5
#Центальная левая нижняя стенка
MyRectangle:
pos: flt.width / 6.31, flt.height / 1.945
size: flt.width / 3.25, flt.height / -96
#Центальняя правая нижняя стенка
MyRectangle:
pos: flt.width / 1.208, flt.height / 1.945
size: flt.width / -3.5, flt.height / -96
#Центральний нижний квадрат
MyRectangle:
pos: flt.width / 3.92, flt.height / 3.8
size: flt.width / 2, flt.height / 4.85
Player:
id: pacman_player
pos: flt.width / 2.13, flt.height / 5.7
<MyRectangle>:
size_hint: None, None
canvas:
Color:
rgb: [0, 0, 1]
Rectangle:
pos: root.pos
size: root.size
注意,Player
的 size
是在 kv
文件中定义的,Image
也使用了相同的大小。这对 collide_widget()
方法来说很重要。
另外,FloatLayout
现在有一个 id
,方便引用。画布的指令被 MyRectangle
的实例替代,而 <MyRectangle>
规则则为每个 MyRectangle
实例执行画布指令。
现在可以修改 update()
方法来检测碰撞了:
def update(self, _dt):
self.pacman.move()
flt = self.ids.flt
for w in flt.children:
if w == self.pacman:
continue
if w.collide_widget(self.pacman):
print('collision:', w.pos, w.size, self.pacman.pos, self.pacman.size)
return