一、事件:
1、鼠标事件:
pygame.mouse.get_pos(): 获取鼠标当前点的坐标
pygame.mouse.get_pressed()[0]: 获取鼠标左键是否为点击
二、复习碰撞精灵
1、首先是3个球在窗口上移动的最基础的版本:
import pygame, sys
class MyBall(pygame.sprite.Sprite):
def __init__(self, point, speed):
self.image = pygame.image.load("beach_ball.png")
self.rect = self.image.get_rect()
self.rect.left = point[0]
self.rect.top = point[1]
self.speed = speed
def move(self):
self.rect = self.rect.move(self.speed)
screen.blit(self.image, self.rect)
pygame.init()
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
screen.fill([255, 255, 255])
balls = []
for i in range(3):
ball = MyBall([180+i*180, 200], [8, 8])
balls.append(ball)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill([255, 255, 255])
for i in range(len(balls)):
balls[i].move()
pygame.display.flip()
pygame.time.delay(20)
这三个球有一个缺点,一旦移动到窗口外,就移不回来了,因此,我们要在移动时,进行窗口边沿的判断
2、增加上下左右边框的判断
这样当球试图超出边界时,将对应方向的速度取反,使得重新回到窗口中来:
def move(self):
self.rect = self.rect.move(self.speed)
if self.rect.right > width: # 新增右边框判断
self.speed[0] = -abs(self.speed[0])
if self.rect.left < 0: # 新增左边框判断
self.speed[0] = abs(self.speed[0])
if self.rect.bottom > height: # 新增下边框判断
self.speed[1] = -abs(self.speed[1])
if self.rect.top < 0: # 新增上边框判断
self.speed[1] = abs(self.speed[1])
screen.blit(self.image, self.rect)
进行到这一步,我们发现小球碰到一起时没有发生碰撞事件,即碰到一起的小球没有互相撤退,这时碰撞检测就使用起来了:
通过pygame.sprite.spritecollide我们可以检测当前小球与别的小球是否发生碰撞,如果发生碰撞,就将小球当前速度取反
3、碰撞处理
1) 碰撞后的处理
def crash(self, balls):
balls.remove(self) # 将自身从碰撞组中删除
if pygame.sprite.spritecollide(self, balls, False): # 检测碰撞发生,将自身的速度取反
self.speed[0] = -self.speed[0]
self.speed[1] = -self.speed[1]
2)针对小球两两组合进行碰撞处理:
for i in range(len(balls)):
for j in range(len(balls)):
if i == j:
continue
balls[i].crash([balls[i], balls[j]])
这样小球互相碰到就会向相反的方向弹去。
三、画出球杆并击打小球
我们可以设想当小球停止下来后才好进行击打小球的动作,因此,可以针对小球做一个减速处理
然后当小球的x,y方向的速度都低于1时即认为小球静止了
1、减速逻辑
def dec_speed(self):
"""减速"""
self.speed[0] *= 0.995
self.speed[1] *= 0.995
2、画出球杆
当小球静时后,我们需要画出一个球杆,画法为小球中心点到当前鼠标的位置画一条线段:
def wait_hit(self):
if abs(self.speed[0]) > 1 or abs(self.speed[1]) > 1: # 在运动状态,退出,注意需要用abs,原因是速度有正和负
pass
else:
pos = pygame.mouse.get_pos() # 获取鼠标坐标
pygame.draw.line(screen, [255, 0, 0], self.rect.center, pos, 5) # 画出小球到鼠标的红线
if pygame.mouse.get_pressed()[0]: # 按下鼠标左键
self.speed[0] = 0.1 * (self.rect.center[0] - pos[0]) # 给小球初始速度
self.speed[1] = 0.1 * (self.rect.center[1] - pos[1])
然后加上调用:
for i in range(len(balls)):
if i == 0:
balls[i].wait_hit() # 只有0号小球静止时画出球杆
balls[i].move()
balls[i].dec_speed() # 减速,使得小球最终静止下来
至此,一开始3个小球都会运动,运动过程中碰到一起也会发生碰撞反弹,随着时间推移,小球越来越慢,最终停下来,然后在0号球处画出了红线,点击鼠标左键,小球会向红线的相反方向移动,就是存在一个问题,当小球碰到静止的小球时,静止的小球不会动
3、碰撞发生后,当发现自身没有速度,使用对方的速度
def crash(self, groups):
groups.remove(self) # 将自身从碰撞组中删除
if pygame.sprite.spritecollide(self, groups, False): # 检测碰撞发生,将自身的速度取反
if abs(self.speed[0]) > 1: # 运动状态用自身的反速度
self.speed[0] = -self.speed[0]
else:
self.speed[0] = -groups[0].speed[0] # 静止状态,用对方的反速度
if abs(self.speed[1]) > 1:
self.speed[1] = -self.speed[1]
else:
self.speed[1] = -groups[0].speed[1]
至此一个简单的弹球游戏就结束了
附完整源代码:
import pygame, sys
class MyBall(pygame.sprite.Sprite):
def __init__(self, point, speed):
self.image = pygame.image.load("beach_ball.png")
self.rect = self.image.get_rect()
self.rect.left = point[0]
self.rect.top = point[1]
self.speed = speed
def move(self):
self.rect = self.rect.move(self.speed)
if self.rect.right > width: # 新增右边框判断
self.speed[0] = -abs(self.speed[0])
if self.rect.left < 0: # 新增左边框判断
self.speed[0] = abs(self.speed[0])
if self.rect.bottom > height: # 新增下边框判断
self.speed[1] = -abs(self.speed[1])
if self.rect.top < 0: # 新增上边框判断
self.speed[1] = abs(self.speed[1])
screen.blit(self.image, self.rect)
def crash(self, groups):
groups.remove(self) # 将自身从碰撞组中删除
if pygame.sprite.spritecollide(self, groups, False): # 检测碰撞发生,将自身的速度取反
if abs(self.speed[0]) > 1: # 运动状态用自身的反速度
self.speed[0] = -self.speed[0]
else:
self.speed[0] = -groups[0].speed[0] # 静止状态,用对方的反速度
if abs(self.speed[1]) > 1:
self.speed[1] = -self.speed[1]
else:
self.speed[1] = -groups[0].speed[1]
def dec_speed(self):
"""减速"""
self.speed[0] *= 0.995
self.speed[1] *= 0.995
def wait_hit(self):
if abs(self.speed[0]) > 1 or abs(self.speed[1]) > 1: # 在运动状态,退出,注意需要用abs,原因是速度有正和负
pass
else:
pos = pygame.mouse.get_pos() # 获取鼠标坐标
pygame.draw.line(screen, [255, 0, 0], self.rect.center, pos, 5) # 画出小球到鼠标的红线
if pygame.mouse.get_pressed()[0]: # 按下鼠标左键
self.speed[0] = 0.1 * (self.rect.center[0] - pos[0]) # 给小球初始速度
self.speed[1] = 0.1 * (self.rect.center[1] - pos[1])
pygame.init()
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
screen.fill([255, 255, 255])
balls = []
for i in range(3):
ball = MyBall([180 + i * 180, 200], [8, 8])
balls.append(ball)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill([255, 255, 255])
for i in range(len(balls)):
if i == 0:
balls[i].wait_hit() # 只有0号小球静止时画出球杆
balls[i].move()
balls[i].dec_speed() # 减速,使得小球最终静止下来
for i in range(len(balls)):
for j in range(len(balls)):
if i == j:
continue
balls[i].crash([balls[i], balls[j]])
pygame.display.flip()
pygame.time.delay(10)