Hola, bienvenido al foro.
Me he mirado el código y para hacer los arreglos que se me ocurren, habría que replantear un poco el tema de las colisiones.
El problema es que primero compruebas si ha habido colisión entre los coches y el personaje y luego le dices al personaje que se pare. Pero como ha habido la colisión primero, se queda en un estado de colisión permanente ("atrapado").
Una posible solución, para la situación de coches parados, edificios y ese tipo de cosas, es moverlo solo si sabes que en la posición siguiente no va a haber colisión. Pongo un ejemplo a continuación. Es la que suelo utilizar yo, mejorandola un poco: si se va a producir una colisión, hago que el personaje se mueva de uno en uno hasta quedarse pegado. El efecto que provoca no hacer esto, se puede apreciar si se pone una velocidad más grande al siguiente ejemplo (por ejemplo, speed = 6).
El ejemplo es una adaptación de un código que puso hugoruscitti en una entrada del foro que no recuerdo (tengo bastante información del proyecto LosersJuegos almacenada en una carpeta).
- Código: Seleccionar todo
- # -*- encoding: utf-8 -*-
 import pygame
 from pygame.sprite import Sprite
 import random
 
 
 class Caja(Sprite):
 
 def __init__(self, x, y):
 Sprite.__init__(self)
 self.crear_imagen_representacion()
 self.rect.center = x, y
 
 def crear_imagen_representacion(self):
 size = random.randint(10, 40)
 self.image = pygame.surface.Surface((size, size))
 self.image.fill((100, 100, 255))
 self.rect = self.image.get_rect()
 
 def draw(self, screen):
 screen.blit(self.image, self.rect)
 
 class Personaje(Sprite):
 "Personaje rectangular que puede caminar y saltar."
 
 def __init__(self):
 Sprite.__init__(self)
 self.crear_imagen_representacion()
 
 def crear_imagen_representacion(self):
 self.image = pygame.surface.Surface((20, 20))
 self.image.fill((100, 255, 100))
 self.rect = self.image.get_rect()
 self.rect.center = 160, 120
 
 def update(self, cajas):
 "Actualiza la posicion del personaje en base al estado del teclado."
 
 teclas = pygame.key.get_pressed()
 
 if teclas[pygame.K_LEFT]:
 self.try_move(-1, 0, cajas)
 elif teclas[pygame.K_RIGHT]:
 self.try_move(1, 0, cajas)
 
 if teclas[pygame.K_UP]:
 self.try_move(0, -1, cajas)
 elif teclas[pygame.K_DOWN]:
 self.try_move(0, 1, cajas)
 
 def draw(self, screen):
 "Muestra al personaje en pantalla."
 screen.blit(self.image, self.rect)
 
 def try_move(self, x, y, cajas):
 dest_rect = pygame.Rect(self.rect.x, self.rect.y, \
 self.rect.w, self.rect.h)
 #en teoria se puede utilizar Pygame.Rect.copy()
 
 speed = 1
 #speed = 6 #efecto no deseado
 dest_rect.move_ip(x * speed, y * speed)
 collide = False
 for caja in cajas:
 if dest_rect.colliderect(caja.rect):
 collide = True
 break
 if not collide:
 self.rect.move_ip(x * speed, y * speed)
 
 
 # Programa principal
 
 if __name__ == '__main__':
 salir = False
 screen = pygame.display.set_mode((320, 240))
 personaje = Personaje()
 cajas = [Caja(50, 50), Caja(200, 200), Caja(100, 40)]
 
 while not salir:
 
 for evento in pygame.event.get():
 if evento.type == pygame.QUIT:
 salir = True
 elif evento.type == pygame.KEYDOWN:
 if evento.key == pygame.K_q:
 salir = True
 
 personaje.update(cajas)
 
 screen.fill((100, 100, 100))
 
 for caja in cajas:
 caja.draw(screen)
 
 personaje.draw(screen)
 pygame.display.flip()
 
 pygame.time.delay(10)
Yo suelo utilizar esta técnica por costumbre (creo que a partir de descubrir que el blit de SDL modificaba los rects si salían fuera de la pantalla) pero hay otras: primero mover y, si hay colisión, retroceder... Y puede que sean mejores, así que si alguien se anima a compartir otras...
Saludos y espero que te sea útil.