Cómo conseguir una cámara tras un coche de arriba abajo en pygame

Como novedad en pygame y en la progtwigción de juegos en general, solo me preguntaba cómo podría conseguir una cámara para seguir un coche (nada especial) en un juego de coches de arriba abajo. Estoy usando Python 3.6, y tengo una bicicleta girando y moviéndose. He mantenido el código aquí más corto, pero tengo una imagen estática como referencia si la cámara funcionó.

Esto es lo que tengo:

import pygame, math, sys, random from pygame.locals import * display_width = 1280 display_height = 800 # Sets size of screen screen = pygame.display.set_mode((display_width, display_height)) # Initialises clock clock = pygame.time.Clock() # Colours white = (255,255,255) black = (0,0,0) class Entity(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) class VehicleSprite(Entity): # Creates a vehicle class MAX_FORWARD_SPEED = 10 MAX_REVERSE_SPEED = 2 ACCELERATION = 0.05 TURN_SPEED = 0.000000000001 def __init__(self, image, position): Entity.__init__(self) # Creates object instance off pygame.sprite.Sprite.__init__(self) self.src_image = pygame.image.load(image) self.position = position self.speed = self.direction = 0 self.k_left = self.k_right = self.k_down = self.k_up = 0 def update(self, time): # SIMULATION self.speed += (self.k_up +self.k_down) if self.speed > self.MAX_FORWARD_SPEED: self.speed = self.MAX_FORWARD_SPEED if self.speed < -self.MAX_REVERSE_SPEED: self.speed = -self.MAX_REVERSE_SPEED # Degrees sprite is facing (direction) self.direction += (self.k_right + self.k_left) x, y = self.position rad = self.direction * math.pi / 180 x += -self.speed*math.sin(rad) y += -self.speed*math.cos(rad) self.position = (x, y) self.image = pygame.transform.rotate(self.src_image, self.direction) self.rect = self.image.get_rect() self.rect.center = self.position class Background(pygame.sprite.Sprite): def __init__(self, image_file, location): pygame.sprite.Sprite.__init__(self) #call Sprite initializer self.image = pygame.image.load(image_file) self.rect = self.image.get_rect() self.rect.left, self.rect.top = location rect = screen.get_rect() # Background BackGround = Background('/home/pi/gametuts/images/backgrounds/bkg_img.png', [0, 0]) # Bike image load bike = VehicleSprite('/home/pi/gametuts/images/BikePixelBig.png', rect.center) bike_group = pygame.sprite.RenderPlain(bike) # Ball image load ball = VehicleSprite('/home/pi/gametuts/images/ironball.png', rect.center) ball_group = pygame.sprite.RenderPlain(ball) # Main game loop def game_loop(): while 1: #USER INPUT # Sets frame rate time = clock.tick(60) for event in pygame.event.get(): if not hasattr(event, 'key'): continue down = event.type == KEYDOWN # Bike Input (Player 1) if event.key == K_d: bike.k_right = down * -5 elif event.key == K_a: bike.k_left = down * 5 elif event.key == K_w: bike.k_up = down * 2 elif event.key == K_s: bike.k_down = down * -2 # Quit elif event.key == K_ESCAPE: sys.exit(0) #RENDERING # Game background screen.fill(white) screen.blit(BackGround.image, BackGround.rect) # Bike render bike_group.update(time) bike_group.draw(screen) ball_group.update(time) ball_group.draw(screen) pygame.display.flip() game_loop() pygame.quit() quit() 

¡Gracias por adelantado!

La forma más sencilla de implementar una cámara es usar un pygame.math.Vector2 como cámara, restar la velocidad del jugador a cada cuadro y agregarlo a la posición de todos los elementos del juego durante la jugada.

 import pygame as pg from pygame.math import Vector2 class Player(pg.sprite.Sprite): def __init__(self, pos, walls, *groups): super().__init__(*groups) self.image = pg.Surface((30, 50)) self.image.fill(pg.Color('dodgerblue')) self.rect = self.image.get_rect(center=pos) self.vel = Vector2(0, 0) self.pos = Vector2(pos) self.walls = walls self.camera = Vector2(0, 0) def update(self): self.camera -= self.vel # Change the camera pos if we're moving. # Horizontal movement. self.pos.x += self.vel.x self.rect.centerx = self.pos.x # Change the rect and self.pos coords if we touched a wall. for wall in pg.sprite.spritecollide(self, self.walls, False): if self.vel.x > 0: self.rect.right = wall.rect.left elif self.vel.x < 0: self.rect.left = wall.rect.right self.pos.x = self.rect.centerx self.camera.x += self.vel.x # Also move the camera back. # Vertical movement. self.pos.y += self.vel.y self.rect.centery = self.pos.y for wall in pg.sprite.spritecollide(self, self.walls, False): if self.vel.y > 0: self.rect.bottom = wall.rect.top elif self.vel.y < 0: self.rect.top = wall.rect.bottom self.pos.y = self.rect.centery self.camera.y += self.vel.y class Wall(pg.sprite.Sprite): def __init__(self, x, y, w, h, *groups): super().__init__(*groups) self.image = pg.Surface((w, h)) self.image.fill(pg.Color('sienna2')) self.rect = self.image.get_rect(topleft=(x, y)) def main(): screen = pg.display.set_mode((640, 480)) clock = pg.time.Clock() all_sprites = pg.sprite.Group() walls = pg.sprite.Group() for rect in ((100, 170, 90, 20), (200, 100, 20, 140), (400, 60, 150, 100), (300, 470, 150, 100)): walls.add(Wall(*rect)) all_sprites.add(walls) player = Player((320, 240), walls, all_sprites) done = False while not done: for event in pg.event.get(): if event.type == pg.QUIT: done = True elif event.type == pg.KEYDOWN: if event.key == pg.K_d: player.vel.x = 5 elif event.key == pg.K_a: player.vel.x = -5 elif event.key == pg.K_w: player.vel.y = -5 elif event.key == pg.K_s: player.vel.y = 5 elif event.type == pg.KEYUP: if event.key == pg.K_d and player.vel.x > 0: player.vel.x = 0 elif event.key == pg.K_a and player.vel.x < 0: player.vel.x = 0 elif event.key == pg.K_w and player.vel.y < 0: player.vel.y = 0 elif event.key == pg.K_s and player.vel.y > 0: player.vel.y = 0 all_sprites.update() screen.fill((30, 30, 30)) for sprite in all_sprites: # Add the player's camera offset to the coords of all sprites. screen.blit(sprite.image, sprite.rect.topleft+player.camera) pg.display.flip() clock.tick(30) if __name__ == '__main__': pg.init() main() pg.quit() 

Edición: Aquí está su ejemplo de código con una cámara. También he intentado mejorar algunas cosas más, por ejemplo, el truco max(min(...)) para fijar el valor de la velocidad. No estoy seguro de si el movimiento funciona como usted desea, pero por supuesto puede ajustarlo usted mismo. (Probablemente haría más modificaciones al método de update ).

 import math import random import pygame pygame.init() screen = pygame.display.set_mode((1280, 800)) rect = screen.get_rect() clock = pygame.time.Clock() WHITE = pygame.Color('white') # Load images globally and reuse them in your program. # Also use the `.convert()` or `.convert_alpha()` methods after # loading the images to improve the performance. VEHICLE1 = pygame.Surface((40, 70), pygame.SRCALPHA) VEHICLE1.fill((130, 180, 20)) VEHICLE2 = pygame.Surface((40, 70), pygame.SRCALPHA) VEHICLE2.fill((200, 120, 20)) BACKGROUND = pygame.Surface((1280, 800)) BACKGROUND.fill((30, 30, 30)) class Entity(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) class VehicleSprite(Entity): MAX_FORWARD_SPEED = 10 MAX_REVERSE_SPEED = 2 ACCELERATION = 0.05 TURN_SPEED = 0.000000000001 def __init__(self, image, position): Entity.__init__(self) self.src_image = image self.image = image self.rect = self.image.get_rect(center=position) self.position = pygame.math.Vector2(position) self.velocity = pygame.math.Vector2(0, 0) self.speed = self.direction = 0 self.k_left = self.k_right = self.k_down = self.k_up = 0 def update(self, time): # SIMULATION self.speed += self.k_up + self.k_down # To clamp the speed. self.speed = max(-self.MAX_REVERSE_SPEED, min(self.speed, self.MAX_FORWARD_SPEED)) # Degrees sprite is facing (direction) self.direction += (self.k_right + self.k_left) rad = math.radians(self.direction) self.velocity.x = -self.speed*math.sin(rad) self.velocity.y = -self.speed*math.cos(rad) self.position += self.velocity self.image = pygame.transform.rotate(self.src_image, self.direction) self.rect = self.image.get_rect(center=self.position) class Background(pygame.sprite.Sprite): def __init__(self, image, location): pygame.sprite.Sprite.__init__(self) self.image = image self.rect = self.image.get_rect(topleft=location) def game_loop(): background = Background(BACKGROUND, [0, 0]) bike = VehicleSprite(VEHICLE1, rect.center) ball = VehicleSprite(VEHICLE2, rect.center) bike_group = pygame.sprite.Group(bike) ball_group = pygame.sprite.Group(ball) all_sprites = pygame.sprite.Group(bike_group, ball_group) camera = pygame.math.Vector2(0, 0) done = False while not done: time = clock.tick(60) for event in pygame.event.get(): if event.type == pygame.QUIT: done = True elif event.type == pygame.KEYDOWN: # Bike Input (Player 1) if event.key == pygame.K_d: bike.k_right = -5 elif event.key == pygame.K_a: bike.k_left = 5 elif event.key == pygame.K_w: bike.k_up = 2 elif event.key == pygame.K_s: bike.k_down = -2 elif event.key == pygame.K_ESCAPE: done = True elif event.type == pygame.KEYUP: if event.key == pygame.K_d: bike.k_right = 0 elif event.key == pygame.K_a: bike.k_left = 0 elif event.key == pygame.K_w: bike.k_up = 0 elif event.key == pygame.K_s: bike.k_down = 0 camera -= bike.velocity all_sprites.update(time) screen.fill(WHITE) screen.blit(background.image, background.rect) for sprite in all_sprites: screen.blit(sprite.image, sprite.rect.topleft+camera) pygame.display.flip() game_loop() pygame.quit()