I am very new to pygame, and was trying to make a basic tower defence game. I have looked around, but cannot grasp how to create multiple sprites (towers) from the image I am using. Here is my code for the TD game. But I do not know how to create more then one sprite(tower).
import pygame
import time
import sys
import os
pygame.init()
WINDOWWIDTH = 1800
WINDOWHEIGHT = 1800
os.environ['SDL_VIDEO_WINDOW_POS'] = '%i,%i' % (0,30)
screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
background_colour = (63,200,70)
GAMETITLE = "Tower Defence"
font = pygame.font.SysFont(None, 100)
def balanceText(balance):
screen_text = font.render(str(balance), True, (255,255,255))
screen.blit(screen_text, [1640,0])
def main():
balance = 100
pygame.display.set_caption(GAMETITLE)
clock = pygame.time.Clock()
spritegroup =pygame.sprite.Group()
sprite =pygame.sprite.Sprite()
tower = sprite.image = pygame.image.load("tower.png")
sprite.image = tower
sprite.rect = sprite.image.get_rect()
drawTower = False
towerPlaced = False
bulletX = 250
sprite.add(spritegroup)
while True:
screen.fill(background_colour)
pygame.draw.rect(screen, (255,255,255), ((0, 100), (1100, 90)))
pygame.draw.rect(screen, (255, 255,255), ((1010, 100), (100, 600)))
pygame.draw.rect(screen, (255,255,255), ((1010, 700), (2400, 90)))
pygame.draw.rect(screen, (139,69,19), ((1600, 0), (2400, 18000)))
pygame.draw.rect(screen, (128, 128,128), (( 300,250), (140,140)))
pygame.draw.rect(screen, (128, 128,128), (( 600,250), (140,140)))
pygame.draw.rect(screen, (128, 128,128), (( 800,700), (140,140)))
pygame.draw.rect(screen, (128, 128,128), (( 1150,500), (140,140)))
balanceText(balance)
if drawTower:
spritegroup.draw(screen)
pygame.display.update()
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if pygame.mouse.get_pressed()[0] and towerPlaced == False:
mousePos = pygame.mouse.get_pos()
if mousePos[0] > 300 and mousePos[0] < 450 and mousePos[1] > 250 and mousePos[1] < 400:
drawTower = True
sprite.rect.center = (370, 320)
towerPlaced = True
balance -= 50
elif mousePos[0] > 600 and mousePos[0] < 750 and mousePos[1] > 250 and mousePos[1] < 400:
drawTower = True
sprite.rect.center = (670, 320)
towerPlaced = True
balance-= 50
elif mousePos[0] > 800 and mousePos[0] < 950 and mousePos[1] > 700 and mousePos[1] < 850:
drawTower = True
sprite.rect.center = (870, 770)
towerPlaced = True
balance-= 50
elif mousePos[0] > 1150 and mousePos[0] < 1300 and mousePos[1] > 500 and mousePos[1] < 650:
drawTower = True
sprite.rect.center = (1220, 570)
towerPlaced = True
balance-= 50
elif event.type == pygame.QUIT:
pygame.display.quit()
main()
Thanks for looking! I know the code is a bit messy right now.
You need to use the Sprite class properly. To do this, you have to define your sprite as a subclass of pygame.sprite.Sprite, like this:
class Tower(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = tower_img
self.rect = self.image.get_rect()
self.rect.center = (x, y)
Then you can spawn a tower any time you like by creating another instance and adding it to the group:
new_tower = Tower(370, 320)
spritegroup.add(new_tower)
I highly recommend looking at the Sprite documentation:
http://www.pygame.org/docs/ref/sprite.html - there's lots of good info in there. There are also many good tutorials out there that go into how you use sprites in Pygame. Here is a link to one that I wrote:
http://kidscancode.org/blog/2016/08/pygame_1-2_working-with-sprites/
Related
This question already has answers here:
Pygame mouse clicking detection
(4 answers)
How can I add an image or icon to a button rectangle in Pygame?
(1 answer)
Closed 2 years ago.
I have a problem in my code that I want one of your guys to answer.
This is my code:
import pygame
import time
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((1280,720))
pygame.display.set_caption('Title Screen')
titleim = pygame.image.load(r'C:\Users\kp876\Desktop\Temorairy\Kill, Buy, Repeat title screen.png')
menuim = pygame.image.load(r'C:\Users\kp876\Desktop\Menu Screen.png')
playim = pygame.image.load(r'C:\Users\kp876\Desktop\play button.png')
stickmanim = pygame.image.load('stickmanfightpose.png')
shopim = pygame.image.load('shop.png')
rplayim = pygame.transform.scale(playim,(640,360))
rshopim = pygame.transform.scale(shopim,(35,30))
popSound = pygame.mixer.Sound('Pop sound effect.wav')
menumusic = pygame.mixer.music.load('awesomeness.mp3')
screen.blit(titleim,(0,0))
pygame.display.update()
running = True
running_title = True
mouse = False
coins = 10
def redrawTitle():
screen.fill((0,0,0))
screen.blit(titleim,(0,0))
def fade(w,h):
val1 = 1
fade = pygame.Surface((w,h))
fade.fill((0,0,0))
for alpha in range(0, 300, 5):
fade.set_alpha(alpha)
redrawTitle()
screen.blit(fade,(0,0))
pygame.display.update()
def mainmenuText():
font = pygame.font.Font("C:\Windows\Fonts\Arial.ttf", 100)
text = font.render('Press Play',True,(210,200,200))
playRect = pygame.Rect((555,480),(150,120))
pygame.draw.rect(screen, (0,0,0), playRect)
screen.blit(menuim,(0,0))
screen.blit(text,(380,340))
screen.blit(rplayim,(310,360))
pygame.display.update()
def shop():
pistol = False
rifle = False
machinegun = False
white = (255,255,255)
screen.fill(white)
global coins
shopfont = pygame.font.Font("C:\Windows\Fonts\Arial.ttf", 60)
shoptxt = shopfont.render('Shop', True,(0,0,0))
coinstxt = shopfont.render('Coins:' + str(coins) + '!',True,(0,0,0))
namefont = pygame.font.Font("C:\Windows\Fonts\Arial.ttf", 35)
pistoltxt = namefont.render('Pistol', True, (0,0,0))
rifletxt = namefont.render('Rifle', True, (0,0,0))
machineguntxt = namefont.render('Machine Gun', True, (0,0,0))
mainshopR = pygame.Rect((0,0),(1280,720))
pistolR = pygame.Rect((50,150),(200,200))
rifleR = pygame.Rect((450,150),(200,200))
machinegunR = pygame.Rect((850,150),(200,200))
pygame.draw.rect(screen, white, mainshopR)
pygame.draw.rect(screen, (128,128,128),pistolR)
pygame.draw.rect(screen, (128,128,128),rifleR)
pygame.draw.rect(screen, (128,128,128),machinegunR)
screen.blit(shoptxt,(500,30))
screen.blit(pistoltxt,(100,220))
screen.blit(rifletxt,(500,220))
screen.blit(machineguntxt,(850,220))
screen.blit(coinstxt,(50,30))
pygame.display.update()
if pistol:
pygame.draw.rect(screen, (0,128,0),pistolR)
if rifle:
pygame.draw.rect(screen, (0,128,0),rifleR)
if machinegun:
pygame.draw.rect(screen, (0,128,0),machinegunR)
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
spot1 = pygame.mouse.get_pos()
if spot1[0] > 50 and spot1[0] < 250 and spot1[1] > 150 and spot1[1] < 350:
if not pistol:
if coins >= 3:
pistolR = pygame.Rect((50,150),(200,200))
pygame.event.get()
pistol = True
coins = coins - 3
screen.blit(coinstxt,(50,30))
pygame.draw.rect(screen, (0,128,0),pistolR)
pygame.display.update()
pygame.event.get()
if spot1[0] > 450 and spot1[0] < 650 and spot1[1] > 150 and spot1[1] < 350:
if not rifle:
if coins >= 10:
pygame.event.get()
rifle = True
coins = coins - 10
print(coins)
screen.blit(coinstxt,(50,30))
pygame.draw.rect(screen, (0,128,0),rifleR)
pygame.display.update()
pygame.event.get()
if spot1[0] > 850 and spot1[0] < 1050 and spot1[1] > 150 and spot1[1] < 350:
if not machinegun:
if coins >= 30:
pygame.event.get()
machinegun = True
coins = coins - 30
print(coins)
screen.blit(coinstxt,(50,30))
pygame.draw.rect(screen, (0,128,0),machinegunR)
pygame.display.update()
pygame.event.get()
mouse1 = False
val1 = 0
nop = True
music = True
pygame.mixer.music.play(-1)
while running_title:
pygame.event.get()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running_title = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse1 = True
if mouse1 and val1 == 1:
spot = pygame.mouse.get_pos()
if spot[0] < 705 and spot[0] > 555 and spot[1] < 600 and spot[1] > 480:
pygame.event.get()
White = (255,250,250)
screen.fill(White)
pygame.display.update()
pygame.mixer.music.stop()
popSound.play()
screen.blit(stickmanim,(0,0))
pygame.display.update()
running_title = False
if val1 == 0:
time.sleep(3)
fade(1280,720)
pygame.display.update()
mouse = True
if mouse and nop:
screen.blit(menuim,(0,0))
time.sleep(1)
mainmenuText()
val1 = 1
nop = False
go = True
while running:
if go:
fightR = pygame.Rect((550,300),(100,60))
fightborderR = pygame.Rect((540,294),(120,72))
fightfont = pygame.font.Font("C:\Windows\Fonts\Arial.ttf", 25)
fightext = fightfont.render('FIGHT!',True,(0,0,0))
shopR = pygame.Rect((100,20),(130,50))
shopborderR = pygame.Rect((90,15),(150,60))
coinsfont = pygame.font.Font("C:\Windows\Fonts\Arial.ttf", 50)
shoptext = fightfont.render('Shop',True,(0,0,0))
pygame.draw.rect(screen,(0,0,0),fightborderR)
pygame.draw.rect(screen,(128,128,128),fightR)
pygame.draw.rect(screen,(0,0,0),shopborderR)
pygame.draw.rect(screen,(128,128,128),shopR)
screen.blit(fightext,(555,303))
screen.blit(rshopim,(110,30))
screen.blit(shoptext,(150,28))
pygame.display.update()
go = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
running_title = False
if event.type == pygame.MOUSEBUTTONDOWN:
spot = pygame.mouse.get_pos()
if spot[0] < 660 and spot[0] > 540 and spot[1] < 366 and spot[1] > 294:
yo = 'yo'
if spot[0] < 240 and spot[0] > 90 and spot[1] > 15 and spot[1] < 85:
shop()
in shop() I want it to recognize when someone clicks an item(pistol,rifle,etc) but whenever i click it nothing happens.
Hopefully this is enough details for you guys to fix my problem.
Thanks!
ps. This code is not finished. So if you are wondering why there is some weird stuff in the code, most likely it's for later on in the project.
I am struggling to understand the logic behind your code, but the problem is caused because you are calling pygame.event.get() multiple times. Its everywhere. Sometimes you are calling it and not even storing it which again is probably not what you want. In your main loop, call it once and store it.
while running:
events = pygame.event.get()
for event in events:
#do stuff with it
Then to get access to events in shop, take events as argument def shop(events) and use this events variable instead of pygame.event.get().
Then in the main loop, when you call shop, pass events as argument.
if spot[0] < 240 and spot[0] > 90 and spot[1] > 15 and spot[1] < 85:
shop(events)
You are also calling pygame.display.update() multiple times, which might cause flickering problems.
They pygame game i made has run into an error i cannot figure out how to fix. I am new to pygame. I am using pycharm and python version 3.
The error is stopping me from doing anything and it won't even let me run it
Here is the error
TypeError: pow expected 2 arguments, got 1
Here is the code:
import math
import random
import pygame
pygame.init()
# Screen (Pixels by Pixels (X and Y (X = right and left Y = up and down)))
screen = pygame.display.set_mode((800, 600))
running = True
# Title and Icon
pygame.display.set_caption("Space Invaders")
icon = pygame.image.load('Icon.png')
pygame.display.set_icon(icon)
# Player Icon/Image
playerimg = pygame.image.load('Player.png')
playerX = 370
playerY = 480
playerX_change = 0
def player(x, y):
# Blit means Draw
screen.blit(playerimg, (x, y))
def enemy(x, y):
# Blit means Draw
screen.blit(enemyimg, (x, y))
def fire_bullet(x, y):
global bullet_state
bullet_state = "fire"
screen.blit(bulletimg, (x + 16, y + 10))
def isCollision(enemyX, enemyY, bulletX, bulletY):
distance = math.sqrt(math.pow(enemyX - bulletX, 2) + math.pow(math.pow(enemyY - bulletY,2)))
if distance < 27:
return True
else:
return False
background = pygame.image.load('247.jpg')
enemyimg = pygame.image.load('space-invaders.png')
enemyX = random.randint(0, 800)
enemyY = random.randint(50, 150)
enemyX_change = 4
enemyY_change = 40
bulletimg = pygame.image.load('bullet.png')
bulletX = 0
bulletY = 450
bulletX_change = 480
bulletY_change = 10
bullet_state = "ready"
score = 0
# Game loop (Put most of code for game in this loop)
while running:
screen.fill((255, 0, 0))
# BAckground
screen.blit(background, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# if keystroke is pressed check whether is right or left
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)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# RGB (screen.fill) = red green blue
# 5 = 5 + - 0.1 -> 5 = 5 - 0.1
# making so nothing can go out of bounds
enemyX += enemyX_change
if enemyX <= 0:
enemyX_change = 4
enemyY += enemyY_change
elif enemyX >= 736:
enemyX_change = -4
enemyY += enemyY_change
playerX += playerX_change
if playerX <= 0:
playerX = 0
elif playerX >= 736:
playerX = 736
# Bullet movement
if bulletY <= 0:
bulletY = 480
bullet_state = "ready"
if bullet_state == "fire":
fire_bullet(bulletX, bulletY)
bulletY -= bulletY_change
# Collison
collision = isCollision(enemyX,enemyY,bulletX,bulletY)
if collision:
bulletY = 480
bullet_state = "ready"
score +=1
player(playerX, playerY)
enemy(enemyX, enemyY)
pygame.display.update()
I believe you are trying to do this:
c = sqrt(pow(a,2) + pow(b,2))
But your code has one pow() too many. Happens to me too, when I make I make game sounds in my head, while coding (pew pew pew)
Try to remove one pow()
from
math.pow(math.pow(enemyY - bulletY,2))
to
math.pow(enemyY - bulletY,2)
I want to add a bomb into my flappy bird game, but it won't detect the collision.
I cannot get the bird to be detected if it made contact with the bomb
The bomb shows up, but I can't make it detect the collision with the bird, here is the code I have so far.Also,i did not class a new object for the bomb, I put it in with the pipes.
I think there is a problem with the colliderect
If you need the images please tell me.
Can someone help me?
I would be really grateful.
Thanks in advance.
import pygame
from pygame.locals import * # noqa
import sys
import random
class FlappyBird:
def __init__(self):
self.screen = pygame.display.set_mode((400, 708))
self.bird = pygame.Rect(65, 50, 50, 50)
self.background = pygame.image.load("background.png")
self.birdSprites = [pygame.image.load("1.png"),
pygame.image.load("2.png"),
pygame.image.load("dead.png")]
self.wallUp = pygame.image.load("bottom.png")
self.wallDown = pygame.image.load("top.png")
self.bomb = pygame.image.load("bomb.png")
self.bombx = 600
self.gap = 130
self.wallx = 400
self.birdY = 350
self.jump = 0
self.jumpSpeed = 10
self.gravity = 5
self.dead = False
self.sprite = 0
self.counter = 0
self.offset = random.randint(-110, 110)
def updateWalls(self):
self.wallx -= 2
if self.wallx < -80:
self.wallx = 400
self.counter += 1
self.offset = random.randint(-110, 110)
self.bombx -= 2
if self.bombx < -80:
self.bombx = 600
self.counter += 1
self.offset = random.randint(-110, 110)
def birdUpdate(self):
if self.jump:
self.jumpSpeed -= 1
self.birdY -= self.jumpSpeed
self.jump -= 1
else:
self.birdY += self.gravity
self.gravity += 0.2
self.bird[1] = self.birdY
upRect = pygame.Rect(self.wallx,
360 + self.gap - self.offset + 10,
self.wallUp.get_width() - 10,
self.wallUp.get_height())
downRect = pygame.Rect(self.wallx,
0 - self.gap - self.offset - 10,
self.wallDown.get_width() - 10,
self.wallDown.get_height())
bombRect = pygame.Rect(self.bombx,
0 - self.gap - self.offset - 10,
self.bomb.get_width() - 10,
self.bomb.get_height()
)
if upRect.colliderect(self.bird):
self.dead = True
if downRect.colliderect(self.bird):
self.dead = True
if bombRect.colliderect(self.bird):
self.bomb = pygame.image.load("bombexplode")
self.dead = True
if not 0 < self.bird[1] < 720:
self.bird[1] = 50
self.birdY = 50
self.dead = False
self.counter = 0
self.wallx = 400
self.offset = random.randint(-110, 110)
self.gravity = 5
def run(self):
clock = pygame.time.Clock()
pygame.font.init()
font = pygame.font.SysFont("Arial", 50)
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if (event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN) and not self.dead:
self.jump = 17
self.gravity = 5
self.jumpSpeed = 10
self.screen.fill((255, 255, 255))
self.screen.blit(self.background, (0, 0))
self.screen.blit(self.wallUp,
(self.wallx, 360 + self.gap - self.offset))
self.screen.blit(self.wallDown,
(self.wallx, 0 - self.gap - self.offset))
self.screen.blit(self.bomb,
(self.bombx, 0 - self.gap - self.offset))
self.screen.blit(font.render(str(self.counter),
-1,
(255, 255, 255)),
(200, 50))
if self.dead:
self.sprite = 2
elif self.jump:
self.sprite = 1
self.screen.blit(self.birdSprites[self.sprite], (70, self.birdY))
if not self.dead:
self.sprite = 0
self.updateWalls()
self.birdUpdate()
pygame.display.update()
if __name__ == "__main__":
FlappyBird().run()
I don't know much about using sprites, but if you can get the position and size of each object, you can give them rectangular hitboxes and check if their corners are inside each other:
Another way is to use circular hitboxes if those fit better. For these, find the distace between the centers and check if it is smaller than their radius' combined.
Edit :
You can use
self.bird = pygame.Rect(65, 50, 50, 50)
# creating bomb hitbox
self.rect_bomb = pygame.Rect(100, 100, 200, 200)
if self.bird.colliderect(self.rect_bomb):
# end up your code
Blockquote I just started coding in this coronatime and I am running into a problem.
The error code is
enemy_icon = [i]
NameError: name 'i' is not defined
And i'm not sure why. I having been looking online but couldn't find any answers.
Hopefully this has given enough resources to be a good enough question.
import pygame
import random
import math
pygame.init()
# game over logo
game_over = pygame.image.load("game-over.png")
# create screen
screen = pygame.display.set_mode((1000, 600))
background = pygame.image.load("8717.jpg")
# Title + Logo
pygame.display.set_caption("Space Invader")
icon = pygame.image.load("chicken.png")
pygame.display.set_icon(icon)
# Player icon
player_icon = pygame.image.load("spaceship.png")
playerX = 400
playerY = 500
player_changeX = 0
player_changeY = 0
# multiple enemy players
enemy_icon = [i]
enemyX = [i]
enemyY = [i]
enemy_changeX = [i]
enemy_changeY = [i]
num_of_enemies = 2
for i in range(num_of_enemies):
enemy_icon.append(pygame.image.load("space-invaders.png"))
enemyX.append(random.randint(0, 936))
enemyY.append(random.randint(-100, -50))
enemy_changeX.append(random.randint(-2, 2))
enemy_changeY.append(random.randint(1, 2))
# bullet #ready you can't see bullet. fire you can
bullet = pygame.image.load("bullet.png")
bulletX = 0
bulletY = 0
bulletY_change = 2
bulletX_change = 0
bullet_state = "ready"
# score
score = 0
def player(x, y):
screen.blit(player_icon, (x, y))
def enemy(x, y, i):
screen.blit(enemy_icon[i], (x, y))
def fire_bullet(x, y):
global bullet_state
bullet_state = "fire"
screen.blit(bullet, (x + 16, y + 10))
def has_collided(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
def collided(enemyX, enemyY, playerX, playerY):
distance2 = math.sqrt((math.pow(enemyX - playerX, 2)) + (math.pow(enemyY - playerY, 2)))
if distance2 < 27:
return True
else:
return False
# game loop
running = True
while running:
# background round colour RGB
screen.fill((0, 0, 0))
# background image
screen.blit(background, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# If key pressed check whether its right or left
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player_changeX = -5
if event.key == pygame.K_RIGHT:
player_changeX = 5
if event.key == pygame.K_UP:
player_changeY = -5
if event.key == pygame.K_DOWN:
player_changeY = 5
# bullet shot
if event.key == pygame.K_SPACE:
if bullet_state == "ready":
bulletX = playerX
bulletY = playerY
fire_bullet(bulletX, bulletY)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
player_changeX = 0
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
player_changeY = 0
# If player reaches boarder
if playerX >= 936:
playerX = 20
if playerX <= 0:
playerX = 936
if playerY <= 0:
playerY = 0
if playerY >= 550:
playerY = 550
# enemy control
for i in range(num_of_enemies):
if enemyX[i] >= 936:
enemyX[i] = 20
if enemyX[i] <= 0:
enemyX[i] = 936
if enemyY[i] <= 0:
enemyY[i] = 0
if enemyY[i] >= 550:
enemyY[i] = random.randint(-100, -50)
# collision bullet and enemy
collision = has_collided(enemyX[i], enemyY[i], bulletX, bulletY)
if collision:
bulletY = playerY
bulletX = playerX
bullet_state = "ready"
score += 1
print(score)
enemyX[i] = random.randint(0, 936)
enemyY[i] = random.randint(-100, -50)
enemy_changeX[i] = random.randint(-2, 2)
enemy_changeY[i] = random.randint(1, 2)
collision2 = collided(enemyX[i], enemyY[i], playerX, playerY)
if collision2:
screen.blit(game_over, (400, 100))
score = 0
playerY = 400
playerX = 500
enemy(enemyX[i], enemyY[i], i)
# bullet movement
if bullet_state == "fire":
fire_bullet(bulletX, bulletY)
bulletY -= bulletY_change
if bulletY <= 0:
bullet_state = "ready"
# collision enemy and player
# Player coordinates
playerX += player_changeX
playerY += player_changeY
# enemy change in coordinates
enemyX += enemy_changeX
enemyY += enemy_changeY
# bullet change in y
bulletY -= bulletY_change
# Results
player(playerX, playerY)
pygame.display.update()
The error seems pretty clear. You have this line in you code:
# multiple enemy players
enemy_icon = [i]
This line says that you are trying to create a variable enemy_icon and initialize it to a list with one element in it. That element is the variable i which has not been previously defined. I suspect that the i is a typo and that you are really trying to initialize with an empty list, in which case just remove the i like this:
# multiple enemy players
enemy_icon = []
The same is true for the 4 variables right after that.
I have a code solved for this issue Image does not remain on screen after executing for loop in pygame. So I decided to put some platforms as a result of what I learned from pygame. I also decided to make the cowboy move to the right and left and go down to the bottom of the screen.
But when a collision occurs on the right or left side of the cowboy as shown in the Diagram, the class PlayerShip is not executed as expected in the main loop through the def draw_reaction () function because I do not see the image of the cowboy represented by player_sprite fixed in position (60, 48). It only works correctly if the cowboy is hit from below.
Assets
missile.png
cowboy.png
police.png
Diagram:
|missile| <--<-- (is touched its right side by the missile)|cowboy| <--<-- direction
^
|
^
|
(fire missile direction vertical)
^
|
d
i
r
e
c
t
i
o
n
My MWE coding:
import pygame
import random
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
PURPLE = (255, 0, 255)
GRAY = (128,128,128)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class PlayerShip( pygame.sprite.Sprite ):
def __init__(self):
super().__init__()
self.image = pygame.image.load( "cowboy.png" )
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.topleft = ( 60, 48 )
player_sprite = PlayerShip()
class Platform(pygame.sprite.Sprite):
def __init__(self, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(GREEN)
self.rect = self.image.get_rect()
class MovingPlatform(Platform):
change_x = 0
change_y = 0
boundary_top = 0
boundary_bottom = 0
boundary_left = 0
boundary_right = 0
player = None
level = None
def update(self):
self.rect.x += self.change_x
hit = pygame.sprite.collide_rect(self, self.player)
if hit:
if self.change_x < 0:
self.player.rect.right = self.rect.left
else:
self.player.rect.left = self.rect.right
self.rect.y += self.change_y
hit = pygame.sprite.collide_rect(self, self.player)
if hit:
if self.change_y < 0:
self.player.rect.bottom = self.rect.top
else:
self.player.rect.top = self.rect.bottom
if self.rect.bottom > self.boundary_bottom or self.rect.top < self.boundary_top:
self.change_y *= -1
cur_pos = self.rect.x - self.level.world_shift
if cur_pos < self.boundary_left or cur_pos > self.boundary_right:
self.change_x *= -1
class Level(object):
def __init__(self, player):
self.platform_list = pygame.sprite.Group()
self.enemy_list = pygame.sprite.Group()
self.player = player
self.background = None
self.world_shift = 0
self.level_limit = -1000
def update(self):
self.platform_list.update()
self.enemy_list.update()
def draw(self, screen):
screen.fill(GRAY)
self.platform_list.draw(screen)
self.enemy_list.draw(screen)
def shift_world(self, shift_x):
self.world_shift += shift_x
for platform in self.platform_list:
platform.rect.x += shift_x
for enemy in self.enemy_list:
enemy.rect.x += shift_x
class Room(object):
wall_list = None
enemy_sprites = None
def __init__(self):
self.wall_list = pygame.sprite.Group()
self.enemy_sprites = pygame.sprite.Group()
class Level_01(Level):
def __init__(self, player):
Level.__init__(self, player)
self.level_limit = -1500
level = [[210, 70, 500, 500],
[210, 70, 800, 400],
[210, 70, 1000, 500],
[210, 70, 1120, 280],
[210, 70, -120, 500],
]
for platform in level:
block = Platform(platform[0], platform[1])
block.rect.x = platform[2]
block.rect.y = platform[3]
block.player = self.player
self.platform_list.add(block)
block = MovingPlatform(70, 40)
block.rect.x = 1350
block.rect.y = 280
block.boundary_left = 1350
block.boundary_right = 1600
block.change_x = 10
block.player = self.player
block.level = self
self.platform_list.add(block)
class Level_02(Level):
def __init__(self, player):
Level.__init__(self, player)
self.level_limit = -1000
level = [[210, 70, 500, 550],
[210, 70, 800, 400],
[210, 70, 1000, 500],
[210, 70, 1120, 280],
]
for platform in level:
block = Platform(platform[0], platform[1])
block.rect.x = platform[2]
block.rect.y = platform[3]
block.player = self.player
self.platform_list.add(block)
block = MovingPlatform(70, 70)
block.rect.x = 1500
block.rect.y = 300
block.boundary_top = 100
block.boundary_bottom = 550
block.change_y = -1
block.player = self.player
block.level = self
self.platform_list.add(block)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
width = 40
height = 60
self.image = pygame.image.load("police.png").convert()
self.rect = self.image.get_rect()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.change_x = 0
self.change_y = 0
self.level = None
def update(self):
self.calc_grav()
self.rect.x += self.change_x
block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for block in block_hit_list:
if self.change_x > 0:
self.rect.right = block.rect.left
elif self.change_x < 0:
self.rect.left = block.rect.right
self.change_y += 0
self.rect.y += self.change_y
block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for block in block_hit_list:
if self.change_y > 0:
self.rect.bottom = block.rect.top
elif self.change_y < 0:
self.rect.top = block.rect.bottom
self.change_y = 0
if isinstance(block, MovingPlatform):
self.rect.x += block.change_x
def calc_grav(self):
if self.change_y == 0:
self.change_y = 1
else:
self.change_y += .35
if self.rect.y >= SCREEN_HEIGHT - self.rect.height and self.change_y >= 0:
self.change_y = 0
self.rect.y = SCREEN_HEIGHT - self.rect.height
def jump(self):
self.rect.y += 2
platform_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
self.rect.y -= 2
if len(platform_hit_list) > 0 or self.rect.bottom >= SCREEN_HEIGHT:
self.change_y = -10
def go_left(self):
self.change_x = -6
def go_right(self):
self.change_x = 6
def stop(self):
self.change_x = 0
class Cowboy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("cowboy.png").convert()
self.rect = self.image.get_rect()
self.image.set_colorkey(BLACK)
class Missile(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("missile.png").convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
def update(self):
self.rect.y += -3
def main():
pygame.init()
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
block_list = pygame.sprite.Group()
all_sprites_list = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
pygame.display.set_caption("Platformer with moving platforms")
apple = pygame.image.load("missile.png").convert()
block = Cowboy()
player_image = pygame.image.load("cowboy.png").convert()
player = Player()
level_list = []
level_list.append(Level_01(player))
level_list.append(Level_02(player))
current_level_no = 0
current_level = level_list[current_level_no]
block_list.add(block)
all_sprites_list.add(block)
all_sprites_list.add(player)
active_sprite_list = pygame.sprite.Group()
player.level = current_level
player.rect.x = 340
player.rect.y = SCREEN_HEIGHT - player.rect.height
active_sprite_list.add(player)
done = False
clock = pygame.time.Clock()
x_speed = 0
y_speed = 0
rect_x = 50
rect_y = 50
rect_change_x = 1
rect_change_y = 90
x = rect_x
y = rect_y
player.rect.y = rect_x
player.rect.y = 480
score = 0
size = (1366, 768)
screen = pygame.display.set_mode(size, pygame.RESIZABLE)
while not done:
x += x_speed
y += y_speed
rect_x += rect_change_x
if rect_x > 280:
rect_change_x *= -1
rect_x += rect_change_x
rect_y += rect_change_y
if rect_x < 0:
rect_change_x *= -1
rect_x += rect_change_x
rect_y += rect_change_y
block.rect.x = rect_x
block.rect.y = rect_y
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
bullet = Missile()
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
all_sprites_list.add(bullet)
bullet_list.add(bullet)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.go_left()
if event.key == pygame.K_RIGHT:
player.go_right()
if event.key == pygame.K_UP:
player.jump()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT and player.change_x < 0:
player.stop()
if event.key == pygame.K_RIGHT and player.change_x > 0:
player.stop()
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 1
print(score)
screen.blit(player_image, [60, 48])
if bullet.rect.y < -10:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
def draw_reaction():
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
all_sprites_list.add( player_sprite )
break
all_sprites_list.update()
active_sprite_list.update()
current_level.update()
if player.rect.right >= 1500:
diff = player.rect.right - 500
player.rect.right = 500
current_level.shift_world(-diff)
if player.rect.left <= 120:
diff = 120 - player.rect.left
player.rect.left = 120
current_level.shift_world(diff)
current_position = player.rect.x + current_level.world_shift
if current_position < current_level.level_limit:
if current_level_no < len(level_list)-1:
player.rect.x = 120
current_level_no += 1
current_level = level_list[current_level_no]
player.level = current_level
else:
done = True
current_level.draw(screen)
active_sprite_list.draw(screen)
x += x_speed
y += y_speed
all_sprites_list.draw(screen)
draw_reaction()
clock.tick(60)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
UPDATE EDIT: If current_level.draw(screen) is not used in the main loop then the code works as expected. But I still don't know what causes it.
I simply added all_sprites_list.add (player_sprite) to the for block in block_hit_list:. In my view this would be necessary to track self.image.get_rect (). Then:
Before:
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 1
print(score)
screen.blit(player_image, [60, 48])
if bullet.rect.y < -10:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
Later:
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
all_sprites_list.add(player_sprite)
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 1
print(score)
screen.blit(player_image, [60, 48])
if bullet.rect.y < -10:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
But I would like someone to explain to me how it really happened behind the code by making more experienced references to the documentation or the style of the code itself. Thankful if anyone can make that explanation.