如何在Python Kivy中实现对象碰撞?

0 投票
1 回答
28 浏览
提问于 2025-04-12 12:41

我想为安卓手机和平板电脑制作一个Luckman风格的吃豆人游戏。但是我在处理迷宫的碰撞检测时遇到了问题。

我试着参考这些文件 https://drive.google.com/file/d/1pwCDpU1UPzB7V7TV7-WmbyDLkpIDODry/view。但是因为我的迷宫不是作为图片存在,而是作为在.kv文件中绘制的对象,所以没有成功。

这是我的程序代码:

main.py
from 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,那么你可以利用 Widgetcollide_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

注意,Playersize 是在 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

撰写回答