Samuel
Samuel

Reputation: 31

When my player sprite runs into a wall sprite from the side, it teleports the player to the top of the wall

I am building a game, and I have a player class, but when it touches the left/right side of a block sprite, it teleports on top of the block. I am not sure how to fix this. No questions I have seen have helped me with this.

I can detect collision by doing

for entity in blocks:
If self.rect.collidepoint(entity.midleft):
Collide = true

For some reason, however, no matter what code I try to implement, it still teleports the player upwards.

I’m quite sure this has something to do with the Player’s velocity, because the values seem to act very weird when it runs into a wall.

Player class:

class Player(pygame.sprite.Sprite):
  def __init__(self):
    super(Player, self).__init__()
    self.surf = pygame.Surface((40,40))
    self.surf.fill((255,0,0))
    self.rect = self.surf.get_rect()
    self.pos = vec(0,144)
    self.vel = vec(0,0)
    self.acc = vec(0,0)
    
  def move(self, pressed_keys):
    self.acc = vec(0,0.5)
    if pressed_keys[K_LEFT]:
      self.acc.x = -ACC
      
    if pressed_keys[K_RIGHT]:
      self.acc.x = ACC

    
    self.acc.x += self.vel.x * FRIC
    self.vel += self.acc
    self.pos += self.vel + 0.5 * self.acc
    
      

    #if self.pos.x > W:
    #  self.pos.x = W
    #if self.pos.x < 0:
    #  self.pos.x = 0

    self.rect.midbottom = self.pos
  
    
  def checkCollision(self):
    for entity in blocks:
      if self.rect.collidepoint(entity.rect.midleft) or self.rect.collidepoint(entity.rect.midright):
        return True
    return False
  def update(self):
    hits = pygame.sprite.spritecollide(self, blocks, False)

    
    if self.vel.y > 0:
      if hits:
        self.pos.y = hits[0].rect.top + 1
        self.vel.y = 0
    
  def jump(self):
    hits = pygame.sprite.spritecollide(self, blocks, False)
    if hits:
      self.vel.y = -15

Block class:


class textureblock(pygame.sprite.Sprite):
  def __init__(self, imagefile, x, y):
    super(textureblock, self).__init__()
    self.imagefile = imagefile
    self.original_image = pygame.image.load(imagefile).convert_alpha()
    self.original_image = pygame.transform.scale(self.original_image, (48,48))
    self.hover_image = self.original_image.copy()
    pygame.draw.rect(self.hover_image, (255, 255, 0), self.hover_image.get_rect(), 6)
    self.image = self.original_image 
    self.rect = self.image.get_rect(center = (x, y))
    self.hover = False
    self.mouse_pos = None
    self.count = 0

  def update(self):
    mouse_pos = pygame.mouse.get_pos()
    self.hover = self.rect.collidepoint(mouse_pos)
    self.image = self.hover_image if self.hover else self.original_image
    if self.hover and mouse_pos == self.mouse_pos:
      self.count += 1
      if self.count > 10:
        self.image = pygame.Surface((48,48))
        self.image.fill((0,191,255))

        item = Item(self.imagefile, self.rect.x, self.rect.y)
        
        items.add(item)
        self.remove(blocks)
        
        
    else:
      self.count = 0
    self.mouse_pos = mouse_pos

Main Loop:


while running:
  for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
      if event.key == pygame.K_SPACE:
        player.jump()
      if event.key == pygame.K_ESCAPE:
        running = False
        pygame.quit()

    if event.type == pygame.QUIT:
      running = False
      pygame.quit()


  
  blocks.update()
  blocks.draw(screen)
  
  
  pressed_keys = pygame.key.get_pressed()

  items.update()
  
  player.update()
  player.move(pressed_keys)
  
  update()

  
  pygame.display.update()
  clock.tick(60)
You 

I just need something to point me in the right direction!

Upvotes: 1

Views: 159

Answers (1)

Rabbid76
Rabbid76

Reputation: 210889

You have to do the collision test for the x and y axis speperately.

First run the collision test for the x-axis. If the player moves into a block, align the player with the block. If the player hits a block from the side, you have to stop (set self.vel.x = 0 and self.acc.x = 0).

hit_side = False
for entity in blocks:
    if self.rect.colliderect(entity.rect):

        # move left and hit the block on the right
        if self.vel.x < 0 and self.rect.right > entity.rect.right:
            self.rect.left = entity.rect.right
            hit_side = True

        # move right and hit the block on the left
        if self.vel.x > 0 and self.rect.left < entity.rect.left:
            self.rect.right = entity.rect.left
            hit_side = True

if hit_side:
    self.vel.x = 0
    self.acc.x = 0

After that you can run the collision test for the y axis:

for entity in blocks:
    if self.rect.colliderect(entity.rect):
        
        # player is falling
        if self.vel.y > 0:
            self.rect.bottom = entity.rect.top

First move the player along the x-axis and do the collision check along the x-axis. Then move the player along the y-axis and hack the collision along the y-axis. If the player collides with a block, you need to update self.rect and self.pos.

Class Player

class Player(pygame.sprite.Sprite):
  def __init__(self):
    super(Player, self).__init__()
    # [...]
    
  def move(self, pressed_keys):
    self.acc = vec(0,0.5)
    if pressed_keys[K_LEFT]:
      self.acc.x = -ACC
      
    if pressed_keys[K_RIGHT]:
      self.acc.x = ACC
    
    self.acc.x += self.vel.x * FRIC
    self.vel += self.acc
    self.pos.x += self.vel.x + 0.5 * self.acc.x
    self.rect.midbottom = self.pos

    hit_side = False
    for entity in blocks:
        if self.rect.colliderect(entity.rect):

            # move left and hit the block on the right
            if self.vel.x < 0 and self.rect.right > entity.rect.right:
                self.rect.left = entity.rect.right
                self.pos.x = self.rect.centerx
                hit_side = True

            # move right and hit the block on the left
            if self.vel.x > 0 and self.rect.left < entity.rect.left:
                self.rect.right = entity.rect.left
                self.pos.x = self.rect.centerx
                hit_side = True

    if hit_side:
        self.vel.x = 0
        self.acc.x = 0

  def update(self):

    self.pos.y += self.vel.y + 0.5 * self.acc.y
    self.rect.midbottom = self.pos

    for entity in blocks:
        if self.rect.colliderect(entity.rect):
            if self.vel.y > 0:
                self.rect.bottom = entity.rect.top
                self.pos.y = self.rect.bottom
                self.vel.y = 0

  # [...]

Do not forget to invoke player.update():

while running:
  # [...]

  player.move(pressed_keys)
  player.update()
  update()

Upvotes: 1

Related Questions