Adjusting the ship's speed and limiting the ship's range Python Pygame

275 Views Asked by At

Good evening,

I am creating a side-scroller game in Pygame but I am running into trouble adjusting the ships speed and limiting the range. I am new to python and still in the learning process.

I keep getting an AttributeError: 'Ship' object has no attribute 'screen_rect'

Please I am stuck and need to understand what I'm doing wrong.

This is the code I have so far:

import pygame

class Ship:
    """A class to manage the ship."""

    def __init__(self, ai_game):
        """Initialize the ship and set its starting position."""
        self.screen = ai_game.screen
        self.settings = ai_game.settings

        # Load the ship image and get its rect.
        self.image = pygame.image.load('images/Blue-5.bmp')
        self.rect = self.image.get_rect()

        # Start each new ship at center left of screen.
        self.rect.midleft = self.screen_rect.midleft

        # Store a decimal value for the ship's horizontal position.
        self.x = float(self.rect.x)
        self.y = float(self.rect.y)


        # Movement Flag
        self.moving_right = False
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False

    def update(self):
        """Update the ship's position based on the movement flag."""
        # Update the ship's x value, not the rect
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.x += self.settings.ship_speed
        if self.moving_left and self.rect.left > 0:
            self.x -= self.settings.ship_speed
        if self.moving_up and self.rect.up < self.screen_rect.up:
            self.y -= self.settings.ship_speed
        if self.moving_down and self.rect.down > 0:
            self.y += self.settings.ship_speed

        # Update rect object from self.x.
        self.rect.x = self.x
        self.rect.y = self.y
    

    def blitme(self):
        """Draw the ship at its current location."""
        self.screen.blit(self.image, self.rect)

2

There are 2 best solutions below

0
On

Use pygame.time.Clock to control the frames per second and thus the game speed.

The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time.
That means that the loop:

clock = pygame.time.Clock()
run = True
while run:
    clock.tick(60)

    # [...]

runs 60 times per second.


pygame.time.Clock.tick returns the number of milliseconds passed since the previous call. If you call it in the application loop, then this is the number of milliseconds passed since the last frame.
Multiply the velocity of the player by the passed time per frame, to get a constant movement independent on the FPS.

For instance define the distance in number of pixel, which the player should move per second (move_per_second). Then compute the distance per frame in the application loop:

move_per_second = 500
FPS = 60
fun = True
clock = pygame.time.Clock()
while run:
    ms_frame = clock .tick(FPS)
    move_per_frame = move_per_second * ms_frame / 1000  

    # [...]
1
On

This might help you solve your issue. Do NOT remove or replace the self.screen_rect = ai_game.screen.get_rect() w/ self.settings = ai_game.settings (Keep both of them like I do)

ship.py:

import pygame

class Ship:
    """A Class to manage the ship."""

    def __init__(self, ai_game):
        """Initialize the ship and set its starting position."""
        *self.screen = ai_game.screen
        self.screen_rect = ai_game.screen.get_rect() # Keep this line
        self.settings = ai_game.settings # Add this line as require*

        # Load the ship image and get it react.
        self.image = pygame.image.load('image/ship.bmp')
        self.rect = self.image.get_rect()

        # Start each new ship at the bottom center of the screen.
        self.rect.midbottom = self.screen_rect.midbottom

        # Store a decimal value for the ship's horiontal & vertical position.
        self.x = float(self.rect.x)
        self.y = float(self.rect.y)

        # Movement flags
        self.moving_right = False
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False

    def update(self):
        """Update the ship's position based on the movement flags."""
        # Update the ship's x value, not the rect.
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.x += self.settings.ship_speed
        elif self.moving_left and self.rect.x > 0:
            self.x -= self.settings.ship_speed
        elif self.moving_up and self.rect.y > 0:
            self.y -= self.settings.ship_speed
        *elif self.moving_down and self.rect.bottom < self.screen_rect.bottom*: # Ship stop at bottom edge
            self.y += self.settings.ship_speed

        # Update rect object from self.x & self.y
        self.rect.x = self.x
        self.rect.y = self.y

    def blitme(self):
        """Draw the ship at its current location."""
        self.screen.blit(self.image, self.rect)

alien_invasion.py:

import sys
import pygame

from settings import Settings
from ship import Ship

class AlienInvasion:
    """Overall class to manage game assets and behavior."""

    def __init__(self):
        """Initialize the game, and create game resources."""
        pygame.init()
        self.settings = Settings()

        self.screen = pygame.display.set_mode(
            (self.settings.screen_width, self.settings.screen_height))
        pygame.display.set_caption("Alien Invasion")

        # Create ship
        self.ship = Ship(self)

    def run_game(self):
        """Start the main loop for the game."""
        while True:
            self._check_events()
            self.ship.update()
            self._update_screen()

    def _check_events(self):
        """Respond to keypresses and mouse events."""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    def _check_keydown_events(self, event):
        """Respond to keypresses."""
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_UP:
            self.ship.moving_up = True
        elif event.key == pygame.K_DOWN:
            self.ship.moving_down = True
        elif event.key == pygame.K_q:
            sys.exit()

    def _check_keyup_events(self, event):
        """Respond to key release."""
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False
        elif event.key == pygame.K_UP:
            self.ship.moving_up = False
        elif event.key == pygame.K_DOWN:
            self.ship.moving_down = False

    def _update_screen(self):
        """Update images on the screen, and flip to new screen."""
        self.screen.fill(self.settings.background_color)
        self.ship.blitme()

        # Make the most recently drawn screen visible.
        pygame.display.flip()

if __name__ == '__main__':
    # Make a game instance, and run the game
    ai = AlienInvasion()
    ai.run_game()