I've been trying to make Space Invaders using PyGame in Python and make a list for the enemy images, and I make a for loop twice to load the images in and append them to the lists; 2 times for 2 rows. Though, the only pictures that appear are the ones from the first for loop.
There are no errors either, so it seems it is working. I also checked to see if the two rows were overlapping each other, but from the way I tested it, I found that they weren't.
I made this before here without using OOP and now I'm using OOP to learn it better, and it isn't working properly, please help.
For Loops Below
EnemyRowBottom = EnemyClass()
EnemyRowTop = EnemyClass()
for i in range(EnemyRowBottom.numEnemies):
EnemyImage1 = pygame.image.load('enemy.png')
EnemyRowBottom.EnemyImage.append( pygame.transform.scale(EnemyImage1, (70, 70)) )
y = i * 10
x = y * 10
EnemyRowBottom.EnemyX.append( x )
EnemyRowBottom.EnemyY.append( 150 )
for i in range(EnemyRowTop.numEnemies):
EnemyImage1 = pygame.image.load('enemy.png')
EnemyRowTop.EnemyImage.append( pygame.transform.scale(EnemyImage1, (70, 70)) )
y = i * 10
x = y * 10
EnemyRowTop.EnemyX.append( x )
EnemyRowTop.EnemyY.append( 50 )
Full Code below
import pygame
import random
import math
from pygame import mixer
import time
# initialize pygame
pygame.init()
# create screen
screenX = 800
screenY = 800
screen = pygame.display.set_mode( (screenX, screenY) )
# background
background = pygame.image.load("background.jpg")
#sounds
mixer.music.load('bgMusic.mp3')
mixer.music.play(-1)
# title and icon
pygame.display.set_caption("Space Invaders")
icon = pygame.image.load('Icon.jpg')
pygame.display.set_icon(icon)
# score
scoreValue = 0
font = pygame.font.Font('freesansbold.ttf', 32)
textX = 10
textY = 10
def show_score(x, y):
global scoreValue
score = font.render("Score: " + str(scoreValue), True, (255, 255, 255))
screen.blit(score, (x, y))
# player
PlayerImage = pygame.image.load('SpaceShip.png')
PlayerImage = pygame.transform.scale(PlayerImage, (84, 84))
playerX = 370
playerY = 650
xChangePlayer = 0
def player(x, y):
screen.blit(PlayerImage, (x, y))
# check collision
def Collision(EX, EX2, EY, EY2, LX, LY):
distance = math.sqrt((math.pow(EX - LX, 2) + math.pow(EY - LY, 2)))
distance2 = math.sqrt((math.pow(EX2 - LX, 2) + math.pow(EY2 - LY, 2)))
if distance <= 35:
return 1
elif distance2 <= 35:
return 2
def DistanceEnemy(EX, EX2, EY, EY2):
distance = math.sqrt((math.pow(EX2 - EX, 2) + math.pow(EY2 - EY, 2)))
if distance <= 70:
return True
# bottom line
Line = pygame.image.load('BottomLine.jpg')
LineX = 0
LineY = 655
def BottomLine(x, y):
global Line
screen.blit(Line, (x, y))
# Laser
LaserImage = pygame.image.load('PlayerLaser.png')
LaserImage = pygame.transform.scale(LaserImage, (50, 50))
LaserX = random.randint(15, (screenX - 85) )
LaserY = 750
xLaserChange = 0
yLaserChange = 1
# can't see bullet on screen until Ready changes to Fire
LaserState = "Ready"
def Firelaser(x, y):
global LaserState
LaserState = "Fire"
screen.blit(LaserImage, (x+17, y+10))
GameOverfont = pygame.font.Font('freesansbold.ttf', 82)
def GameOver():
global GameOverfont
text = GameOverfont.render("GAME OVER", True, (255, 255, 255))
screen.blit(text, (140, 250))
# enemy
class EnemyClass:
EnemyImage = []
EnemyX = []
EnemyY = []
numEnemies = 4
xEnemyChange = 0.2
yEnemyChange = 50
def enemyMovement(self, i):
EnemyClass.EnemyX[i] += EnemyClass.xEnemyChange
if EnemyClass.EnemyX[i] >= 736:
EnemyClass.xEnemyChange = -EnemyClass.xEnemyChange
for x in range(len(EnemyClass.EnemyY)):
EnemyClass.EnemyY[x] += EnemyClass.yEnemyChange
elif EnemyClass.EnemyX[i] <= 0:
EnemyClass.xEnemyChange = .2
for x in range(len(EnemyClass.EnemyY)):
EnemyClass.EnemyY[x] += EnemyClass.yEnemyChange
def enemy(self, i):
screen.blit(EnemyClass.EnemyImage[i], (EnemyClass.EnemyX[i], EnemyClass.EnemyY[i]))
def enemyGameOver(self, x, y):
screen.blit(EnemyClass.EnemyImage[i], (x, y))
def CollisionDetection(self, i, scoreValue, lasery, laserstate):
global LaserY, LaserState
ExplosionSound = mixer.Sound('explosion.wav')
ExplosionSound.play()
LaserY = lasery
LaserState = laserstate
scoreValue += 1
# enemy respawn
OldEnemyY = EnemyClass.EnemyY[i]
EnemyClass.EnemyY[i] = -20
if EnemyClass.EnemyY[i] >= OldEnemyY:
EnemyClass.EnemyY[i] -= 40
EnemyRowBottom = EnemyClass()
EnemyRowTop = EnemyClass()
for i in range(EnemyRowBottom.numEnemies):
EnemyImage1 = pygame.image.load('enemy.png')
EnemyRowBottom.EnemyImage.append( pygame.transform.scale(EnemyImage1, (70, 70)) )
y = i * 10
x = y * 10
EnemyRowBottom.EnemyX.append( x )
EnemyRowBottom.EnemyY.append( 150 )
for i in range(EnemyRowTop.numEnemies):
EnemyImage1 = pygame.image.load('enemy.png')
EnemyRowTop.EnemyImage.append( pygame.transform.scale(EnemyImage1, (70, 70)) )
y = i * 10
x = y * 10
EnemyRowTop.EnemyX.append( x )
EnemyRowTop.EnemyY.append( 50 )
# game loop
running = True
while running:
# setting background
screen.fill( (0, 0, 0) )
screen.blit(background, (0, 0))
# make function that closes program when x pressed
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# keystroke detection
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
xChangePlayer = -0.4
if event.key == pygame.K_RIGHT:
xChangePlayer = +0.4
if event.key == pygame.K_SPACE:
if LaserState == "Ready":
LaserSound = mixer.Sound('shoot.wav')
LaserSound.play()
LaserX = playerX
LaserY = 600
Firelaser(LaserX, LaserY)
LaserY -= yLaserChange
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
xChangePlayer = 0
# player movement
playerX += xChangePlayer
# setting boudries for the player
if playerX < 0:
playerX = 0
elif playerX >= 736:
playerX = 735
# setting boudries for the enemy
for i in range(EnemyRowTop.numEnemies):
BlitEnemy1 = EnemyRowTop.enemy(i)
BlitEnemy2 = EnemyRowBottom.enemy(i)
gameSoundPlayed = 0
if EnemyRowTop.EnemyY[i] >= 600 or EnemyRowBottom.EnemyY[i] >= 600 and (gameSoundPlayed == 0):
GameOver()
BottomLine(LineX, LineY)
player(playerX, playerY)
show_score(textX, textY)
pygame.display.update()
gameSoundPlayed += 1
EnemyRowTop.xEnemyChange = 0
EnemyRowTop.yEnemyChange = 0
EnemyRowBottom.xEnemyChange = 0
EnemyRowBottom.yEnemyChange = 0
mixer.music.stop()
mixer.music.load('GameOverSound.wav')
mixer.music.play()
time.sleep(3)
break
EnemyRowTop.enemyMovement(i)
EnemyRowBottom.enemyMovement(i)
# collision detection
collision = Collision(EnemyRowTop.EnemyX[i], EnemyRowBottom.EnemyX[i], EnemyRowTop.EnemyY[i], EnemyRowBottom.EnemyY[i], LaserX, LaserY)
if collision == 1:
EnemyRowTop.CollisionDetection(i, scoreValue, 600, "Ready")
EnemyDistance = DistanceEnemy(EnemyRowTop.EnemyX[i], EnemyRowBottom.EnemyX[i], EnemyRowTop.EnemyY[i], EnemyRowBottom.EnemyY[i])
if EnemyDistance == True:
EnemyClass.EnemyY[i] -= 50
if collision == 2:
EnemyRowBottom.CollisionDetection(i, scoreValue, 600, "Ready")
EnemyDistance = DistanceEnemy(EnemyRowTop.EnemyX[i], EnemyRowBottom.EnemyX[i], EnemyRowTop.EnemyY[i], EnemyRowBottom.EnemyY[i])
if EnemyDistance == True:
EnemyClass.EnemyY[i] -= 50
BlitEnemy1
BlitEnemy2
# laser movement
if LaserY <= -20:
LaserY = 750
LaserState = "Ready"
if LaserState == "Fire":
Firelaser(LaserX, LaserY)
LaserY -= yLaserChange
# outputting characters
BottomLine(LineX, LineY)
player(playerX, playerY)
show_score(textX, textY)
pygame.display.update()
if gameSoundPlayed == 1:
break
I cant test it but I think I found the problem. You should add a _init_ method to your EnemyClass. There you can define these variables so each row has its own lists of enemies. Because your enemies have the same image you dont need to load the image more than once.
EnemyImage = []
EnemyX = []
EnemyY = []
numEnemies = 4
xEnemyChange = 0.2
yEnemyChange = 50
class EnemyClass:
EnemyImage = pygame.transform.scale(pygame.image.load('enemy.png'), (70, 70))
def __init__(self):
self.EnemyX = []
self.EnemyY = []
self.numEnemies = 4
self.xEnemyChange = 0.2
self.yEnemyChange = 50
EnemyRowBottom = EnemyClass()
EnemyRowTop = EnemyClass()
But it would make more sense to have an object for each enemy instead of the enemy rows.
The problem of your code is, that the list containing the images in your EnemyClass is a class-attribute of your EnemyClass. As a result, every instance of that class accesses the same list of EnemyClass, and has not its own list in EnemyRowBottom and EnemyRowTop.
You can fix this problem by making EnemyImage an object-attribute:
class EnemyClass:
def __init__(self):
self.EnemyImage = []
self.EnemyX = []
self.EnemyY = []
self.numEnemies = 4
self.xEnemyChange = 0.2
self.yEnemyChange = 50
You can then access your images with "self.EnemyImage" inside the methods.
Moreover, to avoid the same image twice, you can put your pygame.image.load-method outside and before the loops, so that the game starts quicker.
Related
I'm kind of new to this site and pygame and I was hoping for a bit of help. So I made this space invader clone which is about 180 lines right, when I run it on Pycharm, it works fine. So I decided to try testing it out further and tried converting it into an .exe file which is where my troubles began. Every time I try running the .exe, this message appears
Traceback (most recent call last):
File "main.py", line 62, in <module>
font = pygame.font.Font('freesansbold.ttf',32)
TypeError: expected str, bytes or os.PathLike object, not BytesIO
I used freeCodeCamp.org's template and applied my own customs, here's a video incase you're interested: https://www.youtube.com/watch?v=FfWpgLFMI7w&t=7041s
Here's the code as well:
import math
import random
import pygame
from pygame import mixer
# Initialize the pygame
pygame.init()
# Create the screen
screen = pygame.display.set_mode((600, 400))
# Background Sound
mixer.music.load('Here he comes.mp3')
mixer.music.play(-1)
# Title and icon
pygame.display.set_caption("Defender of The Blue")
icon = pygame.image.load("Submarine of Vengeance.png")
pygame.display.set_icon(icon)
# Player
playerImg = pygame.image.load('Protagonist.png')
playerX = 250
playerY = 300
playerX_change = 0
# Enemy
enemyImg = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
num_of_enemies = 6
for i in range(num_of_enemies):
enemyImg.append(pygame.image.load('Antagonist.png'))
enemyX.append(random.randint(0, 500))
enemyY.append(random.randint(10, 100))
enemyX_change.append(0.1)
enemyY_change.append(20)
# Energy Blast
# Ready - You can't see the bullet state on the screen
# Fire - The blast is currently moving
energyblastImg = pygame.image.load('energy-blasts.png')
energyblastX = 0
energyblastY = 300
energyblastX_change = 0
energyblastY_change = 1
energyblast_state = "ready"
# score
score_value = 0
font = pygame.font.Font('freesansbold.ttf',32)
textX = 10
textY = 10
game_over_font = pygame.font.Font('freesansbold.ttf',50)
# Game Over Text
def show_score(x,y):
score = font.render("Score :" + str(score_value),True, (250,100,0))
screen.blit(score, (x, y))
def game_over_text():
game_over_text = game_over_font.render("GAME OVER",True, (250,0,0))
screen.blit(game_over_text, (150, 100))
def player(x, y):
screen.blit(playerImg, (x, y))
def enemy(x, y, i):
screen.blit(enemyImg[i], (x, y))
def fire_energyblast(x, y):
global energyblast_state
energyblast_state = "fire"
screen.blit(energyblastImg, (x + -1, y + 10))
def isCollison(enemyX, enemyY, energyblastX, energyblastY):
distance = math.sqrt((math.pow(enemyX - energyblastX, 2)) + (math.pow(enemyY - energyblastY, 2)))
if distance < 27:
return True
else:
return False
# Game Loop
running = True
while running:
# RGB = Red, Green, Blue
screen.fill((0, 0, 128))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# if keystroke is pressed check whether its right or left
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:
if energyblast_state is "ready":
energyblast_sound = mixer.Sound('Energy Blast.mp3')
energyblast_sound.play()
# Get the current x coordinate of the submarine
energyblastX = playerX
fire_energyblast(energyblastX, energyblastY)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# 5 = 5 + -0.1 -> 5 = 5 - 0.1
# 5 = 5 + 0.1
# Checking for boundaries of submarine so it doesn't go out of bounds
playerX += playerX_change
if playerX <= 0:
playerX = 0
elif playerX >= 500:
playerX = 500
# enemy movement
for i in range(num_of_enemies):
# Game Over
if enemyY[i] > 272:
for j in range(num_of_enemies):
enemyY[j] = 2000
game_over_text()
break
enemyX[i] += enemyX_change[i]
if enemyX[i] <= 0:
enemyX_change[i] = 1
enemyY[i] += enemyY_change[i]
elif enemyX[i] >= 500:
enemyX_change[i] = -1
enemyY[i] += enemyY_change[i]
# Collision
collison = isCollison(enemyX[i], enemyY[i], energyblastX, energyblastY)
if collison:
explosion_Sound = mixer.Sound('Explode.mp3')
explosion_Sound.play()
energyblastY = 300
energyblast_state = "ready"
score_value += 50
enemyX[i] = random.randint(0, 500)
enemyY[i] = random.randint(10, 100)
enemy(enemyX[i],enemyY[i], i)
# energy blast movement
if energyblastY <= 0:
energyblastY = 300
energyblast_state = "ready"
if energyblast_state is "fire":
fire_energyblast(energyblastX, energyblastY)
energyblastY -= energyblastY_change
player(playerX, playerY)
show_score(textX,textY)
pygame.display.update()
I'm not quite sure what BytesIO is. Is it like having something used twice and cancels out? How can I change this?
Are you certain that "freesansbold.ttf" is downloaded and in the same directory as the code youre trying to run, if this isnt possible for whatever reason you should be able to replace the
font = pygame.font.Font('freesansbold.ttf',32)
with
font = pygame.font.Font('(file path)freesansbold.ttf',32)
So I am doing a project where I have to make a game that gives the user three lives and each time the beamrect's rectangle collides with the player's, it subtracts a life and puts them in the original position. When lives == 0: there will be a death screen displayed. But for some reason, the death screen isn't being displayed even though I made sure that every time the player rect (being zonicrect) and the beamrect collided, it would subtract 1 from the life variable.
# Your header should go here, each comment should be initialed -DK
import pygame, sys
import os
# https://youtu.be/jO6qQDNa2UY
pygame.init()
FPS = 60
# Useful Variables
# Size
size = height, width = 900, 500
zonhw = zheight, zwidth = 70, 70
scale2 = height2, width2 = 600, 300
lscale = lheight, lwidth = 80, 80
beamsz = bheight, bwidth = 50,25
platz = pheight, pwidth = 10, 70
# RGB
white = (255, 255, 255)
black = (0,0,0)
blue = (0, 0, 128)
green = (0, 255, 0)
brown = (165,42,42)
# Speed
VEL = 5
beamspeed = 3
# Position
laserpos = posx, posy = 500,250
#other
i = 0
life = 3
score = 0
# graphics
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Zonic bootleg")
font = pygame.font.Font('freesansbold.ttf', 32)
zonic = pygame.image.load(os.path.join("image","zonic.gif"))
zonic = pygame.transform.scale(zonic, zonhw)
bg = pygame.image.load(os.path.join("image","sonic-back.jpg"))
bg = pygame.transform.scale(bg, size)
gg = pygame.image.load(os.path.join("image","gg.jpg"))
gg= pygame.transform.scale(gg, size)
lazerz = pygame.image.load(os.path.join("image","Lazerz.gif"))
lazerz = pygame.transform.scale(lazerz, lscale)
beam = pygame.image.load(os.path.join("image","laserbeam.jpg"))
beam = pygame.transform.scale(beam, beamsz)
lives = pygame.image.load(os.path.join("image","health.png"))
lives = pygame.transform.scale(lives,(40,40))
# zoncz = pygame.image.load(os.path.join("image","zoncz.png"))
# zoncz = pygame.transform.scale(zoncz, scale2)
#coalitions
def collider(life,beamrect,zonicrect,lazerect):
beamrect.x -= beamspeed
if zonicrect.colliderect(beamrect):
beamrect.x = lazerect.x+21
zonicrect.x = 0
if beamrect.x <-60:
#screen.blit(beam, (posx, posy))
beamrect.x += 550
def updating(score, beamrect):
if beamrect.x == 0:
score += 1
#Death
def death():
while life <= 0:
death = font.render("Death", True, white)
screen.fill(black)
screen.blit(death,(250, 250))
# zonic movement
def KWS(keyvar, zonicrect,flip):
if keyvar[pygame.K_RIGHT]: # right
zonicrect.x += VEL
flip = False
if zonicrect.x > 500:
zonicrect.x -= VEL
if keyvar[pygame.K_LEFT] and zonicrect.x + VEL > 0: # left
zonicrect.x -= VEL
flip = True
def flipx(flip,zonicrect):
if flip:
screen.blit(pygame.transform.flip(zonic,True,False),(zonicrect.x,zonicrect.y))
if flip == False:
screen.blit(pygame.transform.flip(zonic,False,False),(zonicrect.x,zonicrect.y))
# text = font.render('Lives: {0}'.format(life), True, green, blue)
def heart(beamrect,zonicrect,lazerect):
x = 1
i = -33
while life >= x:
x +=1
i+=32
screen.blit(lives, (2+i,0))
# draw
def drawingfunc(zonicrect,lazerect, beamrect,flip, zonczrect):
#screen.blit(death,(0,0))
screen.blit(bg, (0, 0))
heart(beamrect,zonicrect,lazerect)
flipx(flip,zonicrect)
#screen.blit(zonic,(zonicrect.x, zonicrect.y))
screen.blit(beam, (beamrect.x, beamrect.y+15))
screen.blit(lazerz, (lazerect.x+21,lazerect.y))
# score = font.render('Score: ')
# screen.blit(zonic, (zonczrect.x, zonczrect.y))
sore = font.render("Score: {0}".format(score), True, black, white)
screen.blit(sore, (30, 70))
pygame.draw.rect(screen, brown, pygame.Rect(200, 200, 100, 50))
pygame.display.update()
# mainloop and refresh rate
def main():
jump = False
jumpCount = 0
jumpMax = 15
flip = False
zonicrect = pygame.Rect(10, 250, zheight, zwidth)
lazerect = pygame.Rect(posx, posy, lheight, lwidth)
beamrect = pygame.Rect(posx, posy, bheight, bwidth)
zonczrect = pygame.Rect(50, 25, height2, width2)
# (30,0,32,32)
# livesrect = pygame.Rect(0,0,10,10)
clock = pygame.time.Clock()
run = True
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if not jump and event.key == pygame.K_SPACE:
jump = True
jumpCount = jumpMax
death()
collider(life,beamrect,zonicrect,lazerect)
keyspressed = pygame.key.get_pressed()
KWS(keyspressed, zonicrect,flip)
updating(score, beamrect)
drawingfunc(zonicrect,lazerect, beamrect,flip, zonczrect)
flipx(flip, zonicrect)
if jump:
zonicrect.y -= jumpCount
if jumpCount > -jumpMax:
jumpCount -= 1
else:
jump = False
pygame.quit()
# calling function NOTE: needs to always be at the end of file
if __name__ == "__main__":
main()
Solution
In your death function, you forgot to call pygame.display.update() at the bottom of your loop. That's why you cannot see the death screen even when life is less than or equal to zero. Also, you need to add an event loop in your death function, so that the window will keep responding to events while the loop is running.
So change this:
def death():
while life <= 0:
death = font.render("Death", True, white)
screen.fill(black)
screen.blit(death, (250, 250))
To this:
def death():
while life <= 0:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
death = font.render("Death", True, white)
screen.fill(black)
screen.blit(death, (250, 250))
pygame.display.update()
Full Modified Code
# Your header should go here, each comment should be initialed -DK
import pygame, sys
import os
# https://youtu.be/jO6qQDNa2UY
pygame.init()
FPS = 60
# Useful Variables
# Size
size = height, width = 900, 500
zonhw = zheight, zwidth = 70, 70
scale2 = height2, width2 = 600, 300
lscale = lheight, lwidth = 80, 80
beamsz = bheight, bwidth = 50, 25
platz = pheight, pwidth = 10, 70
# RGB
white = (255, 255, 255)
black = (0, 0, 0)
blue = (0, 0, 128)
green = (0, 255, 0)
brown = (165, 42, 42)
# Speed
VEL = 5
beamspeed = 3
# Position
laserpos = posx, posy = 500, 250
# other
i = 0
life = 3
score = 0
# graphics
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Zonic bootleg")
font = pygame.font.Font('freesansbold.ttf', 32)
zonic = pygame.image.load(os.path.join("image", "zonic.gif"))
zonic = pygame.transform.scale(zonic, zonhw)
bg = pygame.image.load(os.path.join("image", "sonic-back.jpg"))
bg = pygame.transform.scale(bg, size)
gg = pygame.image.load(os.path.join("image", "gg.jpg"))
gg = pygame.transform.scale(gg, size)
lazerz = pygame.image.load(os.path.join("image", "Lazerz.gif"))
lazerz = pygame.transform.scale(lazerz, lscale)
beam = pygame.image.load(os.path.join("image", "laserbeam.jpg"))
beam = pygame.transform.scale(beam, beamsz)
lives = pygame.image.load(os.path.join("image", "health.png"))
lives = pygame.transform.scale(lives, (40, 40))
# zoncz = pygame.image.load(os.path.join("image","zoncz.png"))
# zoncz = pygame.transform.scale(zoncz, scale2)
# coalitions
def collider(life, beamrect, zonicrect, lazerect):
beamrect.x -= beamspeed
if zonicrect.colliderect(beamrect):
beamrect.x = lazerect.x + 21
zonicrect.x = 0
if beamrect.x < -60:
# screen.blit(beam, (posx, posy))
beamrect.x += 550
def updating(score, beamrect):
if beamrect.x == 0:
score += 1
# Death
def death():
while life <= 0:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
death = font.render("Death", True, white)
screen.fill(black)
screen.blit(death, (250, 250))
pygame.display.update()
# zonic movement
def KWS(keyvar, zonicrect, flip):
if keyvar[pygame.K_RIGHT]: # right
zonicrect.x += VEL
flip = False
if zonicrect.x > 500:
zonicrect.x -= VEL
if keyvar[pygame.K_LEFT] and zonicrect.x + VEL > 0: # left
zonicrect.x -= VEL
flip = True
def flipx(flip, zonicrect):
if flip:
screen.blit(pygame.transform.flip(zonic, True, False), (zonicrect.x, zonicrect.y))
if flip == False:
screen.blit(pygame.transform.flip(zonic, False, False), (zonicrect.x, zonicrect.y))
# text = font.render('Lives: {0}'.format(life), True, green, blue)
def heart(beamrect, zonicrect, lazerect):
x = 1
i = -33
while life >= x:
x += 1
i += 32
screen.blit(lives, (2 + i, 0))
# draw
def drawingfunc(zonicrect, lazerect, beamrect, flip, zonczrect):
# screen.blit(death,(0,0))
screen.blit(bg, (0, 0))
heart(beamrect, zonicrect, lazerect)
flipx(flip, zonicrect)
# screen.blit(zonic,(zonicrect.x, zonicrect.y))
screen.blit(beam, (beamrect.x, beamrect.y + 15))
screen.blit(lazerz, (lazerect.x + 21, lazerect.y))
# score = font.render('Score: ')
# screen.blit(zonic, (zonczrect.x, zonczrect.y))
sore = font.render("Score: {0}".format(score), True, black, white)
screen.blit(sore, (30, 70))
pygame.draw.rect(screen, brown, pygame.Rect(200, 200, 100, 50))
pygame.display.update()
# mainloop and refresh rate
def main():
jump = False
jumpCount = 0
jumpMax = 15
flip = False
zonicrect = pygame.Rect(10, 250, zheight, zwidth)
lazerect = pygame.Rect(posx, posy, lheight, lwidth)
beamrect = pygame.Rect(posx, posy, bheight, bwidth)
zonczrect = pygame.Rect(50, 25, height2, width2)
# (30,0,32,32)
# livesrect = pygame.Rect(0,0,10,10)
clock = pygame.time.Clock()
run = True
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if not jump and event.key == pygame.K_SPACE:
jump = True
jumpCount = jumpMax
death()
collider(life, beamrect, zonicrect, lazerect)
keyspressed = pygame.key.get_pressed()
KWS(keyspressed, zonicrect, flip)
updating(score, beamrect)
drawingfunc(zonicrect, lazerect, beamrect, flip, zonczrect)
flipx(flip, zonicrect)
if jump:
zonicrect.y -= jumpCount
if jumpCount > -jumpMax:
jumpCount -= 1
else:
jump = False
pygame.quit()
sys.exit(0)
# calling function NOTE: needs to always be at the end of file
if __name__ == "__main__":
main()
I am trying to make a game but when I run the code the game makes 5 enemies appear on the screen. It says "invalid position for blit". However, I don't know where to put the blit otherwise.
I don't know what to do because I'm still learning. The code was fine before adding the copies. Not sure why that changed anything.
This is the full code:
import pygame
import random
import math
from pygame import mixer
#Imports the pygame, random, math and mixer module.
pygame.init()
#Initializes Pygame
screen = pygame.display.set_mode((800,600))
#Sets the screen to pygame looks and not normal python looks.
pygame.display.set_caption("Draft")
#Changes the title
icon = pygame.image.load('C:/Users/user/Desktop/Python/CodingBee/doctor.png')
pygame.display.set_icon(icon)
#Changing the Icon
#Adding Background Music
mixer.music.load('C:/Users/user/Desktop/Python/CodingBee/sb_indreams.mp3')
mixer.music.play(-1)
#Loads the music and plays if until the window is closed
#Colors
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
green = (0,255,0)
blue = (0,0,255)
#Setting color variables to make it easier to access later in the code.
#Player
player_img = pygame.image.load('C:/Users/user/Desktop/Python/CodingBee/Doctor_Running-removebg-preview (1).png')
playerx = 20
playery = 390
playerx_change = 0
playery_change = 100
#Making the Player Variables.
#Health Kit
HealthImg = pygame.image.load('C:/Users/user/Desktop/Python/CodingBee/first-aid-kit.png')
HealthX = 20
HealthY = 405
HealthX_Change = -10
HealthY_Change = 0
Health_State = "ready"
#Making the Health_Kit Variables.
#Enemy
enemyImg = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
number_of_enemies = 5
#Making a list for all 5 enemies image, x value, y value, x value change, y value change. All variables are a seperate list
for i in range(number_of_enemies):
enemyImg.append(pygame.image.load('C:/Users/user/Desktop/Python/CodingBee/zombie.png'))
enemyX.append(random.randint(0, 736))
enemyY.append(random.randint(50, 150))
enemyX_change.append(0.45)
enemyY_change.append(40)
#Score
Score_Value = 0
font = pygame.font.Font('C:/Users/user/Desktop/Python/Pygame/Space_Invaders/Mostery.ttf', 25)
#The pygame font extension requires 2 values, the font and the font size.
#.ttf is a font extension
#Making the score variable
ScoreX = 10
ScoreY = 25
#Making a variable for the fonts x and y position
#Heading
headingfont = pygame.font.Font('C:/Users/user/Desktop/Python/Pygame/Space_Invaders/Bouncy-PERSONAL_USE_ONLY.otf', 45)
HeadingX = 230
HeadingY = 10
#Game Over
game_over_font = pygame.font.Font('C:/Users/user/Desktop/Python/Pygame/Space_Invaders/Bouncy-PERSONAL_USE_ONLY.otf', 64)
#Creating Classes
class Background():
def __init__(self):
self.bgimage = pygame.image.load('C:/Users/user/Desktop/Python/CodingBee/Ground.png')
self.rectBGimg = self.bgimage.get_rect()
self.bgY1 = 485
self.bgX1 = 0
self.bgY2 = 485
self.bgX2 = self.rectBGimg.width
self.moving_speed = 7
def update(self):
self.bgX1 -= self.moving_speed
self.bgX2 -= self.moving_speed
if self.bgX1 <= -self.rectBGimg.width:
self.bgX1 = self.rectBGimg.width
if self.bgX2 <= -self.rectBGimg.width:
self.bgX2 = self.rectBGimg.width
def render(self):
screen.blit(self.bgimage, (self.bgX1, self.bgY1))
screen.blit(self.bgimage, (self.bgX2, self.bgY2))
class Player():
def draw_player():
screen.blit(player_img,(playerx,playery))
def player_jump():
global playery
playery -= playery_change
class Enemy():
def draw_enemy(enemyx,enemyy,i):
screen.blit(enemyImg[i],(enemyx,enemyy))
def move_enemy():
global enemyX
enemyX[i] += enemyX_change[i]
class Health():
def fire_Health (x,y):
global Health_State
Health_State = "fire"
screen.blit(HealthImg, ( x + 70, y + 10))
class Other():
def isCollision(enemyX, enemyY, HealthX, HealthY):
distance = math.sqrt(math.pow(enemyX - HealthX, 2) + (math.pow(enemyY - HealthY, 2)))
if distance < 27:
return True
else:
return False
def show_heading():
Heading = headingfont.render("Health Run!", True, (255,255,255))
screen.blit(Heading,( HeadingX, HeadingY))
def show_score():
Score = font.render("Score = " + str(Score_Value), True, (255,255,255))
screen.blit(Score,( ScoreX, ScoreY))
def game_over():
Game_Over = headingfont.render("GAME OVER!", True, (255,255,255))
screen.blit(Game_Over,( 200, 400))
Score_Value = 0
#def states an event. The event will not occur unless you call the event in the main game loop.
back_ground = Background()
#Main Game Loop
clock = pygame.time.Clock()
SCEEN_UPDATE = pygame.USEREVENT
pygame.time.set_timer(SCEEN_UPDATE,150)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
Player.player_jump()
if event.key == pygame.K_SPACE:
if Health_State == "ready":
Health_Fire_Sound = mixer.Sound('C:/Users/user/Desktop/Python/Pygame/Space_Invaders/laser.wav')
Health_Fire_Sound.play()
Health.fire_Health(HealthX , HealthY)
HealthY = playery + 10
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
playery += playery_change
screen.fill(blue)
back_ground.update()
back_ground.render()
#Health Kit Movement
if HealthX >= 800:
HealthX = 20
Health_State = "ready"
if Health_State == "fire":
Health.fire_Health(HealthX , HealthY)
HealthX -= HealthX_Change
#Enemy Movement and collision check
for i in range(number_of_enemies):
if enemyX[i] > 20:
for j in range(number_of_enemies):
enemyY = 2000
Other.game_over()
break
Enemy.move_enemy()
collision = Other.isCollision(enemyX[i],enemyY[i],HealthX,HealthY)
if collision:
BulletY = 480
Bullet_State = "ready"
enemyX[i] = random.randint(800,900)
enemyY[i] = 405
HealthX = 20
Health_State = "ready"
Score_Value += 1
# draw objects
Player.draw_player()
Enemy.draw_enemy(enemyX,enemyY, i)
Other.show_heading()
Other.show_score()
# update display
pygame.display.update()
clock.tick(60)
enemyX and enemyY are list of coordinates. You have 2 options:
pass items of the lists to draw_enemy:
class Enemy():
def draw_enemy(enemyx, enemyy, i):
screen.blit(enemyImg[i], (enemyx, enemyy))
Enemy.draw_enemy(enemyX[i], enemyY[i], i)
Get the items from the lists in draw_enemy:
class Enemy():
def draw_enemy(enemyx, enemyy, i):
screen.blit(enemyImg[i], (enemyx[i], enemyy[i]))
Enemy.draw_enemy(enemyX, enemyY, i)
Anyway, I suggest to create an Enemy class with attributes (see Classes). Note that the following code is not complete, it is just an example of how to implement classes and use object instances. I'll leave it up to you as an exercise to incorporate it into your code.
class Enemy():
def __init__(self, image, x, y, changeX, changeY):
self.image = image
self.x = x
self.y = y
self.changeX = changeX
self.changeY = changeY
def move(self):
self.x += self.changeX
self.y += self.changeY
def draw(self):
screen.blit(self.image, (self.x, self.y))
number_of_enemies = 5
enemies = []
enemyImg = pygame.image.load('C:/Users/user/Desktop/Python/CodingBee/zombie.png')
for i in range(number_of_enemies):
x = random.randint(0, 736)
y = random.randint(50, 150)
enemy = Enemy(enemyImg, x, y, 0.45, 0)
enemies.append(enemy)
running = True
while running:
# [...]
for enemy in enemies:
enemy.move()
# [...]
for enemy in enemies:
enemy.draw()
# [...]
I was programming my game, with no lag and no problems. Suddenly, when I added my intro the game started lagging.
I have tried making a lot of functions, and replacing a lot of numbers with variables.
I removed the intro, and the game still lagged.
I just need help finding out why my game lags so much, even if it is as simple as this.
import pygame
import sys
import random
pygame.init()
pygame.display.set_caption("Game Draft")
clock = pygame.time.Clock()
# Variables
width = 500
height = 500
LightBlue = (0, 150, 215)
Pink = (215, 0, 100)
Green = (51, 204, 51)
Black = (0, 0, 0)
Blue = (0,0,255)
Yellow = (255, 255, 0)
White = (255, 255, 255)
background_color = (102, 204, 255)
scoreboard_color = (255,255,255)
plataform_color = (153, 102, 51)
intro_background = (128, 128, 128)
player_name = "Neme"
player_color = (0, 0, 0)
player_size = 20
player_vel_x = 20
player_vel_y = 10
player_pos = [width/2, height - (2 * player_size)]
enemy_color = (102, 51, 0)
enemy_size = 20
enemy_vel_x = 20
enemy_vel_y = 5
enemy_pos = [random.randint(0, width-enemy_size), (24 / 25 * height)]
enemy_list = [enemy_pos]
enemy_dif = 4
enemy_dif_increase = 2
enemy_amount = 30
prop_color = (0, 51, 204)
prop_size = 1
prop_vel_y = 5
prop_vel_x = 5
prop_pos = [random.randint(0, width - prop_size), (21 / 25 * height)]
prop_list = [prop_pos]
prop_dif = 4
prop_dif_increase = 2
prop_amount = 50
intro_size1 = 50
intro_size2 = 40
intro_sentence1 = "Welcome to Game Draft"
intro_sentence2 = "Press key to start!"
scoreboard_font = pygame.font.SysFont("monoface", 50)
ign_font = pygame.font.SysFont("monoface", player_size)
intro_font1 = pygame.font.SysFont("monoface", intro_size1)
intro_font2 = pygame.font.SysFont("monoface", intro_size2)
score = 0
#Velocity Functions
def player_level_y(score, player_vel_y):
pvy = player_vel_y
sc = score
if sc < 100:
pvy = player_size*.25
elif sc < 200:
pvy = player_size*.5
elif sc < 300:
pvy = player_size*.75
elif sc < 400:
pvy = player_size
elif sc < 500:
pvy = player_size*1.25
else:
pvy = player_size * 1.25
return pvy
def player_level_x(score, player_vel_x):
sc = score
pvx = player_vel_x
if sc < 100:
pvx = player_size/2
elif sc < 200:
pvx = player_size*.75
elif sc < 300:
pvx = player_size*1
elif sc < 400:
pvx = player_size*1.15
elif sc < 500:
pvx = player_size*1.25
else:
pvx = player_size * 1.25
return pvx
def enemy_level_y(score, enemy_vel_y):
sc = score
evy = enemy_vel_y
if sc < 100:
evy = enemy_dif + enemy_dif_increase*1
elif sc < 300:
evy = enemy_dif + enemy_dif_increase*2
elif sc < 500:
evy = enemy_dif + enemy_dif_increase*3
elif sc < 700:
evy = enemy_dif + enemy_dif_increase*4
elif sc < 1500:
evy = enemy_dif + enemy_dif_increase*5
else:
evy = enemy_dif + enemy_dif_increase*6
return enemy_vel_y
#Enemey Functions
def drop_enemies(enemy_list):
delay = random.random()
if len(enemy_list) < enemy_amount and delay < 0.1:
x_pos = random.randint(0, width - enemy_size)
y_pos = enemy_size
enemy_list.append([x_pos, y_pos])
def draw_enemies(enemy_list):
for enemy_pos in enemy_list:
pygame.draw.rect(screen, enemy_color, (enemy_pos[0], enemy_pos[1], enemy_size, enemy_size))
def update_enemy_pos(enemy_list, score):
for idx, enemy_pos in enumerate(enemy_list):
if enemy_pos[1] >= 0 and enemy_pos[1] <= height:
enemy_pos[1] += enemy_vel_y
else:
enemy_list.pop(idx)
score += 5
return score
# Prop Functions
def drop_props(prop_list):
delay = random.random()
if len(prop_list) < prop_amount and delay < 0.1:
x_pos = random.randint(0, width - prop_size)
y_pos = prop_size
prop_list.append([x_pos, y_pos])
def draw_props(prop_list):
for prop_pos in prop_list:
pygame.draw.rect(screen, prop_color, (prop_pos[0], prop_pos[1], width/20, prop_size))
def update_prop_pos(prop_list):
for idx, prop_pos in enumerate(prop_list):
if prop_pos[1] >= 0 and prop_pos[1] <= height:
prop_pos[1] += prop_vel_y
else:
prop_list.pop(idx)
# Boarder Functions
def boarder_left(player_pos):
if player_pos[0] <= 0 - player_size:
return True
return False
def boarder_right(player_pos):
if player_pos[0] >= width:
return True
return False
def boarder_down(player_pos):
if player_pos[1] >= height - player_size:
return True
return False
# Game_Over Functions
def collision_check(enemy_list, player_pos):
for enemy_pos in enemy_list:
if detect_collision(enemy_pos, player_pos):
return True
return False
def detect_collision(player_pos, enemy_pos):
p_x = player_pos[0]
p_y = player_pos[1]
e_x = enemy_pos[0]
e_y = enemy_pos[1]
if (e_x >= p_x and e_x < (p_x + enemy_size)) or (p_x >= e_x and p_x < (e_x + player_size)):
if (e_y >= p_y and e_y < (p_y + enemy_size)) or (p_y >= e_y and p_y < (e_y + player_size)):
return True
return False
#Winning Function
def winning(player_pos):
if player_pos[1] <= 0 - player_size:
return True
return False
# Intro Screen
screen = pygame.display.set_mode((width, height))
intro = True
while intro:
pygame.time.delay(100)
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
intro = False
screen.fill(intro_background)
welcome = str(intro_sentence1)
tfs1 = intro_font1.render(welcome, 1, White)
screen.blit(tfs1, (width/4 - intro_size1, height/3))
welcome = str(intro_sentence2)
tfs1 = intro_font2.render(welcome, 1, White)
screen.blit(tfs1, (width/4 - intro_size2, height/2))
clock.tick(60)
pygame.display.update()
# Game Screen
screen = pygame.display.set_mode((width, height))
game_over = False
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
x = player_pos[0]
y = player_pos[1]
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
x -= player_vel_x
elif keys[pygame.K_d] or keys[pygame.K_RIGHT]:
x += player_vel_x
elif keys[pygame.K_s] or keys[pygame.K_DOWN]:
y += player_vel_y
elif keys[pygame.K_w] or keys[pygame.K_UP]:
y -= player_vel_y
elif keys[pygame.K_PERIOD]:
enemy_dif += 1
elif keys[pygame.K_COMMA]:
enemy_dif -= 1
player_pos = [x,y]
screen.fill(background_color)
drop_enemies(enemy_list)
drop_props(prop_list)
score = update_enemy_pos(enemy_list, score)
player_vel_y = player_level_y(score, player_vel_y)
player_vel_x = player_level_x(score, player_vel_x)
enemy_vel_y = enemy_level_y(score, enemy_vel_y)
update_prop_pos(prop_list)
if boarder_left(player_pos):
player_pos[0] = width - player_size/2
if boarder_right(player_pos):
player_pos[0] = 0 - player_size/2
if boarder_down(player_pos):
player_pos[1] = height - player_size
if winning(player_pos):
enemy_amount = 0
enemy_vel_y = 0
player_pos[1] = 0
if collision_check(enemy_list, player_pos):
game_over = True
break
pygame.draw.rect(screen, plataform_color, (0, 0, width, height - (23 / 25 * height) ) )
pygame.draw.rect(screen, player_color, (player_pos[0], player_pos[1], player_size, player_size))
draw_enemies(enemy_list)
draw_props(prop_list)
scoreboard = str(score)
tfs2 = scoreboard_font.render(scoreboard, 1, scoreboard_color)
screen.blit(tfs2, (width - 125, height - ( .98 * height)))
ign = str(player_name)
tfs3 = ign_font.render(ign, 1, White)
screen.blit(tfs3, (player_pos[0] - player_size/4, player_pos[1] + player_size))
clock.tick(60)
pygame.display.update()
No error, just random lag.
[...] need help finding out why my game lags so much [...]
Of course, what do you expect? You've a delay of a 0.1 seconds in the game loop:
pygame.time.delay(100)
Delete the delay. Use pygame.time.Clock to control the frames per second and thus the game speed.
Further move the code which moves the player out of the event loop in the main application loop for a smooth movement:
while run:
#pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# <--
keys = pygame.key.get_pressed()
x = player_pos[0]
y = player_pos[1]
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
x -= player_vel_x
elif keys[pygame.K_d] or keys[pygame.K_RIGHT]:
x += player_vel_x
elif keys[pygame.K_s] or keys[pygame.K_DOWN]:
y += player_vel_y
elif keys[pygame.K_w] or keys[pygame.K_UP]:
y -= player_vel_y
elif keys[pygame.K_PERIOD]:
enemy_dif += 1
elif keys[pygame.K_COMMA]:
enemy_dif -= 1
player_pos = [x,y]
# [...]
clock.tick(30)
First post here. So I am trying to implement a Civilization type of movement game. At the moment, I have one sprite in a cell. I can click it and then if I click another grid, the sprite moves there. What I now want is to spawn 5-6 such sprites, and then do the same thing. Click on a sprite and then click another grid, and that specific sprite moves there without affecting the other sprites. I cannot seem to do that. I can spawn 5-6 random sprites at different grids, but when I click on one of them and then click another grid, all the other sprites are gone. The code is below (not the best as I am learning Pygame). I understand that I have to somehow only update the sprite that was clicked, but I am not sure how to do that.
import pygame
import random
WIDTH = 900
HEIGHT = 700
FPS = 2
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
TURN = "TeamOne"
def main():
# Pygame sprite Example
global x_lines
global y_lines
x_lines = [WIDTH-i*WIDTH/20 for i in range(20,0, -1)]
y_lines = [HEIGHT-j*HEIGHT/20 for j in range(20,0, -1)]
class TeamOne(pygame.sprite.Sprite):
# sprite for the Player
def __init__(self):
# this line is required to properly create the sprite
pygame.sprite.Sprite.__init__(self)
# create a plain rectangle for the sprite image
self.image = pygame.Surface((WIDTH / 20, HEIGHT / 20))
self.image.fill(GREEN)
# find the rectangle that encloses the image
self.rect = self.image.get_rect()
# center the sprite on the screen
self.rect.center = ((random.randint(1,19)*2+1)* WIDTH/ 40, (random.randint(1,19)*2+1)*HEIGHT/40)
def update(self, position):
# any code here will happen every time the game loop updates
(a, b) = position
for index, i in enumerate(x_lines):
if i > a:
self.rect.x = x_lines[index-1]
break
for index, j in enumerate(y_lines):
if j > b:
self.rect.y = y_lines[index-1]
break
# initialize pygame and create window
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("A Game")
clock = pygame.time.Clock()
clicked_sprites = pygame.sprite.Group()
teamone_sprites = pygame.sprite.Group()
for i in range(5):
mob1 = TeamOne()
teamone_sprites.add(mob1)
# Game loop
running = True
j=0
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN and j == 0:
pos = pygame.mouse.get_pos()
for s in teamone_sprites:
if s.rect.collidepoint(pos):
#teamone_sprites.add(s)
clicked_sprites.add(s)
print (clicked_sprites)
j = 1
elif event.type == pygame.MOUSEBUTTONDOWN and j == 1:
new_pos = pygame.mouse.get_pos()
#teamone_sprites.update(new_pos)
clicked_sprites.update(new_pos)
j = 0
# Update
# Draw / render
## screen.fill(BLACK)
## draw_grid(screen)
##
## teamone_sprites.draw(screen)
##
##
##
## # *after* drawing everything, flip the display
## pygame.display.flip()
# Draw / render
screen.fill(BLACK)
draw_grid(screen)
teamone_sprites.draw(screen)
pygame.display.flip()
pygame.quit()
def draw_grid(screen):
for i in range(1, HEIGHT, int(HEIGHT/20)):
pygame.draw.line(screen, GREEN, (1,i) ,(WIDTH,i), 2)
for j in range(1, WIDTH, int(WIDTH/20)):
pygame.draw.line(screen, GREEN, (j,1) ,(j,HEIGHT), 2)
if __name__ == '__main__':
main()
Some tips for you:
Keep your main loop clean
Put logic where it belongs
Only call pygame.display.flip()/pygame.display.update() once
Don't use variable names like j
Since your game is grid based, you should have a way to translate between grid coordinates and screen coordinates
Here's a simple runnable example I hacked together (see the comments for some explanations):
import pygame
import random
WIDTH = 900
HEIGHT = 700
ROWS = 20
COLUMNS = 20
TILE_SIZE = WIDTH / COLUMNS, HEIGHT / ROWS
TILE_W, TILE_H = TILE_SIZE
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
TURN = "TeamOne"
# some functions to translate grid <-> screen coordinates
def posToScreen(pos):
column, row = pos
return column * TILE_W, row * TILE_H
def screenToPos(pos):
column, row = pos
return column / TILE_W, row / TILE_H
def draw_grid(screen):
for i in range(1, HEIGHT, TILE_H):
pygame.draw.line(screen, GREEN, (1,i) ,(WIDTH,i), 2)
for j in range(1, WIDTH, TILE_W):
pygame.draw.line(screen, GREEN, (j,1) ,(j,HEIGHT), 2)
# a class that handles selecting units
class Cursor(pygame.sprite.Sprite):
def __init__(self, units, *groups):
pygame.sprite.Sprite.__init__(self, *groups)
# group of the units that can be controlled
self.units = units
# we create two images
# to indicate if we are selecting or moving
self.image = pygame.Surface(TILE_SIZE)
self.image.set_colorkey((43,43,43))
self.image.fill((43,43,43))
self.rect = self.image.get_rect()
self.selected_image = self.image.copy()
pygame.draw.rect(self.image, pygame.Color('red'), self.image.get_rect(), 4)
pygame.draw.rect(self.selected_image, pygame.Color('purple'), self.image.get_rect(), 4)
self.base_image = self.image
self.selected = None
def update(self):
# let's draw the rect on the grid, based on the mouse position
pos = pygame.mouse.get_pos()
self.rect.topleft = posToScreen(screenToPos(pos))
def handle_click(self, pos):
if not self.selected:
# if we have not selected a unit, do it now
for s in pygame.sprite.spritecollide(self, self.units, False):
self.selected = s
self.image = self.selected_image
else:
# if we have a unit selected, just set its target attribute, so it will move on its own
self.selected.target = posToScreen(screenToPos(pos))
self.image = self.base_image
self.selected = None
class TeamOne(pygame.sprite.Sprite):
def __init__(self, *groups):
pygame.sprite.Sprite.__init__(self, *groups)
self.image = pygame.Surface(TILE_SIZE)
self.image.fill(GREEN)
self.pos = random.randint(0, COLUMNS), random.randint(0, ROWS)
self.rect = self.image.get_rect(topleft = posToScreen(self.pos))
self.target = None
def update(self):
# do nothing until target is set
# (maybe unset it if we reached our target)
if self.target:
if self.rect.x < self.target[0]:
self.rect.move_ip(1, 0)
elif self.rect.x > self.target[0]:
self.rect.move_ip(-1, 0)
elif self.rect.y < self.target[1]:
self.rect.move_ip(0, 1)
elif self.rect.y > self.target[1]:
self.rect.move_ip(0, -1)
self.pos = screenToPos(self.rect.topleft)
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("A Game")
clock = pygame.time.Clock()
all_sprites = pygame.sprite.LayeredUpdates()
team_ones = pygame.sprite.Group()
for i in range(5):
TeamOne(all_sprites, team_ones)
cursor = Cursor(team_ones, all_sprites)
# a nice, simple, clean main loop
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# we could also pass all events to all sprites
# so we would not need this special clause for the cursor...
if event.type == pygame.MOUSEBUTTONDOWN:
cursor.handle_click(event.pos)
all_sprites.update()
screen.fill(BLACK)
draw_grid(screen)
all_sprites.draw(screen)
pygame.display.flip()
if __name__ == '__main__':
main()
I have pretty much the same Problem i have a healthbar for my enemie but i want all enemys to have one so its a spritegroup now if i want to change an attribute of one object out of my spritegroup i dont know how to properly access it. The Problem lays in the Healthbaranimation function. I tried self.healthbar.sprites() self.healthbar.sprites and spritedict nothing really semms to work. Is there an easy way to fix this? P.s sorry for my bad code It is my first real attempt making a small game
from os import path
import pygame
from elements.ammo import AMMO
from elements.bigenemy import BIGENEMY
from elements.enemy import ENEMY
from elements.player import PLAYER
from .base import BaseState
from elements.healthbar import HEALTHBAR
class Gameplay(BaseState):
def __init__(self):
super(Gameplay, self).__init__()
self.next_state = "GAME_OVER"
self.x, self.y = 100, 1030
self.playersprite = PLAYER((self.x, self.y))
self.bigenemy = pygame.sprite.GroupSingle(BIGENEMY())
self.bottomrect = pygame.Rect((0, 1030), (1920, 50))
self.enemysprite = ENEMY()
self.ammosprite = AMMO()
self.healthbar = pygame.sprite.Group(HEALTHBAR())
self.displayedimage = self.playersprite.image
self.displayedrect = self.playersprite.rect
self.highscore = self.load_data()
self.points = 0
self.scoretext = f"SCORE: {self.points}"
self.scoresurf = self.font.render(self.scoretext, True, "red")
self.nhstext = "NEW HIGHSCORE!"
self.nhssurf = self.font.render(self.nhstext, True, "red")
self.ammotext = f"AMMO:{self.playersprite.ammunition}"
self.ammosurf = self.font.render(self.ammotext, True, "red")
self.bulletgroup = pygame.sprite.Group()
self.time_active = 0
self.bigenemyexisting = True
def get_event(self, event):
if event.type == pygame.QUIT:
self.quit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LCTRL:
self.playersprite.crouching = True
elif event.key == pygame.K_SPACE:
self.playersprite.jumping = True
elif event.key == pygame.K_q and self.playersprite.ammunition != 0:
self.playersprite.shooting = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_ESCAPE:
self.done = True
elif event.key == pygame.K_LCTRL:
self.playersprite.crouching = False
elif event.key == pygame.K_q:
self.playersprite.shooting = False
def draw(self, surface):
surface.fill(pygame.Color("black"))
pygame.draw.rect(surface, "red", self.bottomrect)
surface.blit(self.displayedimage, (self.displayedrect))
surface.blit(self.enemysprite.image, (self.enemysprite.rect))
surface.blit(self.ammosprite.image, (self.ammosprite.rect))
self.healthbar.draw(surface)
self.bigenemy.draw(surface)
self.bulletgroup.draw(surface)
surface.blit(self.scoresurf, (0, 0))
surface.blit(self.ammosurf, (0, 1000))
if self.points > self.highscore: surface.blit(self.nhssurf, (1920 / 2 - 100, 1080 / 2))
def lost(self):
self.enemysprite.startposx = 1920
self.enemysprite.startposy = self.enemysprite.gettypeenemy()
self.enemysprite.speed = 10
self.highscorefunc()
self.points = 0
self.playersprite.ammunition = 30
def collidecheck(self):
self.playermask = pygame.mask.from_surface(self.displayedimage)
self.enemymask = pygame.mask.from_surface(self.enemysprite.image)
offsetx = self.enemysprite.rect.left - self.displayedrect.left
offsety = self.enemysprite.rect.top - self.displayedrect.top
if self.displayedrect.colliderect(self.enemysprite.rect):
if self.playermask.overlap(self.enemymask, (offsetx, offsety)):
self.lost()
self.done = True
elif self.enemysprite.rect.x < 0 and self.enemysprite.speed < 25:
self.points += 1
self.enemysprite.speed += 1
elif self.enemysprite.speed > 25:
self.enemysprite.speed += .5
elif self.displayedrect.colliderect(self.ammosprite.rect):
self.ammosprite.startposx = 2300
self.playersprite.ammunition += 30
elif pygame.sprite.groupcollide(self.bigenemy,self.bulletgroup,False,True):
self.bigenemy.sprite.health -= 10
def shooting(self, dt):
if self.playersprite.ammunition != 0:
if self.playersprite.shooting and not self.playersprite.jumping and not self.playersprite.crouching:
self.time_active += dt
if self.time_active >= 100:
self.bulletgroup.add(self.playersprite.createbullet())
self.time_active = 0
self.playersprite.ammunition -= 1
else:
self.playersprite.shooting = False
def highscorefunc(self):
if self.points > self.highscore:
self.highscore = self.points
with open(path.join(self.dir, self.HS_FILE), 'w') as f:
f.write(str(self.highscore))
def animation(self):
if not self.playersprite.shooting and not self.playersprite.jumping and not self.playersprite.crouching:
if self.playersprite.index >= len(self.playersprite.basicanimation):
self.playersprite.index = 0
self.displayedimage = self.playersprite.basicanimation[int(self.playersprite.index)]
self.playersprite.index += .1
elif self.playersprite.shooting and not self.playersprite.jumping:
if self.playersprite.index >= len(self.playersprite.shootanimation):
self.playersprite.index = 0
self.displayedimage = self.playersprite.shootanimation[int(self.playersprite.index)]
self.playersprite.index += .1
elif self.playersprite.jumping:
self.displayedimage = self.playersprite.imagejump
elif self.playersprite.crouching:
self.displayedimage = self.playersprite.slidingimage
def healthbaranimation(self):
if self.bigenemy.sprite.health < 90:
self.healthbar.spritedict.index = 1
if self.bigenemy.sprite.health < 80:
self.healthbar.sprite.index = 2
if self.bigenemy.sprite.health < 70:
self.healthbar.sprite.index = 3
if self.bigenemy.sprite.health < 60:
self.healthbar.sprite.index = 4
if self.bigenemy.sprite.health < 50:
self.healthbar.sprite.index = 5
if self.bigenemy.sprite.health < 40:
self.healthbar.sprite.index = 6
if self.bigenemy.sprite.health < 30:
self.healthbar.sprite.index = 7
if self.bigenemy.sprite.health < 20:
self.healthbar.sprite.index = 8
if self.bigenemy.sprite.health < 10:
self.healthbar.sprite.index = 9
def spawnbigenemies(self):
if self.bigenemyexisting:
if self.bigenemy.sprite.health < 3:
self.bigenemy.add(BIGENEMY())
self.bigenemyexisting = True
def update(self, dt):
try:
self.bigenemy.sprite.update()
except:
pass
self.healthbaranimation()
self.healthbar.update()
self.playersprite.jump()
self.animation()
self.shooting(dt)
self.bulletgroup.update()
self.enemysprite.update()
self.ammosprite.update()
self.collidecheck()
self.spawnbigenemies()
self.scoretext = f"SCORE: {self.points}"
self.scoresurf = self.font.render(self.scoretext, True, "black")
self.ammotext = f"AMMO:{self.playersprite.ammunition}"
self.ammosurf = self.font.render(self.ammotext, True, "red")