本文主要通过依据kivy的官方文档进行学习,有关安装的方面网上都有,这里就不赘述了。这节学的是一个简单的弹球游戏。
首先新建一个简单的文件main.py,然后测试运行。
from kivy.app import App
from kivy.uix.widget import Widget
class PongGame(Widget):
pass
class PongApp(App):
def build(self):
return PongGame()
if __name__ == '__main__':
PongApp().run()
输入python main.py运行
成功运行
需要建立一个类似css的图形文件,因为类名叫做PongApp,就把新建图形文件叫做pong.kv吧。注意这里的图形文件名字要和前面的类名相对应,只去掉后面的App部分,不过大小写是没有限制的。这样才能正确显示图形。
pong.kv内容:
#:kivy 1.11.1
:
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: "0"
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: "0"
代码中第一行声明kivy的版本
...
接下来开始定义所有PongGame实例都适用的规则。
:
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
这其中,self.center_x -5,0表示横条在中心位置偏左5个像素,size:10,self.height表示横条的宽度是10个像素,高度是小部件的高度。
这些部件在调整窗口大小的时候都是可以自动进行调整的。
图形是中间一个竖线,两边显示的是“0”,也就是得分。
首先创建一个PongBall的类:
import kivy
from kivy.app import App
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.vector import Vector
from kivy.uix.widget import Widget
kivy.require('1.11.1') # replace with your current kivy version !
class PongBall(Widget):
velocity_x=NumericProperty(0)
velocity_y=NumericProperty(0)
velocity=ReferenceListProperty(velocity_x,velocity_y)
def mov(self):
self.pos=Vector(*self.velocity) + self.pos
class PongGame(Widget):
pass
class PongApp(App):
def build(self):
return PongGame()
if __name__ == '__main__':
PongApp().run()
在pong.kv中添加球
#:kivy 1.11.1
:
size=50,50
canvas:
Ellipse:
pos:self.pos
seze:self.size
:
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: "0"
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: "0"
PongBall:
center: self.parent.center
接下来要让球动起来,我们现在有一个球,甚至有个move的函数,现在我们要来完善这个函数。有个叫做Clock的库很厉害,能够帮我们完成很多项功能。
Clock.schedule_interval(game.update, 1.0/60.0)
比如上面这行代码,能够让game对象的update函数每秒执行60次。
然后添加一个叫做serve_ball的方法来设置球的速度和重置位置。
至此,可以实现球自己运动,并且在碰到边缘的时候反弹
import kivy
from kivy.app import App
from kivy.properties import NumericProperty, ReferenceListProperty,ObjectProperty
from kivy.vector import Vector
from kivy.uix.widget import Widget
from kivy.clock import Clock
from random import randint
kivy.require('1.11.1') # replace with your current kivy version !
class PongBall(Widget):
velocity_x=NumericProperty(0)
velocity_y=NumericProperty(0)
velocity=ReferenceListProperty(velocity_x,velocity_y)
def move(self):
self.pos=Vector(*self.velocity) + self.pos
class PongGame(Widget):
ball = ObjectProperty(None)
def serve_ball(self):
self.ball.center = self.center
#在Vector中设置速度,rotate中应该是设置初始方向
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.velocity_y *= -1
# 如果球碰到左边或者右边
if (self.ball.x < 0) or (self.ball.right > self.width):
self.ball.velocity_x *= -1
class PongApp(App):
def build(self):
game=PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
if __name__ == '__main__':
PongApp().run()
pong.kv中:
#:kivy 1.11.1
:
size:50,50
canvas:
Ellipse:
pos:self.pos
size:self.size
:
ball:pong_ball
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: "0"
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: "0"
PongBall:
id: pong_ball
center: self.parent.center
这部分主要专注于用户的行为相应,还有记分牌的变化。
为了响应用户的输入,主要使用on_touch_down、on_touch_move、no_touch_up方法。
然后使用NumericProperty保存比分信息
把每个玩家的分数保存在NumericProperty中。通过更改NumericProperty score,可以更新PongGame的分数标签,从而更新PongGame子标签文本属性。之所以会发生这种绑定,是因为Kivy属性自动绑定到它们相应kv文件中的任何引用。当球跑出边线时,我们将更新比分并通过更改PongGame类中的update方法再次发球。PongPaddle类还实现了bounce_ball方法,因此根据击球位置的不同,球的弹跳方式也不同。
最终代码:
main.py中:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import (
NumericProperty, ReferenceListProperty, ObjectProperty
)
from kivy.vector import Vector
from kivy.clock import Clock
class PongPaddle(Widget):
score = NumericProperty(0)
def bounce_ball(self, ball):
if self.collide_widget(ball):
vx, vy = ball.velocity
offset = (ball.center_y - self.center_y) / (self.height / 2)
bounced = Vector(-1 * vx, vy)
vel = bounced * 1.1
ball.velocity = vel.x, vel.y + offset
class PongBall(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class PongGame(Widget):
ball = ObjectProperty(None)
player1 = ObjectProperty(None)
player2 = ObjectProperty(None)
def serve_ball(self, vel=(4, 0)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
# bounce of paddles
self.player1.bounce_ball(self.ball)
self.player2.bounce_ball(self.ball)
# bounce ball off bottom or top
if (self.ball.y < self.y) or (self.ball.top > self.top):
self.ball.velocity_y *= -1
# went of to a side to score point?
if self.ball.x < self.x:
self.player2.score += 1
self.serve_ball(vel=(4, 0))
if self.ball.x > self.width:
self.player1.score += 1
self.serve_ball(vel=(-4, 0))
def on_touch_move(self, touch):
if touch.x < self.width / 3:
self.player1.center_y = touch.y
if touch.x > self.width - self.width / 3:
self.player2.center_y = touch.y
class PongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
if __name__ == '__main__':
PongApp().run()
pong.kv中
#:kivy 1.11.1
:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
:
size: 25, 200
canvas:
Rectangle:
pos:self.pos
size:self.size
:
ball: pong_ball
player1: player_left
player2: player_right
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str(root.player1.score)
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: str(root.player2.score)
PongBall:
id: pong_ball
center: self.parent.center
PongPaddle:
id: player_left
x: root.x
center_y: root.center_y
PongPaddle:
id: player_right
x: root.width - self.width
center_y: root.center_y