BRAN
BRAN

Reputation: 25

Acceleration and Deceleration in pygame

I do not have much experience in coding, and I have been stuck on trying to make my character accelerate to a constant speed when key is held and decelerate when key is let go of. I found some code online on it but I cannot seem to properly implement it into mine. If you don't mind it would be helpful if you thoroughly explained the answer so I can properly understand what I am doing wrong.

here is my code

"""Dot Game"""

#Imports
import pygame, sys

#Constants
WIDTH, HEIGHT = 1000, 1000
TITLE = "Dot."

#pygame initialization
pygame.init()
win = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)
#fps
FPS = 60
fpsclock = pygame.time.Clock()

#colors
bgc = (247, 226, 222)
pc = (152, 193, 217)
pc2 = (61, 90, 128)
ec = (119, 2, 26)
ec2 = (220, 66, 73)

#accel
x_change = 0
y_change = 0
accel_x = 0
accel_y = 0
max_speed = 6

display_width, display_height = pygame.display.get_surface().get_size()
x = display_width * 0.45
y = display_height * 0.8

#Player Class
class Player:
    def __init__(self, x, y):
        self.x = int(x)
        self.y = int(y)
        self.radius = 32
        self.width = 2
        self.color = pc
        self.color2 = pc2
        self.velX = 0
        self.velY = 0
        self.left_pressed = False
        self.right_pressed = False
        self.up_pressed = False
        self.down_pressed = False
        self.speed = 3
        self.hitbox = (self.x -20, self.y -20, 40, 40)
    
    def draw(self, win):
        pygame.draw.circle(win, self.color2, (self.x,self.y), 20)
        pygame.draw.circle(win, self.color, (self.x,self.y), 14)
        self.hitbox = (self.x -20, self.y -20, 40, 40)
        #pygame.draw.rect(win,(255,0,0), self.hitbox,2)
    
    def update(self):
        self.velX = 0
        self.velY = 0
        if self.left_pressed:
            self.velX = -self.speed
        if self.right_pressed:
            self.velX = self.speed
        if self.up_pressed:
            self.velY = -self.speed
        if self.down_pressed :
            self.velY = self.speed
        
        self.x += self.velX
        self.y += self.velY

        self.rect = pygame.Rect(int(self.x), int(self.y), 32, 32)

    def collide(self):
        print('hit')
        pass

#Player Initialization
player = Player(WIDTH/2, HEIGHT/2)

#Main Loop
collide = False
while not collide:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            collide == True
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                accel_x = -.2
                player.left_pressed = True
            elif event.key == pygame.K_RIGHT:
                accel_x = .2
                player.right_pressed = True
            elif event.key == pygame.K_UP:
                accel_y = -.2
                player.up_pressed = True
            elif event.key == pygame.K_DOWN:
                accel_y = .2
                player.down_pressed = True
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                accel_x = 0
                accel_y = 0
                player.left_pressed = False
            elif event.key == pygame.K_RIGHT:
                accel_x = 0
                accel_y = 0
                player.right_pressed = False
            elif event.key == pygame.K_UP:
                accel_x = 0
                accel_y = 0
                player.up_pressed = False
            elif event.key == pygame.K_DOWN:
                accel_x = 0
                accel_y = 0
                player.down_pressed = False
    
    x_change += accel_x  # Accelerate.
    if abs(x_change) >= max_speed:  # If max_speed is exceeded.
        # Normalize the x_change and multiply it with the max_speed.
        x_change = x_change/abs(x_change) * max_speed

    y_change += accel_y  # Accelerate.
    if abs(y_change) >= max_speed:  # If max_speed is exceeded.
        # Normalize the x_change and multiply it with the max_speed.
        y_change = y_change/abs(y_change) * max_speed

    # Decelerate if no key is pressed.
    if accel_x == 0:
        if x_change > 0:
            x_change -= 0.2
            if x_change < 0.2:
                x_change = 0
        elif x_change < 0:
            x_change += 0.2
            if x_change > -0.2:
                x_change = 0
    if accel_y == 0:
        if y_change > 0:
            y_change -= 0.2
            if y_change < 0.2:
                y_change = 0
        elif y_change < 0:
            y_change += 0.2
            if y_change > -0.2:
                y_change = 0

    x += x_change  # Move the object.
    y += y_change

    #Draw
    win.fill((bgc))  
    player.draw(win)

    #update
    player.update()
    pygame.display.update()

    fpsclock.tick(FPS)

The movement itself works, however there is no acceleration and deceleration

Upvotes: 1

Views: 1282

Answers (1)

Rabbid76
Rabbid76

Reputation: 210909

In your code the speed is always constant because you set self.velX = 0 and self.velY = 0 at the beginning of update. Therefore the amount of the speed is always 0 or 3. Do not reset self.velX and self.velY. Change the speed with the acceleration, but limit it at the maximum speed. The acceleration must be a small value. As long as a key is pressed, this small value is added to the speed once per frame. This slowly increases the speed to the maximum.
Improve physics by multiplying speed by friction. The value for the friction must be less than 1, or 1 for no friction:

class Player:
    def __init__(self, x, y):
        # [...]

        self.acceleration = 0.1
        self.friction = 0.99 # = 1   if you don't want any friction

    def update(self):
        if self.left_pressed:
            if self.velX > -max_speed:
                self.velX -= self.acceleration
        if self.right_pressed:
            if self.velX < max_speed:
                self.velX += self.acceleration
        if self.up_pressed:
            if self.velY > -max_speed:
                self.velY -= self.acceleration
        if self.down_pressed :
            if self.velY < max_speed:
                self.velY += self.acceleration
        
        self.x += self.velX
        self.y += self.velY
        self.velX *= self.friction
        self.velY *= self.friction

        self.rect = pygame.Rect(int(self.x), int(self.y), 32, 32)

Upvotes: 3

Related Questions