上节课我们做到当球静止下来后在第0号球上画一个球杆
本节课我们将会让这个球杆将球打出来
1、鼠标事件
pygame.mouse.get_pressed():返回鼠标左键,中间,右键的情况
2、键盘事件:
pygame.key.get_pressed(): 返回所有键盘的情况
3、pygame.Rect:
pygame提供的矩形类,这个类我们在之前有学过,这次主要是用到其初始化的功能
rect = Rect(x,y, width, height)
当我们希望将球击打时,实际是需要将球的速度改为一个不为0的数。可以用从球中心到鼠标点的距离作为一个参考量
def draw_and_wait_hit(self):
# 旧代码略
if pygame.mouse.get_pressed()[0]: # 鼠标左键被按下
self.speed[0] = (self.rect.center[0] - pos[0]) * 0.1
self.speed[1] = (self.rect.center[1] - pos[1]) * 0.1
这样当鼠标左键按下时,球就被打出来了,由于平常的速度在2至4左右,这个计算出来的距离有点大,我们取其中的十分之一作为速度
走到这一步发现球是击出去了,但击到另外的球上,被撞击的球文丝不动。
原因是被撞的球此时速度已经为0了,此时再怎么取返,速度都为0,因此需要重新考虑。
当速度接近0时,使用撞击球的速度取反
这样,我们改一个crash
def crash(self, groups):
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]
当球的速度接近0,就采用撞击的球的相反速度
然后我们发现当A撞击B,B撞C时,新的情况出现了,B和C在那不断抖动,猜想可能是撞击完后没能实现各自的速度取返。
所以我们需要在全部碰撞完成后,调整各球的速度,使他们彼此为反
def adjust(self, groups):
if pygame.sprite.spritecollide(self, groups, False):
if (self.speed[0] > 0 and groups[0].speed[0] > 0) or (self.speed[0] < 0 and groups[0].speed[0] < 0):
self.speed[0] = -self.speed[0]
if (self.speed[1] > 0 and groups[0].speed[1] > 0) or (self.speed[1] < 0 and groups[0].speed[1] < 0):
self.speed[1] = -self.speed[1]
然后在后面针对每一个球做一次调整:
while True:
# 略
for i in range(len(balls)):
for j in range(len(balls)):
if i == j:
continue
balls[i].adjust([balls[j]])
至此A撞B,B撞C就不会贴在一起不断抖动了
做一个球洞比较简单,直接在while True中用circle画一个黑色的圆即可,当然,在此之前我们要思考洞的位置,可放在右下角,用到pygame为我们提供的Rect
circle_rect = Rect(width-100, height-100, 100, 100)
然后在While中画出圆:
pygame.draw.circle(screen, [0, 0, 0], circle_rect.center, 50, 0)
怎样表示球进洞呢,通过Rect有一个contains可以判断一个矩形是否包含另一个矩形,我们可以将小球中心点构造成一个小矩形出来
rect = pygame.Rect(self.rect.center[0], self.rect.center[1], 1, 1)
然后判断黑色的圆形是否包含rect:
if circle_rect.contains(rect):
一旦发现黑色的圆包含某个小球的球心,我们可以将小球显示置为False,只有在小球显示为真时才可以显示小球
至此小球进洞就完成了
补充画一个描准器:
keys = pygame.key.get_pressed()
for k in keys:
if k:
self.show_sighting = not self.show_sighting
break
if self.show_sighting:
pos2 = [0, 0]
if self.rect.center[0] > pos[0]:
pos2[0] = self.rect.center[0] + abs((self.rect.center[0] - pos[0]))
else:
pos2[0] = self.rect.center[0] - abs((self.rect.center[0] - pos[0]))
if self.rect.center[1] > pos[1]:
pos2[1] = self.rect.center[1] + abs((self.rect.center[1] - pos[1]))
else:
pos2[1] = self.rect.center[1] - abs((self.rect.center[1] - pos[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
self.show_sighting = True
self.show = True
def move(self):
if not self.show:
return
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])
rect = pygame.Rect(self.rect.center[0], self.rect.center[1], 1, 1)
if circle_rect.contains(rect):
self.show = not self.show
screen.blit(self.image, self.rect)
def dec_speed(self):
self.speed[0] = self.speed[0] * 0.995
self.speed[1] = self.speed[1] * 0.995
def crash(self, groups):
if not self.show:
return
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 adjust(self, groups):
if pygame.sprite.spritecollide(self, groups, False):
if (self.speed[0] > 0 and groups[0].speed[0] > 0) or (self.speed[0] < 0 and groups[0].speed[0] < 0):
self.speed[0] = -self.speed[0]
if (self.speed[1] > 0 and groups[0].speed[1] > 0) or (self.speed[1] < 0 and groups[0].speed[1] < 0):
self.speed[1] = -self.speed[1]
def draw_and_wait_hit(self):
if abs(self.speed[0]) > 1 or abs(self.speed[1]) > 1:
return
pos = pygame.mouse.get_pos()
pygame.draw.line(screen, [255, 0, 0], self.rect.center, pos, 10)
keys = pygame.key.get_pressed()
for k in keys:
if k:
self.show_sighting = not self.show_sighting
break
if self.show_sighting:
pos2 = [0, 0]
if self.rect.center[0] > pos[0]:
pos2[0] = self.rect.center[0] + abs((self.rect.center[0] - pos[0]))
else:
pos2[0] = self.rect.center[0] - abs((self.rect.center[0] - pos[0]))
if self.rect.center[1] > pos[1]:
pos2[1] = self.rect.center[1] + abs((self.rect.center[1] - pos[1]))
else:
pos2[1] = self.rect.center[1] - abs((self.rect.center[1] - pos[1]))
pygame.draw.line(screen, [255, 0, 0], self.rect.center, pos2, 2)
if pygame.mouse.get_pressed()[0]:
self.speed[0] = (self.rect.center[0] - pos[0])*0.1
self.speed[1] = (self.rect.center[1] - pos[1])*0.1
pygame.init()
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
balls = []
for i in range(3):
ball = MyBall([180 + 180 * i, 180], [4, 4])
balls.append(ball)
circle_rect = pygame.Rect(width-100, height-100, 100, 100)
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].draw_and_wait_hit()
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[j]])
for i in range(len(balls)):
for j in range(len(balls)):
if i == j:
continue
balls[i].adjust([balls[j]])
pygame.draw.circle(screen, [0, 0, 0], circle_rect.center, circle_rect.width/2)
pygame.display.flip()
pygame.time.delay(20)