0
@dataclass
class Collision:
    time: float = 1
    normal: Vector2 = Vector2(0, 0)

    @staticmethod
    def not_occur() -> 'Collision':
        return Collision(1)

    def __bool__(self):
        return self.time < 1

    @property
    def rem_time(self) -> float:
        return 1 - self.time

# ....


    def collide_dt(self, other: 'Collider', dt: float) -> Collision:
        """Detect continuous collision over move and return collision object."""
        if not self.broadphase(other, dt):
            return Collision.not_occur()

        minkowski = other.minkowski_diff(self)
        relative_velocity = (self.velocity - other.velocity) * dt

        intersections = minkowski.clipline((0, 0), relative_velocity)
        if not intersections: return Collision.not_occur()

        total_distance = Vector2(relative_velocity).length()
        if total_distance == 0: return Collision.not_occur()

        distances = [Vector2(point).length() for point in intersections]
        idx = 0 if distances[0] <= distances[1] else 1
        entry_point    = intersections[idx]
        entry_distance = distances[idx]

        # .bottom and .right lie one pixel outside of its actual border.
        normal = Vector2(0, 0)
        if entry_point[0] == minkowski.left:
            normal.x = -1
        elif entry_point[0] == minkowski.right - 1:
            normal.x = 1
        elif entry_point[1] == minkowski.top:
            normal.y = -1
        elif entry_point[1] == minkowski.bottom - 1:
            normal.y = 1
        else:
            raise ValueError("collision normal can't be determined")

        return Collision(entry_distance / total_distance, normal)
# ...

        # test collider thingy
        self.collider.velocity = (0, 0)
        if pressed[pg.K_a]:
            self.collider.velocity.x = -200
        if pressed[pg.K_d]:
            self.collider.velocity.x = 200
        if pressed[pg.K_w]:
            self.collider.velocity.y = -200
        if pressed[pg.K_s]:
            self.collider.velocity.y = 200

        collision = self.collider.collide_dt(self.obstacle, dt)
        self.collider.move_ip(self.collider.velocity * dt)
        response = self.collider.velocity.dot(collision.normal) * -collision.normal
        self.collider.move_ip(response * collision.rem_time * dt)

I suppose this issue is somehow related to this:

The rect.bottom and rect.right attributes of a pygame.Rect object for storing rectangular coordinates always lie one pixel outside of its actual border.

that I found in documentation of pygame.Rect module but I can't figure out how.

Collision is detected in right way when the white square collides with top of left side of red square but wrongly when its right or bottom and even when you will try to move in opposite direction it won't allow you for it.

Here is the entire program in one file so you can run it and test by yourself (the important code is in Game.update and Collider class, other things are irrelevant): https://hastebin.com/ubiyitahes.rb

Do you have any idea for possible fix?

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
cherrrry9
  • 35
  • 1
  • 4
  • because it isn't continuous and causes tunneling – cherrrry9 Oct 18 '22 at 19:06
  • `w = self.width + abs(shift_x)`, `h = self.height + abs(shift_y)`: Why does moving the object change the width or height of the object? `with` and `height` is a size, but not a position. – Rabbid76 Oct 18 '22 at 19:33
  • The arguments to [`pygame.Rect()`](https://www.pygame.org/docs/ref/rect.html) are the left, top, width and height, but not the left, top, right and bottom. – Rabbid76 Oct 18 '22 at 19:41
  • it doesn't change the size it just creates rectangle for broadphase test. It has nothing to do with the bug. – cherrrry9 Oct 18 '22 at 19:53
  • you can delete this test in collide_dt function if you want its the first if – cherrrry9 Oct 18 '22 at 19:54

0 Answers0