Python 之 pygame 学习(Play the ball 实现通过摩擦面板控制)

一、前期准备

前面已经实现了小球的运动,碰撞检测,背景和音效,下面需要实现通过摩擦面板来控制小球的移动

主要思想
鼠标移动会不断地产生事件( 把鼠标的移动看作事件用motion来记录次数),检测一秒钟鼠标在玻璃面板内产生多少事件,然后判定产生的事件是否可以匹配到小球(这里小球的用我们传递的值target 来匹配鼠标产生的事件)

具体过程

  1. 为每个小球设定一个不同的目标(即不用的target值用来匹配鼠标产生的motion)
  2. 创建一个motion 变量来记录鼠标每一秒钟产生的事件数量(即鼠标移动的次数)
  3. 为小球添加一个check()方法,用于判断鼠标在1秒钟时间内产生的事件数量是否匹配此小球对应的target值
  4. 添加一个自定义事件,每一秒钟触发1次。调用每个小球的check()检测motion的值是否匹配某一个小球的目标,并将motion重新初始化(检查完一组初始化一次),以便记录下1秒内的鼠标事件数量
  5. 小球应该添加一个 control 属性,用于记录当前的状态(绿色->玩家控制 or 灰色->随机移动)
  6. 通过检查 control 属性决定绘制什么颜色的小球(从而达到区分的效果)

二、代码

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__() # 利用 super函数继承父类
        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.speed = speed
        # 每个小球都有一个对应的target,用来判断motion是否与该target匹配
        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):
        self.rect = self.rect.move(self.speed)
        # 小球超出屏幕时的应对方案
        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):
        # 限定匹配的范围,这个5可以自己设
        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来控制循环,因为后面牵扯到背景音乐结束,游戏结束
    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()
    # 用来存放小球对象的列表,用 group 完全可以实现
    group = pygame.sprite.Group() # 用于保存和管理精灵对象

    # 创建五个小球,位置随机,速度随机
    for i in range(5):
        # 球的大小是 100 x 100 防止越出边界,所以要宽和高减 100 的范围以内
        position = randint(0,width-100),randint(0,height-100)
        speed = [randint(-10,10),randint(-10,10)]
        # 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)

        # 新建一个球后,将其加入检测组中
        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)
    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) # 延迟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置为0 ,防止过大,跳过检测
                    motion = 0
            # 检测鼠标的事件
            elif event.type == MOUSEMOTION:
                motion +=1 # 只要鼠标移动,motion就加1

        # 注意画的面板顺序,先画背景,再画面板,否则会被盖住
        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 group:
            each.move()
            # 通过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.speed[0] = -each.speed[0]
                each.speed[1] = -each.speed[1]
            group.add(each)
        pygame.display.flip()
        clock.tick(30) #最高每秒不超过 30 次

if __name__ == '__main__':
    main()

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

南淮北安

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

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

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

打赏作者

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

抵扣说明:

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

余额充值