How to draw multiple sprites in one class - python

I am making a space shooter game which has a planets in the background. I've decided to draw some planets in the background and when i move to the right the planets should move to the left. That is for the player to feel the spaceship is moving around the space. However i could have done it for one planet only. When try to apply the other planets in one class it is constantly changing to the other planet.
lanetdic = {'planets':[]}
imagestoload = ['Feza/graphs/sprites/saturne.png']
for i in imagestoload:
img = pg.image.load(i).convert_alpha()
planetdic['planets'].append(img)
this is to load the sprite. and in the below i created a class for the planets.
class Planets(pg.sprite.Group):
def __init__(self):
pg.sprite.Group.__init__(self)
self.frame = 0
self.image = planetdic['planets'][self.frame]
self.rect = self.image.get_rect()
self.rect.center = (500+100*self.frame,HEIGHT/2)
self.pos = vec(500,HEIGHT/2)
self.vel = vec(0,0)
self.acc = vec(0,0)
def update(self):
#self.acc = vec(0,0)
self.frame = (self.frame + 1)%len(planetdic['planets'])
Maybe it is not sensible to create a class for a planet but i couldnt have find anotherway if there is please tell me.
if we get to the point again. In the below i made a for loop to load images. and used again the same for loop
planetdic = {'planets':[]}
imagestoload = ['Feza/graphs/sprites/saturne.png','Feza/graphs/sprites/jupiter.png','Feza/graphs/sprites/venus.png','Feza/graphs/sprites/uranus.png','Feza/graphs/sprites/neptune.png']
for i in imagestoload:
img = pg.image.load(i).convert_alpha()
planetdic['planets'].append(img)
When i apply multi images it changes one to other in miliseconds how can i prevent this happen. I just want to show every planets in the background and make them move.

How to draw multiple sprites in one class
You do not do that. However, you can create multiple instance objects of a class.
The image (Surface) must be a parameter of the constructor of the class Planet. The Planet class is a subcalss of sprite.Sprite, not sprite.Group:
class Planet(pg.sprite.Sprite):
def __init__(self, image):
pg.sprite.Group.__init__(self)
self.frame = 0
self.image = image
self.rect = self.image.get_rect()
self.rect.center = (500+100*self.frame,HEIGHT/2)
self.pos = vec(500,HEIGHT/2)
self.vel = vec(0,0)
self.acc = vec(0,0)
Create a new instance object of the Planet class for each planet:
planets = pg.sprite.Group()
imagestoload = ['Feza/graphs/sprites/saturne.png','Feza/graphs/sprites/jupiter.png','Feza/graphs/sprites/venus.png','Feza/graphs/sprites/uranus.png','Feza/graphs/sprites/neptune.png']
for filepath in imagestoload:
img = pg.image.load(filepath).convert_alpha()
planet = Planet(img)
planets.add(planet)

Related

How do I move variables between Classes and functions without them changing?

I am fairly new to OOP and pygame, so these may be some stupid and basic questions - but I've been stuck on this for days so anything would help.
I am creating a variable called position3 within Gun.shoot(), I want this variable to move to Bullet.reposition() as Bullet.reposition is called upon. I then want the position3 variable to move to the Bullet.update() function, which gets called upon by a different process elsewhere in the code. During this whole process, the position3 variable should not change but should stay the same. I have managed to get the position3 variable to move to Bullet.reposition() from Gun.shoot(), however I can now not get it into Bullet.update(). Help!
class Bullet(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((5,5))
self.image.fill(red)
self.rect = self.image.get_rect()
# self.rect.center = (200,200)
self.positionofm = (23,0)
self.pos = vec(300,300)
self.centerlocation = vec(0,0)
self.position3 = vec(0,0)
def update(self):
self.position3 = reposition.position3
print("update",self.position3)
# self.rect.center = self.position3
self.centerlocation = random.randint(200,400),random.randint(200,400)
self.rect.center =(self.centerlocation)
def reposition(self,position3):
print("repositioning")
self.centerlocation = random.randint(200,400),random.randint(200,400)
self.rect.move(position3)
print("regular",position3)
self.position3 = position3
print("First update",self.position3)
class Gun(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((30,5), pg.SRCALPHA)
self.image.fill(black)
self.rect = self.image.get_rect()
self.original_image = self.image
self.rect.center = (WIDTH/2 , HEIGHT/2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.offset = vec(20, 0)
self.angle=0
self.position2=vec(0,0)
# self.bullet = Bullet()
def shoot(self):
self.BulletEndPos=vec(0,0)
self.BulletEndPos=vec(pg.mouse.get_pos())
# print(self.BulletEndPos,"akshgdjasgdas")
position2x=self.position2[0]
position2y=self.position2[1]
position3=vec(0,0)
position3=(math.floor(position2x)),(math.floor(position2y))
Bullet.reposition(self, position3)
Well your code snippet already has everything you need there you just need to remove the line
self.position3 = reposition.position3
Given that reposition is not an object and will not hold a attribute
The value for position3 is already updated for the object on the reposition method and written in the Bullet object attribute. Another way you could do it would be to rewrite update() somewhat like this:
def update(self, position3= None):
position_3 = position3 if position3 is not None else self.position3
print("update",position_3)
# self.rect.center = position_3
self.centerlocation = random.randint(200,400),random.randint(200,400)
self.rect.center =(self.centerlocation)
This gives you more freedom to pass position3 somewhere else in the code if needed while retaining the logic.
Now just to clarify a few things:
When you write a class you are just declaring the overall structure of the class and not creating any instance of it.
The self keyword refers to the referred instance of the class object, so you need to create an instance of the object that can keep those variables.
Keeping that in mind on your last line of method shoot you are doing nothing, has there is no bullet created to be repositioned and updated. So you kinda need to change your Gun class to this:
class Gun(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((30,5), pg.SRCALPHA)
self.image.fill(black)
self.rect = self.image.get_rect()
self.original_image = self.image
self.rect.center = (WIDTH/2 , HEIGHT/2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.offset = vec(20, 0)
self.angle=0
self.position2=vec(0,0)
self.bullet = Bullet()
def shoot(self):
self.BulletEndPos=vec(0,0)
self.BulletEndPos=vec(pg.mouse.get_pos())
# print(self.BulletEndPos,"akshgdjasgdas")
position2x=self.position2[0]
position2y=self.position2[1]
position3=vec(0,0)
position3=(math.floor(position2x)),(math.floor(position2y))
self.bullet.reposition(self, position3)
OOP might be confusing at times especially at the beginning so you can try some other resources online (e.g. https://www.tutorialspoint.com/python3/python_classes_objects.htm)

Layering sprites in pygame with an unorthodox character class? [duplicate]

I have an image in pygame with multiple instances of the image called in a for loop. is there a way I could move each instance of the image independently without moving the others with the code as is? or will I have to load separate instances of the image individually?
def pawn(self):
y_pos = 100
self.image = pygame.transform.scale(pygame.image.load('pawn.png'), (100,100))
for x_pos in range(0,8,1):
pieceNum = x_pos
screen.blit(self.image, (x_pos*100, y_pos))
I recommend to use pygame.sprite.Sprite and pygame.sprite.Group:
Create a class derived from pygame.sprite.Sprite:
class MySprite(pygame.sprite.Sprite):
def __init__(self, image, pos_x, pos_y):
super().__init__()
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = (pos_x, pos_y)
Load the image
image = pygame.transform.scale(pygame.image.load('pawn.png'), (100,100))
Create a list of sprites
imageList = [MySprite(image, x_pos*100, 100) for x_pos in range(0,8,1)]
and create a sprite group:
group = pygame.sprite.Group(imageList)
The sprites of a group can be drawn by .draw (screen is the surface created by pygame.display.set_mode()):
group.draw(screen)
The position of the sprite can be changed by changing the position of the .rect property (see pygame.Rect).
e.g.
imageList[0].rect = imageList[0].rect.move(move_x, move_y)
Of course, the movement can be done in a method of class MySprite:
e.g.
class MySprite(pygame.sprite.Sprite):
# [...]
def move(self, move_x, move_y):
self.rect = self.rect.move(move_x, move_y)
imageList[1].move(0, 100)

pygame: replacing image of an item/enemy

I'm trying to write a program where the image for the 'enemy' I have changes once its health goes to 0. I have different classes for each element, but I have this under this class.
class Enemy(pygame.sprite.Sprite):
def __init__(self, gs=None):
pygame.sprite.Sprite.__init__(self)
...initialization stuff...
self.image = pygame.image.load("enemy.png") #enemy image
self.rect = self.image.get_rectangle()
self.hp = 40 #initial health
def damage(self):
if self.rect.colliderect(self.user.rect): #collision/how the health goes down
self.hp = self.hp - 5
Now here's the part I'm curious about, I want to load a new image that replaces the old image I have for this enemy. Can I do/add (in the damage function)
if(self.hp == 0):
self.image = pygame.image.load("dead.png")
Will this replace it? Or just load another image on top of it?
Let me know what I'm missing, thank you!
You should load all your images into your init method when you create the object and then you can do the assigning/changing later using lists. Here is an example:
class Enemy(pygame.sprite.Sprite):
def __init__(self, gs=None):
pygame.sprite.Sprite.__init__(self)
self.images = [pygame.image.load("enemy.png"), pygame.image.load("dead.png")]
self.current_image = self.images[0]
self.rect = self.current_image.get_rectangle()
Then later you can do:
if(self.hp == 0):
self.current_image = self.images[1]
This will in fact, as per your concern, replace the current image instead of just drawing over it. Hope that helps.

Understand object orientated python - initialization of objects in pygame

I'm learning Object Orientated Python and understand the main principals of classes and creating objects from classes however I need something explained Re: the pygame code below. I'm struggling to get my head around what's happening when sprite lists are being created and the two lines of code under the code which creates the ball object (allsprites.add etc). In other words what are sprites and why are lists of them created? Why isn't the ball object just created from the class on its own? Why does it need to be added to a sprite list?? What's going on? Any explanation would be greatly appreciated.
"""
Sample Breakout Game
Sample Python/Pygame Programs
Simpson College Computer Science
http://programarcadegames.com/
http://simpson.edu/computer-science/
"""
# --- Import libraries used for this program
import math
import pygame
# Define some colors
black = (0, 0, 0)
white = (255, 255, 255)
blue = (0, 0, 255)
# Size of break-out blocks
block_width = 23
block_height = 15
class Block(pygame.sprite.Sprite):
"""This class represents each block that will get knocked out by the ball
It derives from the "Sprite" class in Pygame """
def __init__(self, color, x, y):
""" Constructor. Pass in the color of the block,
and its x and y position. """
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create the image of the block of appropriate size
# The width and height are sent as a list for the first parameter.
self.image = pygame.Surface([block_width, block_height])
# Fill the image with the appropriate color
self.image.fill(color)
# Fetch the rectangle object that has the dimensions of the image
self.rect = self.image.get_rect()
# Move the top left of the rectangle to x,y.
# This is where our block will appear..
self.rect.x = x
self.rect.y = y
class Ball(pygame.sprite.Sprite):
""" This class represents the ball
It derives from the "Sprite" class in Pygame """
# Speed in pixels per cycle
speed = 10.0
# Floating point representation of where the ball is
x = 0.0
y = 180.0
# Direction of ball (in degrees)
direction = 200
width = 10
height = 10
# Constructor. Pass in the color of the block, and its x and y position
def __init__(self):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create the image of the ball
self.image = pygame.Surface([self.width, self.height])
# Color the ball
self.image.fill(white)
# Get a rectangle object that shows where our image is
self.rect = self.image.get_rect()
# Get attributes for the height/width of the screen
self.screenheight = pygame.display.get_surface().get_height()
self.screenwidth = pygame.display.get_surface().get_width()
def bounce(self, diff):
""" This function will bounce the ball
off a horizontal surface (not a vertical one) """
self.direction = (180 - self.direction) % 360
self.direction -= diff
def update(self):
""" Update the position of the ball. """
# Sine and Cosine work in degrees, so we have to convert them
direction_radians = math.radians(self.direction)
# Change the position (x and y) according to the speed and direction
self.x += self.speed * math.sin(direction_radians)
self.y -= self.speed * math.cos(direction_radians)
# Move the image to where our x and y are
self.rect.x = self.x
self.rect.y = self.y
# Do we bounce off the top of the screen?
if self.y <= 0:
self.bounce(0)
self.y = 1
# Do we bounce off the left of the screen?
if self.x <= 0:
self.direction = (360 - self.direction) % 360
self.x = 1
# Do we bounce of the right side of the screen?
if self.x > self.screenwidth - self.width:
self.direction = (360 - self.direction) % 360
self.x = self.screenwidth - self.width - 1
# Did we fall off the bottom edge of the screen?
if self.y > 600:
return True
else:
return False
class Player(pygame.sprite.Sprite):
""" This class represents the bar at the bottom that the player controls. """
def __init__(self):
""" Constructor for Player. """
# Call the parent's constructor
pygame.sprite.Sprite.__init__(self)
self.width = 75
self.height = 15
self.image = pygame.Surface([self.width, self.height])
self.image.fill((white))
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.screenheight = pygame.display.get_surface().get_height()
self.screenwidth = pygame.display.get_surface().get_width()
self.rect.x = 0
self.rect.y = self.screenheight-self.height
def update(self):
""" Update the player position. """
# Get where the mouse is
pos = pygame.mouse.get_pos()
# Set the left side of the player bar to the mouse position
self.rect.x = pos[0]
# Make sure we don't push the player paddle
# off the right side of the screen
if self.rect.x > self.screenwidth - self.width:
self.rect.x = self.screenwidth - self.width
# Call this function so the Pygame library can initialize itself
pygame.init()
# Create an 800x600 sized screen
screen = pygame.display.set_mode([800, 600])
# Set the title of the window
pygame.display.set_caption('Breakout')
# Enable this to make the mouse disappear when over our window
pygame.mouse.set_visible(0)
# This is a font we use to draw text on the screen (size 36)
font = pygame.font.Font(None, 36)
# Create a surface we can draw on
background = pygame.Surface(screen.get_size())
# Create sprite lists
blocks = pygame.sprite.Group()
balls = pygame.sprite.Group()
allsprites = pygame.sprite.Group()
# Create the player paddle object
player = Player()
allsprites.add(player)
# Create the ball
ball = Ball()
allsprites.add(ball)
balls.add(ball)
# The top of the block (y position)
top = 80
# Number of blocks to create
blockcount = 32
# --- Create blocks
# Five rows of blocks
for row in range(5):
# 32 columns of blocks
for column in range(0, blockcount):
# Create a block (color,x,y)
block = Block(blue, column * (block_width + 2) + 1, top)
blocks.add(block)
allsprites.add(block)
# Move the top of the next row down
top += block_height + 2
# Clock to limit speed
clock = pygame.time.Clock()
# Is the game over?
game_over = False
# Exit the program?
exit_program = False
# Main program loop
while exit_program != True:
# Limit to 30 fps
clock.tick(30)
# Clear the screen
screen.fill(black)
# Process the events in the game
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit_program = True
# Update the ball and player position as long
# as the game is not over.
if not game_over:
# Update the player and ball positions
player.update()
game_over = ball.update()
# If we are done, print game over
if game_over:
text = font.render("Game Over", True, white)
textpos = text.get_rect(centerx=background.get_width()/2)
textpos.top = 300
screen.blit(text, textpos)
# See if the ball hits the player paddle
if pygame.sprite.spritecollide(player, balls, False):
# The 'diff' lets you try to bounce the ball left or right
# depending where on the paddle you hit it
diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2)
# Set the ball's y position in case
# we hit the ball on the edge of the paddle
ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1
ball.bounce(diff)
# Check for collisions between the ball and the blocks
deadblocks = pygame.sprite.spritecollide(ball, blocks, True)
# If we actually hit a block, bounce the ball
if len(deadblocks) > 0:
ball.bounce(0)
# Game ends if all the blocks are gone
if len(blocks) == 0:
game_over = True
# Draw Everything
allsprites.draw(screen)
# Flip the screen and show what we've drawn
pygame.display.flip()
pygame.quit()
You don't need to add the balls and blocks to sprite lists - it's just a matter of convenience. You could manually check each ball for a collision, but it's easier to just tell pygame to check them all for you
# See if the ball hits the player paddle
if pygame.sprite.spritecollide(player, balls, False):
# The 'diff' lets you try to bounce the ball left or right
# depending where on the paddle you hit it
diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2)
# Set the ball's y position in case
# we hit the ball on the edge of the paddle
ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1
ball.bounce(diff)
You could draw each thing to the screen separately on each frame, but it's easier just to tell pygame to do it for you:
# Draw Everything
allsprites.draw(screen)
Things can be in more than one list as required, for example a ball is added to the balls list so that you can easily check for collisions, but also added to the allsprites list so that you can easily draw everything on the screen
# Create the ball
ball = Ball()
allsprites.add(ball)
balls.add(ball)
Edit:
An important distinction is that allsprites is actually a sprite.Group. It has a list of sprites inside it, but it also has other methods like draw.
To address your question of "what is a Sprite", it's simply a thing that gets drawn on screen. pygame methods like sprite.Group.draw expect a list of things with certain attributes - such as update. The easiest way to make sure that you provide all of those attributes with the right names is to subclass Sprite, however this is also a (strongly recommended) convenience thing - for instance, this is from the pygame source code:
While it is possible to design sprite and group classes that don't
derive from the Sprite and AbstractGroup classes below, it is strongly
recommended that you extend those when you add a Sprite or Group
class.
So what specifically does subclassing Sprite get you? Let's take a look at the source. Here's how to find the source code for a python module:
>>> import pygame.sprite
>>> pygame.sprite.__file__
'c:\\Python27\\lib\\site-packages\\pygame\\sprite.py'
>>>
Every python module has a __file__ attribute that tells you where the source is located (well not quite every). If you open it up in your editor, and scroll down, you see the class definition for Sprite:
class Sprite(object):
"""simple base class for visible game objects
pygame.sprite.Sprite(*groups): return Sprite
The base class for visible game objects. Derived classes will want to
override the Sprite.update() and assign a Sprite.image and
Sprite.rect attributes. The initializer can accept any number of
Group instances to be added to.
When subclassing the Sprite, be sure to call the base initializer before
adding the Sprite to Groups.
"""
def __init__(self, *groups):
self.__g = {} # The groups the sprite is in
if groups: self.add(groups)
def add(self, *groups):
"""add the sprite to groups
Sprite.add(*groups): return None
Any number of Group instances can be passed as arguments. The
Sprite will be added to the Groups it is not already a member of.
"""
has = self.__g.__contains__
for group in groups:
if hasattr(group, '_spritegroup'):
if not has(group):
group.add_internal(self)
self.add_internal(group)
else: self.add(*group)
def remove(self, *groups):
"""remove the sprite from groups
Sprite.remove(*groups): return None
Any number of Group instances can be passed as arguments. The Sprite will
be removed from the Groups it is currently a member of.
"""
has = self.__g.__contains__
for group in groups:
if hasattr(group, '_spritegroup'):
if has(group):
group.remove_internal(self)
self.remove_internal(group)
else: self.remove(*group)
def add_internal(self, group):
self.__g[group] = 0
def remove_internal(self, group):
del self.__g[group]
def update(self, *args):
"""method to control sprite behavior
Sprite.update(*args):
The default implementation of this method does nothing; it's just a
convenient "hook" that you can override. This method is called by
Group.update() with whatever arguments you give it.
There is no need to use this method if not using the convenience
method by the same name in the Group class.
"""
pass
def kill(self):
"""remove the Sprite from all Groups
Sprite.kill(): return None
The Sprite is removed from all the Groups that contain it. This won't
change anything about the state of the Sprite. It is possible to continue
to use the Sprite after this method has been called, including adding it
to Groups.
"""
for c in self.__g.keys():
c.remove_internal(self)
self.__g.clear()
def groups(self):
"""list of Groups that contain this Sprite
Sprite.groups(): return group_list
Return a list of all the Groups that contain this Sprite.
"""
return self.__g.keys()
def alive(self):
"""does the sprite belong to any groups
Sprite.alive(): return bool
Returns True when the Sprite belongs to one or more Groups.
"""
return (len(self.__g) != 0)
def __repr__(self):
return "<%s sprite(in %d groups)>" % (self.__class__.__name__, len(self.__g))
So in summary, you don't have to subclass Sprite - you could just provide all of these methods on your own - but it's easier if you do ;)

Using and making sprite groups [duplicate]

I have an image in pygame with multiple instances of the image called in a for loop. is there a way I could move each instance of the image independently without moving the others with the code as is? or will I have to load separate instances of the image individually?
def pawn(self):
y_pos = 100
self.image = pygame.transform.scale(pygame.image.load('pawn.png'), (100,100))
for x_pos in range(0,8,1):
pieceNum = x_pos
screen.blit(self.image, (x_pos*100, y_pos))
I recommend to use pygame.sprite.Sprite and pygame.sprite.Group:
Create a class derived from pygame.sprite.Sprite:
class MySprite(pygame.sprite.Sprite):
def __init__(self, image, pos_x, pos_y):
super().__init__()
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = (pos_x, pos_y)
Load the image
image = pygame.transform.scale(pygame.image.load('pawn.png'), (100,100))
Create a list of sprites
imageList = [MySprite(image, x_pos*100, 100) for x_pos in range(0,8,1)]
and create a sprite group:
group = pygame.sprite.Group(imageList)
The sprites of a group can be drawn by .draw (screen is the surface created by pygame.display.set_mode()):
group.draw(screen)
The position of the sprite can be changed by changing the position of the .rect property (see pygame.Rect).
e.g.
imageList[0].rect = imageList[0].rect.move(move_x, move_y)
Of course, the movement can be done in a method of class MySprite:
e.g.
class MySprite(pygame.sprite.Sprite):
# [...]
def move(self, move_x, move_y):
self.rect = self.rect.move(move_x, move_y)
imageList[1].move(0, 100)

Categories

Resources