我想为头部足球类型的游戏实现碰撞逻辑。我有一个 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
我会考虑使用
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)
此代码尚未经过广泛测试,因此预计需要修改。