Python之深入解析如何使用Python Kivy实现一个“乒乓球”游戏

一、前言

  • 本文是基于 Kivy 开源跨平台的 Python 框架上创作的,Kivy 开源跨平台的 Python 框架能用于开发多点触控的用户界面程序,允许快速简单的交互设计,非常方便。
  • 那么,如何使用 Kivy 编写一款乒乓球游戏呢?我们将从一个基本的应用程序开始,描述创建这个游戏的每个步骤。
  • Kivy 是用 Python 和 Cython 编写的,基于 OpenGL ES 2,支持各种输入设备并拥有丰富的部件库。使用相同的代码,可直接实现多平台应用,包括 Windows、macOS、Linux、Android 和 iOS。
  • 所有 Kivy 部件都支持多点触控。
  • 实现效果,一睹为快:

Python之深入解析如何使用Python Kivy实现一个“乒乓球”游戏_第1张图片

二、准备

① Python 安装

  • 在 windows 上安装 Python:
    • 下载 Python 的最新版本:访问链接 Python 官网,在 Windows 操作系统上安装 Python 3.10.0,大家也可以自行选择最新版的下载。

在这里插入图片描述

    • 进入下一个页面后,拉到最下面,选择 Windows embeddable package (64-bit) 下载:

在这里插入图片描述

    • 双击下载完成的应用程序,记得一定要勾选 ADD Python 3.7 to PATH,否则 cmd 无法运行 Python,然后选择 Install Now 进行安装,默认会安装 pip,而且将安装到 C 盘(推荐)。如果希望换一个盘安装,可以点击 Customize installation 在里面选择其他的盘。
  • 在 macOS 上安装 Python:
    • 其实 macOS 上是默认安装了 Python 的,只不过版本是 Python2.7,其相对于现在的 Python 3 缺少了很多特性,而且速度也比 Python 3 慢,因此还是建议大家装 Python 3。
    • 同样地,下载 Python 的最新版本,访问链接:Python 官网,在 macOS 操作系统上安装 Python 3.7.4,大家也可以自行选择最新版的下载。

在这里插入图片描述

    • 进入下一个页面后,拉到最下面,选择 macOS 64-bit universal2 installer 下载:

在这里插入图片描述

    • 下载完成后得到一个 pkg 文件,双击打开,按照指示步骤进行安装即可。

② 安装依赖

  • 请选择以下任一种方式输入命令安装依赖:
    • Windows 环境打开 Cmd (开始-运行-CMD);
    • MacOS 环境打开 Terminal (command + 空格输入 Terminal);
    • 如果用的是 VSCode 编辑器或 Pycharm,可以直接使用界面下方的 Terminal:
pip install kivy[base] kivy_examples

三、简单使用 Kivy

  • 首先,简单介绍 Kivy 的基本使用,为游戏创建一个目录和一个名为 main.py 的文件:
# 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 运行该应用程序,它应该只显示一个黑色的窗口。因此我们所做的只是创建一个非常简单的 Kivy 应用程序,它创建了一个 PongGame Widget 类的实例,并将其作为应用程序用户界面的根元素返回。
  • 在这一点上应该把它想象成一个 Widget 的分层树,Kivy 将这个 Widget 放在默认的窗口中。在下一步,我们将通过定义 PongGame 小部件的外观来绘制 Pong 的背景和游戏分数。

四、Kivy - 添加简单图形

  • 我们将使用一个 .kv 文件来定义 PongGame 类的外观,由于应用程序类被称为 PongApp,可以简单地在同一目录下创建一个名为 pong.kv 的文件,当应用程序运行时将会自动加载。
  • 因此,为了定义游戏的外观,创建一个名为 pong.kv 的新文件并添加以下内容:
#:kivy 1.0.9

<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文件的名称,例如 pong.kv,必须与应用程序的名称一致,例如 PongApp(Ap p结尾之前的部分)。
  • 如果现在运行这个应用程序,应该看到中间有一个竖条,还有两个零,那里将显示玩家的分数,如下所示:

Python之深入解析如何使用Python Kivy实现一个“乒乓球”游戏_第2张图片

  • 可以看到,在第一行有:
#:kivy 1.0.9
  • 每个 kv 文件都需要第一行,它应该以 #:kivy 及一个空格开头,然后是它要使用的 Kivy 版本(因此 Kivy 可以确保您至少拥有所需的版本,或者稍后处理向后兼容性)。
  • 再往下看 kv 文件里定义了三个元素,一个 canvas 和两个 label:
    • 先说说两个 label,它们代表的是左右两个数字,设定了 font_size(字体大小), center_x(中心位置), top(离顶部距离),text(文本),此外可以看到 root.width 和 root.top 的使用,这样写的好处是能跟跟随窗口宽度和高度的变化而变化。
    • 另一个元素 canvas,它的下面定义了 Rectangle 参数,意思是向画布添加一个矩形,将矩形的 pos 设置为小部件水平中心左侧 5 个像素,y 设置为 0,这就定义了矩形的显示位置。
  • 矩形的大小 size 设置为宽度为 10 像素,高度为小部件的高度,像这样定义图形的好处是,当值表达式中使用的任何小部件的属性发生变化时,渲染的矩形将自动更新。

五、Kivy - 增加乒乓球球体

  • 现在有了一个基本的乒乓球场(虽然很简陋),但我们仍然需要球拍和一个球来打球。从球开始,添加一个新的 PongBall 类来创建一个小部件,它将成为我们的球并使它弹跳起来。
  • PongBall 类:
class PongBall(Widget):

    # velocity of the ball on x and y axis
    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(0)

    # referencelist property so we can use ball.velocity as
    # a shorthand, just like e.g. w.pos for w.x and w.y
    velocity = ReferenceListProperty(velocity_x, velocity_y)

    # ``move`` function will move the ball one step. This
    # will be called in equal intervals to animate the ball
    def move(self):
        self.pos = Vector(*self.velocity) + self.pos
  • 白球的 kv 配置如下:
<PongBall>:
    size: 50, 50
    canvas:
        Ellipse:
            pos: self.pos
            size: self.size
  • 为了使这一切顺利进行,还必须为球体增加所用的 Property 属性类,如下,是这一步更新后的 python 代码和 kv 文件:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.vector import Vector


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):
    pass


class PongApp(App):
    def build(self):
        return PongGame()


if __name__ == '__main__':
    PongApp().run()
  • kv 文件如下:
#:kivy 1.0.9

<PongBall>:
    size: 50, 50 
    canvas:
        Ellipse:
            pos: self.pos
            size: self.size 

<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"
    
    PongBall:
        center: self.parent.center

六、Kivy - 增加乒乓球体运动

  • 现在我们的目的是让这个球动起来,因此必须定期调用 move 函数让它动起来。使用 Kivy 提供的 Clock 函数可以轻易地做到这一点:
Clock.schedule_interval(game.update, 1.0/60.0)
  • 这一行将导致游戏对象的更新函数每秒被调用 60 次。
  • 不过还有一个问题,想确保 PongBall 的移动函数被定期调用,但是在代码中没有任何对球对象的引用,因为只是通过 kv 文件在 PongGame 类的 kv 规则中添加了它。
  • 由于要做的不仅仅是移动球(比如把球从墙上弹下来,然后再弹到球员的球拍上),我们可能需要为 PongGame 类建立一个更新方法:
class PongGame(Widget):

    def update(self, dt):
        # call ball.move and other stuff
        pass

class PongApp(App):

    def build(self):
        game = PongGame()
        Clock.schedule_interval(game.update, 1.0/60.0)
        return game
  • 然而,这仍然不能改变我们没有对 kv 规则所创建的 PongBall 进行操作的这一事实。为了解决这个问题,可以给 PongGame 类添加一个 ObjectProperty,并将其与 kv 规则中创建的 widget 挂钩。
  • 一旦这样做了,就可以很容易地在更新方法中引用球的属性,甚至可以让它从边缘弹起:
class PongGame(Widget):
    ball = ObjectProperty(None)

    def update(self, dt):
        self.ball.move()

        # bounce off top and bottom
        if (self.ball.y < 0) or (self.ball.top > self.height):
            self.ball.velocity_y *= -1

        # bounce off left and right
        if (self.ball.x < 0) or (self.ball.right > self.width):
            self.ball.velocity_x *= -1
  • 在 kv 文件中将其与代码中设定的 id: ball 映射起来:
<PongGame>:
    ball: pong_ball

    # ... (canvas and Labels)

    PongBall:
        id: pong_ball
        center: self.parent.center

七、Kivy - 球拍移动事件

  • 现在,球正在弹来弹去,唯一缺少的是可移动的球拍和对分数的跟踪。这里不会再去讨论创建类和 kv 规则的所有细节,因为这些概念已经在前面的步骤中涵盖了。
  • 相反,把重点放在如何响应用户的输入而移动球拍上,在 Kivy 中,小部件可以通过实现 on_touch_down、on_touch_move 和 on_touch_up 方法对输入做出反应。默认情况下,Widget 类实现这些方法时,只是在其子部件上调用相应的方法来传递事件,直到其中一个子部件返回 True。
  • 乒乓运动是非常简单的,球拍只需要向上和向下移动。事实上,它是如此简单,甚至不需要让球员小部件自己处理事件,只需为 PongGame 类实现 on_touch_move 函数:
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
  • 我们将在 NumericProperty 中保留每个球员的分数,PongGame 的分数标签通过改变 NumericProperty score 来保持更新,这反过来又会更新 PongGame 的子标签文本属性。
  • 这是如何实现的?因为 Kivy 属性会自动绑定到其对应的kv文件中的任何引用。当球从两侧逃出时,我们将通过 PongGame 类中的更新方法来更新分数并再次发球。
  • PongPaddle 类也实现了一个 bounce_ball 方法,这样球就会根据它击中球拍的位置而产生不同方向的弹跳,非常有意思。下面是 PongPaddle 类的代码:
class PongPaddle(Widget):

    score = NumericProperty(0)

    def bounce_ball(self, ball):
        if self.collide_widget(ball):
            speedup = 1.1
            offset = 0.02 * Vector(0, ball.center_y-self.center_y)
            ball.velocity = speedup * (offset - ball.velocity)
  • 结果如下:

Python之深入解析如何使用Python Kivy实现一个“乒乓球”游戏_第3张图片

  • 到这一步,基本就完成了整个乒乓球游戏的制作。如何,你心动了吗?

你可能感兴趣的:(Python实用宝典,Kivy-添加简单图形,Kivy-增加乒乓球球体,Kivy-增加乒乓球体运动,Kivy-球拍移动事件)