Keeping transparent pixels through rotozoom in pygame, Python 3 - python

I'm currently making a game in pygame, Python 3 and a part of the code that has been giving me issues is:
for counter in range(0, 30):
particles = pygame.image.load('particles.png').convert()
particles = pygame.transform.rotozoom(particles, 36*counter, 1.1**counter).convert()
particles.set_colorkey((0, 0, 0, 0))
screen.blit(particles, particles.get_rect(centerx=480, centery=100))
pygame.display.flip()
time.sleep(0.05)
particles.png is just a few colored pixels on a transparent background. The problem is that when the image is rotated and scaled, some of those particles sort of blur out resulting in a mass of black squares around them.
How do I fix this problem? Thanks in advance!!

I've had a bad experience with pygame's rotozoom combined with transparency. Instead of loading the image and then rotozooming it, consider the following:
Using PIL library:
Convert the loaded surface to a string using pygame.image.tostring
Convert the string to an Image object
Use PIL's functions to replace rotozoom (zooming, rotating)
Convert the Image object back into a surface using the same method
Using math:
Load data about the position and color of the particles,
or infer it from the loaded surface
Use simple mathematical functions to caculate their new position,
according to the angle and zoom
Paint them using pygame.gfxdraw, pygame.draw or pygame.Surface.set_at
Good luck!

Related

Crop image to circle shape for PyQt [duplicate]

This question already has answers here:
How to create circular image using pyqt4?
(3 answers)
How to crop an image using QPainterPath without saving rest of image
(2 answers)
Closed 1 year ago.
I am creating a simple application with PyQt6. I want to my code to automatically crop the selected image into circle shape. How can I do that? Please help
Using an external library like pillow or opencv makes things easy as they generally have functions that already provide similar processing, but there's no need to add a dependency for simple manipulations like this, unless performance becomes an issue.
On Qt it's pretty straightforward: create a new QPixmap based on the minimum extent (assuming the circle is going to be the biggest possible in a given rectangle), create a QPainterPath with a circle that will be used for clipping, and finally draw the source contents in it.
def circleImage(imagePath):
source = QtGui.QPixmap(imagePath)
size = min(source.width(), source.height())
target = QtGui.QPixmap(size, size)
target.fill(QtCore.Qt.transparent)
qp = QtGui.QPainter(target)
qp.setRenderHints(qp.Antialiasing)
path = QtGui.QPainterPath()
path.addEllipse(0, 0, size, size)
qp.setClipPath(path)
sourceRect = QtCore.QRect(0, 0, size, size)
sourceRect.moveCenter(source.rect().center())
qp.drawPixmap(target.rect(), source, sourceRect)
qp.end()
return target
This obviously creates an image with transparent background around the circle, you can use any of the global colors or your own QColor.
Note that, no matter the color, the fill() is mandatory, otherwise there will probably be random pixels on the unpainted portions of the image instead (usually some "RAM residue").

Pygame, trying to rotate sprite

I'm trying to rotate of sprite for my game written in pygame
I don't get any consol error, but my sprite stay exactly the same and doesn't rotate :(
Any idea ?
perso.image = pygame.image.load("sprites/[shadow]main_char_sprite.png").convert_alpha()
perso.image = pygame.transform.scale(perso.image, (53,60))
perso.rect = perso.image.get_rect()
perso.rect.x = 200
perso.rect.y = 200
perso.add(perso_group)
while 1:
screen.fill(white)
pygame.transform.rotate(perso.image,30) ########not working :(
all_group.add(statique_group, zombie_group, perso_group)
all_group.draw(screen)
pygame.display.flip()
The documentation for the transform functions says:
All these functions take a Surface to operate on and return a new Surface with the results.
So you need to assign the return value of rotate to the variable:
perso.image = pygame.transform.rotate(perso.image,30)
However, the documentation also says:
Some of the transforms are considered destructive. These means every time they are performed they lose pixel data. Common examples of this are resizing and rotating. For this reason, it is better to retransform the original surface than to keep transforming an image multiple times.
So you may want to keep the original, and keep increasing the rotation angle instead.

What do ".subsurface()", ".convert_alpha()", and "%" do?

I'm trying to learn Pygame, and the tutorial that I am following has a section explaining how to animate sprites. It gives me a sprite sheet that has 8 images measuring 128x128 each, while the entire sheet measures 1024x128.
Then it presents the following code:
#! /usr/bin/env_python
import pygame, sys
from pygame.local import *
pygame.init()
ZONE = pygame.display.set_mode((400,300))
pygame.display.set_caption("Game Zone")
RED = (255,0,0)
clock = pygame.time.Clock()
counter = 0
sprites = []
sheet = pygame.image.load("spritesheet.gif").convert_alpha()
width = sheet.get_width()
for i in range(int(width/128)):
sprites.append(sheet.subsurface(i*128,0,128,128))
while True:
pygame.display.update()
ZONE.fill(RED)
ZONE.blit(sprites[counter],(10,10))
counter = (counter + 1) % 8
clock.tick(16)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
The tutorial is very vague about what those lines do, so I wonder:
What does sheet.subsurface() do? And what do those four parameters stand for? (I believe that the third and fourth are referring to the individual images' width and height.)
What does .convert_alpha() do? The tutorial says it "preserves transparency," but I found it strange since I already used images with transparent backgrounds before and none of those needed such conversion.
What does % do? I already know that / stands for division, but the tutorial never explained %.
subsurface gets you a surface that represents a rectangular section of a larger surface. In this case you have one big surface with lots of sprites on it, and subsurface is used to extract the pieces from that surface. You could also create new surfaces and use blit to copy the pixels, but it's a bit easier to use subsurface and it doesn't need to copy the pixel data.
https://www.pygame.org/docs/ref/surface.html#pygame.Surface.subsurface
Suggested search: pygame subsurface
convert and convert_alpha are both used to convert surfaces to the same pixel format as used by the screen. This ensures that you won't lose performance because of conversions when you're blitting them to the screen. convert throws away any alpha channel, whereas convert_alpha keeps it. The comment that you see refers to the choice to use convert_alpha instead of convert, rather than a choice to use convert_alpha instead of nothing.
https://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert
Suggested search: pygame convert_alpha
The '%' operator isn't a Pygame feature, it's just Python's "modulo/remainder" operator. In this case it's used to make the counter variable loop repeatedly through the values 0 through to 7 and back to 0 again.
https://docs.python.org/2/reference/expressions.html#binary-arithmetic-operations
Suggested search: python percent sign
Let's talk about subsurface(). Assume you have 1,600 images you want to load into the program. There are two ways to do that. (Well, more than two, but I'm making a point here.) First, you could create 1,600 files, load each one into a surface in turn, and start the program. Alternately, you could place them in one file, load that one file into a single surface, and use subsurface(). In this case, spritesheet.gif is 128 pixels high, and contains a new image every 128 pixels.
The two ways basically do the same thing, but one may be more convenient than the other. In particular, opening and reading a file has a small performance cost, and if you need to do this 1,600 times in a row, that cost could be significant.
My understanding of a child surface is that it's basically a Pygame Surface, but defined in terms of a parent surface; if you changed the parent Surface, any child surfaces would be changed in the same way. However, in all other ways, it can be treated as a regular surface.

Pyglet: blit_into texture and alpha

I've been using pyglet for a while now and I really like it. I've got one thing I'd like to do but have been unable to do so far, however.
I'm working on a 2D roleplaying game and I'd like the characters to be able to look different - that is to say, I wouldn't like use completely prebuilt sprites, but instead I'd like there to be a range of, say, hairstyles and equipment, visible on characters in the game.
So to get this thing working, I thought the most sensible way to go on about it would be to create a texture with pyglet.image.Texture.create() and blit the correct sprite source images on that texture using Texture.blit_into. For example, I could blit a naked human image on the texture, then blit a hair texture on that, etc.
human_base = pyglet.image.load('x/human_base.png').get_image_data()
hair_style = pyglet.image.load('x/human_hair1.png').get_image_data()
texture = pyglet.image.Texture.create(width=human_base.width,height=human_base.height)
texture.blit_into(human_base, x=0, y=0, z=0)
texture.blit_into(hair_style, x=0, y=0, z=1)
sprite = pyglet.sprite.Sprite(img=texture, x=0, y=0, batch=my_sprite_batch)
The problem is that blitting the second image into the texture "overwrites" the texture already blitted in. Even though both of the images have an alpha channel, the image below (human_base) is not visible after hair_style is blit on top of it.
One reading this may be wondering why do it this way instead of, say, creating two different pyglet.sprite.Sprite objects, one for human_base and one for hair_style and just move them together. One thing is the draw ordering: the game is tile-based and isometric, so sorting a visible object consisting of multiple sprites with differing layers (or ordered groups, as pyglet calls them) would be a major pain.
So my question is, is there a way to retain alpha when using blit_into with pyglet. If there is no way to do it, please, any suggestions for alternative ways to go on about this would be very much appreciated!
setting the blend function correctly should fix this:
pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA,pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
I ran into the very same problem and couldn't find a proper solution. Apparently blitting two RGBA images/textures overlapping together will remove the image beneath. Another approache I came up with was using every 'clothing image' on every character as an independent sprite attached to batches and groups, but that was far from the optimal and reduced the FPS dramatically.
I got my own solution by using PIL
import pyglet
from PIL import Image
class main(pyglet.window.Window):
def __init__ (self):
TILESIZE = 32
super(main, self).__init__(800, 600, fullscreen = False)
img1 = Image.open('under.png')
img2 = Image.open('over.png')
img1.paste(img2,(0,0),img2.convert('RGBA'))
img = img1.transpose(Image.FLIP_TOP_BOTTOM)
raw_image=img.tostring()
self.image=pyglet.image.ImageData(TILESIZE,TILESIZE,'RGBA',raw_image)
def run(self):
while not self.has_exit:
self.dispatch_events()
self.clear()
self.image.blit(0,0)
self.flip()
x = main()
x.run()
This may well not be the optimal solution, but if you do the loading in scene loading, then it won't matter, and with the result you can do almost almost anything you want to (as long as you don't blit it on another texture, heh). If you want to get just 1 tile (or a column or a row or a rectangular box) out of a tileset with PIL, you can use the crop function.

Any way to speed up Python and Pygame?

I am writing a simple top down rpg in Pygame, and I have found that it is quite slow.... Although I am not expecting python or pygame to match the FPS of games made with compiled languages like C/C++ or event Byte Compiled ones like Java, But still the current FPS of pygame is like 15. I tried rendering 16-color Bitmaps instead of PNGs or 24 Bitmaps, which slightly boosted the speed, then in desperation , I switched everything to black and white monochrome bitmaps and that made the FPS go to 35. But not more. Now according to most Game Development books I have read, for a user to be completely satisfied with game graphics, the FPS of a 2d game should at least be 40, so is there ANY way of boosting the speed of pygame?
Use Psyco, for python2:
import psyco
psyco.full()
Also, enable doublebuffering. For example:
from pygame.locals import *
flags = FULLSCREEN | DOUBLEBUF
screen = pygame.display.set_mode(resolution, flags, bpp)
You could also turn off alpha if you don't need it:
screen.set_alpha(None)
Instead of flipping the entire screen every time, keep track of the changed areas and only update those. For example, something roughly like this (main loop):
events = pygame.events.get()
for event in events:
# deal with events
pygame.event.pump()
my_sprites.do_stuff_every_loop()
rects = my_sprites.draw()
activerects = rects + oldrects
activerects = filter(bool, activerects)
pygame.display.update(activerects)
oldrects = rects[:]
for rect in rects:
screen.blit(bgimg, rect, rect)
Most (all?) drawing functions return a rect.
You can also set only some allowed events, for more speedy event handling:
pygame.event.set_allowed([QUIT, KEYDOWN, KEYUP])
Also, I would not bother with creating a buffer manually and would not use the HWACCEL flag, as I've experienced problems with it on some setups.
Using this, I've achieved reasonably good FPS and smoothness for a small 2d-platformer.
All of these are great suggestions and work well, but you should also keep in mind two things:
1) Blitting surfaces onto surfaces is faster than drawing directly. So pre-drawing fixed images onto surfaces (outside the main game loop), then blitting the surface to the main screen will be more efficient. For exmample:
# pre-draw image outside of main game loop
image_rect = get_image("filename").get_rect()
image_surface = pygame.Surface((image_rect.width, image_rect.height))
image_surface.blit(get_image("filename"), image_rect)
......
# inside main game loop - blit surface to surface (the main screen)
screen.blit(image_surface, image_rect)
2) Make sure you aren't wasting resources by drawing stuff the user can't see. for example:
if point.x >= 0 and point.x <= SCREEN_WIDTH and point.y >= 0 and point.y <= SCREEN_HEIGHT:
# then draw your item
These are some general concepts that help me keep FPS high.
When using images it is important to convert them with the convert()-function of the image.
I have read that convert() disables alpha which is normally quite slow.
I also had speed problems until I used a colour depth of 16 bit and the convert function for my images. Now my FPS are around 150 even if I blit a big image to the screen.
image = image.convert()#video system has to be initialed
Also rotations and scaling takes a lot of time to calculate. A big, transformed image can be saved in another image if it is immutable.
So the idea is to calculate once and reuse the outcome multiple times.
When loading images, if you absolutely require transparency or other alpha values, use the Surface.convert_alpha() method. I have been using it for a game I've been programming, and it has been a huge increase in performance.
E.G: In your constructor, load your images using:
self.srcimage = pygame.image.load(imagepath).convert_alpha()
As far as I can tell, any transformations you do to the image retains the performance this method calls. E.G:
self.rotatedimage = pygame.transform.rotate(self.srcimage, angle).convert_alpha()
becomes redundant if you are using an image that has had convert_alpha() ran on it.
First, always use 'convert()' because it disables alpha which makes bliting faster.
Then only update the parts of the screen that need to be updated.
global rects
rects = []
rects.append(pygame.draw.line(screen, (0, 0, 0), (20, 20), (100, 400), 1))
pygame.display.update(rects) # pygame will only update those rects
Note:
When moving a sprite you have to include in the list the rect from their last position.
You could try using Psyco (http://psyco.sourceforge.net/introduction.html). It often makes quite a bit of difference.
There are a few things to consider for a well-performing Pygame application:
Ensure that the image Surface has the same format as the display Surface. Use convert() (or convert_alpha()) to create a Surface that has the same pixel format. This improves performance when the image is blit on the display, because the formats are compatible and blit does not need to perform an implicit transformation. e.g.:
surf = pygame.image.load('my_image.png').convert_alpha()
Do not load the images in the application loop. pygame.image.load is a very time-consuming operation because the image file must be loaded from the device and the image format must be decoded. Load the images once before the application loop, but use the images in the application loop.
If you have a static game map that consists of tiles, you can buy performance by paying with memory usage. Create a large Surface with the complete map. blit the area which is currently visible on the screen:
game_map = pygame.Surface((tile_size * columns, tile_size * rows))
for x in range(columns):
for y in range(rows):
tile_image = # image for this row and column
game_map.blit(tile_image , (x * tile_size, y * tile_size))
while game:
# [...]
map_sub_rect = screen.get_rect(topleft = (camera_x, camera_y))
screen.blit(game_map, (0, 0), map_sub_rect)
# [...]
If the text is static, the text does not need to be rendered in each frame. Create the text surface once at the beginning of the program or in the constructor of a class, and blit the text surface in each frame.
If the text is dynamic, it cannot even be pre-rendered. However, the most time-consuming is to create the pygame.font.Font/pygame.font.SysFont object. At the very least, you should avoid creating the font in every frame.
In a typical application you don't need all permutations of fonts and font sizes. You just need a couple of different font objects. Create a number of fonts at the beginning of the application and use them when rendering the text. For Instance. e.g.:
fontComic40 = pygame.font.SysFont("Comic Sans MS", 40)
fontComic180 = pygame.font.SysFont("Comic Sans MS", 180)

Categories

Resources