Search code examples
pythonkivykivy-language

kivy move image only when image is pressed


I'm making a game in kivy where when you press on the soccer ball it moves. I want the ball to move only when I press exactly on the ball, if I press anywhere else it shouldn't move. Currently, the ball moves if I press anywhere close to the ball which Is not what I want. Is there anything I can do so that it only moves when I press exactly on the ball? Below is my code!

main.py

class Ball(Image):
velocity = NumericProperty(0)


def on_touch_down(self, touch):
    if self.collide_point(*touch.pos):
        self.source = "icons/ball.png"
        self.velocity = 275
    return super().on_touch_down(touch)

def on_touch_up(self, touch):
    if self.collide_point(*touch.pos):
        self.source = "icons/ball.png"
    return super().on_touch_up(touch)


class MainApp(App):
    GRAVITY = 300


def move_ball(self, time_passed):
    ball = self.root.ids.game_screen.ids.ball
    ball.y = ball.y + ball.velocity * time_passed
    ball.velocity = ball.velocity - self.GRAVITY * time_passed


def start_game(self):
    Clock.schedule_interval(self.move_ball, 1/60.)

kivy code

#:import utils kivy.utils


<GameScreen>:
    FloatLayout:
        canvas:
            Color:
                rgb: utils.get_color_from_hex("#39B3F2")
            Rectangle:
                size: self.size
                pos: self.pos
    GridLayout:
        rows: 1
        pos_hint: {"top": 1, "left": 1}
        size_hint: 1, .1
        Image:
            source: "icons/sun.png"
    GridLayout:
        rows: 1
        pos_hint: {"top": 1, "left": 1}
        size_hint: 1, .2
        Image:
            source: "icons/clouds.png"
    GridLayout:
        rows: 1
        pos_hint: {"bottom": 1, "left": 1}
        size_hint: 1, .5
        Image:
            source: "icons/Field4.png"
            allow_stretch: True
            keep_ratio: False
            pos: self.pos


    Button:
        size_hint: None, None
        font_size: dp(20)
        font_name: 'SackersGothicStd-Medium.otf'
        text: "Start Game"
        color: "gold"
        pos_hint: { "center_x": 0.5, "center_y": 0.3}
        size: 150, 55
        size_hint: None, None
        background_normal: ''
        background_color: (57/255.0, 179/255.0, 242/255.0, .10)


        on_release:
            self.disabled = True
            self.opacity = 0
            root.play_sound()
            app.start_game()


    Ball:
        source: "icons/ball.png"
        size_hint: None, None
        size: 500, 500
        pos_hint: {"center_x": 0.5}
        id: ball

I also tried keep_ratio: True allow_stretch: True but it still didn't work


Solution

  • ball is a circle (or rather disk) which has some radius - if you check distance between ball center and mouse position and it is smaller then radius then you clicked inside this circle or disk.

    More precisely using the Pythagorean formula a**2 + b**2 == c**2
    or rather disk's definition : x**2 + y**2 <= r**2

    (ball.centerx - touch.pos.x)**2 + (ball.centery - touch.pos.y)**2 <= radius**2
    

    Kivy has class Vector to make it simpler - but I can't test it.

    Vector(ball.center).distance(touch.pos) <= radius
    

    And you should use it instead of collide_point()

    class Ball(Image):
    
        velocity = NumericProperty(0)
     
        radius = 50  # you have to get it manually from image     
        
        def on_touch_down(self, touch):
            if Vector(self.center).distance(touch.pos) <= radius:
                self.source = "icons/ball.png"
                self.velocity = 275
    
            return super().on_touch_down(touch)
        
        def on_touch_up(self, touch):
            if Vector(self.center).distance(touch.pos) <= radius:
                self.source = "icons/ball.png"
    
            return super().on_touch_up(touch)
    

    I'm not sure but maybe it even keeps ball position as Vector() and you could use directly

    ball.center.distance(touch.pos) <= radius