游戏部分截图
游戏操作说明
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文件 音乐素材这里发不了,请在评论区自留邮箱,看到发你邮箱
下载链接
或者直接下载,里面代码和各种素材齐全