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

如何在Python Kivy开发的2D头球游戏中实现碰撞处理?

头球游戏碰撞逻辑实现问题

我要给一款头球类型的游戏实现碰撞逻辑,已经定义了Ball类和Player类,希望通过物理模拟实现两者的流畅交互。之前尝试用向量计算力并应用到代码里,但一直没成功:水平方向碰撞时球和玩家的交互效果很差,而且球会陷入玩家的矩形碰撞体中。

以下是当前未包含碰撞逻辑的Python代码:

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

class Ball(Widget):
    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(0)
    velocity = ReferenceListProperty(velocity_x, velocity_y)
    gravity = 0.2
    bounce_factor = 0.7
    mass = 1.0
    max_velocity = 10

    def move(self):
        self.velocity_y -= self.gravity
        self.pos = Vector(*self.velocity) + self.pos
    
        # 检测球是否落地
        if self.y < 0:
            self.y = 0
            self.velocity_y *= -self.bounce_factor
    
        # 避免落地后仍有向下的速度
        if self.velocity_y < 0 and self.y == 0:
            self.velocity_y = 0
    
        # 设置最大速度限制
        if self.velocity_y > self.max_velocity:
            self.velocity_y = self.max_velocity
        if self.velocity_x > self.max_velocity:
            self.velocity_x = self.max_velocity

class Player(Widget):
    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(0)
    velocity = ReferenceListProperty(velocity_x, velocity_y)
    jump_strength = 15
    gravity = 0.5
    speed = 10
    mass = 2.0

    def move(self):
        self.velocity_y -= self.gravity
        self.pos = Vector(*self.velocity) + self.pos
    
        # 确保玩家不会掉到屏幕下方
        if self.y < 0:
            self.y = 0

class GameScreen(Widget):
    ball = ObjectProperty(None)
    player = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(GameScreen, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down, on_key_up=self._on_keyboard_up)
    
    def _keyboard_closed(self):
        self._keyboard.unbind(on_key_down=self._on_keyboard_down, on_key_up=self._on_keyboard_up)
        self._keyboard = None
    
    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'd':
            self.player.velocity_x = self.player.speed
        elif keycode[1] == 'a':
            self.player.velocity_x = -self.player.speed
        elif keycode[1] == 'spacebar':
            self.player.velocity_y = self.player.jump_strength
        return True
    
    def _on_keyboard_up(self, keyboard, keycode):
        if keycode[1] == 'a' and self.player.velocity_x < 0:
            self.player.velocity_x = 0
        elif keycode[1] == 'd' and self.player.velocity_x > 0:
            self.player.velocity_x = 0
        return True
    
    def serve_ball(self):
        self.ball.center = self.center
        self.ball.velocity = Vector(0, -1)
    
    def update(self, dt):
        self.ball.move()
        self.player.move()
    
        # 球碰到上下边界反弹
        if (self.ball.y < 0) or (self.ball.top > self.height):
            self.ball.velocity_y *= -1
    
        # 球碰到左右边界反弹
        if (self.ball.x < 0) or (self.ball.right > self.width):
            self.ball.velocity_x *= -1
    
        # 玩家碰到顶部边界停止上升
        if self.player.top >= self.height:
            self.player.velocity_y *= 0
            self.player.y = self.height - self.player.height
    
        # 玩家碰到左边界停止左移
        if self.player.x < 0:
            self.player.velocity_x *= 0
            self.player.x = 0
    
        # 玩家碰到右边界停止右移
        if self.player.right > self.width:
            self.player.velocity_x *= 0
            self.player.x = self.width - self.player.width

class SoccerApp(App):
    def build(self):
        game = GameScreen()
        game.serve_ball()
        Clock.schedule_interval(game.update, 1.0 / 60.0)
        return game

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

对应的.kv文件代码:

<Ball>:
    size: 25, 25
    canvas:
        Ellipse:
            pos: self.pos
            size: self.size

<Player>:
    size: 50, 50
    canvas:
        Rectangle:
            pos: self.pos
            size: self.size

<GameScreen>:
    ball: game_ball
    player: game_player

    Label:
        font_size: 70
        center_x: root.width / 2 - 30
        top: root.top - 50
        text: "0"
    
    Label:
        font_size: 70
        center_x: root.width * 2 / 4 + 30
        top: root.top - 50
        text: "0"
    
    Ball:
        id: game_ball
        center: self.parent.center
    
    Player:
        id: game_player
        center_x: root.width * 1 / 4
        center_y: root.height * 1/6

内容的提问来源于stack exchange,提问作者Petr Michovský

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 12:07:04