This question already has an answer here:
How do I scale a PyGame image (Surface) with respect to its center?
(1 answer)
Closed 2 years ago.
I want to create a game where circles are generated randomly over the surface and start to grow. When 2 circles touch each other the game ends. So, everything is working except the resizing of the sprite during a loop. When I use transform.scale I get something like this:
Then I found transform.smoothscale in the doc. I used changed my code to use this and then it looked like this:
I also tried to use Rect.inflate but this did nothing to my sprite. And I tried Rect.infalte_ip and if I use this, the sprite won't grow it is more likely to move out of the frame. Any ideas on how I can make these Sprite grow in place and that they resize how they should?
class Bubbles(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image_scale = 100
self.image_scale_factor = 1
self.image = resources.BUBBLE_SKIN[0].convert_alpha()
self.image = pygame.transform.scale(self.image, (self.image_scale, self.image_scale))
self.rect = self.image.get_rect()
self.rect.centerx = (random.randrange(Settings.object_range_limit + (self.image_scale//2), (Settings.width - Settings.object_range_limit - self.image_scale)))
self.rect.centery = (random.randrange(Settings.object_range_limit + (self.image_scale//2), (Settings.height - Settings.object_range_limit - self.image_scale)))
self.growth_rate = random.randint(1, 4)
def update(self):
self.image_scale += self.growth_rate
self.image = pygame.transform.scale(self.image, (self.image_scale, self.image_scale))
You must scale the original sprite, instead of gradually scaling the sprite. Every time you scale the sprint, the quality degrades. If you scale the sprite gradually, the quality gets worse and worse. Store the sprite to an attribute orignal_image and scale the orignal_image.
If you want to scale the image by the center of the image, you must update the rect attribute after scaling the image with the new image size.
See How do I scale a PyGame image (Surface) with respect to its center?
class Bubbles(pygame.sprite.Sprite):
def __init__(self):
# [...]
self.image = resources.BUBBLE_SKIN[0].convert_alpha()
self.image = pygame.transform.scale(
self.image, (self.image_scale, self.image_scale))
self.original_image = self.image
def update(self):
self.image_scale += self.growth_rate
self.image = pygame.transform.scale(
self.original_image, (self.image_scale, self.image_scale))
self.rect = self.image.get_rect(center = self.rect.center)
Related
While testing my new-found python skills using pygame, I ran into an interesting problem.
When I load planet .png images onto the screen, the planets show up with a black halo.
This is really strange since the other images(.png and .bmp) that I have loaded into the game work without a problem.
I have imported the images by using a for loop and converting them with convert_alpha().
When I do not convert the images, the black halo is still there.
I also tried setting the color key with each iteration of the for loop to no avail:
class Pl_Images(pygame.sprite.Sprite):
def __init__(self):
self.pl_images = []
for num in range(1,5):
img = pygame.image.load(f"Images/planets/static/planet{num}.png")
img_rect = img.get_rect()
img = pygame.transform.scale(img, (int(img_rect.width//10), int(img_rect.height//10)))
self.pl_images.append(img)
One of the pre-loaded images are then used (when needed) by a Planet object when it is created by the planet generator class "PlanetSurface" and is placed into a sprite group.
import pygame
from planet import Planet
import random
from pygame.sprite import Sprite
from pl_images import Pl_Images
class PlanetSurface(Sprite):
def __init__(self, settings):
"""
Creates a planet surface and planets that travel across the screen
"""
super(PlanetSurface, self). __init__()
self.settings = settings
self.surface = pygame.surface.Surface(settings.screen.get_size())
self.surface.set_colorkey([0,0,0])
self.images = Pl_Images()
self.planets = pygame.sprite.Group()
self.pl_timer = random.randrange(420, 720) # 7 seconds and 12 seconds
self.max_planets = 3
self.current_num_planets = 0
def update(self):
""" update the planets """
self.planets.update()
for planet in self.planets.copy():
if planet.rect.y > self.settings.screen_height:
self.planets.remove(planet)
self.current_num_planets -= 1
if self.pl_timer == 0:
if self.current_num_planets >= self.max_planets:
pass
else:
self.new_planet = Planet(self.settings, self.images)
self.rect = self.new_planet.rect
self.planets.add(self.new_planet)
self.pl_timer = random.randrange(2100, 3600)
# Redraw planet for a smooth effect
self.surface.fill((0,0,0))
self.planets.draw(self.surface)
self.pl_timer -= 1
Here is the Planet class:
class Planet(Sprite):
def __init__(self, settings, images):
super(Planet, self). __init__()
self.images = images
self.planets = self.images.pl_images
self.settings = settings
self.num_planets = len(self.planets)
self.image_index = random.randrange(self.num_planets - 1)
self.image = self.planets[self.image_index]
self.rect = self.image.get_rect()
self.rect.x = random.randrange(
0,
self.settings.screen_width - self.rect.width
)
self.rect.y = -self.rect.height
self.vel_x = 0
self.vel_y = random.randrange(1, 2)
def update(self):
self.rect.x += self.vel_x
self.rect.y += self.vel_y
The image is then drawn to the planet's surface when the update method is called.
The surface that I am drawing the images to is a simple surface that uses a transparent colorkey:
This custom surface is being drawn by the main function on top of the main background but below the gaming surface:
self.screen.blit(self.background.bgimage, (self.background.bgX2,
self.background.bgY2))
self.screen.blit(self.planet_surface.surface, (0,0))
self.screen.blit(self.game_surface.surface, (0,0))
I am quite baffled by this problem since the planets are .png images with transparent backgrounds.
Does anyone have any idea where this black halo might be coming from?
Is there something that I am doing wrong or is this a glitch in pygame?
I have uploaded a sample planet for analysis.
planet45.png
Thanks in advance!
I examined the example planet you provided in your question. - planet45.png
The alpha channel of the entire image is 255. The image has an opaque white background and a pale blue halo around the planet. You can set a white color (set_colorkey()) to make the background transparent, but the halo will remain opaque. The problem is not with your application, but with the image resource. You need to use another planet image that provides alpha per pixel.
See also How can I make an Image with a transparent Backround in Pygame? or How to convert the background color of image to match the color of Pygame window? respectively How do I blit a PNG with some transparency onto a surface in Pygame?.
So I copied some code from the internet (http://programarcadegames.com/python_examples/f.php?file=platform_moving.py) just to experiment with pygame...
I've tried replacing the self.image.fill(BLUE) with self.rect = pygame.image.load("TheArrow.png")
Here's a little snippet of my code..
def __init__(self):
""" Constructor function """
# Call the parent's constructor
super().__init__()
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
width = 40
height = 60
self.image = pygame.Surface([width, height])
self.image.fill(BLUE)
self.rect = pygame.image.load("TheArrow.png")
# Set a referance to the image rect.
self.rect = self.image.get_rect()
# Set speed vector of player
self.change_x = 0
self.change_y = 0
# List of sprites we can bump against
self.level = None
Here's the original code...
def __init__(self):
""" Constructor function """
# Call the parent's constructor
super().__init__()
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
width = 40
height = 60
self.image = pygame.Surface([width, height])
self.image.fill(RED)
# Set a referance to the image rect.
self.rect = self.image.get_rect()
# Set speed vector of player
self.change_x = 0
self.change_y = 0
# List of sprites we can bump against
self.level = None
I want the image TheArrow.png to show up instead of a rectangle....
Rect object are not meant to store images. pygame.image.load() returns a Surface with the image. It can be used directly or blitted on another Surface.
def __init__(self):
""" Constructor function """
# Call the parent's constructor
super().__init__()
width = 40
height = 60
self.image = pygame.image.load("TheArrow.png") #use the image Surface directly
self.rect = self.image.get_rect()
#the rest as in the original code
or:
def __init__(self):
""" Constructor function """
# Call the parent's constructor
super().__init__()
width = 40
height = 60
myimage = pygame.image.load("TheArrow.png")
self.image = pygame.Surface([width, height])
self.image.blit(myimage) #blit the image on an existing surface
self.rect = self.image.get_rect()
#the rest as in the original code
In the former case, the size of the Surface (its associated rect, which you can get with self.image.get_rect() is the same of the loaded image file.
In the latter, you set the size with [with, height]. If these does not correspond to the image size, the image will be cut (if bigger).
By the way, blitting a Surface on another Surface is what you do display the Surface on the screen. In pygame the screen is just another Surface, a bit special.
Have a look at the intro tutorial for more info.
I'm currently trying to make pixel perfect collisions between my pong ball and my player's paddle using the mask and collide_rect functions. I made my own checkCollision function in the pong class which would check for pixel perfect collision. However, right now, I can't even get the Sprites to work or appear on the screen because my "Pong object is not iterable.
Here is my pong class with the important features: (I will post additional code if needed)
class Pong(pygame.sprite.Sprite):
def __init__(self, screensize):
pygame.sprite.Sprite.__init__(self)
self.screensize = screensize
self.centerx = screensize[0] // 2
self.centery = screensize[1] // 2
self.radius = 25
self.rect = pygame.Rect(self.centerx-self.radius,
self.centery-self.radius,
self.radius*2, self.radius*2)
self.pokeimage = pygame.image.load("pokeball.png")
self.pokeimage = pygame.transform.scale(self.pokeimage, (self.radius, self.radius))
#Create the mask
self.mask = pygame.mask.from_surface(self.pokeimage)
def checkCollision(self, player_paddle, ai_paddle):
col = pygame.sprite.collide_rect(self, player_paddle)
return col
def collisionFormula(self, player_paddle, ai_paddle):
if self.checkCollision(self, player_paddle):
def collision_checks(self, player_paddle, ai_paddle):
#if the ball hits the top or bottom of the screen, change the y direction
if self.rect.top <= 0 or self.rect.bottom >= self.screensize[1] - 1:
self.direction[1] *= -1
#if the pong hits the paddles, change how the pong ball moves
if self.rect.colliderect(player_paddle.rect) or self.rect.colliderect(ai_paddle.rect):
self.collisionFormula(player_paddle, ai_paddle)
def update(self, player_paddle, ai_paddle):
self.update_ball_position()
self.reset_ball()
self.collision_checks(player_paddle, ai_paddle)
In my PlayerPaddle class, I do the same mask initialization.
class PlayerPaddle(pygame.sprite.Sprite):
def __init__(self, screensize):
pygame.sprite.Sprite.__init__(self)
self.screensize = screensize
self.centerx = 50
self.centery = screensize[1]//2
self.height = 100
self.width = 20
self.imageMaster = pygame.image.load("naruto.png").convert_alpha()
self.imageMaster = pygame.transform.scale(self.imageMaster, (self.width, self.height))
self.image = self.imageMaster
#mask
self.mask = pygame.mask.from_surface(self.image)
def turnLeft(self):
self.dir += 45
if self.dir > 360:
self.dir = 45
def turnRight(self):
self.dir -= 45
if self.dir < 0:
self.dir = 315
def update(self):
#Rotate functions
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMaster, self.dir)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
And here is my main function:
def main():
pygame.init()
screensize = (640,700)
screen = pygame.display.set_mode(screensize)
background = pygame.Surface(screen.get_size())
background.fill((0, 255, 0))
clock = pygame.time.Clock()
pong = Pong(screensize)
player_paddle = PlayerPaddle(screensize)
ai_paddle = AIPaddle(screensize)
paddleSprite = pygame.sprite.Group(player_paddle)
pongSprite = pygame.sprite.Group(pong)
while running:
running = True
#object updating phase
ai_paddle.update(pong, player_paddle)
player_paddle.update()
pong.update(player_paddle, ai_paddle)
if pygame.sprite.spritecollide(player_paddle, pong, False, pygame.sprite.collide_mask):
print("Collided")
#rendering phase
ai_paddle.render(screen)
player_paddle.render(screen)
pong.render(screen)
paddleSprite.clear(screen, background)
paddleSprite.update()
paddleSprite.draw(screen)
pongSprite.clear(screen,background)
pongSprite.update()
pongSprite.draw(screen)
pygame.display.flip()
pygame.quit()
main()
I made two "group" objects (the pong and the player_paddle) but I'm not sure why I'm even failing to run the program. Additionally, I know the collision will not work because the pong ball will hit the rectangle of the original image, but not the rotated image, but I'm not sure why that will happen if I use the built in sprite function. Thanks.
Read documentation for spritecollide
Find sprites in a group that intersect another sprite.
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
Second argument has to be group (pygame.sprite.Group), not single Sprite.
It can be group event with one sprite. But you use pong which is single sprite, not group.
See documentation for collide_mask
Collision detection between two sprites, using masks.
collide_mask(SpriteLeft, SpriteRight) -> point
It checks collision between two sprites using mask.
EDIT: in your code you have problem with
spritecollide(player_paddle, pong,...)
because pong is single Sprite, not Group.
With pong you should use collide_mask
collide_mask(player_paddle, pong)
You can use spritecollidewith pongSprite which is Group
spritecollide(player_paddle, pongSprite,...)`
BTW: you could use better names ie. pong_group instead of pongSprite.
And eventually pong_sprite instead of pong (but pong is OK, too).
This question already has answers here:
How do I make my monster move randomly in my game
(1 answer)
Pygame Pacman - Enemy Random Movement
(1 answer)
Closed 2 years ago.
I made part of a game. In the game there is a player and enemies. I decided to add NPC characters to the game which move randomly. The problem is that they do not draw as expected.
NPCs do not run as smoothly as the rest of the code. When an NPC moves around it leaves big patches of white where it has been. I think it's the speed of the update but I'm not quite sure. The framerate is roughly 40 fps. I have tried to find a problem in the update function of NPCs but it looks perfectly fine. However, NPCs only stop creating the weird white patches when I exclude the def update(self). I'm running Window Vista, Python 2.6 and Pygame. Any help is appreciated.
class NPC(pygame.sprite.Sprite):
change_x = 0
def __init__(self, color, width, height):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(white)
self.rect = self.image.get_rect()
def update(self):
self.rect.x += self.change_x
speed = 2
Movementlist = ['moveRight', 'moveLeft']
randomMove = random.choice(Movementlist)
if randomMove == 'moveRight':
self.change_x = -6 * speed
elif randomMove == 'moveLeft':
self.change_x = 6 * speed
Here is the where an NPC is created:
if level == 1:
npc = NPC(white, 20, 15)
npc.rect.x = 400
npc.rect.y = 485
NPC_list.add(npc)
all_sprites_list.add(npc)
And here's the update:
NPC_list.update()
The draw commands are here:
# --- Draw Frame
# Set the screen background
if level < 5 or level == 5:
screen.fill(black)
if level == 10:
screen.fill(silver)
all_sprites_list.draw(screen)
# Go ahead and update the screen with what we've drawn.
pygame.display.update()
# Limit to 40 frames per second
clock.tick(40)
I don't know if this is the correct website, but you guys have been so helpful before, I wanted to get your advice on a problem I'm having with Python and Pygame.
I am making a simple game, and only recently begun learning Python (loving it so far) and at the moment, I having a sprite constructor which I am using. This constructor will manage my objects, but I want it to draw either an ellipse or a rectangle based on an argument passed to it.
#My code
class Block(pygame.sprite.Sprite):
#Variables!
speed = 2
indestructible = True
#Constructor
def __init__(self, color, width, height, name, shapeType):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width,height])
self.image.fill(color)
#Choose what to draw
if shapeType == "Ellipse":
pygame.draw.ellipse(self.image,color,[0,0,width,height])
elif shapeType == "Rect":
pygame.draw.rect(self.image,color,[0,0,width,height])
elif shapeType == "":
print("Shape type for ",name," not defined.")
pygame.draw.rect(self.image,color,[0,0,width,height])
#Init the Rect class for sprites
self.rect = self.image.get_rect()
The coding I am using for drawing a square is below:
#Add 'white star' to the list
for i in range(random.randrange(100,200)):
whiteStar = Block(white, 1, 1, "White Star", "Rect")
whiteStar.rect.x = random.randrange(size[0])
whiteStar.rect.y = random.randrange(size[1])
whiteStar.speed = 2
block_list.add(whiteStar)
all_sprites_list.add(whiteStar)
This works wonderfully. It draws a perfect little white square for me. But this doesn't work:
#Create Planet
planet = Block(green, 15,15, "Planet", "Ellipse")
planet.rect.x = random.randrange(size[0])
planet.rect.y = 30
planet.speed = 1
block_list.add(planet)
all_sprites_list.add(planet)
The 'planet' spawns correctly, but it does so as a square. Why is this happening? And how can I fix it? Should I use a bitmap to correct this? Or is my coding wrong?
Just to clarify, I know for a fact that self.rect = self.image.get_rect() does work to draw an ellipse, because the coding below works.
#Not the code I'm using, but this works and proves self.rect = self.image.get_rect() is not the cause
# 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([width, height])
self.image.fill(white)
self.image.set_colorkey(white)
pygame.draw.ellipse(self.image,color,[0,0,width,height])
# Fetch the rectangle object that has the dimensions of the image
# image.
# Update the position of this object by setting the values
# of rect.x and rect.y
self.rect = self.image.get_rect()
Thankyou for your help. :-)
In the Block constructor, you call self.image.fill(color). That will fill the sprite's entire image with that color, so you get a rectangle.
The example code you have calls self.image.set_colorkey(white) after doing the fill, so that when it gets drawn, the background fill is transparent. That's probably the fastest solution.
You are filling the surface with the given color, and then drawing your shape in the same color. Of course it won't be visible that way, and you just get the solid-coloured surface, which is rectangular.