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)
Related
I am trying to make a Towerdefense Game
to check if the Enemy is in the hitcircle of the tower I want to use the function pygame.sprite.collide_mask to find out if they are touching.
This is the output:
AttributeError: 'pygame.mask.Mask' object has no attribute 'add_internal'
How can I make it work? Am I even remotely in the right direction of detecting the collision for the game?
I hope this code can work as much as it needs
import pygame
pygame.init()
class Tower(pygame.sprite.Sprite):
def __init__(self, window):
self.collision_circle =
pygame.image.load('assets/Towers&Projectiles/Collision_Circle/collision_circle.png')
self.tower_mask = pygame.mask.from_surface(self.collision_circle)
class Enemy(pygame.sprite.Sprite):
def __init__(self, window):
self.enemy_mask_image = pygame.image.load('Assets/Enemy/WALK_000.png')
self.enemy_mask = pygame.mask.from_surface(self.enemy_mask_image)
pygame.display.set_caption("Tower Defense")
width = 1200
height = 840
display_surface = pygame.display.set_mode((width, height))
my_enemy = Enemy(display_surface)
my_tower = Tower(display_surface)
while True:
enemy_sprite = pygame.sprite.GroupSingle(my_enemy.enemy_mask)
tower_sprite = pygame.sprite.GroupSingle(my_tower.tower_mask)
if pygame.sprite.spritecollide(enemy_sprite.sprite,tower_sprite,False,pygame.sprite.collide_mask()):
print("collision")
collide_mask use the .rect and .mask object of the Spirte. Therefore the name of the Mask attribute must be mask, but not tower_mask or enemy_mask.
In addition, the objects must have an attribute named rect, which is a pygame.Rect object with the position of the sprite.
Also the super calls a missing from your class constructors. e.g.:
class TowerCircle(pygame.sprite.Sprite):
def __init__(self, centerx, centery):
super().__init__()
self.image = pygame.image.load('assets/Towers&Projectiles/Collision_Circle/collision_circle.png')
self.mask = pygame.mask.from_surface(self.collision_circle)
self.rect = self.image.get_rect(center = (centerx, centery))
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.image.load('Assets/Enemy/WALK_000.png')
self.mask = pygame.mask.from_surface(self.enemy_mask_image)
self.rect = self.image.get_rect(topleft = (x, y))
See Pygame mask collision and Make a line as a sprite with its own collision in Pygame.
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)
I have an image of a vertical line, and want to create two of them. I want them to randomly spread apart and close the gap (while maintaining a minimum and maximum distance - I'm waiting to fix this first), while moving along the x-axis. The y coords don't move.
There are many examples of creating more complex sprites and games using pygame, but I can't find one simple enough to pinpoint what I'm supposed to do, so any help is appreciated. This code is based off of a simple example found here.
This is the very basic code that I have so far.
class Squeeze(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.img_load()
def update(self):
self.rect.x += random.randint(-1, 1)
if self.rect.x >= 1920:
self.rect.x += -1
if self.rect.x <= 0:
self.rect.x += 1
def img_load(self):
self.image = pygame.Surface([SCREEN_WIDTH, SCREEN_HEIGHT])
self.image.fill(BLACK)
self.rect = self.image.get_rect()
pygame.init()
SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
FPS = 60
wall = pygame.image.load('wall.png')
wall_list = pygame.sprite.Group()
BLACK = (0, 0, 0)
for i in range(2):
wall = Squeeze()
wall.rect.x = random.randrange(SCREEN_WIDTH)
wall.rect.y = random.randrange(SCREEN_HEIGHT)
wall_list.add(wall)
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
wall_list.update()
wall_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
I'm confused about the pygame.sprite.Group() deal. How do I get the two images to load and operate on them separately? Any other advice is appreciated as well.
Basically a "sprite" is a visible game object in your game. Pygames pygame.sprite.Sprite class is known as the "base class" for all these objects.
Every time when creating the Squeeze class, you derive form Pygames build-in sprite base class:
class Squeeze(pygame.sprite.Sprite):
def __init__(self):
...
Each object (or instance) of the pygame.sprite.Sprite class holds
an .update() method and
an .image and
an .rect attribute.
As the documentation states, "* ... derived classes will want to override the Sprite.update() and assign a Sprite.image and Sprite.rect attributes.*"
You did this by calling the your img_load() method in the __init__() method of your Squeeze class.
The problem is now that your .rect is assigned to the same size as your screen (self.image = pygame.Surface([SCREEN_WIDTH, SCREEN_HEIGHT])) and the .image attribute has the same color as your display screen, so you canĀ“t see it.
Try to modify your img_load() method to
def img_load(self, img):
self.image = img
self.rect = self.image.get_rect()
As you can see, the modified img_load() method needs an image object -- which is passed to the img argument. So you also need to change your __init__() method:
def __init__(self, img):
pygame.sprite.Sprite.__init__(self)
self.img_load(img)
When now instantiating (e.g. creating an object of a class) a Squeeze object, you need to pass an image (in our case wall_img) to the constructor:
#... main programm
wall_img = pygame.image.load('wall.png') #load an image
wall_list = pygame.sprite.Group() #create a sprite group
BLACK = (0, 0, 0)
for i in range(2):
wall = Squeeze(wall_img) #create a sprite
wall.rect.x = random.randrange(SCREEN_WIDTH)
wall.rect.y = random.randrange(SCREEN_HEIGHT)
wall_list.add(wall) #add the sprite to the sprite group
The second build-in class you use is the pygame.sprite.Group class -- a container, which can hold and manage (modify) sprites it contains.
Calling the .update() method of a sprite group instance, Pygame automatically calls the update() methods of all sprites in this group. This means you can control a sprites behavior by implementing its update() method, as you did:
#update method of the Squeeze class
def update(self):
#any logic which changes sprite
self.rect.x += random.randint(-1, 1)
if self.rect.x >= 1920:
self.rect.x += -1
if self.rect.x <= 0:
self.rect.x += 1
By calling
wall_list.draw(screen)
your spites will be blitted onto the screen surface using the .image attribute for the source surface, and .rect for the position.
If you now wont to have different images you just need to pass other images (or surfaces) to the Squeeze constructor:
#... main programm
wall_imgs = [pygame.image.load('wall.png'),
pygame.image.load('other_wall.png')] #load images in list
wall_list = pygame.sprite.Group() #create a sprite group
BLACK = (0, 0, 0)
for i in range(2):
wall = Squeeze(wall_imgs[i]) #create a sprite
wall.rect.x = random.randrange(SCREEN_WIDTH)
wall.rect.y = random.randrange(SCREEN_HEIGHT)
wall_list.add(wall) #add the sprite to the sprite group
I hope this helps you a little bit :)
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)
I have a super class Sprite and a sub class character:
class Sprite(pygame.sprite.Sprite):
def __init__self(self,Image,position):
pygame.sprite.Sprite.__init__(self)
self.image =pygame.image.load(Image)
self.rect = self.image.get_rect()
self.rect.topleft = pos
class Character(Sprite):
character = pygame.sprite.Group()
def __init__(self):
self.finalimage = pygame.Surface([5, 5])
self.image = self.finalimage
self.rect = self.image.get_rect()
In the Character class I have methods setimage and collision.I use the sprite class to check if a character collides with another for example:
sprite = Sprite(self.image,(self.x,self.y))
self.character.add(sprite)
pygame.sprite.spritecollide(sprite, Background.character, True)
So i need to call the sprite class for collisions. My problem is when I try and give a image to the sprite in the sprite class this is the image which stays when I Use this class, when I give the image object from the constructor I get the error :
pygame.error: Can't seek in this data source
from this line: self.image =pygame.image.load(Image)
Is there a neat way of overriding/ providing your own images? Clearly my logic doesnt work.
Thanks in advance!
self.image has to be a filename (or file object) in sprite = Sprite(self.image,(self.x,self.y)) but it seems you have surface in self.image
# class Character
self.finalimage = pygame.Surface([5, 5]) # finalimage is Surface
self.image = self.finalimage # finalimage is Surface so image is Surface
# ...
sprite = Sprite(self.image,(self.x,self.y)) # image is Surface
# class Sprite
def __init__self(self,Image,position): # so Image is Surface
# ...
self.image =pygame.image.load(Image) # so Image is Surface