Python 之 pygame 学习(Play the ball 游戏胜利)

一、相关概念

  1. set_repeat(delay,interval):控制如何重复按住键
delay参数:指定第一次发送事件地延迟时间
interval参数:指定重复发送事件的间隔
如果不带参数表示取消重复发送事件
  1. 胜利条件
    通过摩擦面板,获得匹配成功的小球控制权
    将其颜色改为绿色
    通过上下左右对其进行控制
    控制过程中与其他小球碰撞,重新变为失控状态
    当小球运动到小洞上方时,通过空格键实现小球的进洞状态
    小球进洞后,其他小球不再与其相碰,从其上方略过

二、完整代码

import pygame
import sys
from pygame.locals import *
from random import *
# pygame.sprite.Sprite 代表游戏对象的简单基类
class Ball(pygame.sprite.Sprite):
    def __init__(self,grayball_image,greenball_image,position,speed,bg_size,target):
        # 初始化动画精灵
        super(Ball, self).__init__()
        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
        #为了防止碰撞后发送抖动(两个小球碰撞后获得随机速度可能方向相同,则会一直发生碰撞,所以将速度和方向分开表示)
        self.side = [choice([-1,1]),choice([-1,1])] # 方向随机
        self.speed = speed
        self.collide = False # 用来检测是否发生碰撞
        # 设定一个值,用来判定是否控制小球
        self.target = target
        self.contol = False # 判定小球的状态,是被控制,还是失控
        self.width,self.height = bg_size[0],bg_size[1]
        # 由于采用 collide_circle()方法检测圆的碰撞,所以需要设置 radius 属性
        self.radius = self.rect.width / 2
    def move(self):
        # 由于加速度那里由玩家控制速度,速度默认是带方向的,所以应该区分开球是是玩家控制,还是失控状态
        if self.contol:
            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:
            self.rect.left = self.width
        elif self.rect.left >= self.width:
            self.rect.right = 0
        elif self.rect.bottom <= 0:
            self.rect.top = self.height
        elif self.rect.top >= self.height:
            self.rect.bottom = 0
    # 判断鼠标在 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):
        super(Glass, self).__init__()
        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.center = self.glass_rect.center
        # 圆形的鼠标设置不可见
        pygame.mouse.set_visible(False)

def main():
    pygame.init()
    grayball_image = "gray_ball.png"
    greenball_imgae = "green_ball.png"
    bg_image = "background.png"
    glass_imgae = "glass.png"
    mouse_imgae = "hand.png"

    running = True
    # 添加背景音乐
    pygame.mixer.music.load("bg_music.wav")
    pygame.mixer.music.play()

    # 添加音效
    win_sound = pygame.mixer.Sound("dog.wav")
    loser_sound = pygame.mixer.Sound("loser.wav")
    laugh_sound = pygame.mixer.Sound("laugh.wav")
    hole_sound = pygame.mixer.Sound("hole.wav")

    # 音乐播放完毕,游戏结束
    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("Play the ball")
    background = pygame.image.load(bg_image).convert_alpha()
    # 用来存放小洞位置的列表(x1,x2,y1,y2) 代表范围
    hole = [(110,118,195,205),(220,228,386,395),(500,508,315,325),\
    (695,705,190,198),(902,908,416,425)]
    # 用来存放小球对象的列表
    balls = []
    msgs = [] # 打印列表
    group = pygame.sprite.Group()

    # 创建五个小球,位置随机,速度随机
    for i in range(5):
        # 球的大小是 100 x 100 防止越出边界,所以要宽和高减 100 的范围以内
        position = randint(0,width-100),randint(0,height-100)
        speed = [randint(1,10),randint(1,10)] # 只表示大小的速度,不能设置为0,可能存在都是0静止的情况
        # 5*(i+1) 设定执行控制小球的最小事件数
        ball = Ball(grayball_image,greenball_imgae,position,speed,bg_size,5*(i+1))
        # 创建小球时需要检测,防止球与球之间发生重叠
        # while pygame.sprite.spritecollide(ball,group,False):
        while pygame.sprite.spritecollide(ball,group,False,pygame.sprite.collide_circle):
            # 若发生碰撞即有重叠,就重新分配位置
            ball.rect.left,ball.rect.top = randint(0,width-100),randint(0,height-100)
        balls.append(ball)
        # 新建一个球后,将其加入检测组中
        group.add(ball)

    #创建 glass 面板
    glass = Glass(glass_imgae,mouse_imgae,bg_size)
    motion = 0 # 记录鼠标每一秒产生的事件数量

    # 添加一个自定义的事件,第二个自定义事件所以 加1
    Myevent = USEREVENT +1
    # 限定这个事件的间隔 1000毫秒
    pygame.time.set_timer(Myevent,1000)
    # 设置后,按下按键没隔100毫秒重复一次,一次重复100毫秒
    pygame.key.set_repeat(100,100)

    clock = pygame.time.Clock() # 设置帧率

    while running:
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            # 游戏结束,播放音乐
            elif event.type == Gameover:
                loser_sound.play()
                pygame.time.delay(2000)
                laugh_sound.play()
                running = False
            # 检测自定义事件
            elif event.type == Myevent:
                if motion:
                    for each in group: # 检测是否有匹配鼠标运动事件的小球
                        if each.check(motion):
                            each.speed= [0,0] #匹配成功,速度置零,从而实现控制
                            each.contol = True
                    # motion放的位置
                    motion = 0
            # 检测鼠标的事件
            elif event.type == MOUSEMOTION:
                motion +=1 # 只要鼠标移动,motion就加1
            # 响应相关按键的事件:通过键盘控制绿色小球(上下左右键盘)
            elif event.type == KEYDOWN:
                if event.key == K_UP:
                    for each in group:
                        if each.contol:
                            each.speed[1] -=1 #垂直向上移动
                if event.key == K_DOWN:
                    for each in group:
                        if each.contol:
                            each.speed[1] +=1 #垂直向下移动
                if event.key == K_RIGHT:
                    for each in group:
                        if each.contol:
                            each.speed[0] +=1 #垂直向右移动
                if event.key == K_LEFT:
                    for each in group:
                        if each.contol:
                            each.speed[0] -=1 #垂直向左移动
            # 当小球运动到黑洞上方时,玩家可以敲击空格键,将小球固定在黑洞
                if event.key == K_SPACE:
                    for each in group:
                        if each.contol:
                            print(each.rect.left,each.rect.top)
                            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))
                                    balls.insert(0,temp)
                                    hole.remove(i) # 放置一个少一个黑洞
                            if not hole:
                                pygame.mixer.music.stop() # 所有黑洞都被占据,游戏结束
                                win_sound.play()
                                pygame.time.delay(3000) # 音效播放3秒钟
                                msg = pygame.image.load("win.png").convert_alpha
                                msg_pos = bg_size.center
                                msgs.append((msg,msg_pos))
                                laugh_sound.play()


        # 注意画的面板顺序,先画背景,再画面板,否则会被盖住
        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
            # 通过control 来判定小球是否被控制,绿色代表控制,灰色代表随机移动
            if each.contol:
                # 画绿色小球
                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.contol:# 如果是玩家控制的绿色小球发生碰撞
                    each.side[0] = -1
                    each.side[1] = -1
                    each.contol = False # 规定碰撞后,被控制的小球变成失控状态
            group.add(each)
        # 打印内容
        for msg in msgs:
            screen.blit(msg[0],msg[1])
        pygame.display.flip()
        clock.tick(30) #最高每秒不超过 30 次

if __name__ == '__main__':
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南淮北安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值