1Pong Game Tutorial — Kivy 2.3.0 documentation
Introduction¶
Welcome to the Pong tutorial 欢迎来到 乒乓球 导师辅导课
This tutorial will teach you how to write pong using Kivy. We’ll start with a basic application like the one described in the Create an application and turn it into a playable pong game, describing each step along the way.
这导师辅导课将教你如何写一个乒乓球使用KIVY。 我们将开始同一个基础的应用程序 像在Create an apllication描述的一样,并且将它变为一个可以玩儿的乒乓球游戏, 描述每一步伴随着进程。
Here is a check list before starting this tutorial:
这是一个检查列表 在开始这导师辅导课之前:
-
You have a working Kivy installation. See the Installing Kivy section for detailed descriptions
你有一个正在运行的kivy 安装程序。 细节描述请看 安装kivy 节 -
You know how to run a basic Kivy application. See Create an application if you don’t.
你知道如何运行一个基础的kivy程序。 看 创造一个应用 如果你不知道的。
If you have read the programming guide, and understand both basic Widget concepts (A Simple Paint App) and basic concepts of the kv language (Kv language), you can probably skip the first 2 steps and go straight to step 3.
如果你读了项目知道, 并且都明白 基础组件概念(一个简单地绘画APP) 和kv语言基础概念, 你大概可以跳过一开始2步 并且直接径直去到第三步。
Note 注意
You can find the entire source code–and source code files for each step–in the Kivy examples directory under tutorials/pong/.
你可以找到整个源码-- 并且 每一步的源码文件 --- 在kivy案例文件夹的tutorials/pong/
Ready? Sweet, let’s get started!
准备好了吗? 甜心,让我们开始吧!
Getting Started¶ 开始
Getting Started 开始
Let’s start by getting a really simple Kivy app up and running. Create a directory for the game and a file named main.py
让我们开始以一个真正的简单的kivyapp 开始和运行。 为游戏创建一个文件夹和一个叫main.py的文件。
from kivy.app import App
from kivy.uix.widget import Widgetclass PongGame(Widget):passclass PongApp(App):def build(self):return PongGame()if __name__ == '__main__':PongApp().run()
Go ahead and run the application. It should just show a black window at this point. What we’ve done is create a very simple Kivy App, which creates an instance of our PongGame
Widget class and returns it as the root element for the applications UI, which you should imagine at this point as a hierarchical tree of Widgets. Kivy places this widget-tree in the default Window. In the next step, we will draw the Pong background and scores by defining how the PongGame widget
looks.
继续和运行app。 它应该只展示一个黑色的窗口在这时。 我们要做的时创造一个非常简单的KivyAPP, 这app创造了一个我们PongGame组件类的案例, 并且返回它作为应用程序UI的一个根类元素, 你应该想象到在这点作为一个分等级的组件树。Kivy 放置 这个 组件树在默认的窗口。 在下一步, 我们将绘画乒乓球背景和分数通过定义如何PongGame 组件外貌。
Add Simple Graphics¶ 添加简单的图像
Creation of pong.kv pong.kv 的创造
We will use a .kv file to define the look and feel of the PongGame
class. Since our App class is called PongApp
, we can simply create a file called pong.kv
in the same directory that will be automatically loaded when the application is run. So create a new file called ``pong.kv`` and add the following contents.
我们将使用一个.kv文件 来定义 PongGame类的 景象 和 使用。 自从我们App类被叫作PongApp,我们可以简单地创造一个文件叫做 pong.kv 在相同的文件夹, 这文件夹将自动的被运行当应用程序运行时。 因此创造一个新的文件 叫 “pong.kv” 并且添加以下的内容。
#:kivy 1.0.9<PongGame>: canvas:Rectangle:pos: self.center_x - 5, 0size: 10, self.heightLabel:font_size: 70 center_x: root.width / 4top: root.top - 50text: "0"Label:font_size: 70 center_x: root.width * 3 / 4top: root.top - 50text: "0"
Note 注意
COMMON ERROR: The name of the kv file, e.g. pong.kv, must match the name of the app, e.g. PongApp (the part before the App ending).
常见错误: kv文件的名字, 例如 pong.kv , 必须匹配app的名字, 例如 PongApp(之前App结束部分有--上段代码)
If you run the app now, you should see a vertical bar in the middle, and two zeros where the player scores will be displayed.
如果你运行app现在, 你应该看见一个垂直的块在中间, 和俩零 在玩家分数将被展示的地方。
Explaining the Kv File Syntax¶ kv文件语法的说明
Before going on to the next step, you might want to take a closer look at the contents of the kv file we just created and figure out what is going on. If you understand what’s happening, you can probably skip ahead to the next step.
在继续下一步之前,你可能像近一点看看我们刚创造的kv文件的内容,并且弄明白正在干啥勒。如果你明白目前情况,你大概可以跳过继续到下一步。
On the very first line we have: 一开头的第一步我们有的:
#:kivy 1.0.9
This first line is required in every kv file. It should start with #:kivy
followed by a space and the Kivy version it is intended for (so Kivy can make sure you have at least the required version, or handle backwards compatibility later on).
这第一条线是被需求的在每个kv文件里。 它应该始同 #:kivy 被跟随着空格 和 Kivy版本。 Kivy版本是蓄意的(因此 Kivy 可以确保你至少有被要求的版本,或者操控回溯到兼容的后续版本等。)
After that, we begin defining rules that are applied to all PongGame
instances:
在那之后,我们开始定义所有PongGame案例应用的规则:
<PongGame>:...
Like Python, kv files use indentation to define nested blocks. A block defined with a class name inside the <
and >
characters is a Widget rule. It will be applied to any instance of the named class. If you replaced PongGame
with Widget
in our example, all Widget instances would have the vertical line and the two Label widgets inside them because it would define these rules for all Widget instances.
像Python一样,kv文件使用缩进来定义嵌套块。 一个block区块定义一个在<>里的类名,这是Widget组件规则。 它将被应用在任何被命名类的案例。如果你更换我们案例中的PongGame和组件,所有的组件案例应该有这 竖直的线 和两个标签组件,因为它们定义这些规则为了所有的组件案例。
Inside a rule section, you can add various blocks to define the style and contents of the widgets they will be applied to. You can:
在一个规则节内, 你可以添加不同的块来定义将要被应用的组件们的样式和内容。你可以:
-
set property values 设置属性值
-
add child widgets 增加子类组件
-
define a canvas section in which you can add Graphics instructions that define how the widget is rendered. 定义一个画布节, 在这个画布中你可以增加定义如何组件渲染的图形用法说明。
The first block inside the <PongGame>
rule we have is a canvas block:
在<PongGame>规则内的第一块我们所拥有的是画布块:
<PongGame>:canvas:Rectangle:pos: self.center_x - 5, 0size: 10, self.height
So this canvas block says that the PongGame
widget should draw some graphics primitives. In this case, we add a rectangle to the canvas. We set the pos of the rectangle to be 5 pixels left of the horizontal center of the widget, and 0 for y. The size of the rectangle is set to 10 pixels in width, and the widget’s height in height. The nice thing about defining the graphics like this, is that the rendered rectangle will be automatically updated when the properties of any widgets used in the value expression change.
因此这个画布块说明 这PongGame 组件应该画一些图形基本体。 在这个案例中,我们增加一个长方形到画布中。 我们设置长方形的位置是 组件水平中心的左边5像素, 并且y坐标是0. 长方形坐标是被设定为10像素宽, 并且组件的高度是 整个高度。 像这样定义图形们的好事是, 渲染图形将自动更新当任何被使用的组件的属性们在表达值改变的时候。
Note 请注意:
Try to resize the application window and notice what happens. That’s right, the entire UI resizes automatically. The standard behaviour of the Window is to resize an element based on its property size_hint. The default widget size_hint is (1,1), meaning it will be stretched 100% in both x-direction and y-direction and hence fill the available space. Since the pos and size of the rectangle and center_x and top of the score labels were defined within the context of the PongGame
class, these properties will automatically update when the corresponding widget properties change. Using the Kv language gives you automatic property binding. :)
尝试着重新定义应用窗口的大小, 并且注意将要发生什么。 是的,整个UI自动重新定义大小了。 窗口的表现标准是重新定义一个大小元素基于它的 size_hint 属性。 组件size_hint的默认值是(1,1), 意味着它将被拉伸100% 在x y两个方向, 并且因此之后,填满恰当的空白。 自从长方形的位置和大小 和center_x 以及 上部的分数标签在PongGame类的内容中都被定义好了以后, 这些属性们将自动地更新到组件属性们相一致的时候。 使用kv一眼给你自动地属性绑定。
The last two sections we add look pretty similar. Each of them adds a Label widget as a child widget to the PongGame
widget. For now, the text on both of them is just set to “0”. We’ll hook that up to the actual score once we have the logic implemented, but the labels already look good since we set a bigger font_size, and positioned them relatively to the root widget.
上两节我们增加了看起来相同的美丽画面。 他们的每个增加了一个标签组件作为一个子类组件到PongGame组件中。 现在开始, 它们俩的文本 都被 设置为0. 我们将其连接到真正的分数, 一旦当我们有逻辑的执行时, 但是标签们已经看起来不错自从我i们设定了一个更大的font_size, 并且相对放置在根组件上了。 [已经很好了,你还想干嘛呀?]
The root
keyword can be used inside the child block to refer back to the parent/root widget the rule applies to (PongGame
in this case):
root 键 可以被使用在子块内来回溯到 规则应用到的 父类/根类 组件。
<PongGame>:# ...Label:font_size: 70center_x: root.width / 4top: root.top - 50text: "0"Label:font_size: 70center_x: root.width * 3 / 4top: root.top - 50text: "0"
Add the Ball¶ 添加球
Add the Ball
Ok, so we have a basic pong arena to play in, but we still need the players and a ball to hit around. Let’s start with the ball. We’ll add a new PongBall class to create a widget that will be our ball and make it bounce around.
好了,现在我们有一个基础的区域来玩乒乓球了,但我们仍需要玩家们和一个球来围绕着击打。 让我们从球开始。 我们将添加一个新的乒乓球类 来创建一个将是我们的球的组件,并且让这球绕着跳。
PongBall Class¶
Here is the Python code for the PongBall class:
这儿是PongBall类的Python代码
class PongBall(Widget):# velocity of the ball on x and y axis# 球在x 和y 轴的 速率velocity_x = NumericProperty(0)velocity_y = NumericProperty(0)# referencelist property so we can use ball.velocity as# 参考表属性 因此我们可以使用球。 速率作为一个速记的, 像 位置, x y。。。# a shorthand, just like e.g. w.pos for w.x and w.yvelocity = ReferenceListProperty(velocity_x, velocity_y)# ``move`` function will move the ball one step. This# will be called in equal intervals to animate the ball# '''move''' 功能将移动球一步, 着将被召唤有间隔的平等的来动画这球def move(self):self.pos = Vector(*self.velocity) + self.pos
And here is the kv rule used to draw the ball as a white circle:
并且这里是kv规则 被用来画球 作为一个白色的圆圈。
<PongBall>:size: 50, 50canvas:Ellipse:pos: self.possize: self.size
To make it all work, you also have to add the imports for the Properties Property classes used and the Vector.
为了让它都运行,你也不得不添加 imports 来导入Properties 属性类们 , 和Vector
Here is the entire updated python code and kv file for this step:
这是这步整个更新的python代码和kv文件
main.py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.vector import Vectorclass 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.posclass PongGame(Widget):passclass PongApp(App):def build(self):return PongGame()if __name__ == '__main__':PongApp().run()
pong.kv:
#:kivy 1.0.9<PongBall>:size: 50, 50 canvas:Ellipse:pos: self.possize: self.size <PongGame>:canvas:Rectangle:pos: self.center_x - 5, 0size: 10, self.heightLabel:font_size: 70 center_x: root.width / 4top: root.top - 50text: "0"Label:font_size: 70 center_x: root.width * 3 / 4top: root.top - 50text: "0"PongBall:center: self.parent.center
Note that not only a <PongBall> widget rule has been added, but also a child widget PongBall in the <PongGame> widget rule.
注意,不仅仅是一个 <PongBall>组件规则已经被添加, 还有一个子类 PongBall在<PongGame>组件规则里。
from kivy.vector import Vector 中的Vector 是干啥的
在 Kivy 框架中,Vector
类是用于表示和操作二维或三维向量的。Vector
类提供了许多有用的方法来处理向量,如加法、减法、点积、叉积(仅限三维)和归一化等。
在图形编程和物理模拟中,向量是非常常见的数学工具。它们可以表示位置、方向、速度、加速度等。
以下是一些 Vector
类中常见的方法和它们的作用:
1 加法:两个向量相加会产生一个新的向量,其各个分量分别是两个向量对应分量的和。
v1 = Vector((1, 2))
v2 = Vector((3, 4))
result = v1 + v2 # result is (4, 6)
2 减法:一个向量减去另一个向量会产生一个新的向量,其各个分量分别是两个向量对应分量的差。
v1 = Vector((1, 2))
v2 = Vector((3, 4))
result = v1 - v2 # result is (-2, -2)
3 点积:两个向量的点积是一个标量值,等于两个向量对应分量乘积的和。
v1 = Vector((1, 2))
v2 = Vector((3, 4))
dot_product = v1.dot(v2) # dot_product is 1*3 + 2*4 = 11
4 叉积(仅限三维):两个三维向量的叉积会产生一个新的三维向量,这个向量垂直于原来的两个向量。
5 长度:向量的长度(或称为模)是一个标量值,表示向量从原点到其末端的距离。
6 归一化:将一个向量转化为单位向量(长度为1的向量),同时保持其方向不变。
v1 = Vector((3, 4))
normalized = v1.normalize() # normalized is (3/5, 4/5)
在 Kivy 中,Vector
类通常用于处理与图形界面相关的位置和大小信息,以及动画和物理模拟中的速度和加速度等。
Adding Ball Animation¶
Making the ball move
Cool, so now we have a ball, and it even has a move
function… but it’s not moving yet. Let’s fix that.
Scheduling Functions on the Clock¶
We need the move
method of our ball to be called regularly. Luckily, Kivy makes this pretty easy by letting us schedule any function we want using the Clock and specifying the interval:
Clock.schedule_interval(game.update, 1.0/60.0)
This line for example, would cause the update
function of the game object to be called once every 60th of a second (60 times per second).
Object Properties/References¶
We have another problem though. We’d like to make sure the PongBall has its move
function called regularly, but in our code we don’t have any references to the ball object since we just added it via the kv file inside the kv rule for the PongGame
class. The only reference to our game is the one we return in the applications build method.
Since we’re going to have to do more than just move the ball (e.g. bounce it off the walls and later the players racket), we’ll probably need an update
method for our PongGame
class anyway. Furthermore, given that we have a reference to the game object already, we can easily schedule its new update
method when the application gets built: