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

CMU CS Academy Paddle游戏得分异常递增问题求助

Paddle球类游戏得分异常问题解决

问题描述

为学校制作趣味Paddle球类游戏时,遇到得分异常:每次球击中Paddle时得分应加1,但实际会递增2或3。得分增长数值还会随Paddle速度变化。代码运行在CMU CS Academy环境,无法使用while True循环、嵌套循环,不能重复定义函数,也不能用time.sleep()。

问题出在app.scoreVar += 1, app.score.value = app.scoreVar这两行,完整代码如下:

#----- IMPORTS --------
import time
import math

app.score = Label('0', 200, 50, size=25)

ball = Group(
Circle(200, 200, 17, fill=rgb(130, 130, 130), border='black', borderWidth=7)  
)

paddle = Group(
Circle(170, 375, 18, fill=rgb(130, 130, 130), border='black', borderWidth=10),
Circle(240, 375, 18, fill=rgb(130, 130, 130), border='black', borderWidth=10),
Line(170, 362, 240, 362, lineWidth=10),
Line(170, 388, 240, 388, lineWidth=10),
Rect(170, 366.5, 70, 17, fill=rgb(130, 130, 130))
)

app.press = False
app.sec = time.time()
app.xvel = 0
app.yvel = 0
app.paddle_xvel = 0
app.mousex = 0
app.mousey = 0
app.scoreVar = 0
# 新增状态变量,记录球是否正在接触Paddle
app.isBallTouchingPaddle = False

app.ballx = 0
app.bally = 0
app.padx = 0
app.pady = 0

def onMouseMove(mouseX, mouseY):
    app.mousex = mouseX
    app.mousey = mouseY

def onMousePress(mouseX, mouseY):
    app.press = True

def onMouseRelease(mouseX, mouseY):
    app.press = False

def _init_():
    ball.visible = False
    paddle.visible = False
    app.background = 'skyblue'
    app.score.visible = False

_init_()

playBtn = Group(
Circle(140, 210, 30, fill=rgb(150, 225, 170), border='black', borderWidth=10),
Circle(260, 210, 30, fill=rgb(150, 225, 170), border='black', borderWidth=10),
Rect(140, 190, 120, 40, fill=rgb(150, 225, 170)),
Line(140, 185, 260, 185, lineWidth=10),
Line(140, 235, 260, 235, lineWidth=10),
Label('P l a y', 200, 210, size=30, bold=False, font='montserrat')
)

def onStep():
    app.sec = time.time()
    #----- Ball physics ------
    ball.centerY += app.yvel
    ball.centerX += app.xvel
    app.yvel += 1
    app.ballx = ball.centerX
    app.bally = ball.centerY
    app.padx = paddle.centerX
    app.pady = paddle.centerY - 20

    paddle.rotateAngle = (app.paddle_xvel / 2)
    if app.mousex > paddle.centerX:
        app.paddle_xvel += 3
    if app.mousex < paddle.centerX:
        app.paddle_xvel += -3
    app.paddle_xvel = app.paddle_xvel / 1.2
    paddle.centerX += app.paddle_xvel
    
    if app.yvel < -20:
        app.yvel = -20
    if ball.centerX > 390:
        app.xvel = -5
    if ball.centerX < 10:
        app.xvel = 5
    if ball.centerY >= 400:
        ball.centerY = 200
        app.yvel = 0
        app.xvel = 0
        ball.centerX = 200
    
    #---- HIT CHECK -------
    currentTouching = False
    # 先检查是否有任何一个shape被碰撞
    for shape in paddle:
        if ball.hitsShape(shape):
            currentTouching = True
            app.yvel = -20
            app.xvel = app.paddle_xvel
            break  # 只要碰撞到一个shape就停止遍历,避免重复设置速度

    # 只有从非接触变为接触时才加分
    if currentTouching and not app.isBallTouchingPaddle:
        app.scoreVar += 1
        app.score.value = app.scoreVar

    # 更新状态变量
    app.isBallTouchingPaddle = currentTouching
    
    #----- Ball physics explanation ------
    
    # First, we set the ball's y velocity (the speed that it goes down by exponentially). 
    # Then, we make it so that the Y and X values are CHANGED by, not set to, the velocities.
    # Next we do a few checks, like if the ball is touching the wall, if it is touching 
    # the ground, and we reset it. If it touches the wall we just bounce it the opposite way.
    # The ball's terminal velocity is -20, which is the max speed the ball can get to.'''
    
    #---- Play btn physics
    playBtn.rotateAngle = math.sin((((app.sec*2)+100) * 2) + 100)
    playBtn.centerX = math.sin(app.sec*2.5) * 3 + 200
    
    if playBtn.contains(app.mousex, app.mousey):
        if app.press == True:
            paddle.visible = True
            ball.visible = True
            app.background = 'white'
            playBtn.visible = False
            app.score.visible = True

lastTime = app.sec
app.sec -= lastTime

问题原因

  • 多Shape碰撞重复触发:Paddle是由多个Shape(两个圆、两条线、一个矩形)组成的Group,当球击中Paddle时,会遍历每个Shape,只要球和其中任意一个Shape碰撞,就会执行一次得分逻辑。如果球同时接触多个Shape(比如矩形和其中一个圆),就会多次加分。
  • 多帧持续碰撞触发:如果球和Paddle接触的状态持续多帧(比如Paddle移动速度快,球被“粘”住几帧),每帧都会触发碰撞检测,导致多次加分。

解决方案说明

  1. 添加状态变量:新增app.isBallTouchingPaddle记录球当前是否处于接触Paddle的状态
  2. 优化碰撞检测逻辑:
    • 先遍历Paddle的所有Shape,判断当前是否有碰撞发生
    • 仅当球从非接触状态变为接触状态的瞬间,才执行加分操作
    • 检测到碰撞后立即break遍历,避免重复设置球的速度

这种方案既解决了多Shape重复触发的问题,也避免了多帧持续碰撞导致的多次加分,同时符合CMU CS Academy的环境限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:00:00