Pygame convert surface into sprite - python

I want to make a game with cards, something like Heartstone, but a lot simplier (coz I am not pro programmer). This is just part of program
import pygame
class Card:
def AdCard(self, AdCard):
self.AdCard = AdCard
def HpCard(self, HpCard):
self.HpCard = HpCard
def Picture(self, Picture):
self.Picture = Picture
def Special(self, Special):
if Special == "Heal":
pass
pygame.init()
display = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
swordsman = Card()
swordsman_picture = pygame.image.load("Swordsman.png").convert()
swordsman.Picture(swordsman_picture)
print(type(swordsman.Picture))
Now the problem is that it prints that type of Picture is class 'pygame.Surface' but I want for this picture to be sprite.
How to do that.
Tnx.

Sprite is a class which use Surface to keep image and Rect to keep positon and size.
class Card(pygame.sprite.Sprite):
def __init__(self, surface):
pygame.sprite.Sprite.__init__(self)
self.image = surface
self.rect = self.image.get_rect() # size and position
# and then
one_card = Card(swordsman_picture)
(see Pygame documentation: pygame.sprite.Sprite )
or probably but I did't see this before
one_card = pygame.sprite.Sprite()
one_card.image = swordsman_picture
one_card.rect = one_card.image.get_rect() # size and position
BTW: Use "CamelCase" names only for classes names - to make code more readable - even StackOveflor editor treads Picture, AdCard, etc as class name and use blue color. For functions and variables use lower_case names.
This seems useless
def Picture(self, Picture):
self.Picture = Picture
swordsman.Picture(swordsman_picture)
you can do the same in one line - and make it more readable.
swordsman.Picture = swordsman_picture

Related

Scaling a sprite in main loop [duplicate]

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)

Pygame: How do I get rid of the black halo that is showing up on my planet images?

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?.

Does colliderect work with classes, if so then how do I use it?

I am having an issue adding "colliderect" to my game. I have this class for setting values to the objects, and then one for drawing the items to the screen.
**Edited **
Throughout edits I have updated what my code is having issues with. I have yet to make any of the bel
ow solutions work with my class. All answers that have been given are very helpful, but I am still having the same issues. Collierect just doesn't want to work, when I use pygame.sprite.Sprite for collisions I get a sprite begs flashes when it contacts another sprite.rect, but the rect's movement doesn't reverse.
Help on either of these issues would be greatly appreciated, but until I get things solved I will leave the question open.
Error I was getting
if paddle1.Render().colliderect(ball):
AttributeError: 'NoneType' object has no attribute 'colliderect'
Error I am getting now
if paddle1.Render().colliderect(ball):
AttributeError: 'Object' object has no attribute 'colliderect'
I don't know how to fix this issue.
Here's my code for the ball
#Create a class named sprite to use for the paddles and the ball.
class Object():
def __init__(self,x,y,width,height,color):
# Set X value
self.x = x
# Set Y value
self.y = y
# Set Width of object
self.width = width
# Set Height of object
self.height = height
# Set the (default) color of the object
self.color = (255,255,255)
#attirbute for drawing the sprite(s) to the screen
def Render(self):
pygame.draw.rect(screen,self.color,(self.x,self.y,self.width,self.height))
return self
#Create the sprites
paddle1 = Object(50,175,25,150,color[0])
paddle2 = Object(650,175,25,150,color[1])
ball = Object(300,250,25,25, color[2])
This is the code for colliderect
#Commands for ball collision
if paddle1.Render().colliderect(ball):
if True:
pass #do something
if paddle2.Render().colliderect(ball):
if True:
pass #do something
# --- Drawing code should go here
Aside from the if statement, what am I missing for colliderect to work?
Your method render() that your wrote does not return any value (Ok, it returns None.)
Were you intending to return the rect at the end of that method?
def render(self):
self.rect = pygame.draw.rect(screen,self.color,(self.x,self.y,self.width,self.height))
return self.rect
Then you could chain your method calls like you want:
paddle2.render().colliderect(ball):
...although you probably need to calculate and set the ball's rect and pass it:
paddle2.render().colliderect(ball.rect):
I haven't used pygame so I'm helping a little blindly, but I think you have some confusion over classes. Your sprite should be derived from pygame.sprites.Sprite. Then you specialize your sprite to be the kind of sprite you want it to be, but pygame's sprite implements most of the underlying logic.
Here's an example sprite implementation: http://www.101computing.net/creating-sprites-using-pygame/
Something like that might work. To use colliderect Pygame expects a Rect(x,y,width,height) or Rect((x,y),(width,height)).
So basically you want: myrectangle1.colliderect(myrectangle2)
Similarly to use collidepoint you need: myrectangle1.collidepoint(mypoint1)
#Create a class named sprite to use for the paddles and the ball.
class Object():
def __init__(self,x,y,width,height,color):
# Set X value
self.x = x
# Set Y value
self.y = y
# Set Width of object
self.width = width
# Set Height of object
self.height = height
# Create Rect of object=
self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
# Set the (default) color of the object
self.color = (255,255,255)
#attirbute for drawing the sprite(s) to the screen
def Render(self):
pygame.draw.rect(screen,self.color, self.rect)
return self
#Create the sprites
paddle1 = Object(50,175,25,150,color[0])
paddle2 = Object(650,175,25,150,color[1])
ball = Object(300,250,25,25, color[2])
alternatively if you don't want to have a self.rect attribute then you need:
def Render(self):
pygame.draw.rect(screen,self.color,pygame.Rect(self.x,self.y,self.width,self.height))
return self
This Render method however will just draw your rectangle.
If you want it to return the rectangle attribute of your object to test for collision then you need:
def Render(self):
pygame.draw.rect(screen,self.color, self.rect) #draws the rect
return self.rect #returns the rect
To check for collision you would need:
if paddle1.rect.colliderect(ball.rect): #this is why I think having a rect attribute is cleaner than calling pygame.Rect(x,y,w,h) all the time.
or:
if paddle1.pygame.Rect(self.x, self.y, self.width, self.height).colliderect(ball.pygame.Rect(self.x, self.y, self.width, self.height)):

Pygame - designing the code

My apologies for the poor title. I have experience with how game's code is written with C# (XNA) or java (LibGDX), but I have recently started using python and pygame specifically and I have trouble figuring out how to design the code. This is how I thought it would make to do:
game.py file:
import pygame
import entities
width = 400
height = 300
# Set up the window size
screen = pygame.display.set_mode((width, height), 0, 32)
player = entities.Player(width / 2, height / 2) # The player
#main game loop is here...
entities.py file:
import pygame
import game
class Player:
def __init__(self, x, y):
# Texture for the player
self.texture = pygame.image.load('player.png')
def draw(self):
""" Draws the player """
game.screen.blit(self.texture, (self.position.x, self.position.y))
#class Enemy: ...
However this code give me errors. I think the problem is that I am importing entities.py in the game.py and then I import game.py in the entities.py.
How would I go about to properly design this in python ?
I recommend to use what pygame already offer.
Take a look at the classes Sprite and Group (and their more complex subclasses).
So your player class could look like:
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
# Texture for the player
self.image = pygame.image.load('player.png')
self.rect = self.image.get_rect(topleft=(x, y))
# no need for a draw function
# the sprite class will handle this
# just make sure you store the coordinates of
# your entity in self.rect
def update(self, some_game_state):
pass # do something with some_game_state
Then, in your game, you would put all of your entities in one (or more) group(s), and just call draw and update on that group(s).
entities = pygame.sprite.Group(player) # add other sprite here, too
# main loop
while True:
...
# pass the game state to all entities
# how you design this is up to you
# (pass multiple values, pass a single instace of the game class,
# create a class that encapsulates the state necesseray for the entities etc.)
entites.update(some_game_state)
# pass the main surface to the draw function
entities.draw(screen)
...
You should pass the variables into the relevant classes, rather than making them globals and importing them. So, one possible solution would be to make screen an attribute of Player:
class Player:
def __init__(self, x, y, screen):
self.screen = screen
...
def draw(self):
""" Draws the player """
self.screen.blit(...)
Now, there is no need to import game in entities.py, and you can instantiate the player there like this:
player = entities.Player(width / 2, height / 2, screen)
There are probably other ways to do this, but this should give you the idea.

Pygame: Drawing Ellipse or Rectangle based on Constructor Argument

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.

Categories