Python-kivy implements a simple pinball game (including improvements to common rules and simple beautification of the interface)

Table of contents

 Foreword:

1. A brief description of the rules

(1) About collision

(2) About Scoring

2. Improvements to the rules

(1) About the running of the ball

(2) About the addition of mentally handicapped AI

(3) Beautification of the interface

 (4) Add background sound effects and music

3. Emphasis on other details

(1) widget component

(2) Canvas and labels in kv

4. Complete code

5. Insufficient summary


 Foreword:

        This is still a project in my school's study curriculum, because I also have to take the postgraduate entrance examination, so I didn't spend too much time doing good projects. So I made some simple improvements based on a table tennis game on the Internet. The following are the documents and source code download addresses I refer to, and you can pick them up if you need them:

GitHub - attreyabhatt/Kivy-Pong-Game: Pong game created using Kivy https://github.com/attreyabhatt/Kivy-Pong-Game

        If you want to understand the project I am doing, you can finally read the following document, which introduces its most basic operating logic: 

Pong game tutorial — Kivy 2.1.0 documentation https://kivy.org/doc/stable/tutorials/pong.html


1. A brief description of the rules

(1) About collision

        As we all know, the collision logic of the pinball game: when the ball hits the board or the boundary, it will bounce, the vertical speed will be reversed when it hits the upper and lower boundaries, and the horizontal speed will be reversed when it hits the left and right boundaries.

(2) About Scoring

        There are various scoring rules. First of all, you can define to add points when you touch the board, or you can add points to the opponent when you touch the border of your own side, etc. Points can also be set.

2. Improvements to the rules

(1) About the running of the ball

        The running process of the ball is logical, which will make the player predict the direction of the ball when playing, which greatly reduces the difficulty, so I set up a logic of irregular running here, and the speed of the ball and size will change randomly over time. Let's explain it with code:

        I first defined a few parameters to facilitate subsequent changes in speed and size:

speed_x = NumericProperty(0)  # 球的水平速度属性
speed_y = NumericProperty(0)  # 球的垂直速度属性
speed = ReferenceListProperty(speed_x, speed_y)  # 球的速度向量属性
r =  NumericProperty(0)   #球的大小属性
max_speed = 5  # 球的最大速度

        Next, the move function updates the position of the ball in real time. The randomize function means that the speed of the ball is constantly changing. The positive numbers of vel_y and vel_x represent the upward and rightward speeds, and the negative numbers are vice versa. The daxiao function makes the ball change randomly between 20*20 and 80*80.

 def move(self):
        self.pos = Vector(*self.speed) + self.pos  # 根据速度更新球的位置

 def randomize(self):
        # 随机化速度
        vel_x = uniform(-5, 5)  # 在[-5, 10]范围内生成一个随机数作为水平速度
        vel_y = uniform(-5, 5)  # 垂直速度
        self.speed = Vector(vel_x, vel_y).normalize() * self.max_speed  # 设置球的速度向量,并将其归一化后乘以最大速度


 def daxiao(self):
        # 随机化大小
        r = uniform(20, 80)
        self.size = r , r # 在[20, 80]范围内生成一个随机数作为球的大小
        print(self.size)

        Then call it in the subsequent class

def serve_ball(self):
     self.ball.center = self.center  # 将球的中心位置设置为窗口的中心位置
     self.ball.randomize()  # 随机化球的速度
     self.ball.daxiao()     # 随机化球的大小
     self.timer = 0  # 计时器初始化为0

(2) About the addition of mentally handicapped AI

        Why is it called mentally handicapped AI? As the name suggests, its implementation logic is too simple. We only need to let the center of one of the boards move with the center of the ball, and set the running speed. Isn’t it very simple Ababa (• • )~

def move_player2(self):
        # 根据球的位置移动玩家2的挡板
        if self.ball.center_y < self.player2.center_y:
            self.player2.center_y -= 5  # 将玩家2的挡板向下移动5个单位
        elif self.ball.center_y > self.player2.center_y:
            self.player2.center_y += 5

        Then remember to call it in front, so that the board will move with our ball, so that you don't have to control two players by yourself. Of course, you can also set the board of player1 to automatic, so that you can watch the two boards and play by yourself.

(3) Beautification of the interface

        Regarding the beautification of the interface, we mainly modify the kv file. The following is my complete kv file. We can set the size, color, etc. For the specific parameters, you can refer to it online.

<PongBall>:
    canvas:
        Color:
            rgba: .9,.9,.1 ,9
        Ellipse:
            pos: self.pos
            size: self.size
            source: "出生.jpg"

<PongPaddle>:
    size: 25, 150
    canvas:
        Color:
            rgba: 1, 1, 1, .9
        Rectangle:
            pos: self.pos
            size: self.size
            source: "无标题.png"

<PongGame>:
    ball: pong_ball
    player1: player_left
    player2: player_right
    canvas:
        Color:
            rgba:1, 1, 1, .9
        Rectangle:
            size:self.size
            source:"233.jpg"
        Color:
            rgba: .2, 1, 1, .9
        Rectangle:
            pos: self.center_x - 5, 0
            size: 10, self.height

    Label:
        color: .9,.6,.1 ,9
        font_size: 70
        center_x: root.width / 4
        top: root.top - 50
        text: str(root.player2.score)

    Label:
        color: .8,.1,.1 ,9
        font_size: 70
        center_x: root.width * 3/4
        top: root.top - 50
        text: str(root.player1.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

         Here are some pictures I used:

         The final interface effect is as follows:

 (4) Add background sound effects and music

        For adding sound effects and music, we're going to use a library from kivy:

from kivy.core.audio import SoundLoader

        We only need the following lines of code:

sound = SoundLoader.load('away.mp3')
        if sound:
            sound.loop = True  # 设置循环播放
            sound.play()

3. Emphasis on other details

(1) widget component

1. Widget is not a layout, and Widget will not change the position and size of its subcomponents.
2. The default size of Widget is 100*100.
3. The size_hint of the Widget is 1*1 by default. If its parent component is a layout component, its size is the size required by the layout in the parent component.
4. Functions such as on_touch_down(), on_touch_move(), on_touch_up() do not perform component boundary range judgment (collision judgment), and need to call collide_point().

(2) Canvas and labels in kv

Canvas: Create a canvas, the following are some properties of Canvas:

        1 .add(): Add a graphic object to the canvas, which can be a graphic or a group.

        2. insert(): Insert a graphic object before the specified position in the canvas.

        3. remove(): Delete the specified graphic object from the canvas.

        4. clear(): Delete all graphics objects from the canvas.

        5. size: The size of the canvas, its value determines the size of the canvas.

        6. pos: The position of the canvas, its value determines the position of the canvas relative to the parent control.

        7. opacity: The opacity of the canvas, its value is between 0-1.

        8. canvas.before: Graphics objects drawn before all elements in the canvas.

        9. canvas.after: Graphics objects drawn after all elements in the canvas.

        10. canvas.clear(): Clear the entire canvas.

        11. canvas.ask_update(): Forcibly update the contents of the canvas.

        12. canvas.export_to_png(): Save the canvas to a PNG file.

Label label: In Kivy, the Label widget is used to present text. It only supports ASCII and Unicode encoded strings (Chinese is not supported). In Label, you can set text content, font, size, color, alignment, and line break , quotes, and tagged text

        1. text: the text displayed by the label

        2. text_size: label text size

        3. font_name: the file name of the font to be used

        4. font_size: the font size of the text

        5. bold: the font uses bold

        6. italic: the font uses italics

        7. color: font color, the format is rgba, the default is white [1, 1, 1, 1]

        8. halign: the horizontal alignment of the text, the optional parameters are: left, center, right, justify

        9. valign: the vertical alignment of the text, the optional parameters are: bottom, middle (or center), top

        10. markup: Whether to split all marked text

        11. underline: add an underline to the text

        12. padding_x: the horizontal padding of the text inside the widget box

        13. padding_y: the vertical padding of the text inside the widget box

        14. texture: the texture object of the text, the text will be rendered automatically when the property changes

        15. Texture size of texture_size text, determined by font size and text

        16. strikethrough: add a strikethrough to the text

        17. strip: Whether to delete spaces and line breaks

        18. outline_color: the color of the text outline

        19. outline_width: the width of the outline around the text, in pixels

        20. max_line: the maximum number of lines to use, the default is 0, which means unlimited

        21. shorten: Whether the text content should be shortened as much as possible, the default is False

        22. shorten_from: On which side to shorten the text, the default is center, the optional parameters are: left, right and center

        23. is_shortend: Whether to render in a shortened way

        24. line_height: the line height of the text, the default is 1.0

4. Complete code

        Note I have written it in detail for everyone, if you have any questions, you can leave a message at any time to ask questions or chat with me privately

        py code:

from random import randint, uniform  # 导入randint和uniform函数用于生成随机数
from kivy.app import App  # 导入App类,用于创建应用
from kivy.clock import Clock  # 导入Clock类,用于调度函数的定时器
from kivy.core.audio import SoundLoader
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty  # 导入各种属性类,用于定义属性
from kivy.uix.widget import Widget  # 导入Widget类,用于创建控件
from kivy.vector import Vector  # 导入Vector类,用于处理向量运算
from kivy.core.window import Window

class PongPaddle(Widget):
    score = NumericProperty(0) # 分数属性,用于记录得分

    def bounce_ball(self, ball):
        if self.collide_widget(ball): # 如果球与挡板相撞
            ball.speed_x *= -1.1 # 改变球的水平速度,反弹出去


class PongBall(Widget):
    speed_x = NumericProperty(0)  # 球的水平速度属性
    speed_y = NumericProperty(0)  # 球的垂直速度属性
    speed = ReferenceListProperty(speed_x, speed_y)  # 球的速度向量属性
    r =  NumericProperty(0)   #球的大小属性
    max_speed = 5  # 球的最大速度

    def move(self):
        self.pos = Vector(*self.speed) + self.pos  # 根据速度更新球的位置

    def randomize(self):
        # 随机化速度
        vel_x = uniform(-5, 5)  # 在[-5, 10]范围内生成一个随机数作为水平速度
        vel_y = uniform(-5, 5)  # 垂直速度
        self.speed = Vector(vel_x, vel_y).normalize() * self.max_speed  # 设置球的速度向量,并将其归一化后乘以最大速度


    def daxiao(self):
        # 随机化大小
        r = uniform(20, 80)
        self.size = r , r # 在[20, 80]范围内生成一个随机数作为球的大小
        print(self.size)


class PongGame(Widget):
    ball = ObjectProperty(None)  # 球的实例对象属性
    player1 = ObjectProperty(None)  # 玩家1挡板的实例对象属性
    player2 = ObjectProperty(None)  # 玩家2挡板的实例对象属性
    music = None


    def serve_ball(self):
        self.ball.center = self.center  # 将球的中心位置设置为窗口的中心位置
        self.ball.randomize()  # 随机化球的速度
        self.ball.daxiao()     # 随机化球的大小
        self.timer = 0  # 计时器初始化为0

    def update(self, dt):
        self.ball.move()
        if self.timer >= 3:   # 如果计时器大于等于3秒
            self.ball.randomize()  # 随机化球的速度和大小
            self.ball.daxiao()
            self.timer = 0 # 重置计时器为0
        else:
            self.timer += dt  # 更新计时器的值,增加经过的时间
        # 球与上下边界的碰撞检测,如果球的底部超出窗口底部或者球的顶部超出窗口顶部
        if (self.ball.y < 0) or (self.ball.top > self.height):
            self.ball.speed_y *= -1   # 反转球的垂直速度,使其向相反的方向运动
        # 球与左边界的碰撞检测,如果球的左边超出窗口左边界
        if self.ball.x < 0:
            self.ball.speed_x *= -1  # 反转球的水平速度,使其向相反的方向运动
            sound1 = SoundLoader.load('你好.mp3')
            if sound1:
                #sound1.loop = True
                sound1.play()
            self.player2.score += 1       # 2 + 1
        # 球与右边界的碰撞检测,如果球的右边超出窗口右边界
        if self.ball.right > self.width:
            self.ball.speed_x *= -1  # 反转球的水平速度,使其向相反的方向运动
            sound2 = SoundLoader.load('村民叫.mp3')
            if sound2:
                #sound2.loop = True  # 设置循环播放
                sound2.play()
            self.player1.score += 1      # 1 + 1
        self.player1.bounce_ball(self.ball)
        self.player2.bounce_ball(self.ball)
        self.move_player2()

    def move_player2(self):
        # 根据球的位置移动玩家2的挡板
        if self.ball.center_y < self.player2.center_y:
            self.player2.center_y -= 5  # 将玩家2的挡板向上移动5个单位
        elif self.ball.center_y > self.player2.center_y:
            self.player2.center_y += 5

    def on_touch_move(self, touch):
        if touch.x < self.width / 1 / 4:
            self.player1.center_y = touch.y   # 根据触摸点的y坐标移动玩家1的挡板的中心位置



class PongApp(App):
    def build(self):
        self.title = '一个弹球游戏嘿嘿嘿'
        game = PongGame()
        game.serve_ball()  # 开始游戏,将球放在中心位置并随机化速度和大小
        sound = SoundLoader.load('qsbl.mp3')
        if sound:
            sound.loop = True  # 设置循环播放
            sound.play()
        Clock.schedule_interval(game.update, 1.0 / 60.0)  # 使用Clock类的schedule_interval方法以1/60秒的间隔调用game.update函数

        return game

PongApp().run()  # 创建PongApp的实例对象并运行应用程序

        kv code:

<PongBall>:
    canvas:
        Color:
            rgba: .9,.9,.1 ,9
        Ellipse:
            pos: self.pos
            size: self.size
            source: "出生.jpg"

<PongPaddle>:
    size: 25, 150
    canvas:
        Color:
            rgba: 1, 1, 1, .9
        Rectangle:
            pos: self.pos
            size: self.size
            source: "无标题.png"

<PongGame>:
    ball: pong_ball
    player1: player_left
    player2: player_right
    canvas:
        Color:
            rgba:1, 1, 1, .9
        Rectangle:
            size:self.size
            source:"233.jpg"
        Color:
            rgba: .2, 1, 1, .9
        Rectangle:
            pos: self.center_x - 5, 0
            size: 10, self.height

    Label:
        color: .9,.6,.1 ,9
        font_size: 70
        center_x: root.width / 4
        top: root.top - 50
        text: str(root.player2.score)

    Label:
        color: .8,.1,.1 ,9
        font_size: 70
        center_x: root.width * 3/4
        top: root.top - 50
        text: str(root.player1.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

5. Insufficient summary

        There are still some bugs, for example, if the speed is too fast, it will always get stuck at the border. For the difficulty setting, you can make a difficulty selection interface, so that the speed can be adjusted according to the difficulty.

        I hope these can help everyone~ If you think it helps you, please leave me a like~

Guess you like

Origin blog.csdn.net/m0_51440939/article/details/130782670