打乒乓球小游戏(python、pygame)
目录
前提:
一、开发环境
二、游戏说明
三、代码实现
ⅰ、准备乒乓球和球拍,创建实例
ⅱ、控制球拍
ⅲ、记录分数并用pygame.font显示
①、创建字体对象
②、重绘分数
③、跟踪分数
④、跟踪还有几条命
⑤、游戏结束
未添加声音代码:
ⅳ 给PyPong添加声音
①、每次球碰到球拍时要增加一个声音:
②、其他声音
代码:
③、存在问题——让声音只播放一次
④、添加背景音乐
最终代码:
将学到的内容集中在一起(包括动画精灵、碰撞检测和事件),建立一个简单的“球拍与球”游戏,类似于 Pong。
球:
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0:
self.speed[1] = -self.speed[1]
myBall = MyBallClass('wackyball.bmp', [50, 50], ball_speed)
ballGroup = pygame.sprite.Group(myBall)
球拍:
只是使用一个简单的矩形,
class MyPaddleClass(pygame.sprite.Sprite):
def __init__(self, location):
pygame.sprite.Sprite.__init__(self)
image_surface = pygame.surface.Surface([100,20]) #为球拍创建表面
image_surface.fill([0, 0, 0]) #黑色
self.image = image_surface.convert() #将表面转成一个图像
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
paddle = MyPaddleClass([270, 400])
球拍只能左右移动,不能上下移动,让球拍的x位置,和鼠标移动,让鼠标控制球拍—— MOUSEMOTION 事件(球拍会自动限制在窗口的边界以内)
elif event.type == pygame.MOUSEMOTION:
paddle.rect.centerx = event.pos[0] #event.pos[0]鼠标位置
if pygame.sprite.spritecollide(paddle, ballGroup, False):
myBall.speed[1] = -myBall.speed[1]
球拍拍球:
import sys, pygame
from random import *
from pygame.locals import *
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0:
self.speed[1] = -self.speed[1]
class MyPaddleClass(pygame.sprite.Sprite):
def __init__(self, location):
pygame.sprite.Sprite.__init__(self)
image_surface = pygame.surface.Surface([100,20]) #为球拍创建表面
image_surface.fill([0, 0, 0]) #黑色
self.image = image_surface.convert() #将表面转成一个图像
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
pygame.init()
#设置窗口大小和颜色
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background.fill([255, 255, 255])
clock = pygame.time.Clock()
ball_speed = [10, 5]
myBall = MyBallClass('wackyball.bmp', [50, 50], ball_speed)
ballGroup = pygame.sprite.Group(myBall)
paddle = MyPaddleClass([270, 400])
running = True
while running:
clock.tick(30)
screen.fill([255, 255, 255])
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEMOTION:
paddle.rect.centerx = event.pos[0]
if pygame.sprite.spritecollide(paddle, ballGroup, False):
myBall.speed[1] = -myBall.speed[1]
myBall.move()
screen.blit(myBall.image, myBall.rect)
screen.blit(paddle.image, paddle.rect)
pygame.display.flip()
pygame.quit()
显示分数——用pygame.font模块来显示文本
- font 对象,告诉 Pygame 你想要的字体样式和大小。
- 渲染文本,向字体对象传入一个字符串,(此处的字符串,是玩家的分数,在这里需要把整数换成string的格式)它会返回一个绘制有这个文本的新的表面。
- 把这个表面块移到显示表面。
score_font = pygame.font.Font(None, 50) #创建字体对象
score_surf = score_font.render(str(score), 1, (0, 0, 0)) #渲染文本到表面
score_pos = [10, 10] #文本位置
screen.blit(score_surf, score_pos) #重绘分数
在球的类中,用球的 move() 方法检测了球什么时候碰到窗口的顶边(来完成反弹),这是就可以在此添加分数。
def move(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0:
self.speed[1] = -self.speed[1]
score = score + 1
score_surf = score_font.render(str(score), 1, (0, 0, 0))
注意:
当球碰到边界顶边时,应该加分数值时会报错!!
解决方法:
这个问题是命名空间的问题,。尽管确实有一个名为 score 的变量,但是此刻试图从 MyBallClass类的 move() 方法中使用这个变量。这个类在寻找一个名为score 的局部变量,而这个局部变量并不存在。应该定义为调用全局变量!
参考:https://blog.csdn.net/qq_41070511/article/details/115670004
修改如下。
def move(self): global score, score_font, score_surf self.rect = self.rect.move(self.speed) if self.rect.left < 0 or self.rect.right > width: self.speed[0] = -self.speed[0] if self.rect.top < 0: self.speed[1] = -self.speed[1] score = score + 1 score_surf = score_font.render(str(score), 1, (0, 0, 0))
这时可以看见分数在不停增加!
跟踪目前有几条命,目前给3条命,如果漏接球,就会从窗口底部掉下。
这个要放在重绘分数之后(分数刷新在重新赋予生命)
if myBall.rect.top >=screen.get_rect().bottom:
lives = lives - 1
pygame.time.delay(2000)
myBall.rect.topleft = [50, 50]
这是运行效果,小球没有接到会重新从[50, 50],重新落下。
当玩家丢掉最后一条命时要显示一个“游戏结束”的消息。
if myBall.rect.top >=screen.get_rect().bottom:
lives = lives - 1
if lives == 0:
final_text1 = "Game Over"
final_text2 = "Your final score is : " + str(score)
ft1_font = pygame.font.Font(None, 70)
ft1_surf = ft1_font.render(final_text1, 1, (0, 0, 0))
ft2_font = pygame.font.Font(None, 50)
ft2_surf = ft2_font.render(final_text2, 1, (0, 0, 0))
screen.blit(ft1_surf, [screen.get_width()/2 - ft1_surf.get_width()/2, 100])
screen.blit(ft2_surf, [screen.get_width()/2 - ft2_surf.get_width()/2, 100])
pygame.display.flip()
done = True
else:
pygame.time.delay(2000)
myBall.rect.topleft = [(screen.get_rect().width) - 40 * lives, 20]
import sys, pygame
from random import *
from pygame.locals import *
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
global score, score_font, score_surf
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0:
self.speed[1] = -self.speed[1]
score = score + 1
score_surf = score_font.render(str(score), 1, (0, 0, 0))
class MyPaddleClass(pygame.sprite.Sprite):
def __init__(self, location):
pygame.sprite.Sprite.__init__(self)
image_surface = pygame.surface.Surface([100,20]) #为球拍创建表面
image_surface.fill([0, 0, 0]) #黑色
self.image = image_surface.convert() #将表面转成一个图像
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
pygame.init()
#设置窗口大小和颜色
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background.fill([255, 255, 255])
clock = pygame.time.Clock()
ball_speed = [10, 5]
score = 0
lives = 3
score_font = pygame.font.Font(None, 50) #创建字体对象
score_surf = score_font.render(str(score), 1, (0, 0, 0)) #渲染文本到表面
score_pos = [10, 10] #文本位置
myBall = MyBallClass('wackyball.bmp', [50, 50], ball_speed)
ballGroup = pygame.sprite.Group(myBall)
paddle = MyPaddleClass([270, 400])
done = False
running = True
while running:
clock.tick(30)
screen.fill([255, 255, 255])
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEMOTION:
paddle.rect.centerx = event.pos[0]
if pygame.sprite.spritecollide(paddle, ballGroup, False):
myBall.speed[1] = -myBall.speed[1]
myBall.move()
if not done:
screen.blit(myBall.image, myBall.rect)
screen.blit(paddle.image, paddle.rect)
screen.blit(score_surf, score_pos) #重绘分数
for i in range(lives):
width = screen.get_width()
screen.blit(myBall.image, [width - 40 * i, 20])
pygame.display.flip()
if myBall.rect.top >=screen.get_rect().bottom:
lives = lives - 1
if lives == 0:
final_text1 = "Game Over"
final_text2 = "Your final score is : " + str(score)
ft1_font = pygame.font.Font(None, 70)
ft1_surf = ft1_font.render(final_text1, 1, (0, 0, 0))
ft2_font = pygame.font.Font(None, 50)
ft2_surf = ft2_font.render(final_text2, 1, (0, 0, 0))
screen.blit(ft1_surf, [screen.get_width()/2 - ft1_surf.get_width()/2, 100])
screen.blit(ft2_surf, [screen.get_width()/2 - ft2_surf.get_width()/2, 100])
pygame.display.flip()
done = True
else:
pygame.time.delay(2000)
myBall.rect.topleft = [(screen.get_rect().width) - 40 * lives, 20]
pygame.quit()
hit_paddle= pygame.mixer.Sound("hit_paddle.wav")
hit_paddle.set_volume(0.50)
#当球碰到球拍时添加!播放声音。
if pygame.sprite.spritecollide(paddle, ballGroup, False):
myBall.speed[1] = -myBall.speed[1]
hit_paddle.play()
#其他声音
splat = pygame.mixer.Sound("splat.wav")
splat.set_volume(0.6)
hit_wall = pygame.mixer.Sound("hit_wall.wav")
hit_wall.set_volume(0.40)
get_point = pygame.mixer.Sound("get_point.wav")
get_point.set_volume(0.2)
new_lift = pygame.mixer.Sound("new_lift.wav")
new_lift.set_volume(0.40)
game_over = pygame.mixer.Sound("game_over.wav")
game_over.set_volume(0.40)
玩家漏球,球碰到底边时;
漏球声音
if myBall.rect.top >=screen.get_rect().bottom:
splat.play() #漏球声音
lives = lives - 1
球碰到顶边而且玩家得分时 和 球碰到两边的墙时
在反向时还可以播放声音
在顶部这里让球反弹,并为玩家加 1 分。
def move(self):
global score, score_font, score_surf
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > width:
self.speed[0] = -self.speed[0]
hit_wall.play() #换方向播声音
if self.rect.top < 0:
self.speed[1] = -self.speed[1]
score = score + 1
score_surf = score_font.render(str(score), 1, (0, 0, 0))
get_point.play() #得分声音
新的一条命开始时;
else:
pygame.time.delay(1000)
new_life.play()
myBall.rect.topleft = [50, 50]
screen.blit(myBall.image, myBall.rect)
pygame.display.flip()
pygame.time.delay(1000)
游戏结束时。
if lives == 0:
game_over.play()
播 放 game_over声 音和 splat 声 音 的 代 码 放 在 主 while 循 环 中,Pygame 窗口关闭前它们是不会停止的,所以只要 while 循环在运行,声音就会反复播放!需要增加一些代码来确保它只会播放一次。
通过 done 变量来告诉我们游戏何时结束,且能够知道什么时候播放 game_over声音,以及什么时候显示最后的分数消息。
if myBall.rect.top >=screen.get_rect().bottom:
if not done:
splat.play() #漏球声音
lives = lives - 1
if lives == 0:
if not done:
game_over.play()
myBall.move(),在无限循环里一直运行。
解决方法:
- 游戏结束时把球的速度设置为 [0,0] 来阻止球继续移动。
- 查看球是否在窗口底边以下,如果是,就不再播放 hit_wall 声音。
- 检查 done 变量,如果游戏已经结束就不再播放 hit_wall 声音。
def move(self): global score, score_font, score_surf self.rect = self.rect.move(self.speed) if self.rect.left < 0 or self.rect.right > width: self.speed[0] = -self.speed[0] if not done: hit_wall.play() #换方向播声音 if self.rect.top < 0: self.speed[1] = -self.speed[1] score = score + 1 score_surf = score_font.render(str(score), 1, (0, 0, 0)) if not done: get_point.play() #得分声音
pygame.mixer.music.load("bg_music.mp3")
pygame.mixer.music.set_volume(0.3)
pygame.mixer.music.play(-1)
在音乐要停下来的时候,渐渐淡出:
pygame.mixer.music.fadeout(2000)
import sys, pygame
from random import *
from pygame.locals import *
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, location, speed):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(image_file) #加载图片
self.rect = self.image.get_rect() #得到定义图像边界矩形
self.rect.left, self.rect.top = location #设置球的初始位置
self.speed = speed #创建一个速度
def move(self):
global score, score_font, score_surf
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > screen.get_width():
self.speed[0] = -self.speed[0]
if not done:
hit_wall.play() #换方向播声音
if self.rect.top <= 0:
self.speed[1] = -self.speed[1]
score = score + 1
score_surf = score_font.render(str(score), 1, (0, 0, 0))
if not done:
get_point.play() #得分声音
class MyPaddleClass(pygame.sprite.Sprite):
def __init__(self, location):
pygame.sprite.Sprite.__init__(self)
image_surface = pygame.surface.Surface([100,20]) #为球拍创建表面
image_surface.fill([0, 0, 0]) #黑色
self.image = image_surface.convert() #将表面转成一个图像
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
pygame.init()
pygame.mixer.init()
#设置窗口大小和颜色
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background.fill([255, 255, 255])
clock = pygame.time.Clock()
ball_speed = [10, 5]
hit_paddle = pygame.mixer.Sound("hit_paddle.wav")
hit_paddle.set_volume(0.4)
splat = pygame.mixer.Sound("splat.wav")
splat.set_volume(0.6)
hit_wall = pygame.mixer.Sound("hit_wall.wav")
hit_wall.set_volume(0.40)
get_point = pygame.mixer.Sound("get_point.wav")
get_point.set_volume(0.2)
new_life = pygame.mixer.Sound("new_life.wav")
new_life.set_volume(0.40)
game_over = pygame.mixer.Sound("game_over.wav")
game_over.set_volume(0.40)
pygame.mixer.music.load("bg_music.mp3")
pygame.mixer.music.set_volume(0.3)
pygame.mixer.music.play(-1)
score = 0
lives = 3
score_font = pygame.font.Font(None, 50) #创建字体对象
score_surf = score_font.render(str(score), 1, (0, 0, 0)) #渲染文本到表面
score_pos = [10, 10] #文本位置
myBall = MyBallClass('wackyball.bmp', [50, 50], ball_speed)
ballGroup = pygame.sprite.Group(myBall)
paddle = MyPaddleClass([270, 400])
done = False
running = True
while running:
clock.tick(30)
screen.fill([255, 255, 255])
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEMOTION:
paddle.rect.centerx = event.pos[0]
if pygame.sprite.spritecollide(paddle, ballGroup, False):
hit_paddle.play()
myBall.speed[1] = -myBall.speed[1]
myBall.move()
if not done:
screen.blit(myBall.image, myBall.rect)
screen.blit(paddle.image, paddle.rect)
screen.blit(score_surf, score_pos) #重绘分数
for i in range(lives):
width = screen.get_width()
screen.blit(myBall.image, [width - 40 * i, 20])
pygame.display.flip()
if myBall.rect.top >=screen.get_rect().bottom:
if not done:
splat.play() #漏球声音
lives = lives - 1
if lives <= 0:
if not done:
game_over.play()
final_text1 = "Game Over"
final_text2 = "Your final score is : " + str(score)
ft1_font = pygame.font.Font(None, 70)
ft1_surf = ft1_font.render(final_text1, 1, (0, 0, 0))
ft2_font = pygame.font.Font(None, 50)
ft2_surf = ft2_font.render(final_text2, 1, (0, 0, 0))
screen.blit(ft1_surf, [screen.get_width()/2 - ft1_surf.get_width()/2, 100])
screen.blit(ft2_surf, [screen.get_width()/2 - ft2_surf.get_width()/2, 200])
pygame.display.flip()
done = True
pygame.mixer.music.fadeout(2000)
else:
pygame.time.delay(1000)
new_life.play()
myBall.rect.topleft = [50, 50]
screen.blit(myBall.image, myBall.rect)
pygame.display.flip()
pygame.time.delay(1000)
pygame.quit()
链接:https://pan.baidu.com/s/1azvZOoeDgYdnJc2gzNX4ZQ
提取码:3v5r
复制这段内容后打开百度网盘手机App,操作更方便哦
视频链接:https://www.bilibili.com/video/BV1kK4y1o76X?p=1&share_medium=iphone&share_plat=ios&share_source=QQ&share_tag=s_i×tamp=1623896465&unique_k=Aiqqof