1

Making a platform game and expect the sprite to collide with the wall and be aligned with the top of the wall and not accelerate through the wall but the moment it collides with the wall it slowly sinks through the wall, after testing it has shown that during the sinking part it does not actually collide with the wall. At the moment I've just been focusing on gravity and the y axis.

import os
import pygame
import time
import random
vec = pygame.math.Vector2

class player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image=pygame.image.load("rectmo.png").convert_alpha()
        self.image=pygame.transform.scale(self.image, (25,100))
        self.rect = self.image.get_rect()
        self.rect.center = (width/2,height/2)
        self.pos = vec(width/2,height/2)
        self.vel = vec(0,0)
        self.acc = vec(0,0)

    def update(self):

        user_input=pygame.key.get_pressed()

        if user_input[pygame.K_d]:
            self.acc.x=player_acc
        if user_input[pygame.K_a]:
            self.acc.x=-player_acc

        for wall in walls:
            if self.rect.colliderect(wall.rect)==True:
                if self.acc.x > 0:
                    self.rect.right=wall.rect.left
                    self.vel.x=0
                if self.acc.x < 0:
                    self.rect.left=wall.rect.right                    
                    self.vel.x=0
                if self.acc.y > 0:
                    self.rect.bottom=wall.rect.top
                    self.acc.y=0
                    self.vel.y=0
                if self.pos.y < 0:
                    self.rect.top=wall.rect.bottom
                    self.acc.y=0
            if self.rect.colliderect(wall.rect)==False:
                #gravity
                self.acc = vec(0,0.5)


        #adds Friction    
        self.acc.x += self.vel.x * player_friction
        #applying accelerating equation
        self.vel += self.acc
        self.pos += self.vel + 0.5 * self.acc
        self.rect.center = self.pos

I can't tell if the way I made the wall is the problem so I'll just leave it here.

class Wall(object):
    def __init__(self,wx,wy):
        walls.append(self)
        self.rect= pygame.Rect(wx,wy,30,30)
    def reset_wall(self):
        self.active = False

walls=[]

levels= [['WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW',
   'W  E                                         W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W             W                              W',
   'W             W                              W',
   'W             W                              W',
   'W             W                 W            W',
   'W             W                 W            W',
   'W             W                 W            W',
   'W             W                 W            W',
   'W             WWWWWWWWWWWWWWWWWWW            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW',
 ]]

x=y=0
level=random.choice(levels)
for row in level:
    for col in row:
        if col=='W':
            Wall(x,y)
        if col=='E':
            end_rect=pygame.Rect(x,y,30,30)
        x += 30
    y+=30
    x=0

Here's the the whole code for testing:

import os
import pygame
import time
import random
vec = pygame.math.Vector2

class player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image=pygame.image.load("rectmo.png").convert_alpha()
        self.image=pygame.transform.scale(self.image, (25,100))
        self.rect = self.image.get_rect()
        self.rect.center = (width/2,height/2)
        self.pos = vec(width/2,height/2)
        self.vel = vec(0,0)
        self.acc = vec(0,0)

    def update(self):

        user_input=pygame.key.get_pressed()

        if user_input[pygame.K_d]:
            self.acc.x=player_acc
        if user_input[pygame.K_a]:
            self.acc.x=-player_acc

        for wall in walls:
            if self.rect.colliderect(wall.rect)==True:
                if self.acc.x > 0:
                    self.rect.right=wall.rect.left
                    self.vel.x=0
                if self.acc.x < 0:
                    self.rect.left=wall.rect.right
                    self.vel.x=0
                if self.acc.y > 0:
                    self.rect.bottom=wall.rect.top
                    self.vel.y=0
                if self.pos.y < 0:
                    self.rect.top=wall.rect.bottom
                    self.vel.y=0
            if self.rect.colliderect(wall.rect)==False:
                self.acc = vec(0,0.5)


        #adds Friction    
        self.acc.x += self.vel.x * player_friction
        #applying accelerating equation
        self.vel += self.acc
        self.pos += self.vel + 0.5 * self.acc
        self.rect.center = self.pos




class Wall(object):
    def __init__(self,wx,wy):
        walls.append(self)
        self.rect= pygame.Rect(wx,wy,30,30)
    def reset_wall(self):
        self.active = False


os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()

pygame.display.set_caption('A Game')
width = 1366
height= 768
screen=pygame.display.set_mode((width,height))

clock = pygame.time.Clock()
walls=[]
player_acc=0.5
player_friction=-0.05
rectmo=player()

rectmo.rect.x=500
rectmo.rect.y=400
main_colour=(0,0,0)
colour=main_colour
wall_colour=(255,255,255)
current_score=0

levels= [['WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW',
   'W  E                                         W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W             W                              W',
   'W             W                              W',
   'W             W                              W',
   'W             W                 W            W',
   'W             W                 W            W',
   'W             W                 W            W',
   'W             W                 W            W',
   'W             WWWWWWWWWWWWWWWWWWW            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'W                                            W',
   'WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW',
 ]]

x=y=0
level=random.choice(levels)
for row in level:
    for col in row:
        if col=='W':
            Wall(x,y)
        if col=='E':
            end_rect=pygame.Rect(x,y,30,30)
        x += 30
    y+=30
    x=0

running=True

while running==True:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    rectmo.update()










    if running==True:
        screen.fill(main_colour)
        for wall in walls:
            pygame.draw.rect(screen, wall_colour,wall.rect)
        pygame.draw.rect(screen,(255,0,0),end_rect)
        pygame.draw.rect(screen,colour,rectmo.rect)

        all_sprites_list = pygame.sprite.Group()
        all_sprites_list.add(rectmo)
        all_sprites_list.draw(screen)
        pygame.display.flip()

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Banthar
  • 13
  • 3
  • Is the issue solved? – Rabbid76 May 31 '20 at 18:22
  • To an extent yeah, there was bug where if you jumped and press left while landing on a ground, it would freak out. This was due to the test of direction not being sufficient so I took inspiration from another user and changed it a bit. There also a weird bug where sometimes when you collide with the floor, it would teleport a few cm left or right and then continue on with the code and I never got to fix that. Thanks for everything you have done so far. – Banthar Jun 01 '20 at 19:11
  • Yes issue solved I fixed them. Just errors unrelated to wall collision now – Banthar Jun 07 '20 at 17:01

2 Answers2

1

Simply set your self.vel and self.acc variables to vec(0, 0) whenever your sprite is colliding with a wall. That should do it... Hope this helps.

To avoid overlapping positions:

main_loop():
    move_sprite()
    r = mySprite.rect
    if r.colliderect(myWall.rect):
        mySprite.vel, mySprite.acc = vec(0, 0), vec(0, 0)
        # Test which wall he is hitting:
        left = [r.topleft, r.midleft, r.bottomleft]
        for point in left:
            if myWall.rect.collideppoint(point):
                r.left = myWall.rect.right  # Sets the sprite outside of the wall
    pygame.display.flip()

This should work. Make sure you move the sprite before you test for wall collision. Otherwise you may get some glitches, with the sprite moving in and out of the wall. I only wrote code to test for a wall on the left, but you can find anything you need at the pygame documentation:

https://www.pygame.org/docs/ref/rect.html

InstaK0
  • 342
  • 3
  • 9
  • well it works to an extent but now the sprite is inside the wall in the y axis about 1/4 and then acc and vel are set to 0 then it works fine after that but i don't want the sprite to be inside the wall. Any idea? – Banthar May 28 '20 at 15:21
  • I haven't fully read your code, but when you test to see if the sprite is colliding with anything, also set it's position outside of the object it is hitting. I added a code snippet to show this. – InstaK0 May 28 '20 at 17:36
0

Chang the position of the player, before evaluation the collisions. If the player collides, then you've to update self.rect and you have to synchronize self.pos to self.rect.center. For instnace:

if self.val.x > 0:
    self.rect.right=wall.rect.left
    self.vel.x=0
    self.pos = vec(self.rect.center)

Evaluate if the player stays on the ground, by testing if the player would intersect the wall if he would be located 1 pixel below:

test_rect = pygame.Rect(self.rect.x, self.rect.y+1, self.rect.width, self.rect.height) 
if test_rect.colliderect(wall.rect):
   on_ground = True

Set the acceleration in y direction dependent on the state on_ground:

if on_ground:
    self.acc.y = 0
elif self.acc.y == 0:
    self.acc.y = 0.5

Method update:

class player(pygame.sprite.Sprite):
    # [...]

    def update(self):

        user_input=pygame.key.get_pressed()

        self.acc.x = 0
        if user_input[pygame.K_d]:
            self.acc.x=player_acc
        if user_input[pygame.K_a]:
            self.acc.x=-player_acc

        self.vel.x = self.vel.x * 0.95 + self.acc.x
        self.vel.y += self.acc.y
        self.pos += self.vel
        self.rect.center = self.pos

        on_ground = False
        for wall in walls:
            if self.rect.colliderect(wall.rect)==True:
                if self.vel.x > 0:
                    self.rect.right=wall.rect.left
                    self.vel.x=0
                    self.pos = vec(self.rect.center)
                if self.vel.x < 0:
                    self.rect.left=wall.rect.right
                    self.vel.x=0
                    self.pos = vec(self.rect.center)
                if self.vel.y > 0:
                    self.rect.bottom=wall.rect.top
                    self.vel.y=0
                    self.pos = vec(self.rect.center)
                if self.vel.y < 0:
                    self.rect.top=wall.rect.bottom
                    self.vel.y=0
                    self.pos = vec(self.rect.center)

            test_rect = pygame.Rect(self.rect.x, self.rect.y+1, self.rect.width, self.rect.height) 
            if test_rect.colliderect(wall.rect):
                on_ground = True

        if on_ground:
            self.acc.y = 0
        elif self.acc.y == 0:
            self.acc.y = 0.5
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • @GlennMackintosh I see. It's just a copy of the question. – Rabbid76 May 28 '20 at 18:16
  • took your advice. Anytime the sprite collides a wall, it will always go to the top of the wall even though i will be touching the left side of a wall but you were able to get the sprite to not be stuck in a wall due to acceleration and that's the most progress i've made in three days – Banthar May 28 '20 at 20:10
  • @Banthar I've changed the answer. – Rabbid76 May 28 '20 at 20:34