cooldanietje
cooldanietje

Reputation: 89

Pygame - Movement drift

I currently have an object that has code to rotate it around its center and move it around (bby altering its pos values).

However I want to make it so that if up arrow is pressed, it will accelerate in the direction its facing and when its released it will decelerate again back to 0. In the code I use a dt value for the change in time.

I tried clocking the time when a button is pressed and released and use that as the dt value, using this method the dT value can be negative. I also think this wouldn't work because then the rocket would receive let's say a dT value of 1 sec and update its velocity to go really fast instead of having a smooth acceleration/deacceleration.

class Rocket:
    def __init__(self, image, pos, angle):
        self.image = pygame.image.load(image)
        self.imageDimensions = self.image.get_rect()
        self.angle = angle
        self.pos = pos
        self.center = (self.pos[0], self.pos[1])

        self.velocity = [0, 0]

        self.acceleration = [0, 0]
        self.angularVelocity = 0
        self.angularAcceleration = 0

        self.isAccelerating = False
        self.thrust = 50

    def draw(self, surface):
        rotatedImg = pygame.transform.rotate(self.image, self.angle)
        rotatedImgDimensions = rotatedImg.get_rect()

        #display image
        surface.blit(rotatedImg, (self.pos[0] - rotatedImgDimensions.center[0], self.pos[1] - rotatedImgDimensions.center[1]))

    def update(self, dt):
        #update angle
        self.angularVelocity += self.angularAcceleration * dt
        self.angle += self.angularVelocity * dt

        #if accelerating update the acceleration
        if self.isAccelerating:
            self.acceleration[0] -= self.thrust * math.sin(math.radians(self.angle))
            self.acceleration[1] -= self.thrust * math.sin(math.radians(self.angle))

        #update velocity
        self.velocity[0] += self.acceleration[0] * dt
        self.velocity[1] += self.acceleration[1] * dt

        #update position
        self.pos[0] += self.velocity[0] * dt
        self.pos[1] += self.velocity[1] * dt

So in short I expect the rocket to accelerate forward when I press the up arrow, deaccelerate to 0 when I press the down arroy and rotate left and right when pressing arrow left and right.

Please note that the above class is in a different file named Objects.py

Thank you!!

Here is the rest of the code:

import pygame
from pygame.locals import *
import math
import Objects

#colors
WHITE = (255,255,255)
BLACK = (0,0,0)
TRANSPARENT = (0,0,0,0)

#size window
Width, Height = (1280,720)

#Main menu
def game_intro():
    intro = True

    while intro:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    intro = False
        screen.fill(BLACK)


FPS = 30

#initialise pygame
pygame.init()
fpsClock = pygame.time.Clock()

screen = pygame.display.set_mode((Width, Height))
pygame.display.set_caption("Rockets and Missiles")

#Add players
Rocket1 = Objects.Rocket("rocket.png", [100, 100], 0)    #start at pos 50,50

#run main menu first
game_intro()
run = True
while run:

    screen.fill(BLACK)

    Rocket1.draw(screen)

    #event handler

    pressed = pygame.key.get_pressed() #list with all pressed keys
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            Time0 = pygame.time.get_ticks()
        if event.type == pygame.KEYUP:
            Time1 = pygame.time.get_ticks()

    if pressed[pygame.K_UP]:
        dT = Time1 - Time0
        print(dT)
        Rocket1.update(dT)

    pygame.display.update()
    fpsClock.tick(FPS)

pygame.quit()
quit()

Upvotes: 1

Views: 522

Answers (3)

cooldanietje
cooldanietje

Reputation: 89

Thanks guys for helping out, i didnt realise this part :D However i want to answer my own question for people visiting this page later on.

A perhaps better way of doing it is to get the time it took the software to go over each iteration and using that as dT. it would look like the following: BBefore the main loop:fpsClock = pygame.time.Clock()

The main loop:

while run:

    screen.fill(BLACK)

    Rocket1.draw(screen)

    #draw missiles
    for missile in Missiles:
        missile.draw(screen)

    #event handler

    pressed = pygame.key.get_pressed() #list with all pressed keys
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    if pressed[K_UP]:
        Rocket1.acceleration = [0, -100]
    if pressed[K_DOWN]:
        Rocket1.acceleration = [0, 100]
    if pressed[K_RCTRL]:
        Missiles.append(Objects.Missile("missile.png", Rocket1.pos, Rocket1.angle))

    dT = fpsClock.get_time()

    Rocket1.update(dT/1000)

    pygame.display.update()
    fpsClock.tick(FPS)

Upvotes: 0

Valentino
Valentino

Reputation: 7361

I do not think measuring the time the button is pressed is the correct approach. Each iteration of the main loop corresponds to a fixed amount of time, and to produce an animation you want to move the rocket by that fixed amount of time each iteration. So no need to calculate a dt the way you are doing. You already have it, and it's equal to 1/FPS.

What you want to do usually is to set some velocity / acceleration for you rocket. Now they are all 0, but you should set a fixed value different from zero: how much you want it to be faster, or how much faster do you want it to accelerate, when the key button is pressed.
And when the key button corresponding is pressed, call the update method to calculate the new position / angle based on that velocity / acceleration and then redraw the rocket, considering that the time passed is 1/FPS.

And you also need two method to update separately linear motion and rotation. The way is now, you cannot separate the movements based on different keys.

Upvotes: 0

Hoog
Hoog

Reputation: 2298

I don't think you have a very useful definition of dT. As far as I can tell you only call Rocket.update() when a key is pressed, the rockets need to update every frame with small dT if you want smooth motion. Without calling update() in your rocket class more consistently you will not get the nice motion you want.

I suggest something like this for your main loop:

dT = 1/FPS
while run:

    screen.fill(BLACK)

    Rocket1.draw(screen)

    #event handler

    pressed = pygame.key.get_pressed() #list with all pressed keys
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    if pressed[pygame.K_UP]:
        Rocket1.ChangeCurrentAcceleration()
    Rocket1.update()
    pygame.display.update()
    fpsClock.tick(FPS)

For some new ChangeCurrentAcceleration() function that adds to the acceleration in your rocket, you can then change update() to assume that it already has the proper acceleration from thrust and calculates new velocity and position from there (maybe add a 0.95* multiplier to accel so it naturally slows down).

Upvotes: 1

Related Questions