上面的問題是解決了,可是我們發現有的時候兩個綠色的小球在鍵盤的操控下,居然向著相反的方向移動,這是為什麼呢?
新的Bug 出現了,控制權交到玩家手中的時候,小球並不能正確的按照玩家的操縱去移動。我遇到的就是按下A 的時候,兩個綠色小球一個向左,一個向右。
其實在現實的開發中你常常會遇到這樣的事情:好不容易補完一個Bug,或者說添加一個新的功能進去,直接影響了原來的程序邏輯,導致了另一個Bug甚至一些Bug的出現,那麼既然有Bug,我們就應該及時的補上。
為什麼會導致玩家的操作無法正確的控制小球呢?
原來是我們將帶方向的速度拆分為方向和速度,而響應玩家的按鍵這裡我們依然認為速度是帶方向的,所以說你這裡儘管說是減到0 了,它還是會繼續減,而move () 方法這裡寫的是方向乘以速度,如果我的速度減到變為負數,方向為負數,那得到的卻是一個反方向的移動的效果。
為了保留玩家操控小球是帶加速度的效果,我們對於操控綠色小球這一部分的代碼就不能怎麼去修改,如果硬要去改的話,就會使得代碼變得更加麻煩,邏輯上變得更加複雜,所以我們這裡不妨將小球的移動給區分開:
隨機移動的話就按照方向乘以速度,而由玩家操控的移動我們還是保留帶方向的速度。
那應該怎麼做呢?
我們在move這裡對小球進行修改,然後還需要對兩個綠球的相撞進行修改,代碼如下:
-
import pygame
-
import sys
-
from pygame.locals import *
-
from random import *
-
-
# 球类继承自Spirte类
-
class Ball(pygame.sprite.Sprite):
-
def __init__(self, grayball_image, greenball_image, position, speed, bg_size, target):
-
# 初始化动画精灵
-
pygame.sprite.Sprite.__init__(self)
-
-
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
-
#加多一个side表示速度的方向
-
self.side = [choice([-1, 1]), choice([-1, 1])]#第一个元素表示水平方向,第二个元素表示垂直方向,使用random 的choice() 方法随机选择-1和1
-
self.speed = speed
-
#加多一个属性表示是否碰撞了
-
self.collide = False
-
#1、为每个小球设定一个不同的目标;
-
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:
-
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
-
#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)#设置Pygame 光标不可见
-
-
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
-
-
#添加背景音乐
-
pygame.mixer.music.load('bg_music.ogg')
-
pygame.mixer.music.set_volume(0.2)#设置音量
-
pygame.mixer.music.play()#播放
-
-
#加载音效
-
winner_sound = pygame.mixer.Sound("winner.wav")
-
winner_sound.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)
-
hole_sound = pygame.mixer.Sound("hole.wav")
-
hole_sound.set_volume(0.2)
-
-
#背景音乐会贯穿游戏的始终,背景音乐完整播放一次我们视为游戏的时间,
-
-
#音乐播放完时,游戏结束
-
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 - Python Demo")
-
-
background = pygame.image.load(bg_image).convert_alpha()
-
-
# 用来存放小球对象的列表
-
balls = []
-
group = pygame.sprite.Group()
-
-
# 创建五个小球
-
for i in range(5):
-
# 位置随机,速度随机
-
position = randint(0, width-100), randint(0, height-100)
-
#speed 初始化就只为正数了
-
speed = [randint(1, 10), randint(1, 10)]
-
ball = Ball(grayball_image, greenball_image, position, speed, bg_size, 5 * (i + 1)) #target 设为5-30,比较适中
-
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_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:
-
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
-
-
#用户通过WSAD按键移动绿色小球,使用+= 和 -= 而不是 +1 或者 -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_a: #a,左
-
for each in group:
-
if each.control:
-
each.speed[0] -= 1
-
-
if event.key == K_s: #s,下
-
for each in group:
-
if each.control:
-
each.speed[1] += 1
-
-
if event.key == K_d: #d, 右
-
for each in group:
-
if each.control:
-
each.speed[0] += 1
-
#在默认我们是不能感受到加速度的感觉的,
-
#因为在默认情况下,无论你是简单的按一下按键或是按住按键不放,
-
#Pygame都只为你发送一个键盘按下的事件
-
#不过我们可以通过 key 模块的 set_repeat() 方法来设置是否重复响应按下某个按键
-
# pygame.key.set_repeat(delay, interval)
-
#--delay 参数制动第一次发送事件的延迟时间
-
#--interval 参数指定重复发送事件的时间间隔
-
#--如果不带任何参数,表示取消重复发送事件
-
#为了使小球获得加速度的快感,我们设置按键的重复间隔为100毫秒,在while 循环前面设置。
-
-
-
screen.blit(background, (0, 0))
-
-
screen.blit(glass.glass_image, glass.glass_rect)
-
-
#将小手光标画在Pygame 默认光标位置上
-
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.mouse_rect.bottom - glass.mouse_rect.height:
-
glass.mouse_rect.top = glass.mouse_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 属性为 True,就画绿球
-
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)#还要把自己放进去
-
-
pygame.display.flip()
-
clock.tick(30)
-
-
-
if __name__ == "__main__":
-
main()
接下來,當綠色的小球移動到黑洞的正上方的時候,只要玩家點擊鍵盤上的空格鍵,就會提示一個音效,並且填入黑洞中,此後其它小球就會忽略它,直接從它上面飄過,無視它的存在,音樂結束之前,如果你能夠把所有的小球都填入到各個黑洞中,那麼遊戲勝利。
這裡有兩點需要注意的:
1、每個黑洞只能填入一個綠色的小球。
2、當小球填入黑洞時,其它小球是從它上方飄過,而不是從下方。
首先,我們5個黑洞的位置已經定義好了,我在作圖的時候,已經把位置量好了。由於如果要100%命中太難了,所以我們保留了一定活動的範圍,也就是說,代碼給出的範圍比黑洞實際範圍大一點。
最終完整代碼如下:
-
import pygame
-
import sys
-
import traceback
-
from pygame.locals import *
-
from random import *
-
-
# 球类继承自Spirte类
-
class Ball(pygame.sprite.Sprite):
-
def __init__(self, grayball_image, greenball_image, position, speed, bg_size, target):
-
# 初始化动画精灵
-
pygame.sprite.Sprite.__init__(self)
-
-
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
-
#加多一个side表示速度的方向
-
self.side = [choice([-1, 1]), choice([-1, 1])]#第一个元素表示水平方向,第二个元素表示垂直方向,使用random 的choice() 方法随机选择-1和1
-
self.speed = speed
-
#加多一个属性表示是否碰撞了
-
self.collide = False
-
#1、为每个小球设定一个不同的目标;
-
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:
-
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
-
#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)#设置Pygame 光标不可见
-
-
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
-
-
#添加背景音乐
-
pygame.mixer.music.load('bg_music.ogg')
-
pygame.mixer.music.set_volume(0.2)#设置音量
-
pygame.mixer.music.play()#播放
-
-
#加载音效
-
winner_sound = pygame.mixer.Sound("winner.wav")
-
winner_sound.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)
-
hole_sound = pygame.mixer.Sound("hole.wav")
-
hole_sound.set_volume(0.2)
-
-
#背景音乐会贯穿游戏的始终,背景音乐完整播放一次我们视为游戏的时间,
-
-
#音乐播放完时,游戏结束
-
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 - Python Demo")
-
-
background = pygame.image.load(bg_image).convert_alpha()
-
-
#5个黑洞的范围,由于如果要100%命中太难了,所以我们保留了一定活动的范围。
-
#每个黑洞的范围:(x1,x2,y1,y2)
-
hole = [(117, 119, 199, 201), (225, 227, 390, 392), \
-
(503, 505, 320, 322), (698, 700, 192, 194), \
-
(906, 908, 419, 421)]
-
-
msgs = []
-
-
# 用来存放小球对象的列表
-
balls = []
-
group = pygame.sprite.Group()
-
-
# 创建五个小球
-
for i in range(5):
-
# 位置随机,速度随机
-
position = randint(0, width-100), randint(0, height-100)
-
#speed 初始化就只为正数了
-
speed = [randint(1, 10), randint(1, 10)]
-
ball = Ball(grayball_image, greenball_image, position, speed, bg_size, 5 * (i + 1)) #target 设为5-30,比较适中
-
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_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
-
-
#用户通过WSAD按键移动绿色小球,使用+= 和 -= 而不是 +1 或者 -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_a: #a,左
-
for each in group:
-
if each.control:
-
each.speed[0] -= 1
-
-
if event.key == K_s: #s,下
-
for each in group:
-
if each.control:
-
each.speed[1] += 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").conmvert_alpha()
-
msg_pos = (width - msg.get_width()) // 2, \
-
(height - msg.get_height()) // 2
-
msgs.append((msg, msg_pos))
-
#播放音效
-
laugh_sound.play()
-
-
screen.blit(background, (0, 0))
-
-
screen.blit(glass.glass_image, glass.glass_rect)
-
-
#将小手光标画在Pygame 默认光标位置上
-
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.mouse_rect.bottom - glass.mouse_rect.height:
-
glass.mouse_rect.top = glass.mouse_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 属性为 True,就画绿球
-
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()
還有一點需要補充一下:
就是我們平時在IDLE裡面執行,但是我們點關閉(×)沒有反應,有沒有什麼好的辦法?
有的,你只需要在響應QUIT 事件這裡,加一條pygame.quit() 即可。
-
for event in pygame.event.get():
-
if event.type == QUIT:
-
pygame.quit()
-
sys.exit()
還有一種情況就是:用戶雙擊打開遊戲文件,如果有邏輯錯誤或者代碼錯誤,程序就不會給你報錯,而是一打開就閃退。
我們也有解決方案:
就是添加捕獲異常的模塊import traceback
然後:
-
if __name__ == "__main__":
-
# 这样做的好处是双击打开时如果出现异常可以报告异常,而不是一闪而过!
-
try:
-
main()
-
except SystemExit: #这是按下 × 的异常,直接忽略
-
pass
-
except:
-
traceback.print_exc()
-
pygame.quit()
-
input()
-
import pygame
-
import sys
-
from pygame.locals import *
-
from random import *
-
-
# 球类继承自Spirte类
-
class Ball(pygame.sprite.Sprite):
-
def __init__(self, grayball_image, greenball_image, position, speed, bg_size, target):
-
# 初始化动画精灵
-
pygame.sprite.Sprite.__init__(self)
-
-
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
-
#加多一个side表示速度的方向
-
self.side = [choice([-1, 1]), choice([-1, 1])]#第一个元素表示水平方向,第二个元素表示垂直方向,使用random 的choice() 方法随机选择-1和1
-
self.speed = speed
-
#加多一个属性表示是否碰撞了
-
self.collide = False
-
#1、为每个小球设定一个不同的目标;
-
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:
-
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
-
#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)#设置Pygame 光标不可见
-
-
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
-
-
#添加背景音乐
-
pygame.mixer.music.load('bg_music.ogg')
-
pygame.mixer.music.set_volume(0.2)#设置音量
-
pygame.mixer.music.play()#播放
-
-
#加载音效
-
winner_sound = pygame.mixer.Sound("winner.wav")
-
winner_sound.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)
-
hole_sound = pygame.mixer.Sound("hole.wav")
-
hole_sound.set_volume(0.2)
-
-
#背景音乐会贯穿游戏的始终,背景音乐完整播放一次我们视为游戏的时间,
-
-
#音乐播放完时,游戏结束
-
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 - Python Demo")
-
-
background = pygame.image.load(bg_image).convert_alpha()
-
-
# 用来存放小球对象的列表
-
balls = []
-
group = pygame.sprite.Group()
-
-
# 创建五个小球
-
for i in range(5):
-
# 位置随机,速度随机
-
position = randint(0, width-100), randint(0, height-100)
-
#speed 初始化就只为正数了
-
speed = [randint(1, 10), randint(1, 10)]
-
ball = Ball(grayball_image, greenball_image, position, speed, bg_size, 5 * (i + 1)) #target 设为5-30,比较适中
-
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_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:
-
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
-
-
#用户通过WSAD按键移动绿色小球,使用+= 和 -= 而不是 +1 或者 -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_a: #a,左
-
for each in group:
-
if each.control:
-
each.speed[0] -= 1
-
-
if event.key == K_s: #s,下
-
for each in group:
-
if each.control:
-
each.speed[1] += 1
-
-
if event.key == K_d: #d, 右
-
for each in group:
-
if each.control:
-
each.speed[0] += 1
-
#在默认我们是不能感受到加速度的感觉的,
-
#因为在默认情况下,无论你是简单的按一下按键或是按住按键不放,
-
#Pygame都只为你发送一个键盘按下的事件
-
#不过我们可以通过 key 模块的 set_repeat() 方法来设置是否重复响应按下某个按键
-
# pygame.key.set_repeat(delay, interval)
-
#--delay 参数制动第一次发送事件的延迟时间
-
#--interval 参数指定重复发送事件的时间间隔
-
#--如果不带任何参数,表示取消重复发送事件
-
#为了使小球获得加速度的快感,我们设置按键的重复间隔为100毫秒,在while 循环前面设置。
-
-
-
screen.blit(background, (0, 0))
-
-
screen.blit(glass.glass_image, glass.glass_rect)
-
-
#将小手光标画在Pygame 默认光标位置上
-
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.mouse_rect.bottom - glass.mouse_rect.height:
-
glass.mouse_rect.top = glass.mouse_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 属性为 True,就画绿球
-
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)#还要把自己放进去
-
-
pygame.display.flip()
-
clock.tick(30)
-
-
-
if __name__ == "__main__":
-
main()
-
import pygame
-
import sys
-
import traceback
-
from pygame.locals import *
-
from random import *
-
-
# 球类继承自Spirte类
-
class Ball(pygame.sprite.Sprite):
-
def __init__(self, grayball_image, greenball_image, position, speed, bg_size, target):
-
# 初始化动画精灵
-
pygame.sprite.Sprite.__init__(self)
-
-
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
-
#加多一个side表示速度的方向
-
self.side = [choice([-1, 1]), choice([-1, 1])]#第一个元素表示水平方向,第二个元素表示垂直方向,使用random 的choice() 方法随机选择-1和1
-
self.speed = speed
-
#加多一个属性表示是否碰撞了
-
self.collide = False
-
#1、为每个小球设定一个不同的目标;
-
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:
-
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
-
#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)#设置Pygame 光标不可见
-
-
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
-
-
#添加背景音乐
-
pygame.mixer.music.load('bg_music.ogg')
-
pygame.mixer.music.set_volume(0.2)#设置音量
-
pygame.mixer.music.play()#播放
-
-
#加载音效
-
winner_sound = pygame.mixer.Sound("winner.wav")
-
winner_sound.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)
-
hole_sound = pygame.mixer.Sound("hole.wav")
-
hole_sound.set_volume(0.2)
-
-
#背景音乐会贯穿游戏的始终,背景音乐完整播放一次我们视为游戏的时间,
-
-
#音乐播放完时,游戏结束
-
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 - Python Demo")
-
-
background = pygame.image.load(bg_image).convert_alpha()
-
-
#5个黑洞的范围,由于如果要100%命中太难了,所以我们保留了一定活动的范围。
-
#每个黑洞的范围:(x1,x2,y1,y2)
-
hole = [(117, 119, 199, 201), (225, 227, 390, 392), \
-
(503, 505, 320, 322), (698, 700, 192, 194), \
-
(906, 908, 419, 421)]
-
-
msgs = []
-
-
# 用来存放小球对象的列表
-
balls = []
-
group = pygame.sprite.Group()
-
-
# 创建五个小球
-
for i in range(5):
-
# 位置随机,速度随机
-
position = randint(0, width-100), randint(0, height-100)
-
#speed 初始化就只为正数了
-
speed = [randint(1, 10), randint(1, 10)]
-
ball = Ball(grayball_image, greenball_image, position, speed, bg_size, 5 * (i + 1)) #target 设为5-30,比较适中
-
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_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
-
-
#用户通过WSAD按键移动绿色小球,使用+= 和 -= 而不是 +1 或者 -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_a: #a,左
-
for each in group:
-
if each.control:
-
each.speed[0] -= 1
-
-
if event.key == K_s: #s,下
-
for each in group:
-
if each.control:
-
each.speed[1] += 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").conmvert_alpha()
-
msg_pos = (width - msg.get_width()) // 2, \
-
(height - msg.get_height()) // 2
-
msgs.append((msg, msg_pos))
-
#播放音效
-
laugh_sound.play()
-
-
screen.blit(background, (0, 0))
-
-
screen.blit(glass.glass_image, glass.glass_rect)
-
-
#将小手光标画在Pygame 默认光标位置上
-
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.mouse_rect.bottom - glass.mouse_rect.height:
-
glass.mouse_rect.top = glass.mouse_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 属性为 True,就画绿球
-
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()
-
for event in pygame.event.get():
-
if event.type == QUIT:
-
pygame.quit()
-
sys.exit()
-
if __name__ == "__main__":
-
# 这样做的好处是双击打开时如果出现异常可以报告异常,而不是一闪而过!
-
try:
-
main()
-
except SystemExit: #这是按下 × 的异常,直接忽略
-
pass
-
except:
-
traceback.print_exc()
-
pygame.quit()
-
input()
0 留言:
發佈留言