Change color of Pygame Surface - python

I've made a Rectangle class:
class Rectangle(pygame.sprite.Sprite):
def __init__(self, len, x, y, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((len, len), flags=pygame.SRCALPHA)
# self.image.set_colorkey(Constants.WHITE)
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.center = (x + len / 2, y + len / 2)
self.is_blocked = False
self.x_pos = x
self.y_pos = y
self.x = math.floor((x - Constants.PADTOPBOTTOM) / Constants.CELL_SIZE)
self.y = math.floor((y - Constants.PADLEFTRIGHT) / Constants.CELL_SIZE)
def update(self):
if self.is_blocked:
print("is update working?") # it is working.
self.image.fill(Constants.GREEN)
And when update() is called I want to change the color of the rectangle to green but it doesn't work.
I'm new to pygame and I don't know what to do.

Your problem is that you keep creating new sprites every frame, so when you change the color of one Rectangle to green, it will be covered by a white one.
You do this by calling the fill function (which creates new Rectangle instances) inside the drawGrid function.

Related

Source object must be a surface

I am encountering a problem which says that the source object must be a surface. I asked this question before, and the answer worked, but now it's a different scenario. I looked at the previous question I had, and I couldn't figure out how to solve it.
Error Message:
Traceback (most recent call last):
File "C:\Users\Daniel\Desktop\Thing.py", line 22, in <module>
level.run()
File "C:\Users\Daniel\Desktop\level2.py", line 131, in run
self.clouds.draw(self.display_surface, self.world_shift)
File "C:\Users\Daniel\Desktop\decoration.py", line 67, in draw
self.cloud_sprites.draw(surface)
File "C:\Users\Daniel\AppData\Local\Programs\Python\Python310\lib\site-
packages\pygame\sprite.py", line 551, in draw
self.spritedict.update(zip(sprites, surface.blits((spr.image, spr.rect) for
spr in sprites)))
TypeError: Source objects must be a surface
Basically I am trying to blit things onto a surface, and the thing that confuses me the most is that it worked when I was trying to blit water onto the screen, it had the exact same code but instead of 'cloud' it had 'water', however the water blitting worked while the cloud did not.
Water Class:
class Water:
def __init__(self, top, level_width):
water_start = -width
water_tile_width = 192
tile_x_amount = int((level_width + width) / water_tile_width)
self.water_sprites = pygame.sprite.Group()
for tile in range(tile_x_amount):
x = tile * water_tile_width + water_start
y = top
sprite = AnimatedTile(192, x, y, (x, y),
'C:\\Desktop\\Game\\decoration\\water')
self.water_sprites.add(sprite)
def draw(self, surface, shift):
self.water_sprites.update(shift)
self.water_sprites.draw(surface)
Cloud Class:
class Clouds:
def __init__(self, horizon, level_width, cloud_number):
min_x = -width
max_x = level_width + width
min_y = 0
max_y = horizon
cloud_surface_list = 'C:\\Desktop\\Game\\decoration\\clouds'
self.cloud_sprites = pygame.sprite.Group()
for cloud in range(cloud_number):
cloud = choice(cloud_surface_list)
x = randint(min_x, max_x)
y = randint(min_y, max_y)
sprite = StaticTile(0, x, y, (x, y),
'C:\\Desktop\\Game\\decoration\\clouds')
self.cloud_sprites.add(sprite)
def draw(self, surface, shift):
self.cloud_sprites.update(shift)
self.cloud_sprites.draw(surface)
As you can see the draw method is the exact same code, pretty much. The only difference that I can spot that could be causing the problem would be the inheritance. Water inherits from a class called AnimatedTile while Clouds inherits from StaticTile. I will put the code for both of those here:
AnimatedTile:
class AnimatedTile(Tile):
def __init__(self, size, x, y, pos, path):
super().__init__(size, x, y, (x, y))
self.frames = import_folder(path)
self.frame_index = 0
self.image = self.frames[int(self.frame_index)]
def animate(self):
self.frame_index += 0.15
if self.frame_index >= 4:
self.frame_index = 0
self.image = self.frames[int(self.frame_index)]
def update(self, shift):
self.animate()
self.rect.x += shift
StaticTile:
class StaticTile(Tile):
def __init__(self, size, x, y, pos, surface):
super().__init__(size, x, y, (x,y))
self.image = surface
self.surface = surface
Drawing and Updating:
self.clouds.draw(self.display_surface,
self.world_shift)
*I know these are surfaces because I drew them the same exact way to blit the water onto the screen (self.water.draw(self.display_surface, self.world_shift) and it worked.
Other code:
class Level:
def __init__(self, level_data, surface):
self.display_surface = surface
self.world_shift = -8
Tile:
class Tile(pygame.sprite.Sprite):
def __init__(self, size, x, y, pos):
super().__init__()
self.image = pygame.Surface((size, size))
self.rect = self.image.get_rect(topleft = pos)
def update(self, shift):
self.rect.x += shift
world_shift is basically how fast the level is moving (negative means right, positive means left)
I'm sorry if this sounds stupid and the answer is obvious.
You're not correctly assigning to the image attribute of your StaticTile sprite in Clouds. I'm not sure if it's the caller (Clouds) or the tile class that is getting it wrong though, you'll have to decide what the API should be.
The mismatch is between your call, here:
sprite = StaticTile(0, x, y, (x, y),
'C:\\Desktop\\Game\\decoration\\clouds')
And the StaticTile.__init__ method, which is declared like this:
def __init__(self, size, x, y, pos, surface):
super().__init__(size, x, y, (x,y))
self.image = surface
self.surface = surface
Note that the calling code is passing a string as the surface argument. That's similar to how AnimatedTile is initialized (with a path), but the StaticTile class expects a surface object to already be loaded.

How to draw rectangle and circles in Pygame environment

I am trying to create a pygame environment with various shapes of sprites but my code seems not working. Here is what I have:
class Object(pygame.sprite.Sprite):
def __init__(self, position, color, size, type):
# create square sprite
pygame.sprite.Sprite.__init__(self)
if type == 'agent':
self.image = pygame.Surface((size, size))
self.image.fill(color)
self.rect = self.image.get_rect()
else:
red = (200,0,0)
self.image = pygame.display.set_mode((size, size))
self.image.fill(color)
self.rect = pygame.draw.circle(self.image, color,(), 20)
# initial conditions
self.start_x = position[0]
self.start_y = position[1]
self.state = np.asarray([self.start_x, self.start_y])
self.rect.x = int((self.start_x * 500) + 100 - self.rect.size[0] / 2)
self.rect.y = int((self.start_y * 500) + 100 - self.rect.size[1] / 2)
Does anyone notice any issues with the Object class?
You have to create a pygame.Surface, instead of creating a new window (pygame.display.set_mode).
The pixel format of the Surface must include a per-pixel alpha (SRCALPHA). The center point of the circle must be the center of the Surface. The radius
must be half the size of the Surface:
self.image = pygame.Surface((size, size), pygame.SRCALPHA)
radius = size // 2
pygame.draw.circle(self.image, color, (radius, radius), radius)
Class Object:
class Object(pygame.sprite.Sprite):
def __init__(self, position, color, size, type):
# create square sprite
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((size, size), pygame.SRCALPHA)
self.rect = self.image.get_rect()
if type == 'agent':
self.image.fill(color)
else:
radius = size // 2
pygame.draw.circle(self.image, color, (radius, radius), radius)
# initial conditions
self.start_x = position[0]
self.start_y = position[1]
self.state = np.asarray([self.start_x, self.start_y])
self.rect.x = int((self.start_x * 500) + 100 - self.rect.size[0] / 2)
self.rect.y = int((self.start_y * 500) + 100 - self.rect.size[1] / 2)

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)

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)

All rectangles in one iteration of a game loop drawing the same colour despite explicit colour arguments

I'm writing a simple toy in Pygame. When you press a key on the home row, it does a little burst of particles.
class Particle():
x = 0
y = 0
size = 0
colour = (255, 255, 255)
rect = None
def __init__(self, x, y, size, colour):
self.x = x
self.y = y
self.size = size
self.colour = colour # Particle has its own colour
self.rect = pygame.Rect(self.x, self.y, self.size, self.size)
class Burst():
x = 0
y = 0
colour = (255, 255, 255)
count = 0
sound = None
particles = []
def __init__(self, x, y, colour, count, sound):
self.x = x
self.y = y
self.colour = colour # Burst has its own colour, too - all its particles should have the same colour as it
self.count = count
self.sound = sound
self.particles.append(Particle(self.x, self.y, 5, self.colour))
def update(self):
self.particles.append(Particle(random.randint(1, 30) + self.x, random.randint(1, 30) + self.y, 5, self.colour))
def draw(self):
global screen
for p in self.particles:
pygame.draw.rect(screen, p.colour, p.rect) # This draws the particles with the correct colours
#pygame.draw.rect(screen, self.colour, (60, 60, 120, 120), 4) # This draws the particles all the same colour
#screen.fill(p.colour, p.rect) # This draws the particles all the same colour
The line you're looking for is in Burst.draw. For some reason, only the uncommented one works correctly. The other two lines, which should be the same as far as I can tell, only draw the first burst's particles correctly. Any subsequent bursts change all particles onscreen to match their colour.
I can provide more code, but there's not much more to it. Basically keypresses add Bursts to an array, and every tick I step through that array calling update() and draw().
Does anyone know what I did wrong, and then accidentally fixed?
Because all particles in the screen belong to the same collection Burst.particles.
And every time you process a Burst you are processing all the particles, and all gets painted with the last colour.
Just move the initialization particles = [] to the init method.
def __init__(self, x, y, colour, count, sound):
...
self.particles = []
self.particles.append(Particle(self.x, self.y, 5, self.colour))
Update
You are using a Java/C# style of coding classes. You shouldn't put any of the initializations at the class level, unless they are constants or class attributes.
IE:
class Burst():
class_attribute = 0 # declaration of class (static) attribute
def __init__(self, ...):
self.attribute = 0 # declaration of object (regular) attribute
You shouldn't make class declarations of attribute you will use a object attributes.
Just remove all the declarations previous to the init method in both classes.

Categories

Resources