(5)Python笔记:使用pygame库制作打方块小游戏

闲来无事研究了下pygame库,让我想起了大学时用C++写出贪吃蛇时的兴奋感,不过貌似pygame很久没跟新了?而且用python写游戏也不大高效,但是拿来试试思路,算法还是不错的。
首先上张效果图:
这里写图片描述
这里写图片描述
现在包含的功能很简单,就是打开游戏时有一个start的按钮,点击后进入游戏,球击中砖块会有一定概率出现奖励物品(3种:1.球加速,2.板子加速,3.增加球的数量),板子在碰到掉下来的奖励物品时会得到上述中对应的buff,并持续一段时间。球与底部接触时判断游戏结束,可以点击出现的start重新游戏,游戏中按P键暂停,A,D板子左右运动,Q退出游戏。

代码下载:
https://github.com/SecondMagic/pygame-

就挑几个重点的地方讲讲吧:
1.写之前要先考虑好整个游戏中出现的物品的划分,把球,板子,砖块,显示文字,按钮都独立抽象出来,当要使用时新建对象即可直接使用。之后要考虑功能划分,这里是分成游戏物品位置重新计算,物品显示,用户事件,碰撞检测几部分,将相应功能的代码放到一起能够使得代码可读性增加,也方便后期的重构。
下面是游戏的主流程,

#游戏界面,球,板子,砖块,按钮,文字变量初始化
setting = Setting() #获取游戏配置信息
    pygame.init()
    screen = pygame.display.set_mode((setting.screen_width,setting.screen_height))
    pygame.display.set_caption("Game")
    #screen.fill(setting.screen_color)
    button_start = Button('Start') #开始游戏按钮
    bricks = Group() #砖块组初始化
    bat = Bat() #板子初始化
    ball = Ball() 
    balls = Group()
    balls.add(ball) #球组初始化,因为开始时就含有一个球,所以初始化时添加一个球
    buffs = Group() #buff组初始化
    info_score = Info(100,50) #文字初始化
    info_max_score = Info(200,50) #文字初始化
    info_bat_speed = Info(400,50) #文字初始化
    info_ball_speed = Info(600,50) #文字初始化
    rewards = Group() #奖励组初始化
while True:
        screen.blit(setting.background_image,(0,0))
        #用户键盘,鼠标事件
        event_check(setting,button_start,bricks,bat,balls,rewards,buffs)
        #判断游戏是否通关
        if setting.total_score != 0 and setting.total_score <= setting.score :
            setting.game_stats = False
            setting.max_score = setting.score
            setting.score = 0
        #判断游戏是否开启
        if setting.game_stats == False:
            button_start.draw()
        #判断游戏是否暂停
        elif setting.game_stop == True:
            #由于游戏暂停,所以不进行游戏物品的位置更新计算,只绘制暂停时物品的位置
            for brick in bricks:
                brick.draw_brick()
            for reward in rewards:
                reward.draw_reward()
            bat.draw_bat()
            for sball in balls:
                sball.draw_ball()
            button_start.draw()
            info_score.draw_info()
            info_max_score.draw_info()
            info_bat_speed.draw_info()
            info_ball_speed.draw_info()
        #游戏开启,并在进行中
        else :
            #板子,球,砖块,奖励位置重新计算
            for sball in balls :
                sball.update_position(setting)
            bat.update_position(setting)
            rewards.update()
            buffs.update()
            check_change(balls,bat,bricks,setting,rewards,buffs)
            info_score.get_info('scroe:'+str(setting.score))
            info_max_score.get_info('max:'+str(setting.max_score))
            info_bat_speed.get_info('bat speed:'+str(setting.bat_speed))
            info_ball_speed.get_info('ball speed:'+str(setting.ball_speed))

            #buff时间计算,buff时间到了就将其去除出buff队列,并按照buff类型恢复设置
            for buff in buffs:
                if buff.check_time():
                    buff.change_other(setting,balls)
                    buffs.remove(buff)
            #板子,球,砖块,奖励绘制
            for reward in rewards:
                if reward.check() :
                    rewards.remove(reward)
                else :
                    reward.draw_reward()
            for brick in bricks:
                brick.draw_brick()
            bat.draw_bat()
            for sball in balls:
                sball.draw_ball()
            info_score.draw_info()
            info_max_score.draw_info()
            info_bat_speed.draw_info()
            info_ball_speed.draw_info()
        pygame.display.flip()

2.总体功能类别划分好坏直接影响到后续功能扩展时的容易度,完成后可以开始考虑游戏内容,首先是让板子在用户按A,D键时左右移动,通过用户事件监听就可以很轻松的实现,主要麻烦的一点是用户一直按住方向键时让板子也一直运动,这时可以通过给板子设置2个方向标识,比如left和right,初始化时都为0,当用户按住A时left=1,放开时left=0,right同理,在重新计算板子位置时,板子的位置就可以如下: 板子现在x坐标位置=板子原来x坐标位置 - left * 板子运动速度 + right * 板子运动速度 ,这样就可以解决一直按住按键时板子的移动问题了

def update_position(self,setting):
        if self.rect.left - setting.bat_speed*self.dir_left >= 0:
            self.rect.centerx -= setting.bat_speed*self.dir_left
        if self.rect.right + setting.bat_speed*self.dir_right <= setting.screen_width:
            self.rect.centerx += setting.bat_speed*self.dir_right

3.球的运行设置,球的话首先要初始化出现时的位置,由于游戏时如果一直是从一个固定位置发出球,球的方向也固定的话游戏就非常无趣了,所以初始位置,和运动方向最好都做成随机的,方向的话直接使用45度角,初始时随机向左或右,在碰到上左右边界时相应改变运行方向即可,在碰到底部时游戏结束,当然这个放到碰撞检测中也可以。关于球与板子,砖块的碰撞,奖励与板子的碰撞检测,则可以单独出来。使得代码更加清晰。

4.球与板子的碰撞检测,因为如下图,会有左右2种运行轨迹,单靠pygame提供的矩形叠加检测是不够分辨的,所以需要加上更加细致判断:
这里写图片描述

#sball 球 ;bat 板子
if sball.rect.left < bat.rect.left and sball.rect.right >= bat.rect.left and sball.dir_x == 1 :
                #球与板子左边缘相撞,且球的x轴运行方向为向右,则x轴运动方向修改为向左
                sball.dir_x *=-1
            if sball.rect.left <= bat.rect.right and sball.rect.right > bat.rect.right and sball.dir_x == -1:
            #球与板子右边缘相撞,且球的x轴运行方向为向左,则x轴运动方向修改为向右
                sball.dir_x *=-1

5.奖励和buff,当球击中砖块时,有一定概率产生奖励(当然也通过对砖块增加type字段来实现击中某一些特定砖块时产生奖励)奖励初始位置为被击砖块中心处,运行方向向下,当板子接触到奖励时删除该奖励对象并新增一个buff对象,buff对象用来描述当前游戏中所获得各种奖励增益效果和控制其持续时间。在主流程中添加buff组遍历,每经过一次主流程就重新计算buff持续时间,时间到了就恢复因buff而改变的游戏属性即可。

#给出相应片段,可以到上面所提供的总体代码中查找相应位置
if param.down_speed == 3 and len(balls) <3: #球数量加1
            balls.add(Ball())
            buffs.add(Buff(param,setting))
        if param.down_speed == 2 and setting.bat_speed < setting.bat_speed_max : #板子速度增加
            setting.bat_speed += setting.bat_speed_change
            buffs.add(Buff(param,setting))
        if param.down_speed == 1 and setting.ball_speed <= setting.ball_speed_max : #球速度增加
            setting.ball_speed += setting.ball_speed_change
            buffs.add(Buff(param,setting))


#礼物时间计算
            for buff in buffs:
                if buff.check_time():
                    buff.change_other(setting,balls)
                    buffs.remove(buff)

#奖励时间更新
buffs.update()

class Buff(Sprite):
    def __init__(self,reward,setting):
        super().__init__()
        self.type = reward.down_speed
        if self.type == 1:
            self.limit_time = setting.reward_type1_limit_time
        elif self.type == 2:
            self.limit_time = setting.reward_type2_limit_time
        elif self.type == 3:
            self.limit_time = setting.reward_type3_limit_time
    def update(self):
        self.limit_time -= 1
    def check_time(self):
        if self.limit_time <= 0:
            return True
        else :
            return False
    def change_other(self,setting,balls):
        if self.type == 1:  #球加速
            setting.ball_speed -= setting.ball_speed_change
        elif self.type == 2: #板子加速
            setting.bat_speed -= setting.bat_speed_change
        elif self.type == 3: #球数量增加
            i = len(balls)
            k = 1
            for sball in balls:
                if k == i :
                    balls.remove(sball)
                k += 1

大致麻烦的地方基本就这些了,置于后续扩展板子的长度变化,多关卡什么的,也是可以通过少量修改原代码实现的。

猜你喜欢

转载自blog.csdn.net/yeyinglingfeng/article/details/79044436