我应该如何在 2D 游戏中实现碰撞处理? (Python 中的 Kivy)

问题描述 投票:0回答:1

我想为头部足球类型的游戏实现碰撞逻辑。我有一个 Ball 和一个 Player 类,希望它们通过使用物理模拟来无缝交互。

我尝试过计算向量中的力并将其应用到我的代码中,但我似乎从未得到正确的结果。每当我实现某些功能时,当球水平碰撞时,球与玩家的交互效果不佳,并且球会以某种方式被吸进玩家矩形中。下面是我当前的代码,没有任何碰撞逻辑:

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
    
        # Check if the ball is on the ground
        if self.y < 0:
            self.y = 0
            self.velocity_y *= -self.bounce_factor
    
        # Avoid negative velocities due to bounce
        if self.velocity_y < 0 and self.y == 0:
            self.velocity_y = 0
    
        # Set a maximum velocity
        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
    
        # Ensure the player doesn't fall below the screen
        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
python kivy game-physics
1个回答
0
投票

我会考虑使用

collide_widget()
Widget
方法来判断是否存在碰撞。然后使用一些逻辑来确定弹跳的方向。这是可以添加到
Ball
类并在
update()
方法中调用的方法的示例:

def check_collision(self, player):
    if self.collide_widget(player):
        if self.y <= player.top and self.center_y > player.top and self.center_x < player.right and self.center_x > player.x:
            # bounce off top of player
            self.velocity_y *= -self.bounce_factor
            self.y = player.top
        elif self.top >= player.y and self.center_y < player.y and self.center_x < player.right and self.center_x > player.x:
            # bounce off bottom of player
            self.velocity_y *= -self.bounce_factor
            self.top = player.y
        elif self.x <= player.right and self.center_x > player.right and self.center_y < player.top and self.center_y > player.y:
            # bounce off right side of player
            self.velocity_x *= -self.bounce_factor
            self.x = player.right
        elif self.right >= player.x and self.center_x < player.x and self.center_y < player.top and self.center_y > player.y:
            # bounce off left side of player
            self.velocity_x *= -self.bounce_factor
            self.right = player.x
        else:
            print('\tdid not calulate collision:')
            print('\t\tball:', self.pos, self.center, self.top, self.right)
            print('\t\tplayer:', player.pos, player.center, player.top, player.right)

此代码尚未经过广泛测试,因此预计需要修改。

© www.soinside.com 2019 - 2024. All rights reserved.