Kivy: проблема с движением/обновлением объекта (адаптация учебника Kivy Pong)

Я адаптировал часть руководства по kivy pong (https://kivy.org/doc/stable/tutorials/pong.html), чтобы создать класс мяча, который должен обновляться 60 раз в секунду, перемещая мяч по экрану. Точно так же, когда мяч ударяется о борта, он должен отражаться в противоположном направлении. Однако мяч просто неподвижно сидит в углу экрана. Какую синтаксическую/логическую ошибку я делаю?

Вот мой код:

from kivy.lang import Builder
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy import Config
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition,\
SlideTransition
from kivy.uix.widget import Widget
from kivy.animation import Animation
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.clock import Clock
from kivy.vector import Vector
from random import randint

Builder.load_string('''
<Ball>:
    Image:
        source: '58-Breakout-Tiles.png'
        size: 15, 15
        pos: self.pos



<SettingsScreen>:
    close: close
    AnchorLayout:
        anchor_x: 'left'
        anchor_y: 'top'
        Image:
            id: close
            size_hint: .03, .03
            source: 'grey_crossGrey.png'

    GridLayout:
        cols: 2
        Label:
            font_name: 'vgafix.fon'
            text: 'Music: '
        Switch:
            active: True
        Label:
            font_name: 'vgafix.fon'
            text: 'Sounds: '
        Switch:
            active: True

<MenuScreen>:
    cog: cog
    AnchorLayout:
        anchor_x: 'right'
        anchor_y: 'top'
        Image:
            id: cog
            size_hint: .03, .03
            source: 'settings-cog.png'

    BoxLayout:
        orientation: 'vertical'
        Image:
            source: 'brickbreaker log.png'
        Label:
            font_name: 'vgafix.fon'
            text: 'Tap to start'

<GameScreen>:
    ball: ball
    cog: cog
    AnchorLayout:
        anchor_x: 'right'
        anchor_y: 'top'
        Image:
            id: cog
            size_hint: .03, .03
            source: 'settings-cog.png'

    Ball:
        id: ball
        center: self.parent.center
''')

Config.set('graphics', 'multisamples', '0')



class Ball(Widget):
    velocityX, velocityY = NumericProperty(0), NumericProperty(0)
    velocity = ReferenceListProperty(velocityX, velocityY)

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

class Player(Widget):
    pass

class Brick(Widget):
    pass




class SettingsScreen(Screen):
    def __init__(self, **kwargs):
        super(SettingsScreen, self).__init__(**kwargs)
        self.previous = False

    def on_touch_down(self, touch):
        if self.close.collide_point(*touch.pos):
            sm.transition = SlideTransition(direction = 'right')
            sm.current = self.previous

class MenuScreen(Screen):
    def __init__(self, **kwargs):
        super(MenuScreen, self).__init__(**kwargs)

    def on_touch_down(self, touch):
        if self.cog.collide_point(*touch.pos):
            sm.transition = SlideTransition(direction = 'left')
            sm.get_screen('settings').previous = 'menu'
            sm.current = 'settings'
        else:
            sm.transition = FadeTransition()
            sm.current = 'game'

class GameScreen(Screen):
    ball = ObjectProperty(None)

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

    def on_touch_down(self, touch):
        if self.cog.collide_point(*touch.pos):
            sm.transition = SlideTransition(direction = 'left')
            sm.get_screen('settings').previous = 'game'
            sm.current = 'settings'

    def initBall(self):
        self.ball.center = self.center
        self.ball.velocity = Vector(4, 0).rotate(randint(0, 360))

    def update(self, dt):
        self.ball.move()
        if (self.ball.y < 0) or (self.ball.top > self.height):
            self.ball.velocityY *= -1
        # bounce off left and right
        if (self.ball.x < 0) or (self.ball.right > self.width):
            self.ball.velocityX *= -1

sm = ScreenManager(transition = FadeTransition())
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(GameScreen(name='game'))
sm.add_widget(SettingsScreen(name='settings'))



class BrickBreakerInsanityApp(App):
    def build(self):
        Clock.schedule_interval(sm.get_screen('game').update, 1.0/60.0)
        return sm

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

активы кода:

https://i.stack.imgur.com/rR799.png

https://i.stack.imgur.com/ngYvL.png

https://i.stack.imgur.com/AuxI3.png

https://i.stack.imgur.com/ypd7C.png

https://drive.google.com/open?id=1GAnv5DfjNUuAXTybmsan90Dm0OuSVOfb

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
192
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Есть два решения проблемы.

Способ 1 - kv файл

  • Удалить Image:
  • Добавьте size_hint: None, None, чтобы перезаписать размер по умолчанию (1, 1) или (100, 100)
  • Добавьте canvas:

Фрагменты

Builder.load_string('''
<Ball>:
    size_hint: None, None
    size: 15, 15
    canvas:
        Rectangle:
            source: '58-Breakout-Tiles.png'
            pos: self.pos
            size: self.size

Kivy Canvas » источник

source

This property represents the filename to load the texture from. If you want to use an image as source, do it like this:

with self.canvas:
    Rectangle(source='mylogo.png', pos=self.pos, size=self.size)

Here’s the equivalent in Kivy language:

<MyWidget>:
    canvas:
        Rectangle:
            source: 'mylogo.png'
            pos: self.pos
            size: self.size

Способ 2 - файлы kv и py

  • Переместите определения мячей из файла kv в скрипт Python.
  • Создайте текстуру изображения мяча
  • Объявите прямоугольник, содержащий текстуру мяча, на холсте
  • Привяжите прямоугольник self.rect к методу update_ball() всякий раз, когда есть изменения pos или size.

Фрагменты - ру

from kivy.core.image import Image
from kivy.graphics import Rectangle
...
class Ball(Widget):
    velocityX, velocityY = NumericProperty(0), NumericProperty(0)
    velocity = ReferenceListProperty(velocityX, velocityY)

    def __init__(self, **kwargs):
        super(Ball, self).__init__(**kwargs)
        texture = Image('58-Breakout-Tiles.png').texture
        self.size_hint = None, None
        self.size = (15, 15)
        with self.canvas:
            self.rect = Rectangle(texture=texture, pos=self.pos, size=self.size)
        self.bind(pos=self.update_ball, size=self.update_ball)

    def update_ball(self, *args):
        self.rect.pos = self.pos
        self.rect.size = self.size

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

Фрагменты - кв

Builder.load_string('''
<SettingsScreen>:

Kivy Canvas » текстура

texture

Property that represents the texture used for drawing this Instruction. You can set a new texture like this:

from kivy.core.image import Image

texture = Image('logo.png').texture
with self.canvas:
    Rectangle(texture=texture, pos=self.pos, size=self.size)

Usually, you will use the source attribute instead of the texture.

из интереса, в чем преимущество создания прямоугольника для каждого объекта вместо прямого перемещения изображения? я делаю это в pygame для управления игровыми объектами - применимо ли такое же использование?

charm ah 09.04.2019 20:09

Есть два решения проблемы. Я только что обновил свой пост альтернативным решением, похожим на pygame, то есть манипулирование игровыми объектами.

ikolim 09.04.2019 20:28

Ваш код в основном работает. Довольно простое решение — просто изменить Ball на расширение Image (вместо Widget) и добавить size_hint: None, None.

Итак, объявление класса Ball становится:

class Ball(Image):

Сам класс может остаться прежним

Правило для Ball в вашем файле kv упрощается до:

<Ball>:
    source: '58-Breakout-Tiles.png'

И в вашем правиле GameScreen раздел Ball становится:

Ball:
    id: ball
    size_hint: None, None
    center: self.parent.center

Просто добавьте size_hint.

Я думаю, что этого достаточно, чтобы заставить его работать.

Кроме того, вы можете просто добавить size_hint к Ball как:

Ball:
    id: ball
    size_hint: None, None
    center: self.parent.center

и измените pos: self.pos на pos: root.pos в вашем правиле <Ball>: следующим образом:

<Ball>:
    Image:
        source: '58-Breakout-Tiles.png'
        size: 15, 15
        pos: root.pos

Основная проблема с исходным кодом заключается в том, что добавление Image к Widget — это просто добавление дочернего элемента к BallWidget. Widget, который не является Layout, не обрабатывает свои дочерние элементы. Оригинальная Pong игра обходит это, помещая изображение мяча в CanvasBallWidget. Класс Image в основном делает это за вас.

ах, я вижу, в этом есть большой смысл - использование мяча в качестве объекта изображения, а не виджета, содержащего изображение, является более логичным подходом, если его необходимо постоянно обновлять. Спасибо!

charm ah 09.04.2019 21:25

Другие вопросы по теме