2

I am making an Astroid clone where you only move with the keyboard. The player is able to rotate and needs to move forward to were it is facing.

For example: When I rotate the player by 30 degrees and push W to go forward I want the player to go forward in the direction the player is facing. I can't just use self.rect.x += self.vel

I also tried to use this function:

def calculate_new_xy(speed, degrees):
    add_x =  (speed*(math.sin((degrees)*math.pi/180)))
    add_y =  (speed*(math.cos((degrees)*math.pi/180)))
    return add_x, add_y

In theory, it should work because I am taking the sin and cos of this Triangle.

But when I added that to my code the movement of the player wasn't as smooth as I hoped it to be. And sometimes the player moved indirectly forward.

I also noticed that the player moves faster when facing to the top left than to the bottom right.

Due to that the player doesn't move in a circle when turning always right. Instead the player moves elliptical to the top left.

Here is the code:

import pygame
import time
import random
import math
from os import path

#Colors:
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)

#Settings:
pygame.init()
pygame.mixer.init() #for sound
WIDTH = 700
HEIGHT = 500
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Astroids")
clock = pygame.time.Clock()
FPS = 60

#Images/Sounds:
game_folder = path.dirname(__file__)
img_folder = path.join(game_folder, "img")
player = pygame.image.load(path.join(img_folder, 'Player.png'))
stone = pygame.image.load(path.join(img_folder, 'Stein.png'))

#Game Classes
class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.original_image = player
        self.image = self.original_image.copy()
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH/2, HEIGHT/2)
        self.speed_x = 0
        self.speed_y = 0
        self.vel = 3
        self.degrees = 0

    def boundary(self):
        if self.rect.left > WIDTH:
            self.rect.right = 0
        if self.rect.bottom < 0:
            self.rect.top = HEIGHT
        if self.rect.right < 0:
            self.rect.left = WIDTH
        if self.rect.top > HEIGHT:
            self.rect.bottom = 0

    def movement(self):
        '''Player Movement'''
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_q]:
            self.degrees += 3

        if keystate[pygame.K_e]:
            self.degrees -= 3

        if keystate[pygame.K_w]:
            self.rect.centerx += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
            self.rect.centery += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))

        if keystate[pygame.K_s]:
            self.rect.centery += -self.vel * math.sin(math.radians(self.degrees - 90))
            self.rect.centerx +=  self.vel * math.cos(math.radians(self.degrees - 90))

    def rotate(self):
        old_center = self.rect.center 
        self.image = pygame.transform.rotate(self.original_image, self.degrees)
        self.rect = self.image.get_rect()
        self.rect.center = old_center



    def update(self):
        '''Picture is "printed"'''
        self.movement()
        self.boundary()
        self.rotate()
        #self.rect.y += self.speed_y


class Astroid(pygame.sprite.Sprite):
    def __init__(self, life):
        pygame.sprite.Sprite.__init__(self)
        self.original_image = stone
        self.image = self.original_image.copy()
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH/2, HEIGHT/2)
        self.speed = 2
        self.life = life
        self.speed_x = random.randrange(-3, 3)
        self.speed_y = random.randrange(-3, 3)
        self.rect.x = random.randrange(0, WIDTH - self.rect.width)
        self.rect.y = random.randrange(-3, 3) 
        self.last_rotation = pygame.time.get_ticks() # keeps track of time in milliseconds
        self.rotation_deegre = random.randint(0, 360)
        self.rotation_speed = 5



    def rotate(self):
        current_time = pygame.time.get_ticks()
        if current_time - self.last_rotation > 50:
            self.last_rotation = current_time
            self.rotation_deegre += self.rotation_speed
            old_center = self.rect.center 
            self.image = pygame.transform.rotate(self.original_image, self.rotation_deegre)
            self.rect = self.image.get_rect()
            self.rect.center = old_center


    '''def new_astroids(self):
        for i in range(2):
            m = Astroid()
            all_sprites.add(m)
            all_astroids.add(m)''' #to do



    def boundary(self):
        if self.rect.left > WIDTH:
            self.rect.right = 0
        if self.rect.bottom < 0:
            self.rect.top = HEIGHT
        if self.rect.right < 0:
            self.rect.left = WIDTH
        if self.rect.top > HEIGHT:
            self.rect.bottom = 0

    def movement(self):
        self.rect.x += self.speed_x
        self.rect.y += self.speed_y

    def update(self):
        '''Picture is "printed"'''
        self.movement()
        self.boundary()
        self.rotate()


#Game Funktions
def calculate_new_xy(speed, degrees):
    add_x =  (speed*(math.sin((degrees)*math.pi/180)))
    add_y =  (speed*(math.cos((degrees)*math.pi/180)))
    return add_x, add_y

#Game Sprites
all_astroids = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)

for i in range (1):
    m = Astroid(3)
    all_astroids.add(m)
    all_sprites.add(m)

#Main Game Loop
running = True
while running:

    #Keep the game runnung at 60 FPS
    clock.tick(FPS)


    #Check for events:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    #Update for Sprites
    all_sprites.update()

    #Check to see if any Astroids hit the player
    astroid_colision = pygame.sprite.spritecollide(player, all_astroids, False)
    if astroid_colision:
        running = False

    #Draw/Render
    screen.fill(BLACK)
    all_sprites.draw(screen)

    #Update the display
    pygame.display.update()

pygame.quit()

Any help is welcomed.

Glenn Mackintosh
  • 2,765
  • 1
  • 10
  • 18
MyTech
  • 21
  • 3

1 Answers1

1

But when I added that to my code the movement of the player wasn't as smove as i hoped

The issue is caused by the fact, that the attributes of a pygame.Rect are integral values. Every time the position of the rectangle is changed, the fraction of the floating point motion vector is lost.
You have to compute the position with floating point accuracy and to synchronize the integral rectangle location by rounding the floating point position (round).

Add the attributes self.x and self.y:

class Player(pygame.sprite.Sprite):
    def __init__(self):
        # [...]

        self.x, self.y = self.rect.center

Update self.x and self.y when the rectangle goes out of bounds:

class Player(pygame.sprite.Sprite):
    # [...]

    def boundary(self):
        if self.rect.left > WIDTH:
            self.rect.right = 0
            self.x = self.rect.centerx
        if self.rect.bottom < 0:
            self.rect.top = HEIGHT
            self.y = self.rect.centery
        if self.rect.right < 0:
            self.rect.left = WIDTH
            self.x = self.rect.centerx
        if self.rect.top > HEIGHT:
            self.rect.bottom = 0
            self.y = self.rect.centery

Change the the attributes self.x and self.y when the player moves and update self.rect.center by self.x and self.y:

class Player(pygame.sprite.Sprite):
    # [...]

    def movement(self):
        # [...]

        if keystate[pygame.K_w]:
            self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
            self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))

        if keystate[pygame.K_s]:
            self.y += -self.vel * math.sin(math.radians(self.degrees - 90))
            self.x +=  self.vel * math.cos(math.radians(self.degrees - 90))

        self.rect.center = round(self.x), round(self.y)

Class Player:

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.original_image = player
        self.image = self.original_image.copy()
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH/2, HEIGHT/2)
        self.speed_x = 0
        self.speed_y = 0
        self.vel = 3
        self.degrees = 0
        self.x, self.y = self.rect.center

    def boundary(self):
        if self.rect.left > WIDTH:
            self.rect.right = 0
            self.x = self.rect.centerx
        if self.rect.bottom < 0:
            self.rect.top = HEIGHT
            self.y = self.rect.centery
        if self.rect.right < 0:
            self.rect.left = WIDTH
            self.x = self.rect.centerx
        if self.rect.top > HEIGHT:
            self.rect.bottom = 0
            self.y = self.rect.centery

    def movement(self):
        '''Player Movement'''
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_q]:
            self.degrees += 3

        if keystate[pygame.K_e]:
            self.degrees -= 3

        if keystate[pygame.K_w]:
            self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
            self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))

        if keystate[pygame.K_s]:
            self.y += -self.vel * math.sin(math.radians(self.degrees - 90))
            self.x +=  self.vel * math.cos(math.radians(self.degrees - 90))

        self.rect.center = round(self.x), round(self.y)

    def rotate(self):
        old_center = self.rect.center 
        self.image = pygame.transform.rotate(self.original_image, self.degrees)
        self.rect = self.image.get_rect()
        self.rect.center = old_center

    def update(self):
        '''Picture is "printed"'''
        self.movement()
        self.boundary()
        self.rotate()
        #self.rect.y += self.speed_y
Rabbid76
  • 202,892
  • 27
  • 131
  • 174