项目介绍
使用Pygame
做一个乒乓球游戏。左侧为电脑,右侧为玩家。
视频搬运-B站
视频教程约90分钟。
环境:需要pygame
库,可用pip安装:pip install pygame
1. 基础版本
首先进行一些初始化,初始化pygame以及物体的初始状态。
然后是主循环,游戏的主循环主要包含3个内容
- 处理事件(这里主要是键盘按键)
- 更新物体的状态
- 在屏幕上绘制
# 基础 ping pang游戏 import sys import random import pygame # 初始化 pygame.init() clock = pygame.time.Clock() screen_width = 1280 screen_height = 720 screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("PingPang") # 使用长方形表示球和球拍 ball = pygame.Rect(screen_width // 2 - 15, screen_height // 2 - 15, 30, 30) player = pygame.Rect(screen_width - 20, screen_height // 2 - 70, 10, 140) opponent = pygame.Rect(10, screen_height // 2 - 70, 10, 140) bg_color = pygame.Color('grey12') light_grey = (200, 200, 200) ball_speed_x = 7 * random.choice((1, -1)) ball_speed_y = 7 * random.choice((1, -1)) player_speed = 0 opponent_speed = 7 while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_DOWN: player_speed += 7 if event.key == pygame.K_UP: player_speed -= 7 if event.type == pygame.KEYUP: if event.key == pygame.K_DOWN: player_speed -= 7 if event.key == pygame.K_UP: player_speed += 7 # update #ball_animation() #player_animation() #opponent_animation() # draw screen.fill(bg_color) pygame.draw.rect(screen, light_grey, player) pygame.draw.rect(screen, light_grey, opponent) pygame.draw.ellipse(screen, light_grey, ball) pygame.draw.aaline(screen, light_grey, (screen_width / 2, 0), (screen_width / 2, screen_height)) pygame.display.flip() clock.tick(60)
然后我们实现上面的三个更新逻辑,更新物体状态。
ball_animation()
player_animation()
opponent_animation()
def ball_animation(): """更新球的运动""" global ball_speed_x, ball_speed_y ball.x += ball_speed_x ball.y += ball_speed_y if ball.top <= 0 or ball.bottom >= screen_height: ball_speed_y *= -1 if ball.left <= 0 or ball.right >= screen_width: ball_speed_x *= -1 ball_restart() if ball.colliderect(player) or ball.colliderect(opponent): ball_speed_x *= -1 def player_animation(): """更新玩家的运动""" player.y += player_speed if player.top <= 0: player.top = 0 if player.bottom >= screen_height: player.bottom = screen_height def opponent_animation(): """更新对手的运动""" if opponent.top < ball.y: opponent.top += opponent_speed if opponent.bottom > ball.y: opponent.bottom -= opponent_speed if opponent.top <= 0: opponent.top = 0 if opponent.bottom >= screen_height: opponent.bottom = screen_height def ball_restart(): """重置球的位置""" global ball_speed_x, ball_speed_y ball.center = (screen_width // 2, screen_height // 2) ball_speed_y *= random.choice((1, -1)) ball_speed_x *= random.choice((1, -1))
实现了这3个函数后,记得在主循环中的# update 处调用这个三个函数。
2. 添加分数和时间
- 为游戏添加分数显示:添加字体并渲染出分数。
- 发球时有3秒倒计时:通过
pygame.time.get_ticks()
获得时间。
# 添加得分和计时器 import sys import random import pygame pygame.init() clock = pygame.time.Clock() screen_width = 1280 screen_height = 720 screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("PingPang") ball = pygame.Rect(screen_width // 2 - 15, screen_height // 2 - 15, 30, 30) player = pygame.Rect(screen_width - 20, screen_height // 2 - 70, 10, 140) opponent = pygame.Rect(10, screen_height // 2 - 70, 10, 140) bg_color = pygame.Color('grey12') light_grey = (200, 200, 200) ball_speed_x = 7 * random.choice((1, -1)) ball_speed_y = 7 * random.choice((1, -1)) player_speed = 0 opponent_speed = 7 # Text Variables player_score = 0 opponent_score = 0 # 创建字体 game_font = pygame.font.Font("freesansbold.ttf", 32) # Timer score_time = True def ball_animation(): global ball_speed_x, ball_speed_y global player_score, opponent_score global score_time ball.x += ball_speed_x ball.y += ball_speed_y if ball.top <= 0 or ball.bottom >= screen_height: ball_speed_y *= -1 if ball.left <= 0 or ball.right >= screen_width: if ball.left <= 0: player_score += 1 if ball.right >= screen_width: opponent_score += 1 score_time = pygame.time.get_ticks() if ball.colliderect(player) or ball.colliderect(opponent): ball_speed_x *= -1 def player_animation(): player.y += player_speed if player.top <= 0: player.top = 0 if player.bottom >= screen_height: player.bottom = screen_height def opponent_animation(): if opponent.top < ball.y: opponent.top += opponent_speed if opponent.bottom > ball.y: opponent.bottom -= opponent_speed if opponent.top <= 0: opponent.top = 0 if opponent.bottom >= screen_height: opponent.bottom = screen_height def ball_restart(): global ball_speed_x, ball_speed_y global score_time ball.center = (screen_width // 2, screen_height // 2) # 计算耗时,并显示剩余时间 # 获得当前时间 current_time = pygame.time.get_ticks() # 与上次得分时间比较 if current_time - score_time < 700: number_three = game_font.render("3", False, light_grey) screen.blit(number_three, (screen_width // 2 - 10, screen_height // 2 + 20)) if 700 < current_time - score_time < 1400: number_two = game_font.render("2", False, light_grey) screen.blit(number_two, (screen_width // 2 - 10, screen_height // 2 + 20)) if 1400 < current_time - score_time < 2100: number_one = game_font.render("1", False, light_grey) screen.blit(number_one, (screen_width // 2 - 10, screen_height // 2 + 20)) if current_time - score_time < 2100: ball_speed_x, ball_speed_y = 0, 0 else: ball_speed_y = 7 * random.choice((1, -1)) ball_speed_x = 7 * random.choice((1, -1)) score_time = None while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_DOWN: player_speed += 7 if event.key == pygame.K_UP: player_speed -= 7 if event.type == pygame.KEYUP: if event.key == pygame.K_DOWN: player_speed -= 7 if event.key == pygame.K_UP: player_speed += 7 ball_animation() player_animation() opponent_animation() # update # draw screen.fill(bg_color) pygame.draw.rect(screen, light_grey, player) pygame.draw.rect(screen, light_grey, opponent) pygame.draw.ellipse(screen, light_grey, ball) pygame.draw.aaline(screen, light_grey, (screen_width / 2, 0), (screen_width / 2, screen_height)) # 显示得分 player_text = game_font.render(f"{player_score}", False, light_grey) screen.blit(player_text, (660, 360)) opponent_text = game_font.render(f"{opponent_score}", False, light_grey) screen.blit(opponent_text, (600, 360)) if score_time: ball_restart() pygame.display.flip() clock.tick(60)
3. 优化碰撞逻辑、添加声音
如果你运行了第2
节的程序,你会发现有时候球的反弹有时很奇怪,比如有时候会黏在球拍上。
本节我们将
- 优化碰撞逻辑:在
ball_animation()
通过判断球与球拍的位置,修改球的运动。 - 添加碰撞和得分音效:
pygame.mixer.Sound
# 添加得分和计时器 # 基础 ping pang游戏 import sys import random import pygame # setup pygame.init() pygame.mixer.pre_init(44100, -16, 2, 512) clock = pygame.time.Clock() screen_width = 1280 screen_height = 720 screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("PingPang") # Reactangles ball = pygame.Rect(screen_width // 2 - 15, screen_height // 2 - 15, 30, 30) player = pygame.Rect(screen_width - 20 , screen_height // 2 - 70, 10, 140) opponent = pygame.Rect(10, screen_height // 2 - 70, 10, 140) bg_color = pygame.Color('grey12') light_grey = (200, 200, 200) ball_speed_x = 7 * random.choice((1, -1)) ball_speed_y = 7 * random.choice((1, -1)) player_speed = 0 opponent_speed = 7 # Text Variables player_score = 0 opponent_score = 0 game_font = pygame.font.Font("freesansbold.ttf", 32) # Timer score_time = True # Sound pong_sound = pygame.mixer.Sound("pong.ogg") score_sound = pygame.mixer.Sound("score.ogg") def ball_animation(): global ball_speed_x, ball_speed_y global player_score, opponent_score global score_time ball.x += ball_speed_x ball.y += ball_speed_y if ball.top <= 0 or ball.bottom >= screen_height: pong_sound.play() ball_speed_y *= -1 # score if ball.left <= 0 or ball.right >= screen_width: score_sound.play() if ball.left <= 0: player_score += 1 if ball.right >= screen_width: opponent_score += 1 score_time = pygame.time.get_ticks() if ball.colliderect(player) and ball_speed_x > 0: pong_sound.play() if abs(ball.right - player.left) < 10 : ball_speed_x *= -1 elif abs(ball.bottom - player.top) < 10 and ball_speed_y > 0: ball_speed_y *= -1 elif abs(ball.top - player.bottom) < 10 and ball_speed_y < 0: ball_speed_y *= -1 if ball.colliderect(opponent) and ball_speed_x < 0: pong_sound.play() if abs(ball.left - opponent.right) < 10: ball_speed_x *= -1 elif abs(ball.bottom - opponent.top) < 10 and ball_speed_y > 0: ball_speed_y *= -1 elif abs(ball.top - opponent.bottom) < 10 and ball_speed_y < 0: ball_speed_y *= -1 def player_animation(): player.y += player_speed if player.top <= 0: player.top = 0 if player.bottom >= screen_height: player.bottom = screen_height def opponent_animation(): if opponent.top < ball.y: opponent.top += opponent_speed if opponent.bottom > ball.y: opponent.bottom -= opponent_speed if opponent.top <= 0: opponent.top = 0 if opponent.bottom >= screen_height: opponent.bottom = screen_height def ball_restart(): global ball_speed_x, ball_speed_y global score_time ball.center = (screen_width // 2, screen_height // 2) current_time = pygame.time.get_ticks() if current_time - score_time < 700: number_three = game_font.render("3", False, light_grey) screen.blit(number_three, (screen_width // 2 - 10, screen_height // 2 + 20)) if 700 < current_time - score_time < 1400: number_two = game_font.render("2", False, light_grey) screen.blit(number_two, (screen_width // 2 - 10, screen_height // 2 + 20)) if 1400 < current_time - score_time < 2100: number_one = game_font.render("1", False, light_grey) screen.blit(number_one, (screen_width // 2 - 10, screen_height // 2 + 20)) if current_time - score_time < 2100: ball_speed_x, ball_speed_y = 0, 0 else: ball_speed_y = 7 * random.choice((1, -1)) ball_speed_x = 7 * random.choice((1, -1)) score_time = None while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_DOWN: player_speed += 7 if event.key == pygame.K_UP: player_speed -= 7 if event.type == pygame.KEYUP: if event.key == pygame.K_DOWN: player_speed -= 7 if event.key == pygame.K_UP: player_speed += 7 ball_animation() player_animation() opponent_animation() # update # draw screen.fill(bg_color) pygame.draw.rect(screen, light_grey, player) pygame.draw.rect(screen, light_grey, opponent) pygame.draw.ellipse(screen, light_grey, ball) pygame.draw.aaline(screen, light_grey, (screen_width / 2, 0), (screen_width / 2, screen_height)) player_text = game_font.render(f"{player_score}", False, light_grey) screen.blit(player_text, (660, 360)) opponent_text = game_font.render(f"{opponent_score}", False, light_grey) screen.blit(opponent_text, (600, 360)) if score_time: ball_restart() pygame.display.flip() clock.tick(60)