Почему перепланирование тактовых интервалов приводит к сбросу позиций изображения?

#python #python-3.x #kivy #kivy-language

#python #python-3.x #kivy #kivy-language

Вопрос:

Внутри программы можно видеть, что по мере того, как мяч отскакивает, у вас есть возможность открыть страницу настроек из верхнего правого угла. Это приостанавливает движение шара и открывает страницу настроек.

Извините, если это очевидный вопрос, но я снова озадачен странной внутренней работой Kivy, и документы не очень полезны для решения подобных проблем.

Проблема

Мяч всегда начинается с центральной позиции. Хотите, чтобы ball продолжил / возобновил работу с предыдущей позиции перед переключением экрана?

Шаги по воссозданию проблемы

  1. Нажмите на ярлык, «Нажмите, чтобы начать». Мяч начал отскакивать от центральной позиции
  2. Нажмите на изображение зубчатого колеса. Отображается экран настроек
  3. Нажмите «x», чтобы закрыть экран настроек. Мяч начал отскакивать от центральной позиции.

Код:

 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>:
    size_hint: None, None
    source: '58-Breakout-Tiles.png'
    pos: self.pos
    size: 15, 15



<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
        size_hint: None, None
        center: self.parent.center
''')

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



class Ball(Image):
    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')
            if self.previous == 'game':
                sm.get_screen('game').interval()
            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):
    def __init__(self, **kwargs):
        super(GameScreen, self).__init__(**kwargs)
        self.initBall()
        self.interval = Clock.schedule_interval(self.update, 1.0/60.0)

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

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

    def update(self, dt):
        self.ball.move()
        if (self.ball.y < 0) or (self.ball.y > self.height-15):
            self.ball.velocityY *= -1
        # bounce off left and right
        if (self.ball.x < 0) or (self.ball.x > self.width-15):
            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):
        return sm

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

Ресурсы кода (обязательно):

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

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

Комментарии:

1. Не уверен, какая именно последовательность вызывает это, но если вы удалите center: self.parent.center из своего Ball: в kv файле, это исправит это. Кстати, эта строка в любом случае не нужна, поскольку она выполняется в initBall() .

Ответ №1:

Основная причина

SlideTransition И direction заставляет шар начинаться с центра.

Решение

Удалите все ссылки на SlideTransition .

Пример

 from kivy.lang import Builder
from kivy.app import App
from kivy.uix.image import Image
from kivy import Config
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition,     
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty, StringProperty
from kivy.clock import Clock
from kivy.vector import Vector
from random import randint

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

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

    GridLayout:
        cols: 2
        Label:
            font_name: "./assets/fonts/vgafix.fon"
            text: 'Music: '
        Switch:
            active: True
        Label:
            font_name: "./assets/fonts/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: './assets/icons/settings-cog.png'

    BoxLayout:
        orientation: 'vertical'
        Image:
            source: "./assets/icons/brickbreaker log.png"
        Label:
            font_name: "./assets/fonts/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: './assets/icons/settings-cog.png'

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

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


class Ball(Image):
    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 = StringProperty('')

    def on_touch_down(self, touch):
        if self.close.collide_point(*touch.pos):
            self.manager.current = self.previous


class MenuScreen(Screen):

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


class GameScreen(Screen):

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

    def on_pre_enter(self, *args):
        self.interval = Clock.schedule_interval(self.update, 1.0 / 60.0)

    def on_touch_down(self, touch):
        if self.cog.collide_point(*touch.pos):
            self.manager.get_screen('settings').previous = self.manager.current
            self.manager.current = 'settings'

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

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

    def on_pre_leave(self, *args):
        self.interval.cancel()


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):
        return sm


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