Files
ping_pong/main.py
2026-06-22 09:34:10 +08:00

242 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import pgzrun
import random
import time
# 窗口参数
WIDTH = 1920
HEIGHT = 1080
TITLE = "Ping_Pang"
# 计时变量
start_time = time.perf_counter()
elapsed_time = 0
TIME_LIMIT = 60 # 分数模式限时60秒
# 游戏状态menu / playing / gameover
game_state = "menu"
# mode 1=Score Mode(1分钟限时) 2=Game Over Mode
game_mode = 1
#分数
scorep1 = 0
scorep2 = 0
# 防重复加分标记
hit_paddle1 = False
hit_paddle2 = False
#随机方块坐标
random_intX = random.randint(235, 1685)
random_intY = random.randint(235, 845)
# 背景音乐
music.set_volume(0.8)
music.play('bgm')
# 左右球拍
paddle1 = Actor("1")
paddle1.x = 10
paddle1.y = HEIGHT / 2
paddle_speed = 8
paddle2 = Actor("2")
paddle2.x = WIDTH - 10
paddle2.y = HEIGHT / 2
# 障碍方块
tbox = Actor("t_box")
tbox.x = random_intX
tbox.y = HEIGHT / 2
# 乒乓球
ball = Actor("ball")
ball.pos = (WIDTH//2, HEIGHT//2)
ball_speed_x = 7
ball_speed_y = 7
lose_text = ""
# 球拍上下限位
half_h = paddle1.height / 2
top = half_h
bottom = HEIGHT - half_h
bg = Actor("bg")
def draw():
global elapsed_time
screen.clear()
bg.draw()
# 开局选择菜单界面
if game_state == "menu":
screen.draw.text("PING PONG GAME", center=(WIDTH//2, 200), fontsize=90, color="white")
screen.draw.text("Please select game mode", center=(WIDTH//2, 320), fontsize=45, color="yellow")
screen.draw.text("Press D - Score Mode (1 minute time limit)", center=(WIDTH//2, 420), fontsize=40, color="white")
screen.draw.text("Ball out of bounds → add score, ball reset to center", center=(WIDTH//2, 470), fontsize=28, color="#cccccc")
screen.draw.text("Press F - Game Over Mode", center=(WIDTH//2, 570), fontsize=40, color="white")
screen.draw.text("Ball out of bounds → opponent wins, game end", center=(WIDTH//2, 620), fontsize=28, color="#cccccc")
return
# 游戏进行画面
paddle1.draw()
paddle2.draw()
ball.draw()
tbox.draw()
# 左右分数
screen.draw.text(f'Player 1 Score: {scorep1}', (15,10), color=(255,255,255), fontsize=30)
screen.draw.text(f'Player 2 Score: {scorep2}', (1700,10), color=(255,255,255), fontsize=30)
# 顶部居中计时,区分限时模式显示剩余时间
if game_mode == 1:
remain = TIME_LIMIT - elapsed_time
if remain < 0:
remain = 0
screen.draw.text(f"Time Left: {remain:.1f} s", center=(WIDTH//2, 12), color=(255,255,255), fontsize=32)
else:
screen.draw.text(f"Time: {elapsed_time:.2f} s", center=(WIDTH//2, 12), color=(255,255,255), fontsize=32)
# 游戏结束提示
if game_state == "gameover":
screen.draw.text(lose_text, center=(WIDTH//2, HEIGHT//2), fontsize=80, color="red")
screen.draw.text("Press SPACE to restart", center=(WIDTH//2, HEIGHT//2+80), fontsize=40, color="white")
def update():
global ball_speed_x, ball_speed_y, game_state, lose_text
global scorep1, scorep2, hit_paddle1, hit_paddle2
global random_intX, random_intY, elapsed_time, start_time
# 菜单界面不运行游戏逻辑
if game_state == "menu":
return
# 游戏进行中刷新计时
if game_state == "playing":
elapsed_time = time.perf_counter() - start_time
# 分数模式计时到60秒结束游戏
if game_mode == 1 and elapsed_time >= TIME_LIMIT:
game_state = "gameover"
if scorep1 > scorep2:
lose_text = "Player 1 Win!"
elif scorep2 > scorep1:
lose_text = "Player 2 Win!"
else:
lose_text = "Draw!"
sounds.zhanbai.play()
hit_paddle1 = False
hit_paddle2 = False
if game_state != "playing":
return
# 左拍 W S
if keyboard.w and paddle1.y>top:
paddle1.y -= paddle_speed
if keyboard.s and paddle1.y<bottom:
paddle1.y += paddle_speed
# 右拍 ↑ ↓
if keyboard.up and paddle2.y>top:
paddle2.y -= paddle_speed
if keyboard.down and paddle2.y<bottom:
paddle2.y += paddle_speed
# 小球移动
ball.x += ball_speed_x
ball.y += ball_speed_y
# 上下墙壁反弹
if ball.top <= 0 or ball.bottom >= HEIGHT:
ball_speed_y *= -1
sounds.bump.set_volume(1.6)
sounds.bump.play()
# 左球拍碰撞加分
if ball.colliderect(paddle1) and not hit_paddle1:
ball_speed_x *= -1
sounds.bump.play()
scorep1 += 1
hit_paddle1 = True
# 右球拍碰撞加分
if ball.colliderect(paddle2) and not hit_paddle2:
ball_speed_x *= -1
sounds.bump.play()
scorep2 += 1
hit_paddle2 = True
# 撞击方块逻辑
if ball.colliderect(tbox):
sounds.box_co.play()
if ball.centerx < tbox.x:
ball_speed_x = -abs(ball_speed_x) - 1
else:
ball_speed_x = abs(ball_speed_x) + 1
if ball.centery < tbox.y:
ball_speed_y = -abs(ball_speed_y)
else:
ball_speed_y = abs(ball_speed_y)
random_intX = random.randint(235, 1685)
random_intY = random.randint(235, 845)
tbox.x = random_intX
tbox.y = random_intY
# 球出界逻辑,区分两种模式
if ball.left <= 0:
if game_mode == 1:
# 计分模式:仅加分,球回中心继续
scorep2 += 1
ball.pos = (WIDTH//2, HEIGHT//2)
ball_speed_x = 7
ball_speed_y = 7
else:
# 出局即结束模式
game_state = "gameover"
lose_text = "Player 2 Win!"
sounds.zhanbai.play()
if ball.right >= WIDTH:
if game_mode == 1:
scorep1 += 1
ball.pos = (WIDTH//2, HEIGHT//2)
ball_speed_x = 7
ball_speed_y = 7
else:
game_state = "gameover"
lose_text = "Player 1 Win!"
sounds.zhanbai.play()
def on_key_down(key):
global game_state, ball_speed_x, ball_speed_y, scorep1, scorep2
global start_time, elapsed_time, game_mode
# 菜单界面按 D / F 选择模式
if game_state == "menu":
if key == keys.D:
game_mode = 1
game_state = "playing"
start_time = time.perf_counter()
elapsed_time = 0
elif key == keys.F:
game_mode = 2
game_state = "playing"
start_time = time.perf_counter()
elapsed_time = 0
return
# 游戏结束按空格重启
if key == keys.SPACE and game_state == "gameover":
game_state = "playing"
ball.pos = (WIDTH//2, HEIGHT//2)
ball_speed_x = 7
ball_speed_y = 7
scorep1 = 0
scorep2 = 0
start_time = time.perf_counter()
elapsed_time = 0
pgzrun.go()