第三章 Python Kivy 学习 -- Kivy官方入门教程Pong Game

系列文章目录

第一章 Python Kivy 学习 – Kivy介绍及环境安装
第二章 Python Kivy 学习 – Kivy项目开发原理(待编辑)
第三章 Python Kivy 学习 – Kivy官方入门教程Pong Game



第三章 Python Kivy 学习 – Kivy官方入门教程Pong Game

介绍了基本的设计模式和应用程序开发过程。
官方说法:是整个学习进程图中最重要的应用实例。

说明:本人运行的环境已经预先安装了
Python3.10 编译器
Anaconda python环境管理器
PyCharm python代码编辑运行环境

python中包有:
kivy 今天讲解的主角,python编辑android的为数不多的工具
python-for-android 目前还没有用到

请参考使用。


一、入门

该教程使用的是逐步细化的开发模式,会多次重新编辑和完善代码。以求循序渐进的讲解kivy知识。

首先:利用Anaconda 新建环境learn
其次:利用PyCharm 新建项目gameAndroid并配置其编译环境为learn
再次:使用PyCharm新建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()

继续运行应用程序。此时它应该只显示一个黑色窗口。
1、它创建了PongGame类,其继承了超级类Widget ,Widget 是一个控件(kivy一般叫做小部件,我不太顺口,都叫控件了)的标准类,所有控件类都是Widget 的子类。
2、主文件的引导过程:详见最后详解。
3、此文件所创造的类中,app类只是负责引导和设置属性作用,真正的控件,也就是屏幕显示的东西是Widget类。
4、一般kivy中是以 Widget,Screen,Layout三种控件类作为屏幕的图形显示的根类。Screen是屏幕管理器类型,Layout是多种布局类的类型集合。

二、添加简单的图形

接下来 我们将通过定义外观来绘制 游戏的 背景和分数。

在项目目录中新建文件"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"

1、pong.kv由你所定义的类 PongApp(App)自动引用,仅由文件名即可自动执行。当然是指app类才有这个待遇,而一个kivy才有一个app类,其它文件想要套用,请使用Builder方法。例如:Builder.load_file(‘Speech.kv’) 语句
2、在kivy中 .kv 文件用来定义类的外观和相关属性设置

下面分别介绍其语法

1、版本声明

 #:kivy 1.0.9
 

此行在.kv文件中必须含有,它应该且必须以空格开头,而一般下面都空一行。(python编程最讲究的是优雅,最大的优雅是代码的规范性。)
这行代码向kivy声明此kv文件用到的最低版本号,便于打包时将足够的版本内容打包进应用来支持运行。

2、Widget 型的类的外形定义

<PongGame>:
    ...

<>中间定义的是Widget型PongGame类,利用换行缩进来表示以下都是编辑这个类的内容。
本示例中:是PongGame类的外形定义。
如果你使用则会重新定义Widget类的外形和属性,将导致所有Widget的子类的外形均为此定义。这个方法一般用于定义label等常用控件,且一个app中要求有一致性外观的情况。

在此文件中,你可以如下操作:

1、设置类的控件类型的属性值定义
2、添加子小部件
3、定义一个画布部分,您可以在其中添加定义小部件如何呈现的图形指令。
4、定义动作的连接方法(实质是设置属性)

3、定义一块画布canvas及绘制矩形Rectangle

<PongGame>:
    canvas:
        Rectangle:
            pos: self.center_x - 5, 0
            size: 10, self.height

canvas: 创建画布
Rectangle:画布内创建矩形
pos: self.center_x - 5, 0

pos为一个要引用的位置,是常用的属性,用于获取/设置矩形位置的属性。设置为小部件水平中心左侧 5 个像素,垂直为0。
self.为本控件或本小部件

size: 10, self.height

矩形大小为宽度10个像素,高度为小部件高度

1、最好以相对或变量来定义部件的大小,当值表达式中使用的任何小部件的属性发生变化时,渲染的矩形将自动更新。
2、在kivy中我没看到渲染的功能,现在是只要属性变化,它自动渲染,这个很酷!
3、kivy中有三个常用和一个不常用的称呼语:
root,指本控件的父控件
self,指本控件
app,永远都是指向你的app类的,没啥用
children,指本控件的子控件,是个list,需要慢慢研究他的功能和用法。

4、标签Label

<PongGame>:
    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"

lable标签控件
font_size 字号
center_x 中心X轴位置
root.width 父部件宽度的3/4位置
top 上方位置
text 文字

位置、大小、颜色及连接方法属性是kivy中的重要属性设置
位置和大小,详见kivy 控件的代码新建及属性设置
颜色正在研究,连接方法有些坑也正在踩,时间戳:20220604

三、乒乓球类

3.1 main.py中输入如下代码定义乒乓球PongBall类

class PongBall(Widget):

    # 定义球在X,Y轴上的速度
    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(0)

    # 用ball.velocity 作为参考属性
    # 表示ball在两个维度的综合速度
 
    velocity = ReferenceListProperty(velocity_x, velocity_y)

    # ``move`` 函数将以*self.velocity等距移动的形式使球移动
    def move(self):
        self.pos = Vector(*self.velocity) + self.pos

class kivy.properties.NumericProperty()

单个数字属性,一般做为坐标的单值属性,必须是数字格式。

ReferenceListProperty (NumericProperty(),NumericProperty())

属性集合,可含有多个相同的属性单元,数据类型元组

a = kivy.vector(x,y)

2D的矢量坐标,数据类型为python list,可以用a[0],访问x

Property类的属性,是kivy的一个特征,很好用,主要作用是可以定义一个类型已知,但内容未知的属性,这个属性可以被其它类或方法调用,可以有kv文件定义其规则和内容,可以在类内的方法被调用,

这里有个坑 就是在内容未知的时候,请不要在类内调用,可以定义方法有在类外调用,但类内直接调用会出错,因为他没有定义完 是个空的无类型的属性,会被报错。

3.2 main.py中如下添加引用,以保证运行正常

from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.vector import Vector

import kivy.properties # kivy的属性包

不要将Kivy 的属性与Python 的属性混淆。

Kivy 的属性类支持:
1、值检查/验证

当您为属性分配新值时,会根据验证约束检查该值。例如,一个NumericProperty将检查您的值是否为数字类型。这可以在早期防止许多错误。

2、观察者模式

您可以指定属性值更改时应该发生的情况。您可以将自己的函数绑定为对Property的更改和回调。例如,如果您希望在小部件的属性更改时调用一段代码,您可以bind为它创建一个函数连接。

3、更好的内存管理

同一属性的实例在多个小部件实例之间共享。

3.3 pong.kv中输入如下代码,为PongBall类设定规则

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

Ellipse 圆形
pos: self.pos 设定本控件的标的位置为本控件位置
这里的本控件不是指Ellipse 圆形,也不是canvas画布,而是PongBall的Widget控件。一定注意哦!
size大小设置为本控件大小

四、让球动起来(给球添加动画)

4.1 时钟调度功能

上节定义的球并没有按照方法move()来移动,实际上我们只是定义了类,并未实例化和调用其方法。现在我们有一个函数,使得一定的时间间隔来调用某方法。

Clock.schedule_interval(game.update, 1.0/60.0)

使game.update对象的函数每 60 秒被调用一次。

4.2 动画实现的逻辑

要实现的功能

初始时球在一个位置向一侧屏幕运动,
当触碰屏幕边缘时判定本方输,对方积一分。
当碰到球拍时可以弹起,弹起的方向需要计算。
球拍可以移动(由触屏自行移动)

建立球拍类

class PongPaddle(Widget):

    score = NumericProperty(0)
    """定义记录得分字段属性,数字格式"""

    def bounce_ball(self, ball):
        if self.collide_widget(ball):
        """kivy.uix.widget.Widget
               def collide_widget(self, wid: {x, right, y, top}) -> bool
               检查另一个控件同本控件是否碰撞接触"""
            vx, vy = ball.velocity
            offset = (ball.center_y - self.center_y) / (self.height / 2)
            """kivy.uix.widget.Widget.center_y 
               获取控件的中心点y坐标
               "kivy.uix.widget.Widget.height
               获取控件的高度坐标
               两个控件的距离除以球拍高度的一半,反弹y轴变量"""
            bounced = Vector(-1 * vx, vy)
            """将球的X轴反向"""
            vel = bounced * 1.1
            """增加速度1.1倍"""
            ball.velocity = vel.x, vel.y + offset
            """返回给球的综合速度值,y轴需要增加反弹变量"""

kv文件中插入对PongPaddle的定义

<PongPaddle>:
    size: 25, 200
    canvas:
        Rectangle:
            pos: self.pos
            size: self.size

4.3 全部代码(整体讲解)

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)):
   		"""定义球的待服务函数,用于发球,以(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

        # 积分并在得分处发球
        if self.ball.x < self.x:
            self.player2.score += 1
            self.serve_ball(vel=(4, 0))
        if self.ball.right > 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()

代码中的引用关系如下:

代码结尾

PongApp().run(),调用了页面主类PongApp

PongApp()

实例化PongGame类和其serve_ball()方法,实现背景,发球
定义时钟调度功能,调用执行PongGame.update()方法
最终返回实例化的PongGame类,记得一定要返回,否则无法显示已经定义的类。

PongGame类

方法中定义了一个名字为ball和player1、player2的3个属性值

1、ball的kv文件定义中进行了PongBall的直接实例化引用,请详见kv文件。所以ball具有了PongBall一切属性并且使其内置为PongGame类的一个成员属性值。
2、同样player1、player2同样方法将PongPaddle类引用并实例化

serve_ball()

此方法用于发球和重新发球来调用

PongGame.update()

1、作为本游戏的主调度函数,起到连续操作的作用
2、self.ball.move(),首先调用球移动,使得球动起来
3、bounce_ball(),调用判断是否碰撞的函数
4、判断打到顶部和底部的处理方法
5、判断得分并给得胜方发球

on_touch_move(self, touch)

在Kivy中,小部件可以通过编辑和执行on_touch_down,on_touch_move和on_touch_up方法来对输入做出反应。
默认情况下,小部件类仅调用其所有子小部件上的相应方法来传递事件,直到其中一个返回true,从而实现了这些方法。

pong.kv的全部代码

#:kivy 1.0.9

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

一个类的定义可以直接被另一个类定义中的内容引用为小控件。
id是一个控件在本控件中的名称,可以被本定义使用,也可以外部引用。

文档及参考资料:

Kivy官方教程 https://kivy.org/doc/stable/tutorials/pong.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值