I am beginning to code a space invaders game and trying to just get the ship down and let it move back and forth across the screen. I'm very beginner status so it is a big challenge for me. Whenever I try to run it it tells me that I'm requiring another positional argument in my update function but I don't understand what I'm supposed to do.
I've tried moving around the variable initialization for keys but it hasn't changed anything. If anyone wants to take a look at my code and tell me what I could do it would be amazing.
import pygame, sys
from pygame import *
pygame.init()
screen = pygame.display.set_mode((800,600))
pygame.display.set_caption("Space Invaders")
pygame.mouse.set_visible(0)
WIDTH = 800
vel = 5
width = 64
keys = pygame.key.get_pressed()
BLACK = (0, 0, 0)
all_sprites = pygame.sprite.Group()
class Player(pygame.sprite.Sprite):
def _init_(self):
pygame.sprite.Sprite._init_(self)
self.image = pygame.image.load("images\ship.png")
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, 40)
def update(self, keys, *args):
if keys[pygame.K_LEFT] and self.rect.x > vel:
self.rect.x -= vel
if keys[pygame.K_RIGHT] and self.rect.x < 800 - width - vel:
self.rect.x += vel
screen.blit(self.image, self.rect)
player = Player()
all_sprites.add(player)
run = True
while run:
pygame.time.delay(100)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
all_sprites.update()
screen.fill(BLACK)
all_sprites.draw(screen)
pygame.display
pygame.quit()
I know this is just the beginning but I can't figure out what I'm missing here.
You've to pass the current states of the keyboard buttons to .update().
Note, the arguments which are passed to all_sprites.update() are delegated to player.update(). all_sprites is pygame.sprite.Group object. The .update() method calls .update() on each contained sprite and pass the parameters through.
Get the states of the keyboard buttons by pygame.key.get_pressed() and pass it to .update:
keys = pygame.key.get_pressed()
all_sprites.update(keys)
The name of the constructor has to be __init__ rather than _init_ . See Class:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
# [...]
Related
I've been trying to use classes and adding sprites to groups, this works fine but I don't know how to refer to everything in a group, I tried to look this up but I'm still stuck, I might need to use def functions or whatever inside the class but I couldn't get that to work, here my code:
import random
pygame.init()
screen = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
screensurf = pygame.Surface((800,600))
screensurf.fill((255,255,255))
class rain(pygame.sprite.Sprite):
def __init__(self, x_pos, y_pos):
super().__init__()
self.image = pygame.Surface((1,3))
self.image.fill((30,30,30))
self.rect = self.image.get_rect()
self.rect.x = x_pos
self.rect.y = y_pos
self.movey = 0
self.movex = 0
self.rect.x += 0
self.rect.y += 1
RainGroup = pygame.sprite.Group()
for RainDrop in range(100):
RainDrop = rain(random.randrange(0,800), 400)
RainGroup.add(RainDrop)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
RainDrop.rect.y += 1
screen.blit(screensurf,(0,0))
RainGroup.draw(screen)
pygame.display.flip()
clock.tick(60)
Basically it's only moving one at a time.
if you have a .update method in your class, it will be called by Pygame when you call group.update(). Otherwise, just loop through your sprites by using the group with a for loop:
for sprite in RainGroup:
# do things to sprite here
...
I'm trying to program a traffic simulation and need the pygame collision detection. However as soon as i run the program (it isn't finished yet) it tells me i have an attribute error on car_up because it hasn't got the attribute rect. Can someone tell me why it is so and what i can do to improve it? And yes i have been trying figuring it out on my own but with no success.
import pygame
import random
import threading
from pygame.locals import (QUIT, KEYDOWN, K_ESCAPE)
# Define constants for the screen width and height
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 400
grey=[90 , 90 , 90]
green =[125, 255, 50]
spawnrate_up=1500
spawnrate_down=800
dimentions=[75,35]
#CLASSES
#create car
class Car_up (pygame.sprite.Sprite):
def __init__(self, green, dimentions):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
self.image = pygame.Surface(dimentions)
self.image.fill(green)
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect()
self.rect.x = random.randint(-100, -20)
self.rect.y = 150
self.speedx= 10
self.speedy= 0
# move car and delete once out of screen
def update(self):
merge=random.randint(750,1000)
if self.rect.left > SCREEN_WIDTH:
self.kill()
if self.rect.colliderect(car_down.rect):
car_up.speed.x=car_up.speed.y=0
elif self.rect.centery<=266 and self.rect.centerx>merge:
self.speedx=10
self.speedy=3
else:
self.speedx=10
self.speedy=0
self.rect.move_ip(self.speedx, self.speedy)
class Car_down(pygame.sprite.Sprite):
def __init__(self, green, dimentions):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
self.image = pygame.Surface(dimentions)
self.image.fill(green)
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect()
self.rect.x = random.randint(-100,-20)
self.rect.y = 250
self.speedx= 10
self.speedy= 0
# move car and delete once out of screen
def update(self):
self.speedx=10
self.speedy=0
self.rect.move_ip(self.speedx, self.speedy)
if self.rect.left > SCREEN_WIDTH:
self.kill()
pygame.init()
# The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
clock=pygame.time.Clock()
#create custom event
ADDCAR_down= pygame.USEREVENT + 1
ADDCAR_up= pygame.USEREVENT + 2
pygame.time.set_timer(ADDCAR_up, spawnrate_up )
pygame.time.set_timer(ADDCAR_down, spawnrate_down )
#create car
car_up= pygame.sprite.Group()
car_down= pygame.sprite.Group()
car_down.add(Car_down(green,dimentions))
car_up.add(Car_up(green,dimentions))
#infinite loop
run = True
#MAIN
while run:
# Look at every event in the queue
for event in pygame.event.get():
# Did the user hit a key?
if event.type == KEYDOWN:
# Was it the Escape key? If so, stop the loop.
if event.key == K_ESCAPE:
run = False
# Did the user click the window close button? If so, stop the loop.
elif event.type == QUIT:
run = False
elif event.type == ADDCAR_down:
# Create the new CAR and add it to sprite groups
new_car_down = Car_down(green,dimentions)
car_down.add(new_car_down)
elif event.type == ADDCAR_up:
new_car_up = Car_up(green,dimentions)
car_up.add(new_car_up)
#grey screen
screen.fill(grey)
# Draw the car on the screen
car_up.draw(screen)
car_up.update()
car_down.draw(screen)
car_down.update()
pygame.display.flip()
clock.tick(25)
car_down is a pygame.sprite.Group so obviously it doesn't have a rect attribute. However the pygams.sprites.Sprite object in the Group have a rect attribute. You need to use pygame.sprite.spritecollide to detect collision of a Sprite and the objects in a Group:
if self.rect.colliderect(car_down.rect):
if pygame.sprite.spritecollide(self, car_down, False):
See also How do I detect collision in pygame?
Okay, so basically what I'm trying to do is keep the main file a little cleaner and I'm starting with the "Zombie" enemy by making it's own file which will most likely contain all enemies, and importing it in.
So I'm confused on how I'd set-up the Class for a sprite, you don't have to tell me how to get it to move or anything like that I just want it to simply appear. The game doensn't break when I run it as is, I just wanted to ask this question before I goto sleep so I can hopefully get a lot done with the project done tomorrow (School related)
Code is unfinished like I said just wanted to ask while I get some actual sleep just a few google searches and attempts.
Eventually I'll take from the advice given here to make a "Hero" class as well, and as well as working with importing other factors if we have the time.
Zombie code:
import pygame
from pygame.locals import *
class ZombieEnemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('images/zombie.png')
# self.images.append(img)
# self.image = self.images[0]
self.rect = self.image.get_rect()
zombieX = 100
zombieY = 340
zombieX_change = 0
Main Code:
import pygame
from pygame.locals import *
import Zombie
# Intialize the pygame
pygame.init()
# Create the screen
screen = pygame.display.set_mode((900, 567))
#Title and Icon
pygame.display.set_caption("Fighting Game")
# Add's logo to the window
# icon = pygame.image.load('')
# pygame.display.set_icon(icon)
# Player
playerImg = pygame.image.load('images/character.png')
playerX = 100
playerY = 340
playerX_change = 0
def player(x,y):
screen.blit(playerImg,(x,y))
Zombie.ZombieEnemy()
def zombie(x,y):
screen.blit()
# Background
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load('images/background.png')
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
BackGround = Background('background.png', [0,0])
# Game Loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# If keystroke is pressed check right, left.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
#playerX_change = -2.0
BackGround.rect.left = BackGround.rect.left + 2.5
if event.key == pygame.K_RIGHT:
#playerX_change = 2.0
BackGround.rect.left = BackGround.rect.left - 2.5
# if event.type == pygame.KEYUP:
# if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
# BackGround.rect.left = 0
screen.blit(BackGround.image, BackGround.rect)
playerX += playerX_change
player(playerX,playerY)
pygame.display.flip()
Your sprite code is basically mostly there already. But as you say, it needs an update() function to move the sprites somehow.
The idea with Sprites in PyGame is to add them to a SpriteGroup, then use the group functionality for handling the sprites together.
You might want to modify the Zombie class to take an initial co-ordinate location:
class ZombieEnemy(pygame.sprite.Sprite):
def __init__( self, x=0, y=0 ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('images/zombie.png')
self.rect = self.image.get_rect()
self.rect.center = ( x, y ) # NOTE: centred on the co-ords
Which allows the game to create a Zombie at a particular starting point (perhaps even a random one).
So to have a sprite group, first you need to create the container:
all_zombies = pygame.sprite.Group()
Then when you create a new zombie, add it to the group. Say you wanted to start with 3 randomly-positioned zombies:
for i in range( 3 ):
new_x = random.randrange( 0, WINDOW_WIDTH ) # random x-position
new_y = random.randrange( 0, WINDOW_HEIGHT ) # random y-position
all_zombies.add( Zombie( new_x, new_y ) ) # create, and add to group
Then in the main loop, call .update() and .draw() on the sprite group. This will move and paint all sprites added to the group. In this way, you may have separate groups of enemies, bullets, background-items, etc. The sprite groups allow easy drawing and collision detection between other groups. Think of colliding a hundred bullets against a thousand enemies!
while running:
for event in pygame.event.get():
# ... handle events
# Move anything that needs to
all_zombies.update() # call the update() of all zombie sprites
playerX += playerX_change
# Draw everything
screen.blit(BackGround.image, BackGround.rect)
player(playerX,playerY)
all_zombies.draw( screen ) # paint every sprite in the group
pygame.display.flip()
EDIT: Added screen parameter to all_zombies.draw()
It's probably worthwhile defining your player as a sprite too, and having a single-entry group for it as well.
First you could keep position in Rect() in class. And you would add method which draws/blits it.
import pygame
class ZombieEnemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('images/zombie.png')
self.rect = self.image.get_rect()
self.rect.x = 100
self.rect.y = 340
self.x_change = 0
def draw(self, screen):
screen.blit(self.image, self.rect)
Next you have to assign to variable when you create it
zombie = Zombie.ZombieEnemy()
And then you can use it to draw it
zombie.draw(screen)
in
screen.blit(BackGround.image, BackGround.rect)
player(playerX,playerY)
zombie.draw(screen)
pygame.display.flip()
The same way you can create class Player and add method draw() to class Background and then use .
background.draw(screen)
player.draw(screen)
zombie.draw(screen)
pygame.display.flip()
Later you can use pygame.sprite.Group() to keep all objects in one group and draw all of them using one command - group.draw()
I know i need a loop to check if a list of bullets hits a player.
I've tried researching online for 2 hours for reference code but all use sprites and classes.
#Bullet Collision with players
for i in range(len(bullets_2)):
#player is a rectangle style object
if bullets_2[i].colliderect(player):
player_health -= 10
Sadly enough my computer science teacher hasn't taught the class about sprites or classes, so let's avoid that.
I tried having the code above check if the list collides with the rectangle player.
The point of the above code is for the game to to take health away from the health bar if the enemy player's bullets hit the player.
EDIT:
I only have about 8 days to finish this game
TLDR:
How do I check to see if a list collides with a rectangle.
As in the comment, here's the code.
Here's the images I am using
import pygame, time, sys
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
class playerClass(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("player.png").convert()
self.image.set_alpha(196)
self.rect = self.image.get_rect()
self.rect.y = 260
self.rect.x = 500
def update(self):
pass
class bulletClass(pygame.sprite.Sprite):
def __init__(self, speed, starty):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("bullet.png").convert()
self.rect = self.image.get_rect()
self.rect.y = starty
self.rect.x = 0
self.speed = speed
def update(self):
self.rect.x += self.speed
gameExit = False
player = playerClass()
bullet1 = bulletClass(7,265)
bullet2 = bulletClass(10,295)
bullet3 = bulletClass(7,325)
bulletSprites = pygame.sprite.RenderPlain((bullet1, bullet2, bullet3))
playerSprites = pygame.sprite.RenderPlain((player))
bulletRectList = [bullet1.rect, bullet2.rect, bullet3.rect]
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.fill((255,255,255))
bulletSprites.update()
bulletSprites.draw(screen)
playerSprites.update()
playerSprites.draw(screen)
collidedBulletList = player.rect.collidelistall(bulletRectList)
if len(collidedBulletList) > 0:
for i in collidedBulletList:
print(i)
pygame.display.update()
clock.tick(10)
In this code, if you want to add a bullet just declare a new object
bullet4 = bulletClass(speed, yCoordinateToBeginWith)
and append it to bulletSprites and the bulletRectList and you are done. Using sprites simplifies this. Making your code sprite friendly would be difficult at the beginning but appending it is definitely easy afterwards.
I'm trying to make a bullet move in Pygame. Apologies if it has a simple fix, I just can't think at the moment.
This is what I run when I detect that the "1" button is pressed.
if pygame.event.get().type == KEYDOWN and e.key == K_1:
bullet = Bullet()
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
entities.add(bullet)
bullet_list.add(bullet)
bullet.update()
...and this is the actual bullet class. The spacing is a bit off.
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super(Bullet, self).__init__()
self.image = pygame.Surface([4, 10])
self.image.fill(pygame.Color(255, 255, 255))
self.isMoving = False
self.rect = self.image.get_rect()
def update(self):
for i in range(20):
self.rect.x += 3
I understand that the update method is happening instantly rather than the slower movement I want. How am I supposed to make the bullet move slower?
All the answers that I've seen involve completely stopping the program instead of just stopping the one object. Is there a way around it?
You need to update all bullets on every game tick, not just when a player presses a button.
So, you'll want something like this as your event loop:
clock = pygame.time.Clock()
while True:
clock.tick(60)
for event in pygame.event.get():
if event == KEYDOWN and event.key == K_1:
bullet = Bullet()
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
entities.add(bullet)
bullet_list.add(bullet)
for bullet in bullet_list:
bullet.update()
And, modify the Bullet class to do incremental moves, like so:
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super(Bullet, self).__init__()
self.image = pygame.Surface([4, 10])
self.image.fill(pygame.Color(255, 255, 255))
self.isMoving = False
self.rect = self.image.get_rect()
def update(self):
self.rect.x += 3