我改编了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
答案 0 :(得分:1)
有两种解决方法。
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
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
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>:
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
类基本上是为您完成此操作的。