90.pygame游戏-玩个球(play the ball)最终版

简介: 90.pygame游戏-玩个球(play the ball)最终版

游戏部分截图



游戏操作说明


1.鼠标上下左右晃动,在摩擦板中摩擦,会使得灰色求变绿色


2.上下左右(W,S,A,D)四个键可控制浅绿色的小球


3.将绿色小球移动到背景的黑洞上,覆盖黑色球


4.绿色球到黑洞位置时按下空格键,若听到音效声音,则覆盖成功


5.直至五个球全部归位,游戏胜利,自动结束游戏;倘若开始游戏伴随的音乐(一首歌)

放完,五个球还没有全部覆盖黑洞,则游戏失败,将播放嘲讽的声乐。


代码


一个模块版本


main.py


import pygame
import sys                      # 退出需要
import traceback
from pygame.locals import *     # 把pygame的常量名导进去
from random import *            # 随机生成数需要
class Ball(pygame.sprite.Sprite):    # 球类,继承自Spirte类
    def __init__(self, grayball_image, greenball_image, position, speed, bg_size, target):
        pygame.sprite.Sprite.__init__(self)      # 初始化动画精灵
        # 使用 .convert_alpha() 可以转换像素格式,提高 blit 的速度
        self.grayball_image = pygame.image.load(grayball_image).convert_alpha()    # 加载灰球图片
        self.greenball_image = pygame.image.load(greenball_image).convert_alpha()  # 加载绿球图片
        self.rect = self.grayball_image.get_rect()                                 # 获取小球位置
        # 将小球放在指定位置
        self.rect.left, self.rect.top = position            # position赋值给小球的左边和上边
        # choice() 方法返回一个列表,元组或字符串的随机项。
        self.side = [choice([-1, 1]), choice([-1, 1])]      # 移动小球方向
        self.speed = speed      # 移动速度
        self.collide = False    # 默认未碰撞
        self.target = target    # 为每一个小球设定一个不同的目标
        # 5、小球应该添加一个 control 属性,用于记录当前的状态(绿色 -> 玩家控制 or 灰色 -> 随机移动)
        self.control = False
        self.width, self.height = bg_size[0], bg_size[1]    # 窗口的宽高
        self.radius = self.rect.width / 2                   # 增加半径属性
    def move(self):         # 球的移动方法
        if self.control:    # 为真
            self.rect = self.rect.move(self.speed)    # 移动后的矩形位置
        else:               # 为假
            self.rect = self.rect.move(self.side[0] * self.speed[0],
                                       self.side[1] * self.speed[1])    # 移动小球矩形位置
        """" 
        如果小球的左侧出了边界,那么将小球左侧的位置改为右侧的边界
        这样便实现了从左边进入,右边出来的效果
        """
        if self.rect.right <= 0:                # 小球右边界<0,即小球出左边
            self.rect.left = self.width        # 从右边进来
        elif self.rect.left >= self.width:      # 小球左边界>窗口宽度,即小球出右边
            self.rect.right = 0                # 从左边进来
        elif self.rect.bottom <= 0:             # 小球下边界<0,即小球出上边
            self.rect.top = self.height        # 从下边出来
        elif self.rect.top >= self.height:      # 小球上边界>窗口高度
            self.rect.bottom = 0               # 从上边出来
    # 3、为小球添加一个 check() 方法,用于判断鼠标在1秒钟内产生的事件数量是否匹配此目标;
    def check(self, motion):
        if self.target < motion < self.target + 5:
            return True
        else:
            return False
class Glass(pygame.sprite.Sprite):    # 摩擦板类
    def __init__(self, glass_image, mouse_image, bg_size):   # 这两行代码跟前面的一样
        pygame.sprite.Sprite.__init__(self)                  # 初始化动画精灵
        self.glass_image = pygame.image.load(glass_image).convert_alpha()   # 加载摩擦板图像
        self.glass_rect = self.glass_image.get_rect()                       # 获取摩擦板的矩形位置
        # 摩擦板矩形为左边和上边界,水平位置在中心,垂直位置在下边
        self.glass_rect.left, self.glass_rect.top = \
            (bg_size[0] - self.glass_rect.width) // 2, \
            bg_size[1] - self.glass_rect.height
        self.mouse_image = pygame.image.load(mouse_image).convert_alpha()   # 加载手图像
        self.mouse_rect = self.mouse_image.get_rect()                       # 获取手的矩形位置
        # 手的左边和上边界 = 摩擦板的左边和上边界, 相当于初始化位置
        self.mouse_rect.left, self.mouse_rect.top = \
            self.glass_rect.left, self.glass_rect.top
        pygame.mouse.set_visible(False)  # 鼠标不可见
def main():         # 主函数,把一群东西封装起来了
    pygame.init()   # 初始化
    grayball_image = "gray_ball.png"   # 球图像
    greenball_image = "green_ball.png"  # 球图像
    bg_image = "background.png"    # 背景图像
    glass_image = "glass.png"      # 摩擦板图像
    mouse_image = "hand.png"       # 摩擦的手的图像
    running = True   # 初始化running为True
    # 添加背景音乐
    pygame.mixer.music.load("bg_music.ogg")  # 加载背景音乐
    pygame.mixer.music.set_volume(0.2)       # 设置音量
    pygame.mixer.music.play()                # 播放背景音乐
    # 添加音效
    loser_sound = pygame.mixer.Sound("fail.ogg")     # 失败的时候的声音
    loser_sound.set_volume(0.2)
    laugh_sound = pygame.mixer.Sound("laugh.ogg")     # 嘲笑的声音
    laugh_sound.set_volume(0.2)
    winner_sound = pygame.mixer.Sound("win.ogg")   # 成功的时候的声音
    winner_sound.set_volume(0.2)
    hole_sound = pygame.mixer.Sound("hole.ogg")
    hole_sound.set_volume(0.2)
    """"
    # 背景音乐会贯穿游戏的始终,背景音乐完整播放一次我们视为游戏的时间,
    # 因此我们需要想办法让游戏在背景音乐停止时结束,我们应该有留意到:
    # music 模块有一个 set_endevent() 方法,该方法的作用就是在音乐播放完时
    # 发送一条事件消息,发送什么消息是我们自定义的,USEREVENT 就是自定义消息,
    # Pygame 给我们预定了很多事件,像我们熟悉的 键盘事件、鼠标事件等。这些预定义
    # 的事件都有一个标记符,例如:MOUSEBUTTONDOWN 、KEYDOWN等。这些都是
    # 一些数字的等值定义,其实在内部,2 就表示鼠标按下,但是人类难以记住,所以定义为
    # MOUSEBUTTONDOWN。
    # USEREVENT 就是数字24,24以上就是我们可以自定义的事件,我们可以像这样自定义事件:
    # MY_EVENT = USEREVENT。
    # MY_EVENT_1 = USEREVENT + 1........
    """
    # 音乐播放完时游戏结束
    GAMEOVER = USEREVENT
    pygame.mixer.music.set_endevent(GAMEOVER)
    # 根据背景图片指定游戏界面尺寸
    bg_size = width, height = 1024, 681         # 窗口尺寸
    screen = pygame.display.set_mode(bg_size)   # 绘制窗口
    pygame.display.set_caption("小游戏")         # 窗口名
    background = pygame.image.load(bg_image).convert_alpha()   # 加载背景图片
    # 黑洞范围(x1, x2, y1, y2)
    hole = [(115, 121, 197, 203), (223, 229, 388, 394), (501, 507, 318, 324),
            (696, 702, 190, 196), (904, 910, 417, 423)]
    msgs = []
    balls = []    # 用来存放小球对象的列表
    group = pygame.sprite.Group()   # 这里的group在碰撞检测需要
    # 创建五个小球,位置随机,速度随机
    for i in range(5):
        # position = (小球的左边,上边)
        position = randint(0, width - 100), randint(0, height - 100)   # 随机生成位置
        speed = [randint(1, 10), randint(1, 10)]                       # 随机生成速度
        """四个参数ball_image, position, speed, bg_size 前面都设置了"""
        ball = Ball(grayball_image, greenball_image, position, speed, bg_size, 5 * (i+1))   # 实例化小球
        """
        spritecollide(sprite, group, dokill, collided = None)
        第一个参数 sprite 是指定被检测的精灵;(就是我们写的里面的 item)
        第二个参数 group 是指定一个组(就是我们写的里面的 target 列表),它是 sprite 的组,因此要使用 sprite.Group() 来生成;
        第三个参数 dokill 是设置是否从组中删除检测到碰撞的精灵,设置为True,则删除;
        第四个参数 collided 是指定一个回调函数,用于定制特殊的检测方法,如果第四个参数忽略的话,默认是检测精灵之间的 rect 属性。
        """
        # 在创建小球这里必须进行一下碰撞检测
        while pygame.sprite.spritecollide(ball, group, False, pygame.sprite.collide_circle):
            #                             小球   group  不删除   圆形属性
            # 随机生成的小球碰撞了就再次随机生成位置
            ball.rect.left, ball.rect.top = randint(0, width - 100), randint(0, height - 100)
        balls.append(ball)   # 列表用append
        group.add(ball)      # 集合用add
    glass = Glass(glass_image, mouse_image, bg_size)  # 实例化摩擦板
    # 2、创建一个motion 变量来记录每一秒钟产生事件数量
    motion = 0
    # 4.1、添加一个自定义事件,每一秒钟触发一次
    MYTIMER = USEREVENT + 1  # 自定义事件的知识点可以查看上一节课的末尾注解
    pygame.time.set_timer(MYTIMER, 1000)
    pygame.key.set_repeat(100, 100)
    clock = pygame.time.Clock()          # 设置帧率需要
    while running:  # 主循环
        for event in pygame.event.get():  # 遍历事件
            if event.type == QUIT:        # ×掉窗口
                pygame.quit()
                sys.exit()                # 退出游戏
            elif event.type == GAMEOVER:  # 判断事件是否为我们自定义的GAMEOVER事件
                loser_sound.play()        # 失败的声音播放
                pygame.time.delay(2000)   # 暂停2秒
                laugh_sound.play()        # 嘲笑的声音播放
                running = False           # 退出循环了
            # 4.2、 调用每个小球的 check() 检测 motion 的值是否匹配某一个小球的目标,
            # 并将motion重新初始化,以便记录下1秒鼠标事件数量
            elif event.type == MYTIMER:
                if motion:
                    for each in group:
                        if each.check(motion):
                            each.speed = [0, 0]
                            each.control = True
                    motion = 0
            # 需要计算一下motion
            elif event.type == MOUSEMOTION:    # 鼠标事件
                motion += 1
            elif event.type == KEYDOWN:       # 键按下
                if event.key == K_w:          # 按下w键
                    for each in group:        # 上移
                        if each.control:
                            each.speed[1] -= 1
                if event.key == K_s:          # 按下s键
                    for each in group:        # 下移
                        if each.control:
                            each.speed[1] += 1
                if event.key == K_a:         # 按下a键
                    for each in group:       # 左移
                        if each.control:
                            each.speed[0] -= 1
                if event.key == K_d:         # 按下d键
                    for each in group:       # 右移
                        if each.control:
                            each.speed[0] += 1
                if event.key == K_SPACE:     # 按下空格键
                    for each in group:
                        if each.control:
                            for i in hole:
                                if i[0] <= each.rect.left <= i[1] and \
                                 i[2] <= each.rect.top <= i[3]:
                                    hole_sound.play()
                                    each.speed = [0, 0]  # 固定该小球
                                    group.remove(each)   # 不会被撞到
                                    # 下面两行语句就是完成该小球第一个绘制,
                                    # 然后其它小球就在该小球上边了
                                    temp = balls.pop(balls.index(each))  # 先从temp 中弹出
                                    balls.insert(0, temp)  # 然后插入到第一个的位置,就会第一个画了
                                    hole.remove(i)  # 把这个黑洞去掉
                            if not hole:  # 所有洞补完,游戏结束
                                pygame.mixer.music.stop()   # 停止背景音乐
                                winner_sound.play()  # 播放胜利音效
                                pygame.time.delay(3000)   # 播放音效需要时间,延迟3秒
                                msg = pygame.image.load("win.png").convert_alpha()
                                msg_pos = (width - msg.get_width()) // 2, \
                                          (height - msg.get_height()) // 2
                                msgs.append((msg, msg_pos))
                                # 播放音效
                                laugh_sound.play()
        """blit:第一个参数为一个Surface对象,第二个为左上角位置。画完以后得用update更新,否则画面一片漆黑。"""
        # 更新图像的位置,这里是更新背景图像的位置
        screen.blit(background, (0, 0))
        screen.blit(glass.glass_image, glass.glass_rect)  # 更新摩擦板位置
        glass.mouse_rect.left, glass.mouse_rect.top = pygame.mouse.get_pos()  # 获得鼠标当前位置
        if glass.mouse_rect.left < glass.glass_rect.left:    # 鼠标左边<摩擦板左边,即从左边出去了
            glass.mouse_rect.left = glass.glass_rect.left    # 让鼠标手保持在左边界
        if glass.mouse_rect.left > glass.glass_rect.right - glass.mouse_rect.width:
            glass.mouse_rect.left = glass.glass_rect.right - glass.mouse_rect.width
        if glass.mouse_rect.top < glass.glass_rect.top:
            glass.mouse_rect.top = glass.glass_rect.top
        if glass.mouse_rect.top > glass.glass_rect.bottom - glass.mouse_rect.height:
            glass.mouse_rect.top = glass.glass_rect.bottom - glass.mouse_rect.height
        screen.blit(glass.mouse_image, glass.mouse_rect)  # 更新手位置
        for each in balls:  # 遍历小球
            each.move()     # 移动小球
            if each.collide:
                each.speed = [randint(1, 10), randint(1, 10)]  # 随机生成速度
                each.collide = False
            if each.control:
                screen.blit(each.greenball_image, each.rect)
            else:
                screen.blit(each.grayball_image, each.rect)  # 更新小球位置
        for each in group:
            group.remove(each)  # 把自身拿出来
            if pygame.sprite.spritecollide(each, group, False, pygame.sprite.collide_circle):  # 把自己和别的球进行碰撞检测
                each.side[0] = -each.side[0]
                each.side[1] = -each.side[1]
                each.collide = True
                if each.control:
                    each.side[0] = -1
                    each.side[1] = -1
                    each.control = False
            group.add(each)  # 还要把自己放进去
        # 打印消息
        for msg in msgs:
            screen.blit(msg[0], msg[1])
        pygame.display.flip()   # 刷新界面
        # 数越大越快
        clock.tick(30)
if __name__ == "__main__":  # 运行
    try:
        main()
    except SystemExit:  # 这是按下 × 的异常,直接忽略
        pass
    except:
        traceback.print_exc()
        pygame.quit()
        input()


所有图片素材


1.背景(background.png)



2.摩擦板(grass.png)



3.胜利显示的图片(win.png)



4.灰球(gray_ball.png)



5.绿球 (green_ball.png)



6.摩擦板内的摩擦手(hand.png)



音乐素材


以下的 .ogg文件 音乐素材这里发不了,请在评论区自留邮箱,看到发你邮箱



下载链接


或者直接下载,里面代码和各种素材齐全


https://download.csdn.net/download/qq_39236499/12692654

相关文章
|
5天前
|
Python
pygame 烟花效果
pygame 烟花效果
15 0
|
8月前
|
Python
pygame编写井字棋游戏
pygame编写井字棋游戏
123 0
|
10月前
【sunny land】利用Animation编辑器实现近战敌人判定
【sunny land】利用Animation编辑器实现近战敌人判定
Egret学习笔记 (Egret打飞机-3.实现背景循环滚动)
Egret学习笔记 (Egret打飞机-3.实现背景循环滚动)
97 0
|
容器
Egret学习笔记 (Egret打飞机-9.子弹对敌机和主角的碰撞)
Egret学习笔记 (Egret打飞机-9.子弹对敌机和主角的碰撞)
78 0
pygame游戏play_the_ball版本二(多模块版)
pygame游戏play_the_ball版本二(多模块版)
pygame游戏--飞机大战(plane)
pygame游戏--飞机大战(plane)
pygame游戏--飞机大战(plane)
pygame游戏--外星人入侵(alien_invasion)
pygame游戏--外星人入侵(alien_invasion)
pygame游戏--外星人入侵(alien_invasion)
Pygame中的精灵和碰撞检测
Pygame中的精灵和碰撞检测
Game->打砖块(源码:详解)
Game->打砖块(源码:详解)
117 0