Kivy:对象运动/更新问题(Kivy Pong教程改编)

时间:2019-04-09 15:24:56

标签: python python-3.x kivy kivy-language

我改编了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

2 个答案:

答案 0 :(得分:1)

有两种解决方法。

方法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

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.rectupdate_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

摘要-kv

Builder.load_string('''
<SettingsScreen>:

Kivy Canvas » texture

texture
     

表示用于绘制此纹理的纹理的属性   指令。您可以像这样设置新的纹理:

from kivy.core.image import Image

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

通常,您将使用source属性而不是纹理。

答案 1 :(得分:1)

您的代码大部分有效。一个相当简单的解决方法是仅将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的{​​{1}}不能处理绘制其子级。原始的Layout游戏通过将球图像放在Pong Canvas的{​​{1}}中来解决此问题。 Ball类基本上是为您完成此操作的。