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

Я адаптировал часть учебника по киви-понгу (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

Ответы [ 2 ]

1 голос
/ 09 апреля 2019

Ваш код в основном работает.Довольно простое решение - просто изменить 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 означает просто добавление дочернего элемента к Ball Widget.Widget, который не является Layout, не обрабатывает рисование своих дочерних элементов.Оригинальная игра Pong позволяет обойти это, поместив изображение мяча в Canvas из Ball Widget.Класс Image в основном делает это для вас.

1 голос
/ 09 апреля 2019

Существует два решения проблемы.

Метод 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

Это свойство представляет имя файла для загрузки текстуры.Если вы хотите использовать изображение в качестве источника, сделайте это следующим образом:

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

Вот эквивалент на языке Kivy:

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

Метод 2 - файлы kv & py

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

Фрагменты кода - py

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

Snippets - kv

Builder.load_string('''
<SettingsScreen>:

Текстура Kivy Canvas »

texture

Свойство, представляющее текстуру, используемую для рисования этой Инструкции.Вы можете установить новую текстуру следующим образом:

from kivy.core.image import Image

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

Обычно вместо текстуры вы будете использовать атрибут источника.

...