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

Kivy重力模拟中collide_widget碰撞检测异常问题

Kivy小球碰撞检测异常问题解决

你的问题核心在于collide_widget的边界盒检测特性和当前碰撞逻辑的缺陷:

  • collide_widget只要两个Widget的矩形区域有重叠就返回True,不是等边缘刚好接触才触发
  • 帧更新时小球已经移动了一段距离,可能已经穿透paddle一部分才检测到碰撞
  • 硬编码的ball.y+5和错误的位置判断逻辑,导致触发条件混乱

修正方案

  1. 碰撞检测时,先判断小球是否向下运动(velocity_y < 0),避免处理向上弹起时的误判
  2. 检测小球底部是否触碰到paddle顶部,同时修正小球位置到刚好接触的位置,防止穿透
  3. 基于Widget自身属性(如height)计算位置,去掉硬编码数值
  4. 调整idle逻辑,只有当小球静止在paddle上且没有向上速度时才设置为1

修正后的代码

gravity.py

import kivy
kivy.require('1.11.1')

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.vector import Vector
from kivy.clock import Clock

DELTA_TIME = 1.0 / 60.0

class Ball(Widget):
    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(0)
    velocity = ReferenceListProperty(velocity_x, velocity_y)

    def move(self, dt):
        self.pos = Vector(*self.velocity) * dt + self.pos

class Paddle(Widget):
    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(0)
    velocity = ReferenceListProperty(velocity_x, velocity_y)

    def move(self, dt):
        self.pos = Vector(*self.velocity) * dt + self.pos

class GravityBall(Ball):
    def update(self, dt, idle):
        if idle == 0:
            # 模拟真实重力加速度,基于帧时间计算更平滑
            self.velocity_y -= 980 * dt
            self.move(dt)
        else:
            self.velocity_y = 0
            # 确保小球稳定停在paddle顶部
            self.y = self.parent.paddle.top

class GravityGame(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.idle = 0
        self.ball = GravityBall()
        self.ball.center = (200, 300)
        self.add_widget(self.ball)

        self.paddle = Paddle()
        self.paddle.center = (200, 100)
        self.add_widget(self.paddle)

        Clock.schedule_interval(self.update, DELTA_TIME)

    def update(self, dt):
        self.ball.update(dt, self.idle)
        self.paddle.move(dt)

        # 碰撞检测:小球向下运动,且底部触碰到paddle顶部
        if self.ball.velocity_y < 0 and self.ball.y <= self.paddle.top:
            # 修正位置避免穿透
            self.ball.y = self.paddle.top
            # 添加能量损耗,模拟真实弹跳衰减
            self.ball.velocity_y *= -0.8
            self.idle = 0

        # 静止判断:小球速度几乎为0,且刚好在paddle顶部
        if abs(self.ball.velocity_y) < 1 and self.ball.y == self.paddle.top:
            self.idle = 1

class GravityApp(App):
    def build(self):
        return GravityGame()

if __name__ == '__main__':
    GravityApp().run()

gravity.kv

#:kivy 1.11.1

<GravityBall>:
    size: 30, 30
    canvas:
        Ellipse:
            pos: self.pos
            size: self.size

<Paddle>:
    size: 100, 30
    canvas:
        Color:
            rgb: 1, 0, 1
        Rectangle:
            pos: self.pos
            size: self.size

关键修改说明

  • 重力计算优化:把固定数值改为基于帧时间的980 * dt,模拟真实重力加速度,运动更平滑自然
  • 碰撞位置修正:检测到碰撞时直接将小球y坐标设为paddle.top,彻底避免穿透和位置偏移问题
  • 弹跳能量损耗:velocity_y *= -0.8让每次弹跳高度逐渐降低,最终自然静止
  • 静止判断优化:只有当小球速度趋近于0且精准停在paddle顶部时,才触发静止状态,避免误判

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 14:25:23