python kivy collision detection isn't working

Question:

Im trying to create a game with Python’s Kivy but my collision detection system isnt working i’ve tried many different methods on youtube but still no success it either does detect anything or just gives me error messages

def collides(self, player, ball2):

    r1x = player.pos[0]
    r1y = player.pos[1]
    r2x = ball2.pos[0]
    r2y = ball2.pos[1]
    r1w = player.size[0]
    r1h = player.size[1]
    r2w = ball2.size[0]
    r2h = ball2.size[1]

    if r1x < r2x + r2w and r1x + r1w > r2x and r1y < r2y + r2h and r1y + r1h > r2y:
        print("True")
        return True
    else:
        return False
        print('False')
Asked By: Derrick Mungwira

||

Answers:

Your code in collides seems OK but rest of code (in repo) doesn’t look good.

I took code from repo and first I made many changes to make it cleaner – I made class Sprite similar to pygame.Sprite

And next I tried use collisions and they work for me.

I keep all balls on list so I can use for-loop to work with all ball. And I can add more balls and it will still works the same. And I can remove ball from list when it is "killed".

I also run all with one schedule_interval. When I click button then I only change speed vx without running another schedule_interval. And this way in update() I can first I make calculation, next I can check collisions and at the end I can move rect on canvas – and this way rect doesn’t blik when I have to move it back to previous position (ie. when I detect collision with border).

from kivy.app import App
from kivy.graphics import Ellipse, Rectangle, Color
from kivy.metrics import dp
from kivy.properties import Clock, ObjectProperty, NumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget


# How to play the game: Click left and right to move along the 'x' axis to stop click button to move in the opposite
# direction once, to go faster just repeatedly click the direction you want to go BUT there's a catch the faster
# you are going the harder it is to stop sp be carefull. you can teleport to the other side of the screen but only from
# the right side to the left side

# Objective: Dodge all incoming enemies until you reach the next level

# Level layout: Lvl 1: Space invaders type mode Lvl 2: Platform runner type mode Lvl 3: undecided...

# Goal: Make this game playable both on mobile and pc

class Sprite():
    def __init__(self, x, y, size, color, vx, vy):
        '''Set all values.'''
        
        self.start_x = x
        self.start_y = y
        self.x = x
        self.y = y
        self.size = size
        self.color = color
        self.vx = vx
        self.vy = vy
        self.rect = None
        #self.alive = True
        
    def create_rect(self):
        '''Execute it in `with canvas:` in `on_size()`.'''
        
        Color(*self.color)
        self.rect = Rectangle(pos=(self.x, self.y), size=(self.size, self.size))
    
    def set_start_pos(self, center_x, center_y):
        '''Move to start position.'''
        
        self.x = center_x + self.start_x
        self.y = center_y + self.start_y
        
    def move(self):
        '''Calculate new position without moving object on `canvas`.'''
        
        self.x += self.vx
        self.y += self.vy
        
    def draw(self):
        '''Move object on canvas.'''
        
        self.rect.pos = (self.x, self.y)
        
    def check_collision_circle(self, other):
        
        distance = (((self.x-other.x)**2) + ((self.y-other.y)**2)) ** 0.5

        #if distance < (self.size + other.size)/2:
        #    print(True)
        #    return True
        #else:
        #    return False

        return distance < (self.size + other.size)/2:
        
    def check_collision_rect(self, other):

        # code `... and ...` gives `True` or `False`
        # and it doesn't need `if ...: return True else: return False`
        
        return (
            (other.x <= self.x + self.size) and 
            (self.x <= other.x + other.size) and 
            (other.y <= self.y + self.size) and 
            (self.y <= other.y + other.size)
        )
    
class MainCanvas(Widget):
    rec_x = NumericProperty(0)
    inc = dp(3)
    ball_size = dp(35)
    my_player = ObjectProperty(Rectangle)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.player = Sprite(x=-self.ball_size/2, y=145, size=dp(15), vx=dp(0), vy=dp(0), color=(1, .3, .5))
        
        self.balls = [
            Sprite(x=0,    y=-2000, size=dp(15), vx=dp(0), vy=dp(8), color=(1, 0, 0)),
            Sprite(x=100,  y=-1000, size=dp(15), vx=dp(0), vy=dp(5), color=(1, 1, 0)),
            Sprite(x=-200, y=-1000, size=dp(30), vx=dp(0), vy=dp(5), color=(1, 1, 1)),
            Sprite(x=300,  y=-600,  size=dp(15), vx=dp(0), vy=dp(5), color=(1, 1, 1)),
        ]

        with self.canvas:
            for ball in self.balls:
                ball.create_rect()
        
            self.player.create_rect()
            
        Clock.schedule_interval(self.update, 1/60)

    def on_size(self, *args):
        print(f'on_size : {self.width}x{self.height}')
        
        for ball in self.balls:
            ball.set_start_pos(self.center_x, self.center_y)
            
        self.player.set_start_pos(self.center_x, self.center_y)

        self.rec_x = self.player.x

    def update(self, dt):
        # all in one function to control if it check collision after move, and draw only after all calculations 
        
        # --- moves (without draws) ---  
        
        self.player_move(dt)
        
        # move green rectangle below player
        self.rec_x = self.player.x
        
        self.ball_move(dt)

        # --- collisions (without draws) ---
        
        live_balls = []
        
        for ball in self.balls:
            if self.player.check_collision_rect(ball):
            #if self.player.check_collision_circle(ball):
                print('killed')
                
                # hide
                #ball.set_start_pos(self.center_x, self.center_y)
                #ball.draw()
                
                # or remove from canvas
                self.canvas.remove(ball.rect)
            else:
                live_balls.append(ball)
                
        self.balls = live_balls
        
        # --- draws ---

        self.player.draw()

        for ball in self.balls:
            ball.draw()

    def on_left_click(self):
        print('Left Clicked')
        self.player.vx -= self.inc

    def on_right_click(self):
        print('Right Clicked')
        self.player.vx += self.inc

    def ball_move(self, dt):
        for ball in self.balls:
            ball.move()

            if ball.y + ball.size > self.height:
                ball.set_start_pos(self.center_x, self.center_y)

    def player_move(self, dt):
        self.player.move()
        
        # moving left and stop on screen border
        if self.player.vx < 0 and self.player.x < 0:
            self.player.x = 0
            self.player.vx = 0

        # moving right and jump to left side when leave screen
        if self.player.vx > 0 and self.width < self.player.x:
            self.player.x = 0

class TheFalling(App):
    pass

app = TheFalling()
app.run()
#app.stop()
app.root_window.close()
Answered By: furas
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.