Space Invaders bug pygame - python-3.x

After finally beeing able to make multiple enemies in the game I confronted myself with the most annoying bug ever. I have multiple enemies, a space ship and I can even shoot bullets, but right after I added the enemies to the game and followed the exact same steps as show in this video, the background is gone for some reason, what made the speed of the player character, of the enemies and the bullet went like crazy. Plus, the sprites are duplicating themselves and wont go away, and the only think that I can think is "What the hell am I doing wrong??" because I feel like I can't even follow the steps and copy the code from a video (btw I'm not receiving any error messages). Anyways, here's the full game code.
import pygame
import random
import math
# Initiate pygame
pygame.init()
# Display the game window
screen = pygame.display.set_mode((800, 600))
# Title and Icon
pygame.display.set_caption('Space Invaders')
icon = pygame.image.load('icon.png')
pygame.display.set_icon(icon)
# Background
background = pygame.image.load('background.png')
# Player
playerSprite = pygame.image.load('player.png')
playerX = 370
playerY = 480
playerX_change = 0
# Enemies
enemySprite = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
num_of_enemies = 6
for i in range(num_of_enemies):
enemySprite.append(pygame.image.load('enemy.png'))
enemyX.append(random.randint(0, 736))
enemyY.append(random.randint(50, 150))
enemyX_change.append(4)
enemyY_change.append(40)
# Bullet
bulletSprite = pygame.image.load('bullet.png')
bulletX = 0
bulletY = 480
bulletY_change = 10
# When bullet_state = 'ready', you can't see the bullet on the screen
# When bullet_state = 'fire', the bullet is currently moving
bullet_state = 'ready'
score = 0
def player(x, y):
screen.blit(playerSprite, (x, y))
def enemy(x, y, i):
screen.blit(enemySprite[i], (x, y))
def fire_bullet(x, y):
global bullet_state
bullet_state = 'fire'
screen.blit(bulletSprite, (x + 16, y + 10))
def isCollision(enemyX, enemyY, bulletX, bulletY):
distance = math.sqrt((math.pow(enemyX - bulletX, 2)) + (math.pow(enemyY - bulletY, 2)))
if distance < 27:
return True
else:
return False
# Game Loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# If a key is pressed, check if it's the right or left arrow key
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -5
if event.key == pygame.K_RIGHT:
playerX_change = 5
if event.key == pygame.K_SPACE:
if bullet_state == 'ready':
bulletX = playerX
fire_bullet(playerX, bulletY)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# Player movement
playerX += playerX_change
# Prevents the player from going off the border
if playerX <= 0:
playerX = 0
elif playerX >= 736:
playerX = 736
# Enemies movement
for i in range(num_of_enemies):
enemyX[i] += enemyX_change[i]
if enemyX[i] <= 0:
enemyX_change[i] = 4
enemyY[i] += enemyX_change[i]
elif enemyX[i] >= 736:
enemyX_change[i] = -4
enemyY[i] += enemyY_change[i]
# Collision
collision = isCollision(enemyX[i], enemyY[i], bulletX, bulletY)
if collision:
bulletY = 480
bullet_state = 'ready'
score += 1
print(score)
enemyX[i] = random.randint(0, 736)
enemyY[i] = random.randint(50, 150)
enemy(enemyX[i], enemyY[i], i)
# Bullet movement
if bulletY <= 0:
bulletY = 480
bullet_state = 'ready'
if bullet_state == 'fire':
fire_bullet(bulletX, bulletY)
bulletY -= bulletY_change
player(playerX, playerY)
pygame.display.update()

the background is gone for some reason
Of course, because the background is never blit to screen.
the sprites are duplicating themselves and wont go away,
That's because you don't draw the background.
blit the background in the main application loop, before the objects of the scene are drawn:
while running:
# [...]
screen.blit(background, (0, 0))
# Enemies movement
for i in range(num_of_enemies):
# [...]
the speed of the player character, of the enemies and the bullet went like crazy.
Use pygame.time.Clock() to control the frames per second.
clock = pygame.time.Clock()
running = True
while running:
clock.tick(60)
# [...]
Another (less sophisticated) possibility, which is probably use in the tutorial is to use pygame.time.delay():
running = True
while running:
pygame.time.delay(10)
# [...]

Related

in my incomplete version of space invaders, the 1 bullet is not being fired.. but no error message is shown. can somebody review it suggest a solution [duplicate]

This question already has answers here:
How can i shoot a bullet with space bar?
(1 answer)
How do I stop more than 1 bullet firing at once?
(1 answer)
Closed 6 days ago.
import pygame
import random
# get the window
pygame.init
# window var declarataion and parameters
window = pygame.display.set_mode((800, 600))
background = pygame.image.load("space bg.png")
# characters
# spaceship
playerImg = pygame.image.load("spaceship-2.png")
playerX = 360
playerY = 535
playerX_change = 0
#enemy
enemyImg = pygame.image.load("space-invaders.png")
enemyX = random.randint(0, 536)
enemyY = random.randint(0, 120)
enemyX_change = 1
enemyY_change = 30
#bullet
bulletImg = pygame.image.load("space-invaders.png")
bulletX = playerX
bulletY = 535
bulletX_change = 0
bulletY_change = -100
bullet_state = "ready"
# character functions
#spaceship
def player(x, y):
window.blit(playerImg, (playerX, playerY))
def enemy(x, y):
window.blit(enemyImg, (enemyX, enemyY))
def fire_bullet(x, y):
global bullet_state
if bullet_state == "fire":
window.blit(bulletImg, (playerX + 16, playerY + 10))
# full loop
running = True
while running:
# to change the color of window
window.fill((255, 255, 255))
window.blit(background, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# check if key pressed is right or left and executes events accordingly
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -1
if event.key == pygame.K_RIGHT:
playerX_change = 1
if event.key == pygame.K_SPACE:
fire_bullet(playerX, bulletY)
bulletY -= bulletY_change
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# enemy movement
# loops the enemy movement
enemyX += enemyX_change
# to change the enemy direction when border of window hit
if enemyX <= 0:
enemyY += enemyY_change
enemyX_change = 1
elif enemyX >= 736:
enemyY += enemyY_change
enemyX_change = -1
# calling function
playerX += playerX_change
if playerX <= 0:
playerX = 0
elif playerX >= 736:
playerX = 736
player(playerX, playerY)
enemy(enemyX, enemyY)
pygame.display.update()
i tried to make the bullet move along the y axis of the spaceship by adding the code shown below into the keystroke code section:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -1
if event.key == pygame.K_RIGHT:
playerX_change = 1
if event.key == pygame.K_SPACE:
fire_bullet(playerX, bulletY)
bulletY -= bulletY_change
but no error message is shown, the bullet is not being shown, but everything else works perfectly.. like the invader moving along the x axis infinitely, and the spaceship can still be controlled by my arrow key.. only thing that doesn't work is the bullet not firing from the spaceship.. and I can't seem to make it work with this code.. if someone could tell me where I went wrong, it would be of tons of help, and I've been on this problem for days now.. so
it would save me a lot of time in the future. thanks

Differentiate between key pressed just once and key held [pygame]

So in Pygame, I am trying to create this bat/ball game where the ball comes from the top of the screen (like a meteor) at various speeds, and the bat is near the bottom of the screen that can be moved left/right. Depending on the key pressed (direction the ball goes) and if there is a hit (i.e collision), the ball goes in a certain direction. For example, if you press the "d" key then the ball goes to the right side (provided there was a hit/collision).
Now everything seems to be working fine, however, what I actually want is the player timing the key press WHEN the ball is near the bat. At the moment, I can do that, but also if I keep holding the "d" key from much earlier, the ball still hits the bat. This is definitely not what I want, as it defeats the purpose of timing and having different speeds for the ball. I understand why this is happening as pygame.key.get_pressed() doesn't seem to differentiate between one key press and hold, and considers them one event. So, essentially, I would like to only let the collision happen if the key was pressed as the ball passed the bat and not while it was held from the very start. Here is the Ball class which contains the main code.
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = meteor_img
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
self.radius = int(self.rect.width * .85 / 2)
#pygame.draw.circle(self.image, RED, self.rect.center, self.radius)
self.rect.x = WIDTH /2 + 10
self.rect.y = HEIGHT - 770
self.speedy = random.randint(7, 14)
self.speedx = 0.3
self.j = 0
def update(self):
if self.rect.y < HEIGHT - 450 and self.j!= 1:
Ball.old_ball(self)
self.rect.x -= self.speedx
self.rect.y += self.speedy
#print (self.rect.x, self.rect.y)
elif self.j !=1:
Ball.change_ball(self)
self.rect.x += self.speedx
self.rect.y += self.speedy
#print (self.rect.x, self.rect.y)
else:
Ball.old_ball(self)
self.rect.x += self.speedx
self.rect.y += self.speedy
#print (self.rect.y)
shot_played = pygame.key.get_pressed()
hits = pygame.sprite.spritecollide(player, mobs, False, pygame.sprite.collide_circle)
#print (hits)
if hits and shot_played[pygame.K_e]:
#print ("Perfect Timing")
self.speedx = random.uniform(1, 5)
self.speedy = -random.uniform(2, 4)
self.j = 1
elif hits and shot_played[pygame.K_d]:
#print ("Perfect Timing")
self.speedx = random.uniform(6, 10)
self.speedy = -random.uniform(-2, 2)
self.j = 1
elif hits and shot_played[pygame.K_a]: #Leg Side
#print ("Perfect Timing")
self.speedx = -random.uniform(6, 10)
self.speedy = -random.uniform(-2, 2)
self.j = 1
if self.rect.top > HEIGHT + 10 or self.rect.left < -25 or self.rect.right > WIDTH + 20 or self.rect.y < 10:
self.rect.x = WIDTH /2+10
self.rect.y = HEIGHT - 770
self.speedy = random.randint(7, 14)
self.speedx = 0.3
self.j = 0
time.sleep(1)
#self.speedy = random.randrange(1, 8)
def change_ball(self):
self.image = ball_pitched_img
self.image.set_colorkey(WHITE)
def old_ball(self):
self.image = meteor_img
self.image.set_colorkey(WHITE)
You could just use an event loop instead of the state checking. When you press a key, pygame adds a pygame.KEYDOWN event to the queue once and a pygame.KEYUP event when the key is released. Calling pygame.event.get() empties the queue and returns a list of events over which you can iterate with a for loop to handle one event after the other.
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
done = False
while not done:
# for each event in the event queue.
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
print('d pressed')
# Check if the bat collides with the ball here.
screen.fill((30, 30, 30))
pygame.display.flip()
clock.tick(60)

Powerbar decrease script, pygame

I'm trying to create a "powerbar."
In the following code I can press left & right key to increase the bar, and the prg terminates when the bar_start_width > 400 px.
I would like the "powerbar" to decrease while bar_start_width > 0. So, if you stop to press the buttons, the bar will slowly decrease until bar_start_width > 0.
With my code it is slowly decreasing bar_start_width, but not updating the display. The while loop also continues running.
Here is full code:
import time
import random
import pygame
import time
import pygame
pygame.init()
display_width = 800
display_height = 600
black = (0, 0, 0)
white = (255, 255, 255)
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
def powerbar(thingx, thingy, thingw, thingh, color):
pygame.draw.rect(gameDisplay, color, [thingx, thingy, thingw, thingh])
def game_quit():
pygame.quit()
quit()
def game_loop():
decrease_speed = -1
bar_start_width = 0
bar_x = 100
bar_y = 100
bar_height = 50
bar_color = black
x_change = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white) # the background color before img
# Should change so you can ONLY press right after left (or opposite)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = 5
if event.key == pygame.K_RIGHT:
x_change = 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
bar_start_width += x_change
# Draw powerbar
powerbar(bar_x, bar_y, bar_start_width, bar_height, bar_color)
if bar_start_width > 10:
while True:
bar_start_width += decrease_speed
time.sleep(0.5)
print(bar_start_width) #See output in terminal
if bar_start_width == 0:
break
if bar_start_width > 400:
game_quit()
pygame.display.update()
clock.tick(60)
game_loop()
pygame.quit()
quit()
I have tried also to think in terms of a for loop to solve my problems but after long time of trying out different things this is the best I can come up with.
Can anyone help, or link me to a similar discussion?
Thanks!
I'm not sure why you have a nested while loop in the main while loop. If I understand you correctly, you want to decrease the width every frame if (not while) the width is greater than 0. Just remove the while loop and the time.sleep call and decrease the width if bar_start_width > 0:.
import pygame
pygame.init()
black = (0, 0, 0)
white = (255, 255, 255)
gameDisplay = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
def game_loop():
decrease_speed = -1
bar_start_width = 0
bar_x = 100
bar_y = 100
bar_height = 50
x_change = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = 5
elif event.key == pygame.K_RIGHT:
x_change = 5
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
bar_start_width += x_change
if bar_start_width > 0:
bar_start_width += decrease_speed
if bar_start_width <= 0: # Set it to the minimum width of 1.
bar_start_width = 1
if bar_start_width > 400:
print('> 400')
gameDisplay.fill(white)
# Draw powerbar
pygame.draw.rect(gameDisplay, black, (bar_x, bar_y, bar_start_width, bar_height))
pygame.display.update()
clock.tick(60)
game_loop()
pygame.quit()
I've rearranged a few things, because the game logic and the event loop were mixed. You should generally try to separate the event handling, game logic and the rendering.

Pressing a button to have constant movement

I followed an online tutorial to make a snake game and want some help to make some changes. As of now, holding the left or right arrow keys will cause the snake to move. Would it be able to make the snake move to the left or right with only a tap of the button so the user doesn't have to hold down the arrow keys?
question = True
while not gameExit:
#Movement
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
direction = "left"
start_x_change = -block_size_mov
start_y_change = 0
elif event.key == pygame.K_RIGHT:
leftMov = False
direction = "right"
start_x_change = block_size_mov
start_y_change = 0
The solution is to start off by storing the x,y coordinates of the sprite, set a modifier (increase or decrease amount) on keypress, and then add the modifier to the coordinates while looping. I've written a quick demo of such a system:
import pygame
from pygame.locals import *
pygame.init()
# Set up the screen
screen = pygame.display.set_mode((500,500), 0, 32)
# Make a simple white square sprite
player = pygame.Surface([20,20])
player.fill((255,255,255))
# Sprite coordinates start at the centre
x = y = 250
# Set movement factors to 0
movement_x = movement_y = 0
while True:
screen.fill((0,0,0))
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_LEFT:
movement_x = -0.05
movement_y = 0
elif event.key == K_RIGHT:
movement_x = 0.05
movement_y = 0
elif event.key == K_UP:
movement_y = -0.05
movement_x = 0
elif event.key == K_DOWN:
movement_y = 0.05
movement_x = 0
# Modify the x and y coordinates
x += movement_x
y += movement_y
screen.blit(player, (x, y))
pygame.display.update()
Note that you need to reset the x movement modifier to 0 when changing y, and vice-versa - otherwise you end up with interesting diagonal movements!
For a snake game, you might want to modify the snake size as well as/instead of position - but you should be able to achieve something similar using the same structure.

make sprite crouch in current position python

Hi i'm making a fighting game to get some practice with pygame but I have run into a problem with crouching/ducking. When I press the down button it goes back to its original location then ducks. If you need more info to help i will provide.
import pygame
import random
display_height = 600
display_width = 1000
dis_screen = pygame.display.set_mode((display_width, display_height))
FPS = 30
clock = pygame.time.Clock()
img = pygame.image.load('foo.png')
crouchimg = pygame.image.load('crouchimg.png')
# Simple player object
class Player(object):
def __init__(self, x, y, image):
self.x = x
self.y = y
self.image = image
# Method to draw object
def draw(self):
dis_screen.blit(self.image, (self.x, self.y))
# Method to move object
def move(self, speedx, speedy):
self.x += speedx
self.y += speedy
class MainRun(object):
def __init__(self, displayw, displayh):
self.dw = displayw
self.dh = displayh
self.Main()
def Main(self):
# Put all variables up here
stopped = False
x_move = 0
y_move = 0
p1_y_loc = 200
p1_x_loc = 200
x = pygame.Rect().x
greg = Player(p1_x_loc, p1_y_loc, img)
# Main Loop
while not stopped:
print(x)
dis_screen.fill((255, 255, 255)) # Tuple for filling display... Current is white
# Event Tasking
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
y_move = 0
x_move = 5
elif event.key == pygame.K_LEFT:
y_move = 0
x_move = -5
elif event.key == pygame.K_UP:
y_move = -5
x_move = 0
elif event.key == pygame.K_DOWN:
p1_y_loc = 300
p1_x_loc = 0
greg = Player(p1_x_loc, p1_y_loc, crouchimg)
if event.type == pygame.KEYUP:
if event.key == pygame.K_DOWN:
p1_y_loc = 200
greg = Player(p1_x_loc, p1_y_loc, img)
if event.key == pygame.K_UP:
y_move = 0
if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT:
x_move = 0
greg.move(x_move, y_move)
greg.draw()
pygame.display.update()
clock.tick(FPS)
pygame.quit()
quit()
run = MainRun(display_width, display_height)
run.Main()
That's because when you're pressing the down key you're creating a new player at the x-position p1_x_loc (which you've set to 0) and y-position p1_y_loc (which you've set to 300). So when the player crouches it also moves it to (0, 300) no matter the position the player currently is.
A way to solve this would be to just change the player's image, and not create a completely new player. You can do it this way, player.image = crouchimg instead of greg = Player(p1_x_loc, p1_y_loc, crouchimg). And when the player gets up you just change the image again: player.image = img
And if you have to change the players' y-position when crouching you could do it the same way: player.y = 200 and player.y = 300.

Resources