You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Pygame的篮球游戏抛体运动实现问题求助

篮球游戏抛体运动实现问题

我正在开发一款篮球游戏,用户可控制篮球的投射方向与施加力度。目前尚未完成篮筐模块,卡在抛体运动(Projectile Motion)的实现环节。设计逻辑为:按下键盘"r"键设置投射力度,按下"w"键旋转向量以确定抛物线运动方向,但球既不沿向量方向运动,也未呈现正确的抛物线轨迹。参考网上代码仍未解决问题,完整代码如下:

import pygame
import math

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
FPS = 60
clock = pygame.time.Clock()
screen_x = 900
screen_y = 500
ball_x = 40
ball_y = 40
time = 0
screen = pygame.display.set_mode((screen_x, screen_y))
pygame.display.set_caption("Basketball Game")

jumping = False
X_POSITION = 400
Y_POSITION = 375
Y_GRAVITY = 1
JUMP_HEIGHT = 20
Y_VELOCITY = JUMP_HEIGHT

VEL_BALL = 15
ARROW_X = 500
ARROW_Y = 300
LENGHT_FORCE = 49

ANGLE = 0

ball = pygame.image.load("ball.svg.png")
ball = pygame.transform.scale(ball, (80, 80))
ball_rect = ball.get_rect(center = (X_POSITION, Y_POSITION))
ball_width = ball.get_width()

def ball_path():
    Vx = math.cos(angle)* JUMP_HEIGHT
    Vy = math.sin(angle)* JUMP_HEIGHT
    distx = Vx * time
    disty = (Vy * time) +((-4.9 * (time)**2)/2)
    
    newx = round(distx + X_POSITION)
    newy = round(Y_POSITION-disty)

    return(newx, newy)

    #X_POSITION = newx
    #Y_POSITION = newy

def find_angle(pos):

    sX = X_POSITION
    sY = Y_POSITION
    try:
        angle = math.pi / 2
    except:
        angle = math.pi / 2
    if pos[1] < sY and pos[0] > sX:
        angle = abs(angle)
    elif pos[1] < sY and pos[0] < sX:
        angle = math.pi - angle
    elif pos[1] > sY and pos[0] < sX:
        angle = math.pi + abs(angle)
    elif pos[1] > sY and pos[0] > sX:
        angle = (math.pi * 2) - angle

    return angle


run = True
while run:
    VECTOR_STARTPOINT = pygame.math.Vector2(X_POSITION, Y_POSITION)
    VECTOR_ENDPOINT = pygame.math.Vector2(100, 100)
    CURRENT_ENDPOINT = VECTOR_STARTPOINT + VECTOR_ENDPOINT.rotate(ANGLE)

    screen.fill(BLACK)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False


    keys_pressed = pygame.key.get_pressed()

    if keys_pressed[pygame.K_SPACE]:
        jumping = True
    if keys_pressed[pygame.K_d] and X_POSITION < screen_x-ball_width//2:
        X_POSITION += VEL_BALL
    if keys_pressed[pygame.K_a] and X_POSITION > 0 + ball_width//2:
        X_POSITION -= VEL_BALL

    screen.fill(BLACK)
    
    #draw force line, red below, white above
    FORCE_MAX = pygame.draw.line(screen, (255, 0, 0) , (400, 50), (50, 50), 5)
    FORCE = pygame.draw.line(screen, WHITE , (LENGHT_FORCE, 50), (50, 50), 5)


    if keys_pressed[pygame.K_r] and LENGHT_FORCE <= 400:
        LENGHT_FORCE += 5

        #coould do it with no hard coding
        if LENGHT_FORCE <= 200 and LENGHT_FORCE > 99:
            print("low")
            JUMP_HEIGHT = 20
            Y_VELOCITY = JUMP_HEIGHT

        if LENGHT_FORCE > 200 and LENGHT_FORCE < 300:
            print("medium")
            JUMP_HEIGHT = 25
            Y_VELOCITY = JUMP_HEIGHT

        if LENGHT_FORCE >= 300 and LENGHT_FORCE < 400:
            print("strong")
            JUMP_HEIGHT = 30
            Y_VELOCITY = JUMP_HEIGHT

        if LENGHT_FORCE == 399:
            print("maximum")
            JUMP_HEIGHT = 35
            Y_VELOCITY = JUMP_HEIGHT

    #start moving vector
    if keys_pressed[pygame.K_w]:
        ANGLE = (ANGLE + 5) % 360
        VECTOR_LINE = pygame.draw.line(screen, WHITE, VECTOR_STARTPOINT, CURRENT_ENDPOINT, 4)
    
    #stop moving vector
    if event.type == pygame.KEYUP :
        if event.key == pygame.K_w :
            if Y_POSITION < screen_y and Y_POSITION >= 0:
                pos = CURRENT_ENDPOINT[0], CURRENT_ENDPOINT[1]
                angle = find_angle(pos)

                VECTOR_LINE = pygame.draw.line(screen, (255, 0, 0), VECTOR_STARTPOINT, CURRENT_ENDPOINT, 4)
                
                time += 0.05
                po = ball_path()
                X_POSITION = po[0]
                Y_POSITION = po[1]
                
            else:
                time = 0
                Y_POSITION = 375


    if jumping:
        Y_POSITION -= Y_VELOCITY
        Y_VELOCITY -= Y_GRAVITY
        if Y_VELOCITY < -JUMP_HEIGHT:
            jumping = False
            Y_VELOCITY = JUMP_HEIGHT
        ball_rect = ball.get_rect(center = (X_POSITION, Y_POSITION))
        screen.blit(ball, ball_rect)
    else:
        ball_rect = ball.get_rect(center = (X_POSITION, Y_POSITION))
        screen.blit(ball, ball_rect)


    pygame.display.flip()
    clock.tick(FPS)

核心问题梳理

  • 角度计算完全失效:find_angle函数未实际计算向量夹角,仅返回固定值,导致投射方向完全错误。
  • 三角函数单位不匹配:math.cos/math.sin需要弧度值,但ANGLE是角度值,未做转换,方向计算彻底错误。
  • 抛体运动仅触发一次:仅在松开"w"键瞬间更新一次位置,未在每一帧持续计算轨迹,球无法呈现连续抛物线运动。
  • 重力公式逻辑混乱:未适配Pygame坐标系(Y轴向下为正),竖直方向运动不符合物理规律。
  • 变量作用域错误:局部angle变量未关联到全局使用,ball_path函数无法获取正确角度。

修正方案及代码

import pygame
import math

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
FPS = 60
clock = pygame.time.Clock()
screen_x = 900
screen_y = 500
screen = pygame.display.set_mode((screen_x, screen_y))
pygame.display.set_caption("Basketball Game")

# 篮球基础状态
X_POSITION = 400
Y_POSITION = 375
ball = pygame.image.load("ball.svg.png")
ball = pygame.transform.scale(ball, (80, 80))
ball_width = ball.get_width()
ball_rect = ball.get_rect(center=(X_POSITION, Y_POSITION))

# 运动参数
Y_GRAVITY = 0.5
JUMP_HEIGHT = 20
VEL_BALL = 15
LENGTH_FORCE = 49  # 修正拼写错误
FORCE_MAX_LENGTH = 400

# 投射状态
ANGLE = 0
is_shooting = False
shoot_time = 0.0
shoot_angle = 0.0

def ball_path():
    # 计算水平/竖直初速度
    vx = math.cos(shoot_angle) * JUMP_HEIGHT
    vy = math.sin(shoot_angle) * JUMP_HEIGHT
    
    # 适配Pygame坐标系的抛体公式
    dist_x = vx * shoot_time
    dist_y = vy * shoot_time + 0.5 * Y_GRAVITY * (shoot_time ** 2)
    
    new_x = X_POSITION + dist_x
    new_y = Y_POSITION + dist_y
    
    return (round(new_x), round(new_y))

def find_angle(end_pos):
    # 通过向量差计算正确弧度角度
    dx = end_pos[0] - X_POSITION
    dy = end_pos[1] - Y_POSITION
    return math.atan2(dy, dx)

run = True
while run:
    VECTOR_STARTPOINT = pygame.math.Vector2(X_POSITION, Y_POSITION)
    VECTOR_ENDPOINT = pygame.math.Vector2(100, 0)
    CURRENT_ENDPOINT = VECTOR_STARTPOINT + VECTOR_ENDPOINT.rotate(ANGLE)

    screen.fill(BLACK)
    
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        # 松开w键触发投射
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_w and not is_shooting:
                is_shooting = True
                shoot_time = 0.0
                shoot_angle = find_angle(CURRENT_ENDPOINT)
            # 重置篮球位置
            if event.key == pygame.K_s:
                is_shooting = False
                X_POSITION = 400
                Y_POSITION = 375

    keys_pressed = pygame.key.get_pressed()

    # 非投射状态下的移动、力度调整
    if not is_shooting:
        if keys_pressed[pygame.K_SPACE]:
            Y_POSITION -= 20
        if keys_pressed[pygame.K_d] and X_POSITION < screen_x - ball_width//2:
            X_POSITION += VEL_BALL
        if keys_pressed[pygame.K_a] and X_POSITION > 0 + ball_width//2:
            X_POSITION -= VEL_BALL

        # 调整投射力度
        if keys_pressed[pygame.K_r] and LENGTH_FORCE <= FORCE_MAX_LENGTH:
            LENGTH_FORCE += 5
            if LENGTH_FORCE <= 200:
                JUMP_HEIGHT = 20
            elif LENGTH_FORCE <= 300:
                JUMP_HEIGHT = 25
            elif LENGTH_FORCE <= FORCE_MAX_LENGTH:
                JUMP_HEIGHT = 30
            if LENGTH_FORCE == FORCE_MAX_LENGTH:
                JUMP_HEIGHT = 35

        # 旋转方向向量
        if keys_pressed[pygame.K_w]:
            ANGLE = (ANGLE + 5) % 360

    # 投射状态下持续更新轨迹
    if is_shooting:
        shoot_time += 0.1
        new_pos = ball_path()
        X_POSITION, Y_POSITION = new_pos
        # 落地后停止投射
        if Y_POSITION >= screen_y - ball_width//2:
            is_shooting = False
            Y_POSITION = screen_y - ball_width//2

    # 绘制力度条
    pygame.draw.line(screen, (255, 0, 0), (50, 50), (FORCE_MAX_LENGTH, 50), 5)
    pygame.draw.line(screen, WHITE, (50, 50), (LENGTH_FORCE, 50), 5)

    # 绘制方向向量
    if not is_shooting:
        pygame.draw.line(screen, WHITE, VECTOR_STARTPOINT, CURRENT_ENDPOINT, 4)
    else:
        pygame.draw.line(screen, (255, 0, 0), VECTOR_STARTPOINT, CURRENT_ENDPOINT, 4)

    # 绘制篮球
    ball_rect = ball.get_rect(center=(X_POSITION, Y_POSITION))
    screen.blit(ball, ball_rect)

    pygame.display.flip()
    clock.tick(FPS)

内容的提问来源于stack exchange,提问作者Proton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 10:05:32