How to get camera following a top down car in pygame

懵懂的女人 提交于 2019-12-01 06:59:45

问题


new to pygame and game programming in general, just wondered how I could get a camera to follow a car (nothing fancy) in a top down car game - think Micro Machines! I'm using Python 3.6, and have got a bike rotating, and moving around. I've kept the code here shorter but I do have a static image for reference if the camera worked!

Here's what I have:

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()

Thanks in advance!


回答1:


The simplest way to implement a camera is to use a pygame.math.Vector2 as the camera, subtract the player velocity from it each frame and add it to the position of all game elements during the blitting.

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()

Edit: Here's your code example with a camera. I've also tried to improve a few more things, for example the max(min(...)) trick to clamp the speed value. I'm not sure if the movement works as you want, but you can of course adjust it yourself. (I'd probably make even more modifications to the update method.)

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()


来源:https://stackoverflow.com/questions/45403451/how-to-get-camera-following-a-top-down-car-in-pygame

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!