Как сделать так, чтобы виджет шара отклонялся виджетом прямоугольника в киве? - PullRequest
0 голосов
/ 23 января 2020

На самом деле я работаю над одним игровым приложением, в котором я хочу, чтобы мяч отклонялся прямоугольником в направлении движения шара на 90 градусов. В приведенном ниже коде я выполняю задачу, но это не так, как хотелось бы. Виджет шара, когда нажата кнопка ниже, сталкивается с виджетом прямоугольника, и сначала он немного скользит вместе с ним, а затем отклоняется в нужном направлении. Я просто хочу сделать это реалистичным c, поскольку шар должен отклоняться виджетом прямоугольника, просто сталкиваясь с ним в одной точке. Попробуйте выполнить приведенный ниже код, чтобы лучше понять, чего я хочу достичь и с какой проблемой я сталкиваюсь, поскольку это немного сложно объяснить. Пожалуйста, помогите!

from kivy.app import App
from kivy.graphics import Rotate, Rectangle, Ellipse, Color
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, CardTransition
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, 
NumericProperty,ReferenceListProperty, ListProperty, BooleanProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
from kivy.vector import Vector
from kivy.graphics.context_instructions import PopMatrix, PushMatrix

Builder.load_string('''

<PongBall>:
    size: 50, 50
    canvas:
        Color:
            rgba: 0,0,1,1
        Ellipse:
            pos: self.pos
            size: self.size


<Game>:
    ball: pong_ball
    object: Object
    FloatLayout:
        Button:
            pos_hint:{"x":2.6,"y":0}
            size_hint: 3, 1
            text:"Throw"
            background_color: 2,1,190,1
            border: 30,30,30,30
            on_release:
                root.start()

        Button:
            pos_hint:{'x':7.3, 'y':5.3}
            size_hint: 0.5,0.5
            text:'restart'
            on_release:
                root.serve_ball()

    PongBall:
        id: pong_ball
        center: self.center

    Object:    
        id: Object
        center: self.rotate_origin

<Game1>:
    ball: pong_ball
    object1: Object1
    object2: Object2
    FloatLayout:
        Button:
            pos_hint:{"x":2.6,"y":0}
            size_hint: 3, 1
            text:"Throw"
            background_color: 2,1,190,1
            border: 30,30,30,30
            on_release:
                root.start()

        Button:
            pos_hint:{'x':7.3, 'y':5.3}
            size_hint: 0.5,0.5
            text:'restart'
            on_release:
                root.serve_ball()

    PongBall:
        id: pong_ball
        center: self.center

    Object1:
        id: Object1
        center: self.rotate_origin
    Object2:
        id: Object2
        center: self.rotate_origin

<Manager>:
    id: screen_manager

    Screen:
        name:"P"
        FloatLayout:

            Button:
                pos_hint:{"x":0.2,"y":0.05}
                size_hint: 0.6, 0.2
                font_size: (root.width**2 + root.height**2) / 13**4
                text: "Play"
                background_color: 255,0,1,1
                on_release:
                    root.transition.direction = "up";s3.serve_ball()        
                    root.current = "again"

    Screen:
        name: 'again'
        Game1:
            id:s3
''')
class Object1(Widget):
    def __init__(self, *args, **kwargs):
        Widget.__init__(self, *args, **kwargs)
        self.rect_pos_x = 500
        self.rect_pos_y = 370
        self.rect_pos = self.rect_pos_x, self.rect_pos_y
        self.rect_width = 200
        self.rect_height = 30
        self.rect_size = self.rect_width, self.rect_height
        self.rotate_origin_x = self.rect_pos_x + self.rect_width / 2
        self.rotate_origin_y = self.rect_pos_y + self.rect_height / 2
        self.rotate_origin = self.rotate_origin_x, self.rotate_origin_y
        self.angle = 135
        print('rect 1')
        with self.canvas:
            PushMatrix()
            Rotate(origin=self.rotate_origin, angle=self.angle)
            Color(rgb=(0,197,68))
            Rectangle(pos=self.rect_pos, size=self.rect_size)
            PopMatrix()

    def rotate(self):
        self.canvas.clear()
        self.angle += 90
        if (self.angle > 315):
            self.angle = 225
        with self.canvas:
            Rotate(origin=self.rotate_origin, angle=self.angle)
            Color(rgb=(0, 255, 100))
            Rectangle(pos=self.rect_pos, size=self.rect_size)

    def deflect_ball(self, ball):
        if self.collide_widget(ball):
            if not ball.collided:
                vx, vy = ball.velocity
                if self.angle == 135:
                    ball.velocity = Vector(-vx, vy).rotate(90)
                if self.angle == 225:
                    ball.velocity = Vector(-vx, vy).rotate(270)
                if self.angle == 315:
                    ball.velocity = Vector(-vx, vy).rotate(90)
                ball.collided = True
        else:
            ball.collided = False


    def on_touch_up(self, touch):
        if self.collide_point(*touch.pos):
            self.rotate()
            print(self.angle)

class Object2(Widget):
    def __init__(self, *args, **kwargs):
        Widget.__init__(self, *args, **kwargs)
        self.rect_pos_x = 500
        self.rect_pos_y = 170
        self.rect_pos = self.rect_pos_x, self.rect_pos_y
        self.rect_width = 200
        self.rect_height = 30
        self.rect_size = self.rect_width, self.rect_height
        self.rotate_origin_x = self.rect_pos_x + self.rect_width / 2
        self.rotate_origin_y = self.rect_pos_y + self.rect_height / 2
        self.rotate_origin = self.rotate_origin_x, self.rotate_origin_y
        self.angle = 135
        print('rect 1')
        with self.canvas:
            PushMatrix()
            Rotate(origin=self.rotate_origin, angle=self.angle)
            Color(rgb=(0,197,68))
            Rectangle(pos=self.rect_pos, size=self.rect_size)
            PopMatrix()

    def rotate(self):
        self.canvas.clear()
        self.angle += 90 
        if (self.angle > 315):
            self.angle = 225
        with self.canvas:
            PushMatrix()
            Rotate(origin=self.rotate_origin, angle=self.angle)
            Color(rgb=(0, 255, 100))
            Rectangle(pos=self.rect_pos, size=self.rect_size)
            PopMatrix()
    def deflect_ball(self, ball):
        if self.collide_widget(ball):
            if not ball.collided:
                vx, vy = ball.velocity
                if self.angle == 135:
                    ball.velocity = Vector(-vx, vy).rotate(90)
                if self.angle == 225:
                    ball.velocity = Vector(-vx, vy).rotate(270)
                if self.angle == 315:
                    ball.velocity = Vector(-vx, vy).rotate(90)
                ball.collided = True
        else:    
            ball.collided = False


    def on_touch_up(self, touch):
        if self.collide_point(*touch.pos):
            self.rotate()
            print(self.angle)

class PongBall(Widget):
    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(0)
    velocity = ReferenceListProperty(velocity_x, velocity_y)
    collided = BooleanProperty(None)


    def move(self):
        self.pos = Vector(*self.velocity) + self.pos

class Game1(Widget):
    ball = ObjectProperty(None)
    object1 = ObjectProperty(None)
    object2 = ObjectProperty(None)
    def start(self):
        Clock.schedule_interval(self.update, 1.0 / 60.0)

    def serve_ball(self, vel=(5, 0)):
        Clock.unschedule(self.update)
        self.ball.center = 40, 380
        self.ball.velocity = vel

    def update(self, dt):
        self.ball.move()
        self.object1.deflect_ball(self.ball)
        self.object2.deflect_ball(self.ball)

        if (self.ball.y < self.y+50) or self.ball.x <0:
            self.ball.velocity_y = 0
            Clock.unschedule(self.update)
            print('tested')
            self.serve_ball()

class Manager(ScreenManager):
    pass

sm = Manager()

class ScreensApp(App):
    def build(self):
        return sm

if __name__ == '__main__':
    ScreensApp().run()

1 Ответ

1 голос
/ 24 января 2020

Проблема заключается в том, что после обнаружения столкновения и изменения скорости мяча следующий update не перемещает шар достаточно далеко, чтобы метод collide_widget() возвратил False вместо True. Один из способов исправить это - сохранить свойство collided для PongBall, которое можно использовать, чтобы избежать множественных столкновений. Таким образом, вы можете добавить DictProperty к PongBall следующим образом:

collided = DictProperty()

, где каждый ключ в DictProperty равен Object, а значение равно True или False.

и измените deflect_ball на:

def deflect_ball(self, ball):
    if self.collide_widget(ball):
        if not ball.collided[self]:
            vx, vy = ball.velocity
            if self.angle == 135:
                ball.velocity = Vector(-vx, vy).rotate(90)
            if self.angle == 225:
                ball.velocity = Vector(-vx, vy).rotate(270)
            if self.angle == 315:
                ball.velocity = Vector(-vx, vy).rotate(90)
            ball.collided[self] = True
    else:
        ball.collided[self] = False

И свойство должно быть инициализировано в Game1 как:

def start(self):
    self.ball.collided[self.object1] = False
    self.ball.collided[self.object2] = False
    Clock.schedule_interval(self.update, 1.0 / 60.0)
...