Luqman Muro
Luqman Muro

Reputation: 19

How to make your character move without tapping repeatedly on buttons?

I'm working on a racing game. I have been using this code but my player keeps moving once only. How do I solve this problem?

change = 7
dist = 7
change_r = 0
change_l = 0
dist_u = 0
dist_d = 0
pygame.display.update()  


for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_RIGHT:
           change_r = True

        elif event.key == pygame.K_LEFT:
            change_l = True


        elif event.key == pygame.K_UP:
            dist_u = True     

        elif event.key == pygame.K_DOWN:
            dist_d = True



    if event.type == pygame.KEYUP:
        if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT or event.key == pygame.K_UP or event.key == pygame.K_DOWN:
            change_r = False
            change_l = False
            dist_u = False
            dist_d = False
    clock.tick(60)     

    if change_r == True:
        x += change
    if change_l == True:
        x -= change
    if dist_u == True:
        y -= dist
    if dist_d == True:
        y += dist

Upvotes: 1

Views: 836

Answers (2)

Chris
Chris

Reputation: 22963

There are different ways you could go about making move-able shapes/sprites using Pygame. The code you posted in your question seems overly complex. I'll give you two different ways that are simpler, and easier to use. Using a sprite class, and using a function.

It may not seem like I'm answering your question, but if I just told you what to fix in your code you posted, I'd just be knowingly steering you down a road that will lead to many problems, and much complexity in the future.

Method 1: Using a sprite class

To make a your race car sprite, you could use a sprite class like the one below.

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 50))
        self.image.fill((255, 0, 0))
        self.rect = self.image.get_rect()
        self.rect.x = WIDTH / 2
        self.rect.y = HEIGHT / 2
        self.vx = 0
        self.vy = 0

    def update(self):
        self.vx = 0
        self.vy = 0
        key = pygame.key.get_pressed()
        if key[pygame.K_LEFT]:
            self.vx = -5
        elif key[pygame.K_RIGHT]:
            self.vx = 5
        if key[pygame.K_UP]:
            self.vy = -5
        elif key[pygame.K_DOWN]:
            self.vy = 5
        self.rect.x += self.vx
        self.rect.y += self.vy

Since your class is inheriting from Pygame's sprite class, You must name your image self.image and you must name your rectangle for the image self.rect. As you can also see, the class has two main methods. One for creating the sprite(__init__) and one for updating the sprite(update)

To use your class, make a Pygame sprite group to hold all your sprites, and then add your player object to the group:

sprites = pygame.sprite.Group()
player = Player()
sprtites.add(player)

And to actual render your sprites to the screen call sprites.update() and sprites.draw() in your game loop, where you update the screen:

sprites.update()
window_name.fill((200, 200, 200))
sprites.draw(window_name)
pygame.display.flip()

The reason i highly recommended using sprite classes, is that it will make your code look much cleaner, and be much easier to maintain. You could even move each sprite class to their own separate file.

Before diving fully into the method above however, you should read up on pygame.Rect objects and pygame.sprite objects, as you'll be using them.

Method 2: Using A function

If you prefer not to get into sprite classes, you can create your game entities using a function similar to the one below.

def create_car(surface, x, y, w, h, color):
    rect = pygame.Rect(x, y, w, h)
    pygame.draw.rect(surface, color, rect)
    return rect

If you would still like to use a sprites, but don't want to make a class just modify the function above slightly:

def create_car(surface, x, y, color, path_to_img):
    img = pygame.image.load(path_to_img)
    rect = img.get_rect()
    surface.blit(img, (x, y))

Here is an example of how i would use the functions above to make a movable rectangle/sprite:

import pygame

WIDTH = 640
HEIGHT = 480
display = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Moving Player Test")
clock = pygame.time.Clock()
FPS = 60

def create_car(surface, x, y, w, h, color):
    rect = pygame.Rect(x, y, w, h)
    pygame.draw.rect(surface, color, rect)
    return rect

running = True
vx = 0
vy = 0
player_x = WIDTH / 2 # middle of screen width
player_y = HEIGHT / 2 # middle of screen height
player_speed = 5
while running:
    clock.tick(FPS)
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            running = False
            pygame.quit()
            quit()
        if e.type == pygame.KEYDOWN:
            if e.key == pygame.K_LEFT:
                vx = -player_speed
            elif e.key == pygame.K_RIGHT:
                vx = player_speed
            if e.key == pygame.K_UP:
                vy = -player_speed
            elif e.key == pygame.K_DOWN:
                vy = player_speed
        if e.type == pygame.KEYUP:
            if e.key == pygame.K_LEFT or e.key == pygame.K_RIGHT or\
               e.key == pygame.K_UP or e.key == pygame.K_DOWN:
                vx = 0
                vy = 0

    player_x += vx
    player_y += vy
    display.fill((200, 200, 200))
    ####make the player#####
    player = create_car(display, player_x, player_y, 10, 10, (255, 0, 0))
    pygame.display.flip()

As you may see above, your code for moving your race car can be simplified.

I should note, I'm assuming a few things with each method outlined above.

  1. That your either using a circle, square, or some type of pygame shape object.

  2. Or that your using a sprite.

If your not currently using any of the methods outlined above, I suggest that you do. Doing so will make your code much eaiser to maintain once you begin to build larger and more complex games.

Upvotes: 2

Bartlomiej Lewandowski
Bartlomiej Lewandowski

Reputation: 11180

You should have a global loop irrespective of your event handling. You should place your clock.tick() and your movement there. Namely:

#some constants here
while True:    
    pygame.display.update()  
    clock.tick(60)
    #update you game state
    if change_r == True:
        x += change
    if change_l == True:
        x -= change
    if dist_u == True:
        y -= dist
    if dist_d == True:
        y += dist

    for event in pygame.event.get():
        # react to player input by changing the game state

Upvotes: 0

Related Questions