import pygame
import sys
from pygame import *
from random import *
from math import *
from traceback import *
#定义 球 类,pygame.sprite.Sprite是python自带的一个基类(动画精灵),这里直接继承
class Ball(pygame.sprite.Sprite):
def __init__(self,grayball_image,greenball_image,position,speed,bg_size,target):#初始化Ball类
pygame.sprite.Sprite.__init__(self)#初始化pygame.sprite.Sprite类
self.grayball_image=pygame.image.load(grayball_image).convert_alpha()#载入图片(灰色小球)
self.rect=self.grayball_image.get_rect()#得到处理该图片(矩形图片)的方法
self.greenball_image = pygame.image.load(greenball_image).convert_alpha() # 载入图片(绿色小球)
#因为绿色与灰色小球只有颜色不同,所有这里不再设置rect(形状之类的属性)
self.rect.left,self.rect.top=position#得到左上角坐标(给出左上角坐标和图片就可以在屏幕生成一张矩形图片)
self.side=[choice([-1,1]),choice([-1,1])]#设置速度方向(水平(左右)与竖直(上下))
self.collide=False #是否碰撞
self.speed=speed#得到小球速度
self.width,self.height=bg_size[0],bg_size[1]#得到窗口尺寸
self.radius=self.rect.width/2#得到小球半径
self.target=target #为每一个小球设立一个特定的目标,当符合这个目标后小球响应相应事件(MYTIMER)
self.control=False#检查小球是否在可控状态
def move(self):#小球移动的方法
if self.control:
# 在可控状态下速度不区分方向(self.side),因为side=-1时会反方向加速度,与实际运动方向不符合,所以单独分开
self.rect = self.rect.move(self.speed)
else:
self.rect=self.rect.move((self.speed[0]*self.side[0]),(self.speed[1]*self.side[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
def check(self,motion): #检测鼠标移动事件
if self.target<motion<self.target+5:
return True
else:
return False
'''
def collide_check(item,target):#碰撞检测,不建议自己写此模块
col_balls=[]#存放有碰撞的球
for each in target:
#距离公式((x1-x2)^2+(y1-y2)^2)^(0.5)
distance=sqrt(\
pow((item.rect.center[0]-each.rect.center[0]),2)+\
pow((item.rect.center[1] - each.rect.center[1]), 2))
if distance<=(item.rect.width+each.rect.width)/2:
col_balls.append(each)
return col_balls
'''
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='b.png'#小球图片(gray)
greenball_image='green_ball.png' #小球图片(green)
#小球图片(green)
foo=pygame.image.load(grayball_image)
foo.get_rect()
ball_diameter=foo.get_width()#得到小球直径
#print(int(ball_radius))
bg_image='bg.png'#背景图片
glass_image='glass.png'
mouse_image='hand.png'
bg_size = width, height = 1024, 681
screen=pygame.display.set_mode(bg_size)
pygame.display.set_caption('Ball Game')
# 初始化音效模块
pygame.mixer.pre_init()
pygame.mixer.init()
'''
sound_cat = pygame.mixer.Sound('cat.wav') # 音效
sound_cat.set_volume(0.2) # 调节音量
sound_dog = pygame.mixer.Sound('dog.wav')
sound_dog.set_volume(0.2)
'''
loser_sound=pygame.mixer.Sound('loser.wav') # 音效
loser_sound.set_volume(0.2) # 调节音量
laugh_sound = pygame.mixer.Sound('laugh.wav')
laugh_sound.set_volume(0.2)
winner_sound = pygame.mixer.Sound('winner.wav')
winner_sound.set_volume(0.2)
hole_sound = pygame.mixer.Sound('hole.wav')
hole_sound.set_volume(0.2)
#背景音乐播放完播放音效loser_sound和laugh_sound,同时游戏失败
GAMEOVER=USEREVENT
pygame.mixer.music.set_endevent(GAMEOVER)
pygame.mixer.music.load('My Soul.ogg') #背景音乐
pygame.mixer.music.set_volume(0.2) #调节音量
pygame.mixer.music.play() #播放背景音乐()
pause=False
loser=False #设置游戏失败标志
win=True #设置成功通过标志
background=pygame.image.load(bg_image).convert_alpha()#加载背景图片
#(117,119,199,201)表示小球进黑洞只需要坐标(这里是左上角坐标)满足117<=x<=119,199<=y<=201就行(给3个像素的容错率)
hole=[(117,119,199,201),(225,227,390,392),(503,505,320,322),(698,700,192,194),(906,908,419,421)] #黑洞的位置
#hole=[(0,300,0,300),(301,600,0,300),(601,1024,0,300),(0,300,301,681),(301,1024,301,681)] #测试
msgs = []
msgs1=[]
BALL_NUM=5
balls=[]#存放小球的列表
group=pygame.sprite.Group() #sprite.Group,用来判断与处理后面的碰撞情况设立的一个集合(set)
for i in range(BALL_NUM):#创建(BALL_NUM)个小球并设置一些基础属性
position=randint(0,width-ball_diameter),randint(0,height-ball_diameter)
speed=[randint(1,10),randint(1,10)]
ball=Ball(grayball_image,greenball_image,position,speed,bg_size,5*(i+1)) # 调用Ball类生成画面
# 检查小球产生时会不会碰在一起,如果碰在一起,这些碰在一起的小球位置重置
# pygame.sprite.spritecollide(sprite,group,dokill,collided=None)是pygame提供的碰撞检测方法
# (4参数:1:单个球(对象)本身,2:其他球(对象),3:碰撞后对象是否消失,4:计算碰撞的回调函数,下面的按圆形计算)
while pygame.sprite.spritecollide(ball,group,False,pygame.sprite.collide_circle):
ball.rect.left,ball.rect.top=randint(0,width-ball_diameter),randint(0,height-ball_diameter)
balls.append(ball)
group.add(ball)
glass=Glass(glass_image,mouse_image,bg_size)#创建下方中间的玻璃板
motion=0#记录鼠标(这里是1s内)产生的事件数量
MYTIMER=USEREVENT+1 # 创建计时器事件
pygame.time.set_timer(MYTIMER,1000) # 设置计时器事件(1000ms时间内的事件数量)
# 设置第一次键盘事件响应时间延迟0.1s,重复事件间隔0.1s才能再次响应
pygame.key.set_repeat(100,100)
clock=pygame.time.Clock()#帧率控制器
running=True
while running:
for event in pygame.event.get():
if event.type==pygame.QUIT: #判断相应事件是否触发(下面也一样)
pygame.quit()
sys.exit()#退出事件
elif event.type==GAMEOVER:
for each in balls:
each.speed=[0,0]
group.remove(each)
if win: # 如果胜利了就不再响应失败的事件
msg1 = pygame.image.load('rookie.png').convert_alpha()
msg1_pos = (width - msg1.get_width()) // 2, (height - msg1.get_height()) // 2
msgs1.append((msg1, msg1_pos))
loser=True
loser_sound.play()
pygame.time.delay(2000)
laugh_sound.play()
elif event.type==MYTIMER:
if motion:
for each in group:
if each.check(motion):
each.speed=[0,0]
each.control=True
motion=0
elif event.type==MOUSEMOTION:
motion+=1
elif event.type==KEYDOWN:
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)) # 移除该小球(保留图像,移除其它一切属性和方法)
balls.insert(0,temp) #将temp放到前面(保证进黑洞的小球在没进黑洞的小球底层绘制(先绘制的被后绘制的覆盖),也就是显示的优先级)
hole.remove(i) # 小球进黑洞了,该黑洞也被移除(一个黑洞只能容纳一个小球)
if len(hole)==0: # 游戏胜利后(所有黑洞都有球了)
pygame.mixer.music.stop()
winner_sound.play()
pygame.time.delay(3000)
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()
win=False
if event.key==K_w: # wsad代表给被control的小球 上下左右 加速度 加一
for each in group:
if each.control:
each.speed[1]-=1
if event.key==K_s:
for each in group:
if each.control:
each.speed[1]+=1
if event.key==K_a:
for each in group:
if each.control:
each.speed[0]-=1
if event.key==K_d:
for each in group:
if each.control:
each.speed[0]+=1
screen.blit(background,(0,0))#填充(绘制)背景
screen.blit(glass.glass_image,glass.glass_rect) #绘制玻璃板,这一步与上一步步骤不能颠倒(否则背景会覆盖图片glass.png)
glass.mouse_rect.left, glass.mouse_rect.top=pygame.mouse.get_pos()
#限制我们的hand.png图片不能跑出glass.png图片外
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:
#图片hand.png不能超出图片glass.png右边
#则图片hand.png左上角与图片glass.png右边界最小距离不能超过图片hand.png的宽度
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:
#在control状态下小球变为绿色
screen.blit(each.greenball_image, each.rect) # 绘制小球(green-control)
else:
screen.blit(each.grayball_image,each.rect) # 绘制小球(gray-uncontrol)
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 msg1 in msgs1: # 绘制失败的画面
if loser:
screen.blit(msg1[0], msg1[1])
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:
print_exc()
pygame.quit()
input()