Python >> Tutoriel Python >  >> Python GUI >> Pygame GUI

Comment puis-je créer une physique automobile de base dans Pygame ?

Voici le code amélioré :

import pygame, math
pygame.init()

window = pygame.display.set_mode((600,600))
pygame.display.set_caption("car game")
img = pygame.image.load("1.png")

class Car:
    def __init__(self, x, y, height, width, color):
        self.x = x - width / 2
        self.y = y - height / 2
        self.height = height
        self.width = width
        self.color = color
        self.rect = pygame.Rect(x, y, height, width)
        self.surface = pygame.Surface((height, width)) # 1
        self.surface.blit(img, (0, 0))
        self.angle = 0
        self.speed = 0 # 2

    def draw(self): # 3
        self.rect.topleft = (int(self.x), int(self.y))
        rotated = pygame.transform.rotate(self.surface, self.angle)
        surface_rect = self.surface.get_rect(topleft = self.rect.topleft)
        new_rect = rotated.get_rect(center = surface_rect.center)
        window.blit(rotated, new_rect.topleft)

white = (255, 255, 255)
car1 = Car(300, 300, 73, 73, white) # 4
clock = pygame.time.Clock()

runninggame = True
while runninggame:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            runninggame = False
    
    pressed = pygame.key.get_pressed()
    car1.speed *= 0.9 # 5
    if pressed[pygame.K_UP]: car1.speed += 0.5 # 6
    if pressed[pygame.K_DOWN]: car1.speed -= 0.5 # 6

    if pressed[pygame.K_LEFT]: car1.angle += car1.speed / 2 # 7
    if pressed[pygame.K_RIGHT]: car1.angle -= car1.speed / 2 # 7
    car1.x -= car1.speed * math.sin(math.radians(car1.angle)) # 8
    car1.y -= car1.speed * math.cos(math.radians(-car1.angle)) # 8
    
    window.fill((0, 0, 0)) # 9
    car1.draw()
    pygame.display.flip()
    clock.tick(60) # 10

pygame.quit()

Quelques éléments à noter :

  1. J'ai créé une nouvelle surface à utiliser pour dessiner l'image. Cela facilite sa rotation.
  2. J'ai créé une variable de vitesse pour la voiture, pour stocker sa vitesse. Je l'utilise plus tard pour l'élan.
  3. La fonction de dessin fait pivoter l'image dans le sens inverse des aiguilles d'une montre, car c'est ainsi que fonctionne Pygame. Découvrez le code que j'ai utilisé.
  4. Les dimensions de la voiture que j'ai utilisées sont 73, 73. Faites-en la largeur et la hauteur de votre image, sinon la voiture ne tournera pas correctement.
  5. Je diminue très légèrement la vitesse, de sorte que lorsque vous n'avancez pas, la voiture continue un peu.
  6. Lorsque la voiture avance et recule, sa vitesse maximale est de 5 pixels par image. (Parce que 5 * 0,9 + 0,5 =5.)
  7. L'angle de rotation de la voiture dépend de la vitesse.
  8. Voici la trigonométrie que j'essayais de dire plus tôt. Étant donné que math.sin et math.cos utilisent des radians, je dois convertir des degrés en radians.
  9. J'ai rempli l'écran de noir pour que vous ne voyiez pas les images précédentes.
  10. Le clock.tick est utilisé pour l'empêcher d'aller trop vite, et cela signifie "un maximum de 60 images par seconde".

J'espère que vous comprenez tout.


Je voulais ajouter une réponse basée sur PyGame Sprite à cette question. L'implémentation de ce genre de chose en tant que sprite facilite l'utilisation des fonctions de collision de PyGame. Par exemple, n'importe quel nombre de CarSprites peut être créé, mais leur collision est vérifiée par rapport au CarSrpite du joueur en un seul appel à groupcollide() .

Cette implémentation utilise PyGame.math.Vector2() pour la vitesse et la position. Cela permet un modèle de rotation et de vitesse assez simple utilisant la fonction de coordonnées polaires du Vector2. Au départ, cela donnait un résultat étrange et déroutant... jusqu'à ce que je réalise le Vector2.from_polar() requis l'angle en degrés. (Pas des radians contrairement à environ tous autre fonction de langage de programmation qui prend des angles.)

Lors de la création initiale du sprite, le code créera de nombreuses images pré-rotées. Cela fait le virage le plus fluide à environ 1 par degré (360), mais si l'utilisation de la mémoire était un problème, cela pourrait aussi être beaucoup moins.

Quoi qu'il en soit, le code est assez explicite. Il nécessite un car_128.png image, et une image de texture de fond road_texture.png . Veuillez commenter toute question.

import pygame
import math

# Window size
WINDOW_WIDTH    = 600
WINDOW_HEIGHT   = 600
WINDOW_SURFACE  = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE


class CarSprite( pygame.sprite.Sprite ):
    """ Car Sprite with basic acceleration, turning, braking and reverse """

    def __init__( self, car_image, x, y, rotations=360 ):
        """ A car Sprite which pre-rotates up to <rotations> lots of
            angled versions of the image.  Depending on the sprite's
            heading-direction, the correctly angled image is chosen.
            The base car-image should be pointing North/Up.          """
        pygame.sprite.Sprite.__init__(self)
        # Pre-make all the rotated versions
        # This assumes the start-image is pointing up-screen
        # Operation must be done in degrees (not radians)
        self.rot_img   = []
        self.min_angle = ( 360 / rotations ) 
        for i in range( rotations ):
            # This rotation has to match the angle in radians later
            # So offet the angle (0 degrees = "north") by 90° to be angled 0-radians (so 0 rad is "east")
            rotated_image = pygame.transform.rotozoom( car_image, 360-90-( i*self.min_angle ), 1 )
            self.rot_img.append( rotated_image )
        self.min_angle = math.radians( self.min_angle )   # don't need degrees anymore
        # define the image used
        self.image       = self.rot_img[0]
        self.rect        = self.image.get_rect()
        self.rect.center = ( x, y )
        # movement
        self.reversing = False
        self.heading   = 0                           # pointing right (in radians)
        self.speed     = 0    
        self.velocity  = pygame.math.Vector2( 0, 0 )
        self.position  = pygame.math.Vector2( x, y )

    def turn( self, angle_degrees ):
        """ Adjust the angle the car is heading, if this means using a 
            different car-image, select that here too """
        ### TODO: car shouldn't be able to turn while not moving
        self.heading += math.radians( angle_degrees ) 
        # Decide which is the correct image to display
        image_index = int( self.heading / self.min_angle ) % len( self.rot_img )
        # Only update the image if it's changed
        if ( self.image != self.rot_img[ image_index ] ):
            x,y = self.rect.center
            self.image = self.rot_img[ image_index ]
            self.rect  = self.image.get_rect()
            self.rect.center = (x,y)

    def accelerate( self, amount ):
        """ Increase the speed either forward or reverse """
        if ( not self.reversing ):
            self.speed += amount
        else: 
            self.speed -= amount

    def brake( self ):
        """ Slow the car by half """
        self.speed /= 2
        if ( abs( self.speed ) < 0.1 ):
            self.speed = 0

    def reverse( self ):
        """ Change forward/reverse, reset any speed to 0 """
        self.speed     = 0
        self.reversing = not self.reversing

    def update( self ):
        """ Sprite update function, calcualtes any new position """
        self.velocity.from_polar( ( self.speed, math.degrees( self.heading ) ) )
        self.position += self.velocity
        self.rect.center = ( round(self.position[0]), round(self.position[1] ) )


### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption("Car Steering")


### Bitmaps
road_image = road_image = pygame.image.load( 'road_texture.png' )
background = pygame.transform.smoothscale( road_image, ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
car_image  = pygame.image.load( 'car_128.png' ).convert_alpha()


### Sprites
black_car = CarSprite( car_image, WINDOW_WIDTH//2, WINDOW_HEIGHT//2 )
car_sprites = pygame.sprite.Group() #Single()
car_sprites.add( black_car )


### Main Loop
clock = pygame.time.Clock()
done = False
while not done:

    # Handle user-input
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True
        elif ( event.type == pygame.VIDEORESIZE ):
            WINDOW_WIDTH  = event.w
            WINDOW_HEIGHT = event.h
            window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
            background = pygame.transform.smoothscale( road_image, ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
        elif ( event.type == pygame.MOUSEBUTTONUP ):
            # On mouse-click
            pass
        elif ( event.type == pygame.KEYUP ):
            if ( event.key == pygame.K_h ):  
                print( 'meep-meep' )
            elif ( event.key == pygame.K_r ):  
                print( 'resersing' )
                black_car.reverse()
            elif ( event.key == pygame.K_UP ):  
                print( 'accelerate' )
                black_car.accelerate( 0.5 )
            elif ( event.key == pygame.K_DOWN ):  
                print( 'brake' )
                black_car.brake( )

    # Continuous Movement keys
    keys = pygame.key.get_pressed()
    if ( keys[pygame.K_LEFT] ):
        black_car.turn( -1.8 )  # degrees
    if ( keys[pygame.K_RIGHT] ):
        black_car.turn( 1.8 )

    # Update the car(s)
    car_sprites.update()

    # Update the window
    window.blit( background, ( 0, 0 ) ) # backgorund
    car_sprites.draw( window )
    pygame.display.flip()

    # Clamp FPS
    clock.tick_busy_loop(60)

pygame.quit()

car_128.png (Source :https://openclipart.org)

road_texture.png