1

I'm currently making a tile based platformer and programming the "engine" for it. I am pretty far, but lately noticed that the player is moving faster going left than right for no apparent reason. I check for the velocity but it is as intended limited bewteen -5.5 and 5.5. When checking for the distance bewteen the last position and current position it's visible though. When moving right the console outputs the delta position to be about 3 ("about" because I made the movement depending on the delta time so it varies when the pc is running faster), but when moving left it says the difference is -4. Is that a common bug? I tried limiting the negative velocity to a lower level when negative but that is a very janky solution and not very percise. I know that pythons float numbers are a bit weird but I can't explain to myself why this happening.

Here is the movement code (note that this is not the whole script, but where the bug happens). Also, limit(__value, __min, __max) is a function I made myself for as you can tell limiting a value between two values

            self.state = "stand"

            # move animation
            if math.fabs(self.vel.x) > 0.1:
                temp = math.fabs(self.vel.x) / 30
                if temp < 0.4:
                    temp = 0.4
                self.state = "walk" + str(int(math.fmod(int(self.walk_time * temp), 2)))
                self.walk_time += dt
            else:
                self.walk_time = 0

            # gravity
            if not self.is_grounded:
                self.vel.y += (self.grav * (1 + float(input["down"]))) * dt
            self.vel.y = limit(self.vel.y, -21, 21)

            # set acc to input
            self.acc.x = (float(input["right"]) - float(input["left"])) * float(not input["down"])
            if input["right"] and not input["left"]:
                self.dir = 1
            if input["left"] and not input["right"]:
                self.dir = -1

            # apply acc
            self.vel.x += (self.acc.x / 2)
            self.vel.x = limit(self.vel.x, -self.speed, self.speed)

            # handle in_air
            if self.is_grounded:
                self.in_air = 0
            else:
                self.in_air += 1 * dt

            # handle jumping
            if self.in_air <= 3:
                if input["down"] and self.in_air <= 3:
                    self.state = "duck"
                self.fall_time = 0
                # apply friction
                if self.acc.x == 0:
                    if self.vel.x > 0:
                        self.vel.x -= self.fric * dt
                        if self.vel.x <= self.fric:
                            self.vel.x = 0
                    if self.vel.x < 0:
                        self.vel.x += self.fric * dt
                        if self.vel.x >= -self.fric:
                            self.vel.x = 0
            else:
                if self.vel.y < 0:
                    self.state = "jump"
                elif self.vel.y > 1:
                    self.state = "fall" + str(int(math.fmod(int(self.fall_time / 30), 2)))
                self.fall_time += 1 * dt

            if self.is_grounded:
                self.can_jump = True
            else:
                if input["up_up"]:
                    self.can_jump = False

            # jumping
            if self.jumping == 0 and input["up"]:
                if self.in_air <= 3:
                    self.jumping += 1 * dt
            elif (0 < self.jumping <= 8) and input["up"]:
                self.jumping += 1 * dt
            else:
                self.jumping = 0
            # jump
            if input["up"] and (self.in_air <= 3 or 0 < self.jumping <= 6):
                self.vel.y = -8
                self.state = "jump"

        # collision
        self.is_grounded = False
        self.is_ceiled = False
        self.is_walled = False

        self.rect.x += self.vel.x * dt
        colls = self.collision(tiles)
        for tile in colls:
            if self.vel.x > 0:
                self.rect.right = tile.left
                self.vel.x = 0
            if self.vel.x < 0:
                self.rect.left = tile.right
                self.vel.x = 0
            # check vars
            if tile.right == self.rect.left or tile.left == self.rect.right:
                self.is_walled = True

        self.rect.y += self.vel.y * dt
        colls = self.collision(tiles)
        for tile in colls:
            if self.vel.y > 0:
                self.rect.bottom = tile.top
                self.vel.y = 0
            if self.vel.y < 0:
                self.rect.top = tile.bottom
                self.vel.y = 0

            # check vars
            if tile.top == self.rect.bottom:
                self.is_grounded = True
                self.vel.y = 0
            if tile.bottom == self.rect.top:
                self.is_ceiled = True
                self.jumping = 99

        print((self.rect.x - self.last_pos[0], self.rect.y - self.last_pos[1]))
        self.last_pos = [self.rect.x, self.rect.y]

Every help is appreciated

Styfour
  • 11
  • 1

1 Answers1

1

This is an accuracy problem. Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.

The coordinates for Rect objects are all integers. [...]

The fraction part of the coordinates gets lost when the velocity is add to the Rect object:

self.rect.x += self.vel.x * dt
self.rect.y += self.vel.y * dt

Therefore the object moves "faster" to the left and top than to the right an bottom.

Add attributes with floating point accuracy for the position of the object

self.x = ...
slef.y = ...

Add the velocity to this attributes. round the position and assign it to the pygame.Rect object:

self.x += self.vel.x * dt
self.rect.x = round(self.x)
self.y += self.vel.y * dt
self.rect.y = round(self.y)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174