5

enter image description here

I was wondering how I could make a car that moves and rotates using the arrow keys. I am trying to make a car physics game where the player controls the car and drives around and parks, but I am having trouble with how to start implementing the controls. How could I make my car move the direction it's rotating with the arrow keys?

For example, if I am pressing the back arrow key, the car should reverse, and if the car is reversing while also turning, it should move the way the car is turning.

Here is my code right now. There isn't really anything going on right now.

import pygame pygame.init() window = pygame.display.set_mode((800,800)) pygame.display.set_caption("car game") class car: def __init__(self,x,y,height,width,color): self.x = x self.y = y self.height = height self.width = width self.color = color self.carimage = pygame.image.load("1.png") self.rect = pygame.Rect(x,y,height,width) def draw(self): self.rect.topleft = (self.x,self.y) window.blit(self.carimage,self.rect) white = (255,255,2555) car1 = car(300,300,20,20,white) def ReDrawWindow(): car1.draw() # main loop runninggame = True while runninggame: for event in pygame.event.get(): if event.type == pygame.QUIT: runninggame = False ReDrawWindow() pygame.display.update() pygame.quit() 

2 Answers 2

4
+200

I wanted to add a PyGame Sprite based answer to this question. Implementing this sort of thing as a sprite makes it easier to use the PyGame collision functions. For example, any number of CarSprites could be made, but their collision checked against the player's CarSrpite in a single call to groupcollide().

This implementation uses PyGame.math.Vector2() for velocity and position. This allows for a fairly simple turning and speed model utilising the Vector2's polar co-ordinate function. Initially this gave weird and confusing result... until I realised the Vector2.from_polar() required the angle in degrees. (Not radians unlike just about every other programming language function that takes angles.)

When the sprite is initially created, the code will make a lot of pre-rotated images. This does the smoothest turning at around 1 per degree (360), but if memory-usage was a issue, it could also be much less.

Anyway, the code is fairly self-explanatory. It requires a car_128.png image, and a background texture image road_texture.png. Please comment any questions.

demo-image

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 car_128.png (Source: https://openclipart.org )

road_texture.png road_texture.png

Sign up to request clarification or add additional context in comments.

Comments

4

Here is the improved code:

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

Some things to notice:

  1. I created a new surface to use to draw the picture. This makes it easier to rotate it.
  2. I created a speed variable for the car, to store its speed. I use it later for momentum.
  3. The draw function rotates the image anticlockwise, because that's how Pygame works. Check out the code that I used.
  4. The car dimensions that I used are 73, 73. Make this the width and height of your picture, otherwise the car won't turn properly.
  5. I decrease the speed ever so slightly, so that when you don't press forward, the car goes on for a bit.
  6. When the car moves forward and backward, its maximum speed is 5 pixels per frame. (Because 5 * 0.9 + 0.5 = 5.)
  7. The angle that the car turns depends on the speed.
  8. Here is the trigonometry that I was trying to say earlier. Because math.sin and math.cos use radians, I have to convert from degrees to radians.
  9. I filled the screen with black so you wouldn't see the earlier frames.
  10. The clock.tick is used to keep it from going too fast, and it means "a maximum of 60 frames per second".

I hope you understand everything.

3 Comments

I dont see the car being displayed on the window video video
last question is there anyway I could make the image a little bigger because it looks way to small like when I resize it the rotation works good but the image splits I am trying to make a parking game video
How big is your image? Make the size of the car the exact size of your picture. In my case I used a 73x73 image, so I made the car 73 wide and 73 high.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.