python game: lives_left drops too fast and game is over unexpectedly - pygame

Introduction: Different letters fall down from the top of the screen after a time interval and the letter will vanish while you strike the corresponding key on the keyboard. The x position of each letter is random. Game is over when more than 10 letters have reached the bottom.
Progress:I have implemented some functions such as generating random falling letters and eliminating letters by striking the corresponding keys.
Problem: Game should be over when more than 10 letters had reached the bottom. However, after running the codes, game is immediately over while the first falling letter has reached the bottom. I suppose there's something wrong with 'check_letter_bottom()' method in game_functions.py since it is observed that lives_left drops quickly from 10 to 0. What corrections should I make?
Here are my codes:
alphabet_zoo.py
import pygame
import time
from pygame.locals import *
from settings import Settings
import game_functions as gf
from game_stats import GameStats
def run_game():
pygame.init()
az_settings =Settings()
screen = pygame.display.set_mode((0,0), RESIZABLE)
pygame.display.set_caption("Alphabet Zoo")
stats = GameStats(az_settings)
letters = pygame.sprite.Group()
start = time.time()
sleepTime = 3
while True:
now = time.time()
gf.check_events(letters)
if stats.game_active:
print(stats.lives_left)
letters.update()
gf.update_screen(az_settings, stats, screen, letters)
if now - start >sleepTime:
gf.letter_generator(az_settings ,screen, letters)
start = now
run_game()
settings.py
class Settings():
def __init__(self):
self.bg_color = (0, 0, 0)
self.letter_speed_factor = 10
self.lives_limit = 10
game_functions.py
import sys
import pygame
from letter import Letter
def letter_generator(az_settings, screen, letters):
new_letter = Letter(az_settings, screen)
letters.add(new_letter)
def check_events(letters):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
for ltr in letters:
if ltr.ascii == event.key:
letters.remove(ltr)
def letter_fallen(stats):
if stats.lives_left > 0:
stats.lives_left -= 1
else:
stats.game_active = False
def check_letter_bottom(screen, letters, stats):
screen_rect = screen.get_rect()
for ltr in letters.sprites():
if ltr.rect.bottom >= screen_rect.bottom: # there might be some problems
letter_fallen(stats)
def update_screen(az_settings, stats, screen, letters):
screen.fill(az_settings.bg_color)
check_letter_bottom(screen, letters, stats)
letters.draw(screen)
letters.update()
pygame.display.flip()
letter.py
import pygame
import random
from pygame.sprite import Sprite
class Letter(Sprite):
def __init__(self, az_settings, screen):
super().__init__()
self.screen = screen
self.az_settings = az_settings
a = random.randint(97, 123)
c = chr(a)
name = c.upper() + '.png'
b = 'images/' + name
print(b)
self.image = pygame.image.load(b)
self.ascii = a
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.rect.centerx = random.randint(0, self.screen_rect.right)
self.rect.top = self.screen_rect.top
self.center = float(self.rect.centerx)
def update(self):
if self.rect.bottom < self.screen_rect.bottom:
self.rect.centery += self.az_settings.letter_speed_factor
game_stats.py
class GameStats():
def __init__(self, az_settings):
self.az_settings = az_settings
self.reset_stats()
self.game_active = True
def reset_stats(self):
self.lives_left = self.az_settings.lives_limit

If you want to display the letters after scoring them, you could remove them from the letters group and add them to a different group.
So in your run_game() function, add scored_letters = pygame.sprite.Group().
Then in check_letter_bottom(…)
def check_letter_bottom(screen, letters, scored_letters, stats):
screen_rect = screen.get_rect()
for ltr in letters.sprites():
if ltr.rect.bottom >= screen_rect.bottom:
letter_fallen(stats)
letters.remove(ltr)
scored_letters.add(ltr)
Then include scored_letters.draw(screen) in update_screen(…). Probably before letters.draw() so falling letters are on top.

I have found it is actually simple to solve my problem by adding ltr.rect.bottom = screen_rect.bottom before letter_fallen(stats) and making a little change of the if statement(> instead of >=). It's like this:
def check_letter_bottom(screen, letters, stats):
screen_rect = screen.get_rect()
for ltr in letters.sprites():
if ltr.rect.bottom > screen_rect.bottom:
ltr.rect.bottom = screen_rect.bottom
letter_fallen(stats)

Related

why doesn't play_button.rect.collidepoint(mouse_x , mouse_y) function?

This is a typing game in Python. Click the 'Play' button and the game is supposed to start. Random letters will fall down and each letter will disappear when you strike the corresponding key. However, it failed to begin when I click on the button. I think the problem must be related to the if statements if play_button.rect.collidepoint(mouse_x , mouse_y): in method check_play_button() of game_functions.py since if the if statement is removed, the stats.game_active is changed to True. On the other hand, if the if statement is removed, then click anywhere on the screen would change the value of stats.game_active to True. I am quite puzzled about the malfunction of the if statement. Please shed light on it.
Here are my codes( a bit long but a challenge:P):
alphabet_zoo.py
import pygame
import time
from pygame.locals import *
from settings import Settings
import game_functions as gf
from game_stats import GameStats
from button import Button
def run_game():
pygame.init()
az_settings =Settings()
screen = pygame.display.set_mode((0,0), RESIZABLE)
pygame.display.set_caption("Alphabet Zoo")
play_button = Button(screen, "Play")
stats = GameStats(az_settings)
letters = pygame.sprite.Group()
start = time.time()
sleepTime = 3
while True:
now = time.time()
gf.check_events(letters, stats, play_button)
if stats.game_active:
gf.update_screen(az_settings, stats, screen, letters, play_button)
if now - start >sleepTime:
gf.letter_generator(az_settings ,screen, letters)
start = now
else:
gf.update_screen(az_settings, stats, screen, letters, play_button)
run_game()
button.py
import pygame.font
class Button():
def __init__(self, screen, msg):
self.screen = screen
self.screen_rect = screen.get_rect()
self.width, self.height = 200, 50
self.button_color = (0, 0, 255)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
self.prep_msg(msg)
def prep_msg(self, msg):
self.msg_image = self.font.render(msg, True, self.text_color,
self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
settings.py
class Settings():
def __init__(self):
self.bg_color = (0, 0, 0)
self.letter_speed_factor = 10
self.lives_limit = 10
game_stats.py
class GameStats():
def __init__(self, az_settings):
self.az_settings = az_settings
self.reset_stats()
self.game_active = False
def reset_stats(self):
self.lives_left = self.az_settings.lives_limit
letter.py
import pygame
import random
from pygame.sprite import Sprite
class Letter(Sprite):
def __init__(self, az_settings, screen):
super().__init__()
self.screen = screen
self.az_settings = az_settings
a = random.randint(97, 122)
c = chr(a)
self.image = pygame.image.load('images/' + c.upper() + '.png')
self.ascii = a
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.rect.centerx = random.randint(0, self.screen_rect.right)
self.rect.top = self.screen_rect.top
self.center = float(self.rect.centerx)
def update(self):
if self.rect.bottom < self.screen_rect.bottom:
self.rect.centery += self.az_settings.letter_speed_factor
game_functions.py
import sys
import pygame
from letter import Letter
def letter_generator(az_settings, screen, letters):
new_letter = Letter(az_settings, screen)
letters.add(new_letter)
def check_events(letters, stats, play_button):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
check_play_button(stats, play_button, mouse_x, mouse_y)
print(stats.game_active) # for test
elif event.type == pygame.KEYDOWN:
for ltr in letters:
if ltr.ascii == event.key:
letters.remove(ltr)
def check_play_button(stats, play_button, mouse_x, mouse_y):
if play_button.rect.collidepoint(mouse_x , mouse_y):
stats.game_active = True
def letter_fallen(stats):
if stats.lives_left > 0:
stats.lives_left -= 1
else:
stats.game_active = False
def check_letter_bottom(screen, letters, stats):
screen_rect = screen.get_rect()
for ltr in letters.sprites():
if ltr.rect.bottom > screen_rect.bottom: # there might be some problems
ltr.rect.bottom = screen_rect.bottom
letter_fallen(stats)
def update_screen(az_settings, stats, screen, letters, play_button):
screen.fill(az_settings.bg_color)
check_letter_bottom(screen, letters, stats)
letters.draw(screen)
letters.update()
if not stats.game_active:
play_button.draw_button()
pygame.display.flip()
Obviously I cannot test this but I think your problem is that you are not assigning position to your play_button.rect. By default, rect's positions values are (0, 0). I had the same problem a little while ago, so you are read some more details in answer to this question. One way to fix the problem - in button class make a getRect method:
class Button:
def getRect(self):
return pygame.Rect(x, y, width, height) # Since you will
#be using this rect for collision, enter the arguments accordingly
Then,
if play_button.getRect().collidepoint(mouse_x , mouse_y):
Other option is to directly assign position to the button rect but I prefer the first solution, its just a personal preference:
class Button():
def __init__(self, screen, msg):
self.rect.topleft = #your button position

Python game: How to control the speed of generating A?

In this game, different letters fall down from the top of the screen after a time interval and the letter will vanish while you strike the corresponding key on the keyboard. The x position of each letter is random and the falling speed will accelerate as the game progress. Game will end under a certain condition(e.g. screen height is occupied by letters). This seems a great chanllenge for me. Thus, during the first stage, my codes are simplified for same letters 'A' rather than varied letters.
The question is how to control the speed of generating each 'A' in the game. At present, it is too fast. I've tried time.sleep(5) but it also stops the falling down of each letter.
Here are the codes:
alphabet_zoo.py
import sys
import pygame
from pygame.locals import *
from settings import Settings
from letter import Letter
import game_functions as gf
from pygame.sprite import Group
def run_game():
pygame.init()
az_settings =Settings()
screen = pygame.display.set_mode((0,0), RESIZABLE)
pygame.display.set_caption("Alphabet Zoo")
letters = pygame.sprite.Group()
while True:
gf.letter_generator(az_settings ,screen, letters)
gf.check_events(letters)
letters.update()
gf.update_screen(az_settings, screen, letters)
run_game()
settings.py
def __init__(self):
self.bg_color = (0, 0, 0)
self.letter_speed_factor = 1
game_functions.py
import sys
import pygame
import time
from letter import Letter
def letter_generator(az_settings, screen, letters):
# time.sleep(5)
# This setting not only slowdowns the generating of A
# but also stops the falling down of each A for a short time.
new_letter = Letter(az_settings, screen)
letters.add(new_letter)
def check_events(letters):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
letters.empty()
def update_screen(az_settings, screen, letters):
screen.fill(az_settings.bg_color)
letters.draw(screen)
letters.update()
pygame.display.flip()
letter.py
import pygame
import random
from pygame.sprite import Sprite
class Letter(Sprite):
def __init__(self, az_settings, screen):
super().__init__()
self.screen = screen
self.az_settings = az_settings
self.image = pygame.image.load('images/A.png')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.rect.centerx = random.randint(0, self.screen_rect.right)
self.rect.top = self.screen_rect.top
self.center = float(self.rect.centerx)
def update(self):
if self.rect.bottom < self.screen_rect.bottom:
self.rect.centery += self.az_settings.letter_speed_factor
How can I achieve my goal to control the speed of generating A?
You can look into time.time() function from time module. Example:
import time
start = time.time()
sleepTime = 1
while True:
print("This is being printed meaning the program isn't stopped")
now = time.time()
if now - start > sleepTime:
print("generate A in this area. This will only happen every one second.")
start = now

pygame position error

First of all ,I loaded a picture of the ship and initialized its location. thereafter I add bullet to my program. After that, I found that no matter how I debug it, it can't be in the right place.
# 1. - import library
import pygame,sys
from pygame.locals import *
from pygame.sprite import Sprite
class Player(Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('image/pig.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
class Bullet(Sprite):
def __init__(self, player):
super().__init__()
self.rect = pygame.Rect(0, 0, bullet_width, bullet_height )
self.color = bullet_color
self.rect.center = player.rect.center
self.rect.left = player.rect.right
# 2. - Initialize the game
pygame.init()
width,height = 800,600
screen = pygame.display.set_mode((width,height))
keys = [False,False,False,False]
playerpos = [0,288]
bullet_width = 15
bullet_height = 6
bullet_color = (200, 200 , 0)
player = Player()
bullet = Bullet(player)
grass = pygame.image.load("image/bg.bmp")
# 4. - keep looping through
while True:
# 5. - clear the screen before drawing it again.
screen.fill(0)
# 6. - Draw the screen elements.
screen.blit(grass,(0,0))
screen.blit(player.image, playerpos)
pygame.draw.rect(screen, bullet.color, bullet.rect)
# 7. - update the screen
pygame.display.flip()
# 8. - loop through the events
for event in pygame.event.get():
# check if the event is the X button.
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
and why bullet appear in top-left
enter image description here
I hope bullet appear in ship's right side,but I can't do it if I don't use coordinate(x,y),how can I do it?
You are drawing the ship in a position unrelated to its rect's position, using playerpos. You need to make the link the ship's position linked to its rect, so that the bullet can access it:
# 1. - import library
import pygame,sys
from pygame.locals import *
from pygame.sprite import Sprite
class Player(Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('image/pig.bmp')
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
class Bullet(Sprite):
def __init__(self, player):
super().__init__()
self.rect = pygame.Rect(0, 0, bullet_width, bullet_height )
self.color = bullet_color
self.rect.center = player.rect.center
self.rect.left = player.rect.right
# 2. - Initialize the game
pygame.init()
width,height = 800,600
screen = pygame.display.set_mode((width,height))
keys = [False,False,False,False]
bullet_width = 15
bullet_height = 6
bullet_color = (200, 200 , 0)
player = Player()
player.rect.topleft = [0,288]
bullet = Bullet(player)
grass = pygame.image.load("image/bg.bmp")
# 4. - keep looping through
while True:
# 5. - clear the screen before drawing it again.
screen.blit(grass, (0, 0))
# 6. - Draw the screen elements.
screen.blit(player.image, player.rect.topleft)
pygame.draw.rect(screen, bullet.color, bullet.rect)
# 7. - update the screen
pygame.display.flip()
# 8. - loop through the events
for event in pygame.event.get():
# check if the event is the X button.
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
This is because a surface's get_rect() method has no idea where the surface is going to be blitted on to another surface, so it just gives its position as (0, 0). get_rect() is only useful for obtaining a surface's dimensions.

Sprites do not behave as it supossed

I have a problem I'm stuck now for last two days. In file game_function in nested loop I'm creating aliens, when I added randint to create random numbers of aliens in a row I run in to problem. Not always (just re-run the game) but sometimes when I detect sprite edge as a method of Alien class Aliens/sprites won't change the direction, follow x axis to the right and constantly dropping down Aliens each pass of check edge. I don't know what the heck is that. Before when I haven't been using randit to generate random numbers of aliens in a row, everything was just fine.
settings.py
class Settings():
"""A class to store all settings for Alien Invasion"""
def __init__(self):
"""Initialize the game settings"""
#Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230,230,230)
#ship settings
self.ship_speed_factor = 1.5
#Bullet settings
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
self.bullets_allowed = 3
# Alien settings
self.alien_speed_factor = 1
self.fleet_drop_speed = 10
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
ship.py
import pygame
class Ship():
def __init__(self,ai_settings, screen):
"""Initialize the ship and sets the starting position."""
self.screen = screen
self.ai_settings = ai_settings
#load the ship image and get its rect
self.image = pygame.image.load('images/ship.png')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# Start each new ship at the bottom center of the screen
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.center = float(self.rect.centerx)
#Movement Flag
self.moving_right = False
self.moving_left = False
def update(self):
"""Update the ship's position based on the movement Flag."""
#Update the ship's center value, not the rect
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
#update rect object from self.center
self.rect.centerx = self.center
def blitme(self):
"""Draw the ship at its current location"""
self.screen.blit(self.image, self.rect)
In a function create_fleet(ai_settings, screen, ship, aliens) is nested loop, when instead of generating random number between 3-9 and then placing new instance in row I've used constant calculation which always gives 9 aliens in row everything runned just fine. Aliens changed everytime directions and just one time dropped down until next check_edge event passed the condition. So from one wall to another. Now when the randint is in place, NOT ALWAYS, when check_edge method confirms true, then call to function change_fleet_direction() is made and there I see problem, it just sometimes doesn't change the direction. where for direction is used just simple +1 or -1 and in calling for update in Alien class it should either decrease x axis or increase until edge event.
game_functions.py
import sys
import pygame
from bullet import Bullet
from alien import Alien
from random import randint
def create_fleet(ai_settings, screen, ship, aliens):
"""Create a full fleet of aliens."""
#Create an Alien and find the number of aliens in a row
name= 'First unused'
alien = Alien(ai_settings, screen,name)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height,
alien.rect.height)
# Create the fleet of aliens.
for row_number in range(number_rows):
random_num = randint(3, number_aliens_x)
for alien_number in range(0, random_num):
create_alien(ai_settings, screen, aliens, alien_number,
row_number)
def get_number_aliens_x(ai_settings, alien_width):
"""Determine the number of aliens that fit in a row."""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def get_number_rows(ai_settings, ship_height, alien_height):
"""Determine the number of rows of aliens that fit on the screen."""
available_space_y = (ai_settings.screen_height -
(3 * alien_height) - ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
"""Create alien and place it in the row"""
name = "Alien number " + str(alien_number) + " in row " + str(row_number)
alien = Alien(ai_settings, screen, name)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""Respond to key presses"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit()
def fire_bullet(ai_settings, screen, ship, bullets):
# Create a new bullet and add it to the bullets group.
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event,ship):
"""Respond to key releases"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, ship, bullets):
"""Respond to keypress and mouse events"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship, aliens, bullets):
"""Update images on the screen and flip to the new screen."""
# Redraw the screen during each pass through the loop.
screen.fill(ai_settings.bg_color)
# Redraw all bullets behind ship and aliens.
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# Make the most recently drawn screen visible.
pygame.display.flip()
def update_bullets(bullets):
"""Update position of bullets and get rid of old bullets."""
# Update bullet positions.
bullets.update()
# Get rid of bullets that have disappeared.
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def check_fleet_edges(ai_settings, aliens):
"""Respond appropriately if any aliens have reached an edge."""
for alien in aliens.sprites():
if alien.check_edges():
print(alien.name)
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
"""Drop the entire fleet and change the fleet's direction."""
print("old direction " + str(ai_settings.fleet_direction))
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
if ai_settings.fleet_direction == 1:
ai_settings.fleet_direction = -1
else:
ai_settings.fleet_direction = 1
print("new direction" + str(ai_settings.fleet_direction))
def update_aliens(ai_settings, aliens):
"""
Check if the fleet is at an edge,
and then update the positions of all aliens in the fleet.
"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
Main file alien_invasion.py
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
# Initialize game and create a screen object.
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
ship = Ship(ai_settings,screen)
#Make a group to store bullets in
bullets = Group()
aliens = Group()
#Create the fleet of aliens
gf.create_fleet(ai_settings, screen, ship, aliens)
# Start the main loop for the game.
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
gf.update_bullets(bullets)
gf.update_aliens(ai_settings, aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage bullets fired from the ship"""
def __init__(self, ai_settings, screen, ship):
"""create a bullet object at the ship's current position"""
super().__init__()
self.screen = screen
#Create a bullet rect at (0, 0) and then set correct position
self.rect = pygame.Rect(0, 0, ai_settings.bullet_width,
ai_settings.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
#Store the bullet position as a decimal value.
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
def update(self):
"""Move bullet up the scereen"""
#Update the decimal position of the bullet
self.y -= self.speed_factor
#Update the rect position
self.rect.y = self.y
def draw_bullet(self):
"""Draw the bullet to the screen"""
pygame.draw.rect(self.screen, self.color, self.rect)
alien.py
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""A class to represent a single alien in the fleet"""
def __init__(self, ai_settings, screen, name):
"""Initialize the alien and set its starting position"""
super().__init__()
self.screen = screen
self.ai_settings = ai_settings
#Load the alien image and set its rect attribute.
self.image = pygame.image.load('images/alien.bmp')
self.rect = self.image.get_rect()
#Start each alien near the top left of the screen
self.rect.x = self.rect.width
self.rect.y = self.rect.height
#Store the alien's exact position
self.x = float(self.rect.x)
self.name = name
# def blitme(self):
# """Draw the alien at its current location."""
# self.screen.blit(self.image, self.rect)
#
def check_edges(self):
"""Return True if alien is at edge of screen."""
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:
print("Right >= right screen" +' direction is' + str(self.ai_settings.fleet_direction))
return True
elif self.rect.left <= 0:
print("Left <= left")
return True
def update(self):
"""Move the alien right or left."""
self.x += (self.ai_settings.alien_speed_factor *
self.ai_settings.fleet_direction)
self.rect.x = self.x
I'm new to pygame and sprites, I don't understand it why randit has such impact. I've placed in the code when crucial changes are about to be made print statements to debug it from console (but I dunno if it is logic problem). Anyone who can shed a light to it I would appreciate so much. Lost 2 days of learning and programming already. Running python 3.4 from Eclipse Neon on win 10 Thanks very much.
Found the mistake, indentation in func change_fleet_direction(), if block: cannot be in for loop.

pong in pygame: ball doesnt bounce

I'm making a pong in pygame, but I have problem. When the ball collides with a paddle, it doesn't bounce off it. Here's my code:
main file:
import pygame as py
import game_functions as gf
from game_objects import Platform, Platform2, Ball
from settings import Settings
def run_game():
py.init()
ai_settings = Settings()
screen = py.display.set_mode((
ai_settings.screen_width, ai_settings.screen_height
))
py.display.set_caption('Pong')
platform = Platform(ai_settings, screen)
platform2 = Platform2(ai_settings, screen)
ball = Ball(ai_settings, screen)
while True:
gf.check_events(platform, platform2, ball)
platform.update(), platform2.update(), ball.update()
gf.collide_check(ball, platform, platform2, ai_settings)
gf.update_screen(ai_settings, screen, platform, platform2, ball)
run_game()
class for ball:
class Ball:
def __init__(self, ai_settings, screen):
self.ai_settings = ai_settings
self.screen = screen
self.screen_rect = screen.get_rect()
self.image = py.image.load('pics/ball.png')
self.rect = self.image.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.centery = self.screen_rect.centery
self.moving = False
def update(self):
if self.moving:
self.rect.centerx -= self.ai_settings.ball_speed
def draw_ball(self):
self.screen.blit(self.image, self.rect)
and here is my function to change direction:
def collide_check(ball, platform, platform2, ai_settings):
if ball.rect.colliderect(platform.rect):
ball.rect.centerx += ai_settings.ball_speed
elif ball.rect.colliderect(platform2.rect):
ball.rect.centerx -= ai_settings.ball_speed
Thanks a lot for help :/
P.S. move flag self.moving will change to true if any key for paddle move is pressed.
Your update function is not fully complete as you need to add:
def update(self):
self.collide_check(ball, platform, platform2, ai_settings)
if self.moving:
self.rect.centerx -= self.ai_settings.ball_speed
So that your code updates the direction of the ball. Since it is now in the update section it will check for collisions every time the update cycle happens.
As a result of this you probably need to add the
collide_check()
function into the ball class.
I should also add that:
if ball.rect.colliderect(platform.rect):
ball.rect.centerx += ai_settings.ball_speed
Should instead be:
if ball.rect.colliderect(platform.rect):
ai_settings.ball_speed * -1
That way it makes ai_settings.ball_speed go in the opposite direction, as n * -1 = -1 and -n * -1 = 1.