您可以帮助我在pygame物理模拟中修复此错误吗?

时间:2019-04-08 16:14:19

标签: python pygame

我在pygame中编写了这个物理模拟,并且碰撞机制无法正常工作。当我从墙的上方或左侧将玩家角色与墙碰撞时似乎起作用,而对于底部或右侧的碰撞则无效

一段时间以来,我一直在尝试查找此错误,但对于可能导致此错误的原因我一无所知。我正在使用python 3.7.3和pygame 1.9.5(截止到目前为最新版本)

很抱歉粘贴整个文件,但我不知道问题出在哪里

import pygame  # import the pygame library to have access to game building tools
import math

# these variables will be used to hold game objects and draw them
rigid_bodies = []
g = 100
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
PURPLE = (127, 0, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)


class RigidBody(pygame.Rect):
    """
    represents a rectangular object that acts according to newton's laws of motion
    """

    def __init__(self, canvas, color, m, u, x, y, w, h):
        """
        called automatically when a new object is created to initialize the object
        :param canvas: the canvas on which to draw the object
        :param color: the color of the object
        :param m: the mass of the object
        :param u: Coefficient of friction
        :param x: the starting position of the object on the x axis
        :param y: the starting position of the object on the y axis
        :param w: the width of the object
        :param h: the height of the object
        """
        super().__init__(x, y, w, h)  # initialize the parent Rect object
        self.canvas = canvas
        self.color = color
        self.m = m
        self.u = u
        self.x_speed = 0  # the speed of the object on the x axis
        self.y_speed = 0  # the speed of the object on the y axis

    def apply_force(self, axis, F, initiator=None):
        """
        used to apply force on the object
        :param axis: the axis of the force
        :param F: the amount of force to apply
        :param initiator: the object that is applying the force
        """
        a = F / self.m  # calculate the acceleration the object should have
        if axis == 'y':
            self.y_speed += a
        elif axis == 'x':
            self.x_speed += a
        if initiator:
            initiator.apply_force(axis, -1 * F)  # apply normal force
            print('colliding')

    def inertia(self):
        """
        shall be run every frame to make the object move according to its speed
        if possible and take the necessary steps if not
        """
        # go:
        self.x += self.x_speed
        self.y += self.y_speed
        for body in rigid_bodies:
            if self.colliderect(body):  # if collide with another object:
                self.x -= self.x_speed  # go back
                self.y -= self.y_speed
                body.apply_force('x', self.m * self.x_speed, self)  # and apply force on that object
                body.apply_force('y', self.m * self.y_speed, self)
                break

    def draw(self):
        """
        shall be run every frame to draw the object on the canvas
        """
        pygame.draw.rect(self.canvas, self.color, (self.x, self.y, self.w, self.h))


class Controller:
    def __init__(self, character, F):
        """
        initialize the controller object
        :param character: the character to control
        :param F: the force to apply to the character for every frame a button is being pressed
        """
        self.character = character
        self.up = 0  # whether to move up or not
        self.down = 0  # whether to move down or not
        self.left = 0  # whether to move left or not
        self.right = 0  # whether to move right or not
        self.F = F

    def stop(self):
        """
        stops applying force on the object
        """
        self.up = 0
        self.down = 0
        self.left = 0
        self.right = 0

    def run(self):
        """
        shall be run every frame to apply force on the character according to user input
        """
        self.character.apply_force('y', -self.F * self.up)
        self.character.apply_force('y', self.F * self.down)
        self.character.apply_force('x', -self.F * self.left)
        self.character.apply_force('x', self.F * self.right)


def main():
    """
    the main function contains the main loop
    that runs repeatedly while the game is running
    """
    crashed = False  # tells if the program crashed or if the window was closed
    pygame.init()  # required to use pygame
    canvas = pygame.display.set_mode((1000, 700))  # define the canvas
    clock = pygame.time.Clock()  # will be used to limit the number of times a loop runs per second
    pygame.display.set_caption('the dot game V2')
    character = RigidBody(canvas, WHITE, 1000, 0.3, 500, 500, 20, 50)  # initialize the character
    player = Controller(character, 500)  # initialize the controller
    rigid_bodies.append(RigidBody(canvas, WHITE, math.inf, 0, 300, 300, 300, 20))  # initialize the wall
    while not crashed:
        # handle inputs:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                crashed = True
            elif event.type == pygame.MOUSEBUTTONUP:
                pass
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    player.up = 1
                elif event.key == pygame.K_DOWN:
                    player.down = 1
                elif event.key == pygame.K_LEFT:
                    player.left = 1
                elif event.key == pygame.K_RIGHT:
                    player.right = 1
            elif event.type == pygame.KEYUP:
                player.stop()
        player.run()
        character.inertia()
        canvas.fill(BLACK)
        character.draw()
        for body in rigid_bodies:
            body.draw()
        pygame.display.update()
        clock.tick(60)


if __name__ == '__main__':
    main()

我怀疑问题出在“惯性”或“ apply_force”函数上,但我只是无法弄清楚这些函数出了什么问题

角色每次碰到墙壁时都应该停止移动,但是当它从下方或从右侧碰到墙壁时,它会卡住并且只能向上或向左移动

1 个答案:

答案 0 :(得分:3)

此问题是由将浮点值强制转换为int引起的,可以通过以下方法解决:

stored_pos = (self.x, self.y)
self.x += self.x_speed
self.y += self.y_speed
for body in rigid_bodies:
    if self.colliderect(body):  # if collide with another object:
        self.x, self.y = stored_pos

注意,

self.x -= self.x_speed
self.y -= self.y_speed

不是...的逆运算

self.x += self.x_speed
self.y += self.y_speed

例如:a = 2b = 0.5

int(a + b) == int(2 + 0.5) == 2
int(a - b) == int(2 - 0.5) == 1

解决方案是存储self.xself.y的原始值

stored_pos = (self.x, self.y)

,并在发生碰撞时将其恢复:

self.x, self.y = stored_pos