Multidimensional Array in Python and Pygame - python

As you might probably be able to guess, I'm quite new to Python! I'm trying to write a (very) simple Space Invaders game. Nothing flashy. No sounds, no explosions, not even any scores tracking (although, at some point, the aliens will move and shoot!).
Right now though the problem with my code is that I can shoot (and destroy) any aliens in the bottom row - but I can't destroy aliens in any other row. It's most perplexing - and I'd welcome any advice that anyone can offer.
This is my code:
# !/usr/bin/python
import pygame
bulletDelay = 40
class Bullet(object):
def __init__(self, xpos, ypos):
self.image = pygame.image.load("bullet.bmp")
self.rect = self.image.get_rect()
self.x = xpos
self.y = ypos
def current_position(self):
return [self.x, self.y]
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
class Player(object):
def __init__(self, screen):
self.image = pygame.image.load("spaceship.bmp") # load the spaceship image
self.rect = self.image.get_rect() # get the size of the spaceship
size = screen.get_rect()
self.x = (size.width * 0.5) - (self.rect.width * 0.5) # draw the spaceship in the horizontal middle
self.y = size.height - self.rect.height # draw the spaceship at the bottom
def current_position(self):
return self.x
def draw(self, surface):
surface.blit(self.image, (self.x, self.y)) # blit to the player position
class Alien(object):
def __init__(self, xpos, ypos):
self.image = pygame.image.load("alien.bmp") # load the alien image
self.rect = self.image.get_rect() # get the size of the alien
self.x = xpos
self.y = ypos
def current_position(self):
return [self.x, self.y]
def draw(self, surface):
surface.blit(self.image, (self.x, self.y)) # blit to the player position
def collision(alien,bullet):
if ((alien.current_position()[0] < bullet.current_position()[0] + 10) and
(alien.current_position()[0] > bullet.current_position()[0] - 10) and
(alien.current_position()[1] == bullet.current_position()[1])):
return True
else:
return False
def destroyObject(objectArray,killList):
if len(killList) > 0: # remove any bullets that have hit an alien
for item in killList:
del objectArray[item]
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
player = Player(screen) # create the player sprite
missiles = [] # create missile array
aliensW = 6
aliensH = 3
# layout the initial field of aliens
aliens = [[Alien((screen.get_rect().width/7)*(x+0.75),(screen.get_rect().height/5)*(y+0.5)) for x in range(aliensW)] for y in range(aliensH)]
running = True
counter = bulletDelay
while running: # the event loop
counter=counter+1
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
key = pygame.key.get_pressed()
dist = 5 # distance moved for each key press
if key[pygame.K_RIGHT]: # right key
player.x += dist
elif key[pygame.K_LEFT]: # left key
player.x -= dist
elif key[pygame.K_SPACE]: # fire key
if counter > bulletDelay:
missiles.append(Bullet(player.current_position(),screen.get_rect().height))
counter=0
screen.fill((255, 255, 255)) # fill the screen with white - without this the old graphics will remain onscreen.
for m in missiles:
if m.y < (screen.get_rect()).height+1 and m.y > 0:
m.draw(screen)
m.y -= 5
else:
missiles.pop(0)
alienGrid = []
for a in aliens:
killList=[]
spentBullets=[]
alienNumber=0
alienRow = a
for b in alienRow:
#need to move aliens as well!
missileNumber=0
for m in missiles:
if (collision(b,m)):
killList.insert(0,alienNumber)
spentBullets.insert(0,missileNumber)
missileNumber+=1
alienNumber += 1
b.draw(screen)
destroyObject(alienRow,killList)
destroyObject(missiles,spentBullets)
alienGrid.insert(0,alienRow)
aliens = alienGrid
player.draw(screen) # draw the spaceship to the screen
pygame.display.update() # update the screen
clock.tick(40)
Of course, I also welcome any other advice that you might be able to offer for improving my code!

Use print() to check values in variables and which part of code is executed. This way you can find problem (if you don't know how to use debuger)
Problem is
alien.current_position()[1] == bullet.current_position()[1]
which not always is true because missile moves 5 pixels, not 1px.
Besides you use 0.75 and 0.5 in calculations of alien positions so you have float values.
You could use self.rect to keep position and then you could use
def collision(alien, bullet):
return alien.rect.colliderect(bullet.rect)
and
def draw(self, surface):
surface.blit(self.image, self.rect)
My version with many modifications. I use class inherition. And I use time (instead of counting frame) to control missiles.
# !/usr/bin/python
import pygame
# --- constants ---
BULLET_DELAY = 1000 # 1000ms = 1s
BULLET_DIST = 5 # distance moved for each key press
RED = (255, 0, 0)
WHITE = (255, 255, 255)
FPS = 40
# --- classes ---
class Sprite(object):
def __init__(self, xpos, ypos, image=None):
if image:
self.image = pygame.image.load(image)
else:
self.image = pygame.Surface(50,50)
self.image.fill(RED)
self.rect = self.image.get_rect(x=xpos, y=ypos)
def current_position(self):
return self.rect
def draw(self, surface):
surface.blit(self.image, self.rect)
class Bullet(Sprite):
def __init__(self, xpos, ypos):
Sprite.__init__(self, xpos, ypos, "bullet.bmp")
# modificate position
self.rect.centerx = xpos
self.rect.bottom = ypos
class Alien(Sprite):
def __init__(self, xpos, ypos):
Sprite.__init__(self, xpos, ypos, "alien.bmp")
class Player(Sprite):
def __init__(self, xpos, ypos):
Sprite.__init__(self, xpos, ypos, "spaceship.bmp")
# modificate position
self.rect.centerx = xpos
self.rect.bottom = ypos
# --- main ---
pygame.init()
screen = pygame.display.set_mode((640, 480))
screen_rect = screen.get_rect()
# -
player = Player(screen_rect.centerx, screen_rect.bottom) # create the player sprite
# -
missiles = [] # create missile array
# -
aliensW = 6
aliensH = 3
# layout the initial field of aliens
sx = screen_rect.width/7
sy = screen_rect.width/7
aliens = [[Alien(sx*(x+0.75),sy*(y+0.5)) for x in range(aliensW)] for y in range(aliensH)]
# move direction
move_left = False
# ---
current_time = pygame.time.get_ticks()
# missile delay
next_missile_time = current_time
# --- mainloop ---
clock = pygame.time.Clock()
running = True
while running: # the event loop
current_time = pygame.time.get_ticks()
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
key = pygame.key.get_pressed()
if key[pygame.K_RIGHT]: # right key
player.rect.x += BULLET_DIST
if player.rect.right > screen_rect.right:
player.rect.right = screen_rect.right
if key[pygame.K_LEFT]: # left key
player.rect.x -= BULLET_DIST
if player.rect.left < screen_rect.left:
player.rect.left = screen_rect.left
if key[pygame.K_SPACE]: # fire key
if current_time >= next_missile_time:
print('[D] fire')
missiles.append(Bullet(player.rect.centerx, player.rect.top))
# missile delay
next_missile_time = current_time + BULLET_DELAY
# --- updates (without draws) ---
# move missiles (and remove if leaves screen)
temp = []
for m in missiles:
m.rect.y -= BULLET_DIST
if m.rect.bottom >= 0:
temp.append(m)
missiles = temp
# move aliens (and check collisio)
temp = []
next_direction = move_left
for row in aliens:
temp_row = []
for a in row:
if move_left:
# move
a.rect.x -= 1
# check collision
for i, m in enumerate(missiles):
if a.rect.colliderect(m.rect):
# remove missile
del missiles[i]
# don't check collisions with other misilles
break
else: # chech only if "no break" so "no collide"
# chech if change direction
if a.rect.left == screen_rect.left:
# need to change direction but don't change `move_left` yet
next_direction = False
# add if not collide
temp_row.append(a)
else:
# move
a.rect.x += 1
# check collision
for i, m in enumerate(missiles):
if a.rect.colliderect(m.rect):
# remove missile
del missiles[i]
# don't check collisions with other misilles
break
else: # chech only if "no break" so "no collide"
# chech if change direction
if a.rect.right == screen_rect.right:
# need to change direction but don't change `move_left` yet
next_direction = True
# add if not collide
temp_row.append(a)
temp.append(temp_row)
# change after all checkings
move_left = next_direction
# new list without hitted aliens
aliens = temp
# --- draws (without updates) ---
screen.fill(WHITE) # fill the screen with white - without this the old graphics will remain onscreen.
for row in aliens:
for a in row:
a.draw(screen)
for m in missiles:
m.draw(screen)
player.draw(screen) # draw the spaceship to the screen
pygame.display.update() # update the screen
# --- FPS ---
clock.tick(FPS)

Related

Creating a Asteroid type game, problem is when player waits to press play button many asteroids spawn instead of controlled amount

About 500 lines of code, so to make it easier to read I believe the bug comes from _update_asteroids(self) function. Simply put, when the user is in the paused game state and must press play, if the player decides to wait lets say a minute. A minutes worth of asteroids will spawn at the exact same time. I believe this is because the pygame.time.get_ticks() function continues to gain ticks while game is paused. Is there any way to reset the ticks or make it so that 100s of asteroids do not spawn when the user decides to wait before pressing play button?
import pygame
import sys
from pygame.sprite import Sprite
import random
from time import sleep
import pygame.font
class Game(Sprite):
""" a class the creates a window with a blue screen """
def __init__(self):
pygame.init()
super().__init__()
#--------------------------------------------------------------------------------------------
#screen size, color, caption
self.screen = pygame.display.set_mode((1200,800)) #create attribute to hold display settings
self.bg_color = (0,0,255) #create attribute to hold RGB color (blue)
pygame.display.set_caption("Blue Screen")
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#tank drawing
self.screen_rect = self.screen.get_rect() #get the screen rect dim
self.image = pygame.image.load('images/tank.gif') #load the image from directory
self.rect = self.image.get_rect() #get the image rect dim
self.rect.center = self.screen_rect.center #store the screens center x/y coord
self.x = float(self.rect.x)
self.y = float(self.rect.y)
#tank movement
self.tank_moving_left = False
self.tank_moving_right = False
self.tank_moving_up = False
self.tank_moving_down = False
self.tank_speed = 0.5 #tank pixels
self.direction_right = self.image #holds right image
self.direction_left = pygame.transform.flip(self.image, True, False) #holds left image
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#UI
self.health = 100
self.visable = True
self.sb = Scoreboard(self)
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#bullet
self.bullets = pygame.sprite.Group()
self.current_direction = self.direction_right
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#asteroids
self.asteroids = pygame.sprite.Group()
self.next_object_time = 0
self.time_interval = 250
#self._create_asteroids()
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#button
self.game_active = False
self.play_button = Button(self, "Play")
#--------------------------------------------------------------------------------------------
def _create_asteroids(self):
""" create the asteroid shower """
#create a asteroid
number_of_aliens = 1
for asteroid_num in range(number_of_aliens):
self._create_asteroid()
def _create_asteroid(self):
asteroid = Asteroid(self)
if asteroid.x <= 0:
asteroid.direction = 1
elif asteroid.x >= 1160:
asteroid.direction = -1
self.asteroids.add(asteroid)
def move(self):
""" move tnak tank_speed based on direction of movement (key pressed)
also detect collision """
if self.tank_moving_right and self.rect.right < self.screen_rect.right:
self.x += self.tank_speed
self.rect.x = self.x
if self.tank_moving_left and self.rect.left > self.screen_rect.left:
self.x -= self.tank_speed
self.rect.x = self.x
if self.tank_moving_down and self.rect.bottom < self.screen_rect.bottom:
self.y += self.tank_speed
self.rect.y = self.y
if self.tank_moving_up and self.rect.top > self.screen_rect.top:
self.y -= self.tank_speed
self.rect.y = self.y
def draw_healthbar(self):
pygame.draw.rect(self.screen, (255,0,0), (self.rect.x, self.rect.y - 20, 100, 10))
pygame.draw.rect(self.screen, (0,255,0), (self.rect.x, self.rect.y - 20, 100 - (100 - self.health), 10))
def blitme(self):
""" draw the image of the tank """
self.screen.blit(self.image, self.rect)
def tank_asteroid_collision(self):
#get rid of asteroids that collide with tank
for asteroid in self.asteroids.copy():
if pygame.Rect.colliderect(self.rect, asteroid.rect):
if self.health > 25:
self.health -= 25
self.asteroids.remove(asteroid)
print(self.health)
else:
self._tank_death()
self.sb.reset_score()
self.game_active = False
pygame.mouse.set_visible(True)
def _update_screen(self):
""" update screen """
self.screen.fill(self.bg_color)
self.blitme()
for bullet in self.bullets.sprites():
self.bullets.draw(self.screen)
collisions = pygame.sprite.groupcollide(self.bullets, self.asteroids, True, True)
if collisions:
for asteroids in collisions.values():
self.sb.score += 100 * len(asteroids)
self.sb.prep_score()
self.sb.check_high_score()
#draw healthbar if game active
if self.game_active:
self.draw_healthbar()
self.asteroids.draw(self.screen)
#draw the play button and other buttons if game is paused
if not self.game_active:
self.play_button.draw_button()
self.sb.show_score()
pygame.display.flip()
def _tank_death(self):
sleep(0.5)
self.bullets.empty()
self.asteroids.empty()
self.center_ship()
self.health = 100
def _check_KEYDOWN(self, event):
""" when key is press either quit, or move direction of arrow pressed and flip image """
if event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_RIGHT:
self.tank_moving_right = True
self.image = self.direction_right
self.current_direction = self.direction_right
elif event.key == pygame.K_LEFT:
self.tank_moving_left = True
self.image = self.direction_left
self.current_direction = self.direction_left
elif event.key == pygame.K_UP:
self.tank_moving_up = True
elif event.key == pygame.K_DOWN:
self.tank_moving_down = True
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_KEYUP(self, event):
""" when key is let go stop moving """
if event.key == pygame.K_RIGHT:
self.tank_moving_right = False
elif event.key == pygame.K_LEFT:
self.tank_moving_left = False
elif event.key == pygame.K_UP:
self.tank_moving_up = False
elif event.key == pygame.K_DOWN:
self.tank_moving_down = False
def _fire_bullet(self):
""" create a bullet and add it to the bullets group """
if self.current_direction == self.direction_left:
#create new bullet and set bullet path
new_bullet = Bullet(self)
new_bullet.bullet_shootRight = False
new_bullet.bullet_shootLeft = True
#change direction of bullet starting point
new_bullet.x -= 100
#add bullet to sprite list
self.bullets.add(new_bullet)
elif self.current_direction == self.direction_right:
#create new bullet and set bullet path
new_bullet = Bullet(self)
new_bullet.bullet_shootRight = True
new_bullet.bullet_shootLeft = False
#add bullet to sprite list
self.bullets.add(new_bullet)
def center_ship(self):
""" centers the ship in middle of screen """
self.rect.center = self.screen_rect.center
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def _update_tank(self):
""" move tank or quit game based on keypress """
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_KEYDOWN(event)
elif event.type == pygame.KEYUP:
self._check_KEYUP(event)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)
def _check_play_button(self, mouse_pos):
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and self.play_button.rect.collidepoint(mouse_pos):
self.game_active = True
pygame.mouse.set_visible(False)
def _update_asteroids(self):
""" afer 'x' milliseconds create a new asteroid """
current_time = pygame.time.get_ticks()
if current_time > self.next_object_time:
self.next_object_time += self.time_interval
self._create_asteroids()
def run_game(self):
""" loops the game/ updates screen/ checks for key clicks"""
while True:
self._update_tank()
if self.game_active:
self.move()
self.bullets.update()
self.tank_asteroid_collision()
#after certain number of time create a asteroid
self._update_asteroids()
self.asteroids.update()
#get rid of asteroids that have disapeared
for asteroid in self.asteroids.copy():
if asteroid.x >= 1200 or asteroid.x <= 0:
self.asteroids.remove(asteroid)
#get rid of bullets that have disapeared
for bullet in self.bullets.copy():
if bullet.rect.left >= 1200 or bullet.rect.right <= 0:
self.bullets.remove(bullet)
self._update_screen()
class Bullet(Sprite):
""" A class to manage bullets fired from the ship """
def __init__(self, game):
""" create a bullet object at the ships current position """
super().__init__()
self.screen = game.screen
self.bullet_speed = 2.0
self.bullet_width = 20
self.bullet_height = 5
self.bullet_color = (0, 200, 200)
self.bullet_shootRight = False
self.bullet_shootLeft = False
self.image = pygame.Surface((self.bullet_width, self.bullet_height))
self.image.fill(self.bullet_color)
self.rect = self.image.get_rect()
self.rect.midright = game.rect.midright
self.rect.y -= 5 #the tanks barrel is 5 pixels above center
self.rect.x += 15
#store the bullets position as a decimal value
self.y = float(self.rect.y)
self.x = float(self.rect.x)
def update(self):
""" move the bullet up the screen """
#update the decimal position of the bullet.
if self.bullet_shootRight:
self.x += self.bullet_speed
self.rect.x = self.x
elif self.bullet_shootLeft:
self.x -= self.bullet_speed
self.rect.x = self.x
class Asteroid(Sprite):
""" a class that represents a single asteroid """
def __init__(self, game):
""" initialize the asteroid and set its starting position """
super().__init__()
self.screen = game.screen
self.speed = 0.1
self.direction = 1
#load the asteroid image onto the screen
self.image = pygame.image.load('images/asteroid.gif')
self.rect = self.image.get_rect()
#start each new asteroid in a random part just outside the map on either the left or right
self.rect.x = random.randint(*random.choice([(0, 0), (1160, 1160)]))
self.rect.y = random.randint(0, 760)
#store the asteroid's exact horizontal positon
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def update(self):
""" move the asteroid to the right or left """
self.x += self.speed * self.direction
self.rect.x = self.x
self.y += (self.speed / 3) * self.direction
self.rect.y = self.y
class Button:
def __init__(self, game, msg):
""" Initialize button """
self.screen = game.screen
self.screen_rect = self.screen.get_rect()
#set the dimensions and properties of the button
self.width, self.height = 200, 50
self.button_color = (0, 255, 0)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
#Build the button's rect object and center it.
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):
""" Turn msg into a rendered image and center text on the button """
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):
""" draw blank button then draw message """
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
class Scoreboard:
""" A class to report scoring information """
def __init__(self, game):
""" initialize scorekeeping attributes """
self.screen = game.screen
self.screen_rect = self.screen.get_rect()
self.score = 0
self.highscore = 0
# Font settings for scoring information
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)
self.prep_score()
self.prep_highscore()
def reset_score(self):
self.score = 0
self.prep_score()
def prep_highscore(self):
""" Turn the high score into a rendered image. """
high_score = round(self.highscore, -1)
high_score_str = "{:,}".format(high_score)
self.high_score_image = self.font.render(high_score_str, True, self.text_color, (255, 255, 255))
#center the high score at the top of the screen
self.high_score_rect = self.high_score_image.get_rect()
self.high_score_rect.centerx = self.screen_rect.centerx
self.high_score_rect.top = self.score_rect.top
def check_high_score(self):
""" check to see if there's a new high score """
if self.score > self.highscore:
self.highscore = self.score
self.prep_highscore()
def prep_score(self):
""" Turn the score into a renered image """
rounded_score = round(self.score, -1)
score_str = "{:,}".format(rounded_score)
self.score_image = self.font.render(score_str, True, self.text_color, (255, 255, 255))
#Display the score at the top of the screen
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right
self.score_rect.top = 0
def show_score(self):
""" draw the score to the screen """
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
if __name__ == '__main__':
a = Game()
a.run_game()
The spawning of the asteroids depends on the next_object_time. next_object_time is initialized with 0. You need to set self.next_object_time when the play button is pressed:
class Game(Sprite):
# [...]
def _check_play_button(self, mouse_pos):
if self.play_button.rect.collidepoint(mouse_pos):
self.game_active = True
pygame.mouse.set_visible(False)
self.next_object_time = pygame.time.get_ticks() + self.time_interval
To make the algorithm more robust you can set next_object_time depending on the current time when an asteroid spawns (this is optional):
class Game(Sprite):
# [...]
def _update_asteroids(self):
""" afer 'x' milliseconds create a new asteroid """
current_time = pygame.time.get_ticks()
if current_time > self.next_object_time:
# self.next_object_time += self.time_interval
self.next_object_time = current_time + self.time_interval
self._create_asteroids()

Moving bullet in Pygame

I'm making a simple game in pygame, in which you're supposed to dodge or shoot the targets that descend through the screen. So far, I've created the ship and managed to set the movement of both the bullet and the ship, as for their key bindings. However, the bullet's x coordinate doesn't seem to change at all, even though I've defined in the init method in the bullet class that the bullet x coordinate is equal to the ship x coordinate, therefore, the bullet would always be leaving the ship's position. Instead, the bullet always leaves the middle of the screen. I can't find the issue. Any help would be very much appreciated
import pygame, sys
pygame.init()
clock = pygame.time.Clock()
screen_width = 600
screen_height = 800
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Space Race Game")
class ROCKET:
def __init__(self):
self.rocketImg = pygame.image.load("spaceship.png")
self.rocket_x = screen_width/2 - 32
self.rocket_y = screen_height/2 + 100
def draw_rocket(self):
screen.blit(self.rocketImg, (self.rocket_x, self.rocket_y))
def move_rocket(self):
key = pygame.key.get_pressed()
if key[pygame.K_LEFT] and self.rocket_x + 15 > 0:
self.rocket_x -= 5
if key[pygame.K_RIGHT] and self.rocket_x < screen_width - 40:
self.rocket_x += 5
class BULLET(ROCKET):
def __init__(self):
super().__init__()
self.bullet_width = 10
self.bullet_height = 20
self.bullet_x = self.rocket_x + 25
self.bullet_y = self.rocket_y
self.move = [0, 0]
self.bullet_speed = 5
self.bullet_rect = pygame.Rect(self.bullet_x, self.bullet_y, self.bullet_width, self.bullet_height)
def draw_bullet(self):
key = pygame.key.get_pressed()
if key[pygame.K_SPACE]:
self.bullet_x = self.rocket_x
self.move[1] = -1
self.bullet_y += self.move[1] * self.bullet_speed
self.bullet_rect.topleft = (self.bullet_x, self.bullet_y)
if self.bullet_y < self.rocket_y - 10:
pygame.draw.rect(screen, (0, 0, 0), self.bullet_rect)
if self.bullet_y < - 20:
self.bullet_y = self.rocket_y
self.move[1] = 0
rocket = ROCKET()
bullet = BULLET()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill((255, 255, 255))
rocket.draw_rocket()
rocket.move_rocket()
bullet.draw_bullet()
pygame.display.flip()
clock.tick(60)
You have to set the x-coordinate of the bullet by the current x-coordinate of the rocket.
Add an argument rocket to the method draw_bullet of the class BULLET. Make sure that you can only fire the bullet if the bullet has not yet been fired (self.move[1] == 0). Compute the center of the rocket and set the position of the bullet (rocket.rocket_x + self.rocketImg.get_width() // 2):
class BULLET(ROCKET):
# [...]
def draw_bullet(self, rocket):
key = pygame.key.get_pressed()
if key[pygame.K_SPACE] and self.move[1] == 0:
rocket_center_x = rocket.rocket_x + self.rocketImg.get_width() // 2
self.bullet_x = rocket_center_x - self.bullet_width // 2
self.move[1] = -1
# [...]
Passe the instance of ROCKET to the method draw_bullet:
while True:
# [...]
bullet.draw_bullet(rocket)
# [...]
If you want to fire multiple bullets, see the answers to the questions:
How can i shoot a bullet with space bar?
How do I stop more than 1 bullet firing at once?

enemy class overlap with each other

import pygame
import os
import random
from pygame.locals import * # Constants
import math
import sys
import random
pygame.init()
screen=pygame.display.set_mode((1280,700)) #(length,height)
screen_rect=screen.get_rect()
background = pygame.Surface(screen.get_size())
background.fill((255,255,255)) # fill the background white
background = pygame.image.load('stage.png').convert()
Black=(0,0,0)
class Player(pygame.sprite.Sprite):
x = 20
y = 615
def __init__(self):
super().__init__() # calls the parent class allowing sprite to initalize
self.image = pygame.Surface((50,25)) # this is used to create a blank image with the size inputted
self.image.fill((0,0,128)) # fills the blank image with colour
self.rect = self.image.get_rect(topleft =(20,615)) # This place the player at the given position
self.dist = 10
def update(self): # code to make the character move when the arrow keys are pressed
if self.rect.right > 100: # These are to make the player so move constantly
self.rect.y += 1
self.rect.x += 2
if self.rect.bottom == 700:
pygame.quit()
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
self.rect.move_ip(-1,0)
elif keys[K_RIGHT]:
self.rect.move_ip(0.5,0)
elif keys[K_UP]:
self.rect.move_ip(0,-0.5)
elif keys[K_DOWN]:
self.rect.move_ip(0,1)
self.rect.clamp_ip(screen_rect)
#while self.rect == (20,615):
if keys [K_SPACE]:
self.rect = self.image.get_rect(topleft =(100,100))
class Enemy(pygame.sprite.Sprite): # the enemy class which works fine
def __init__(self):
super().__init__()
x = random.randint(50,450)
self.image = pygame.Surface((50,25))
self.image.fill((128,0,0))
self.rect = self.image.get_rect(topleft (300, 50))
self.direction = 0
def update(self):
self.rect.y += 2 if self.direction == 0 else -2
if self.rect.bottom >= 600:
self.direction = 1
if self.rect.top <= 50:
self.direction = 0
clock = pygame.time.Clock() # A clock to limit the frame rate.
player = Player()
enemy = Enemy()
enemy_list = pygame.sprite.Group() # a group where the enemys will be put
sprites = pygame.sprite.Group(player) # The group where evry spirte will be put into
for i in range (5): # creates 5 enemy spirtes
enemy = Enemy() # calls the enemy class
enemy.rect.x = random.randrange(200, 1100) # makes the enemny spawn random
enemy.rect.y = random.randrange(50, 600)
enemy_list.add(enemy) # adds the enemy to the group
sprites.add(enemy)
I got the code so that the enemies will randomly and then they will move up and down however since the spawning is random they will sometimes overlap i was wondering how i would get it so that they don't overlap when they move up and down
i was wondering if i could do it so they have a gap when they spawns e.g. 50 in x axis but still spawn five enemies
def main(): #my main loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
sprites.update()
screen.blit(background, (0, 0))
sprites.draw(screen)
clock.tick(100) # Limit the frame rate to 60 FPS.
pygame.display.flip() #updates the whole screen
#Collison check
player_hit_list = pygame.sprite.spritecollide(player, enemy_list, True)
for enemy in player_hit_list:
pygame.quit()
if __name__ == '__main__':
main()
Try this approach (I've purposely made the code a little verbose to try to explain what's going on):
...
number_of_enemies = 5
min_enemy_x = 200
max_enemy_x = 1100
enemy_x_range = max_enemy_x - min_enemy_x # zone in which enemies can spawn
enemy_zone_width = enemy_x_range / number_of_enemies # zone width for each enemy
pixel_buffer = 50 # gap between enemies
for i in range (number_of_enemies): # creates 5 enemy spirtes
enemy = Enemy() # calls the enemy class
min_x = min_enemy_x + enemy_zone_width * i + pixel_buffer / 2 # minimum x at which current enemy can spawn (including buffer)
max_x = min_enemy_x + enemy_zone_width * (i + 1) - pixel_buffer / 2 # maximum x at which current enemy can spawn (including buffer)
enemy.rect.x = random.randrange(min_x, max_x) # makes the enemy spawn random
enemy.rect.y = random.randrange(50, 600)
enemy_list.add(enemy) # adds the enemy to the group
...
Note that this will produce a 25 pixel buffer at the start and end (so enemies will actually spawn between 225 and 1075), but you can either adjust min_enemy_x and max_enemy_x to compensate, or remove the buffering for the first and last loop iterations.

Pygame: Adding a background

I'm making a project for my senior year. Its a game where i can move a user. I would like to add a background that goes behind all the pictures i've blitted. I've searched everywhere but i can't seem to find the solution. Could anybody help?
import pygame
import os
class Player(object):
def __init__(self):
self.image = pygame.image.load("player1.png")
self.image2 = pygame.transform.flip(self.image, True, False)
self.coffee=pygame.image.load("coffee.png")
self.computer=pygame.image.load("computer.png")
self.flipped = False
self.x = 0
self.y = 0
def handle_keys(self):
""" Movement keys """
key = pygame.key.get_pressed()
dist = 5
if key[pygame.K_DOWN]:
self.y += dist
elif key[pygame.K_UP]:
self.y -= dist
if key[pygame.K_RIGHT]:
self.x += dist
self.flipped = False
elif key[pygame.K_LEFT]:
self.x -= dist
self.flipped = True
def draw(self, surface):
if self.flipped:
image = self.image2
else:
image = self.image
surface.blit(image, (self.x, self.y))
surface.blit(self.coffee, (700,500))
surface.blit(self.computer,(0,500))
pygame.init()
screen = pygame.display.set_mode((810, 610)) #creates the screen
player = Player()
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # quit the screen
running = False
player.handle_keys() # movement keys
screen.fill((255,255,255)) # fill the screen with white
player.draw(screen) # draw the player to the screen
pygame.display.update() # update the screen
clock.tick(60) # Limits Frames Per Second to 60 or less
A background image is no different from any other image. Just .blit it first.

Collisions detection in pygame

I'm trying to detect collisions between the player and the floor. This is part of my school project so any insight would be helpful. Also suggestions to improve the code will be appreciated. Here is my code:
import pygame
#initialise pygame
pygame.init()
#variables
level = 0
velx = 0
vely = 0
health = 1
floor_group = set([])
clock = pygame.time.Clock()
#collisions
def detectCollisions(x1, y1, w1,h1, x2, y2, w2, h2):
if (x2 + w2 >= x1 >= x2 and y2 + h2 >= y1 >= y2):
return True
elif (x2 + w2 >= x1 + w1 >= x2 and y2 + h2 >= y1 >= y2):
return True
elif (x2 + w2 >= x1 >= x2 and y2 + h2 >= y1 + h1 >= y2):
return True
elif (x2 + w2 >= x1 + w1 >= x2 and y2 + h2 >= y1+ h1 >= y2):
return True
else:
return False
#screen size the same size as my background
window = pygame.display.set_mode((900,563))
#Load Images: Backgrounds
background0 = pygame.image.load("background0.png").convert()
#Load Sprites
halfspike = pygame.image.load("halfspike.png").convert_alpha()
spike = pygame.image.load("spike.png").convert_alpha()
platform = pygame.image.load("platform.png").convert_alpha()
spider = pygame.image.load("spider.png").convert_alpha()
char1 = pygame.image.load("char1.png").convert_alpha()
char2 = pygame.image.load("char2.png").convert_alpha()
#Window title
pygame.display.set_caption("Super Boshy Brothers")
# character class
class Sprite:
def __init__(self,x,y):
self.x = x
self.y = y
self.width = 42
self.height = 44
self.velx = 0
self.vely = 0
self.image0 = pygame.image.load("char1.png")
self.image1 = pygame.image.load("char2.png")
self.timeTarget = 10
self.timeNumber = 0
self.currentImage = 0
def update(self):
self.timeNumber += 1
if (self.timeNumber == self.timeTarget):
if (self.currentImage == 0):
self.currentImage = 1
else:
self.currentImage = 0
self.timeNumber = 0
self.render()
def render(self):
if (self.currentImage == 0):
window.blit(self.image0, (self.x, self.y))
else:
window.blit(self.image1, (self.x, self.y))
# Floor class
class Floor:
def __init__(self,x,y):
self.x = x
self.y = y
self.width = 43
self.height = 44
self.image0 = pygame.image.load("floor.png")
def update(self):
self.render()
def render(self):
window.blit(self.image0, (self.x, self.y))
def floor_spawner(row):
global floor_group
for i in range(0,946,43):
floor_group.add(Floor(i,row))
#Create first level floor
floor_spawner(519)
floor_spawner(475)
#player
player = Sprite(0,431)
#create our main loop
gameloop = True
while gameloop:
for event in pygame.event.get(): #get function handles events
if (event.type == pygame.QUIT): #if Quit (red x) pressed exit loop
gameloop = False
if (event.type == pygame.KEYDOWN): #If a key is pressed down
if (event.key ==pygame.K_LEFT): #If Left Arrow
velx = -7
if (event.key ==pygame.K_RIGHT): #If Right Arrow
velx = 7
if (event.key ==pygame.K_UP): #If Up Arrow
vely = -7
if (event.type == pygame.KEYUP): #If a key is pressed down
if (event.key ==pygame.K_LEFT): #If Left Arrow
velx = 0
if (event.key ==pygame.K_RIGHT): #If Right Arrow
velx = 0
if (event.key ==pygame.K_UP): #If Up Arrow
vely = 0
#Level 0
if level == 0:
window.blit(background0, (0,0)) #Bottom bricks
for f in list(floor_group):
f.render()
if player.x <= 0: #Left side collision
player.x = 0
if player.x >= 900: #Level change
level = 1
player.x = 0
#Level 1
if level == 1:
window.blit(background0, (0,0)) #Bottom bricks
for f in list(floor_group):
f.render()
player.x += velx
player.y += vely
player.update()
clock.tick(50) #Tick Tock Tick Tock
pygame.display.flip() #Updates the window
pygame.quit()
It's better if you use pygame sprite to do your assignment.
For managing the floor collision problem, just detect the collision as I do in the code below, if a collision between player and floor occurs then simply move the player back to the previous position.
Here's my code:
import pygame
import random
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 255, 0)
WHITE = (255, 255, 255)
#Sprite are basically game images in pygame
#Through sprites collision detection and rendering becomes much easier
class Block(pygame.sprite.Sprite):
#Here I've a block class which is a subclass of the pygame's sprite class
def __init__(self, image):
pygame.sprite.Sprite.__init__(self)
# Here I've initialised he superclass Sprite
self.image = image
#Image of sprite = image .. that's it
self.rect = self.image.get_rect()
#It get's all dimesion's of image which will help it in detecting collision
pygame.init()
infoObject = pygame.display.Info()
# pygame.display.Info() provides us with the information about cureent resolution and a bunch of other stuff
screen = pygame.display.set_mode((infoObject.current_w, infoObject.current_h))
char1 = pygame.image.load("char1.png").convert_alpha()
char2 = pygame.image.load("char2.png").convert_alpha()
char2_list = pygame.sprite.Group()
# Sprite groups are sets for sprites
# i.e. We have different types of object stored in different groups
# In our game the char1 and char2 are of different
#internal interaction between all char2 does'nt matter
#It's the interaction between the char1 and char2 that we've to deal with
all_list = pygame.sprite.Group()
#I've made a group which contains all the blocks which helps me in rendering them all together
for i in range(50):
block = Block(char1)
block.rect.x = random.randrange(infoObject.current_w)
block.rect.y = random.randrange(infoObject.current_h)
charimport pygame
import random
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 255, 0)
WHITE = (255, 255, 255)
#Sprite are basically game images in pygame
#Through sprites collision detection and rendering becomes much easier
class Block(pygame.sprite.Sprite):
#Here I've a block class which is a subclass of the pygame's sprite class
def __init__(self, image):
pygame.sprite.Sprite.__init__(self)
# Here I've initialised he superclass Sprite
self.image = image
#Image of sprite = image .. that's it
self.rect = self.image.get_rect()
#It get's all dimesion's of image which will help it in detecting collision
pygame.init()
infoObject = pygame.display.Info()
# pygame.display.Info() provides us with the information about cureent resolution and a bunch of other stuff
screen = pygame.display.set_mode((infoObject.current_w, infoObject.current_h))
char1 = pygame.image.load("char1.png").convert_alpha()
char2 = pygame.image.load("char2.png").convert_alpha()
char2_list = pygame.sprite.Group()
# Sprite groups are sets for sprites
# i.e. We have different types of object stored in different groups
# In our game the char1 and char2 are of different
#internal interaction between all char2 does'nt matter
#It's the interaction between the char1 and char2 that we've to deal with
all_list = pygame.sprite.Group()
#I've made a group which contains all the blocks which helps me in rendering them all together
for i in range(50):
block = Block(char1)
block.rect.x = random.randrange(infoObject.current_w)
block.rect.y = random.randrange(infoObject.current_h)
char2_list.add(block)
all_list.add(block)
player = Block(char2)
running = True
clock = pygame.time.Clock()
score = 1
all_list.add(player)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(BLACK)
pos = pygame.mouse.get_pos()
#Gets position of the mouse
player.rect.x = pos[0]
player.rect.y = pos[1]
char_hit_list = pygame.sprite.spritecollide(player, char2_list, True)#Set it to false and see the result
#Checks collision
for block in char_hit_list:
score += 1
print score
all_list.draw(screen)
#renders(draw) all the sprites onto screen
pygame.display.update()
#update's display
clock.tick(60)
# Sets Frame Rate to 60
pygame.quit()2_list.add(block)
all_list.add(block)
player = Block(char2)
running = True
clock = pygame.time.Clock()
score = 1
all_list.add(player)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(BLACK)
pos = pygame.mouse.get_pos()
#Gets position of the mouse
player.rect.x = pos[0]
player.rect.y = pos[1]
char_hit_list = pygame.sprite.spritecollide(player, char2_list, True)#Set it to false and see the result
#Checks collision
for block in char_hit_list:
score += 1
print score
all_list.draw(screen)
#renders(draw) all the sprites onto screen
pygame.display.update()
#update's display
clock.tick(60)
# Sets Frame Rate to 60
pygame.quit()
And one more thing try not to hard-code stuff like you did for screen dimension.
For learning more about pygame Sprites have a look at this.
If you have any problem, let me know by commenting in the comments section below, I'll try my best to help you out with your problem.
Keep pygaming :)

Categories

Resources