Home > Back-end >  Pygame collision with rectangles in list not working right
Pygame collision with rectangles in list not working right

Time:11-22

I'm making python pygame labyrinth game. Moving works with moving walls, but not player because player never escapes the screen. Currently I'm working on moving on X axis, but something goes wrong. When player collides with left wall, it normally doesn't let it go more to the left. But when going to the right it slightly ignores collision and goes more to the right.

Reproducible example:

import pygame
import math
pygame.init()
surface = pygame.display.set_mode((1000,600))
clock = pygame.time.Clock()
surfrect = surface.get_rect()
player=pygame.Rect((0,0), (35,35))
player.center=surfrect.w/2,surfrect.h/2
touched = False
levelR=[]
control=pygame.Rect((0,0), (50,50))
controld=[]
yspeed,xspeed=0,0
for i in range(-1,2):
    oxr=surfrect.w/2-75*3/2
    oyr=surfrect.h/2-75*3/2 75*3*i
    yr,xr=oyr,oxr
    gf=[]
    for y in '#-#n#-#n#-#'.split('n'):
        for x in y:
            if x=='#':
                gf.append(pygame.Rect((xr, yr), (75,75)))
            xr =75
        yr =75
        xr=oxr
    levelR.append([gf,pygame.Rect((oxr,oyr),(75*3,75*3))])
while True:
    for ev in pygame.event.get():
        if ev.type == pygame.QUIT:
            pygame.quit()
        elif ev.type == pygame.MOUSEBUTTONDOWN:
            touched = True
        elif ev.type == pygame.MOUSEBUTTONUP:
            touched = False
    surface.fill((0,0,0))
    for sd in levelR:pygame.draw.rect(surface,(35,35,35),sd[1])
    pygame.draw.circle(surface, (200,200,200), player.center, player.width/2-player.width/19, player.width)
    for sd in levelR:
        for rect in sd[0]:
            scale=0.6
            recti=pygame.Rect((rect.x,rect.y), (rect.width*scale,rect.height*scale))
            recti.center=rect.center
            pygame.draw.rect(surface,(255,255,255),rect)
            pygame.draw.rect(surface,(0,0,0),recti)
    if touched and not controld:
        controld=pygame.mouse.get_pos()
        control.center=controld
    elif touched and controld:
        radius = 150
        x,y=pygame.mouse.get_pos()
        distance=math.dist((x, y),controld)
        if distance < radius:
            pos=(x, y)
        else:
            dx=x-controld[0]
            dy=y-controld[1]
            ratio=radius/distance
            pos=controld[0] ratio*dx, controld[1] ratio*dy
        control.center=pos
        try:xmd=(x-controld[0])/abs(x-controld[0])
        except:xmd=0
        try:ymd=(y-controld[1])/abs(y-controld[1])
        except:ymd=0
        sr=0.02*(150/radius)
        xspeed=xmd*abs(control.centerx-controld[0])*sr
        yspeed=ymd*abs(control.centery-controld[1])*sr
        collisiont=5
        for sd in levelR:
            for block in sd[0]:
                block.x-=xspeed
            sd[1].x-=xspeed
        for rect in [i for sd in levelR for i in sd[0]]:
            if player.colliderect(rect):
                for sd in levelR:
                    for block in sd[0]:
                        block.x =xspeed
                    sd[1].x =xspeed
                break
        pygame.draw.circle(surface, (255,255,255), control.center, control.width,control.width)
        pygame.draw.circle(surface, (255,255,255), controld, radius, 5)
    elif not touched and controld:controld=()
    pygame.display.flip() 
    clock.tick(60)

I have tried debugging the collision and discovered that the moving to the right when collides with walls it stops the player from moving more to the right but player slightly moves to the right.

I expected walls not moving when player collides with walls.

It happened that the moving to the right when player collides with wall doesn't stop it directly.

CodePudding user response:

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 fractional part of the coordinates is lost when the position of the Rect object is changed. In your case this means that the movement to the left and right behaves differently and block.x = xspeed is not the inverse operation of block.x -= xspeed. Consider that, 5 - 0.1 is 4, but 4 0.1 is still 4.

You can solve the problem by testing the object for collisions before you actually move it, and not moving the object at all if a collision is detected:

collide = False
for rect in [i for sd in levelR for i in sd[0]]:
    text_rect = rect.copy()
    text_rect.x -= xspeed
    if player.colliderect(text_rect):
        collide = True
        break

if not collide:
    for sd in levelR:
        for block in sd[0]:
            block.x-=xspeed
        sd[1].x-=xspeed
  • Related