本篇文章用於對Kivy框架官方所給出的一個「乒乓球」小游戲的源碼進行簡單地解析。我會盡可能的將方方面面的內容都說清楚。在文章的最下方為官方所給出的這個小游戲的教程以及游戲源碼。
由於篇幅所限,本文只簡單介紹Kivy框架,對於Python語言的簡介,請大家通過別的文章進行學習。
Kivy簡介
Kivy 是基於Python編程語言的一種用於開發跨平台GUI客戶端程序的框架。可以打包成iOS、Android、Windows、OSX等多種平台。目前已經實現一次編碼,到處運行的目的。
Kivy框架主要包含兩種文件,一種為Python源碼文件,一種為KV文件。Python源碼文件用於控制程序的運行邏輯,KV文件用於定義用戶界面。
由於Kivy本質上是使用OpenGL進行界面的繪制的,而且通過對Kivy的經典應用「2048 by kivy」進行了簡單的運行,Kivy所開發出的應用,在性能上還是可圈可點的,從某種程度上來說,要比使用HTML5/JS的應用在性能以及穩定性上,要好很多。
關於Kivy框架的基本使用以及開發環境,可以參考我的另外兩篇文章:
創建應用
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
from random import randint
class PongGame(Widget):
pass
class PongApp(App):
def build(self):
return PongGame()
if __name__ == '__main__':
PongApp().run()
App
類是創建Kivy應用的基礎。我們可以認為它是整個應用的入口,如果在我們的應用中沒有引入這個類的話,我們的應用是沒有辦法運行起來的。Widget
類是創建一個「Widget」的基礎。Widget是我們繪制界面最重要的類,我們的界面元素都應該繼承它,不論是內置好的「文本框」還是用於繪制我們自定義界面元素的「畫布」,都需要引入這個類,才能夠正常工作。PongGame
是一個「界面元素」類,在整個Pong示例中,小球、球拍等,都是界面元素,所不同的僅僅是界面元素的層級關系。PongApp
是我們的「主程序」類,主要用於各種操作以及界面元素的交互等的調度。不建議在這里寫入太復雜的邏輯,因為如果把復雜邏輯寫在這里的話,隨着我們應用的不斷擴展、維護,這個類將逐步陷入「不可維護」的尷尬境地。- 最后一塊用於「啟動應用」。每一個Kivy應用都應該有這段代碼。
在Kivy中,我們使用
XXXApp
來控制應用的運行,使用XXX
來控制應用的行為,比如PongApp
以及PongGame
繪制界面
kv文件是kivy框架所使用的界面描繪文件,類似於c#的xaml。我們可以在kv文件中,添加各種由kivy框架本身所提供的控件,比如button
、label
等。還可以在kv文件中使用canvas
標簽,並在其中繪制各種圖形,比如矩形、圓形等
在kv文件中,對於界面的描繪,是__樹形__的,也就是說,在最頂層需要一個__根節點__用來作為整個應用的根。
#:kivy 1.8.0
<PongGame>:
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"
在上面的kv文件中,
- 第一行
#:kivy 1.8.0
聲明當前kv文件中所用到的kivy的版本。(當前最新的版本是1.9.0 beta) canvas
向應用中添加了一個畫板,用於在界面中繪制乒乓球游戲的各種擋板之類的界面元素。- 之后在
canvas
中對畫板進行設置,包括畫板的位置、尺寸等。 - 每一個根標簽,都將對應於在項目文件中的一個類。
- 在最后加入兩個標簽,並在標簽中設置相應的屬性,這里包括字號、位置、默認文字。
添加小球
小球類
# ...
class PongBall(Widget):
# 小球在x、y軸上的速度
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
# 創建索引屬性列表,以精簡對坐標屬性的使用。
velocity = ReferenceListProperty(velocity_x, velocity_y)
# ``move`` 方法將移動小球一步。
# 這個方法將會在需要小球移動的時候被調用。
def move(self):
self.pos = Vector(*self.velocity) + self.pos
# ...
- 為了讓我們的小球會動,我們需要給小球一個位置,然后控制這個位置以一定的速度進行改變,從而實現讓小球移動的目的。
ReferenceListProperty
允許創建一個由其他屬性構成的__元組__。在這里,我們提供了兩個數值型屬性,用於標示坐標。- 在PongBall類中,我們提供用於控制速度與位置的類屬性,以及一個用於控制移動的方法。
kivy.vector.Vector(*largs)
用於表示一個矢量。利用矢量屬性與當前的位置,可以控制移動的方向以及速度。
繪制小球
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
加入球拍
繪制球拍
<PongPaddle>:
size: 25, 200
canvas:
Rectangle:
pos:self.pos
size:self.size
...
<PongGame>:
...
player1: player_left
player2: player_right
...
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
事件處理
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 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()
# 敲擊小球
self.player1.bounce_ball(self.ball)
self.player2.bounce_ball(self.ball)
# 限定小球在屏幕中的上下邊界
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