In this article, we’ll explain how exactly you can improve the performance of your game. The end goal should be a steady frame rate, with no significant frame drops at any point.
While Pygame may be lacking features and optimization when compared to actual game development frameworks, low performance in it is not often its fault. If your simple 2D game is having performance issues, the problem lies in your coding, not Pygame.
In this article on Improving Performance in Pygame, we’ll explain how exactly you can improve the performance of your game. The end goal should be a steady frame rate, with no significant frame drops at any point.
If you actually manage to implement all the tips and suggestions I have listed below, you can easily 5x (and more) the speed of your game. Even half of the below recommendations will probably be enough to get you a steady frame rate of your choosing. Better safe than sorry though (not everyone has a good PC!).
You can check out other Pygame Projects, which includes a simple Car dodging game, Platformer and an RPG Fighter.
Do not skip any of these, even if you’ve been able to bump up your performance to your required target. Not all of them are just for speed, some improve the loading times and the audio sync in your game. So pay attention till the end.
Using convert() on images
Whenever you import images, you should always use the
convert() function or
convert_alpha() function on them. This significantly improves performance when it comes to handling these images.
img = pygame.image.load("Background.png").convert()
Adding this function into a Pygame Project of ours which used over 50 images extensively, gave it an almost 5x improvement in performance.
convert_alpha() when there is meant to be some transparency to the image. You’ll be needing this most of the time.
Upgrade to Pygame 2.0
After over a decade in development (and delays), Pygame 2.0 has recently been released. It brings in a whole lot of support with new and improved functions, and most importantly, it improves and speeds up the Pygame engine. Run the below command in the command prompt to upgrade pygame.
pip install pygame --upgrade
Killing Dead Sprites (Memory Leak)
Let’s say you have an RPG game where you are killing enemies. And every time you kill an enemy, you just prevent them from displaying on screen, making it look as though they are dead. There is a problem here though. You have not removed them from the memory, and they are using up space.
Eventually, you may reach a point where you have 1000 enemies in memory, out of which only 5 may be alive. And since your game can only safely handle 100 at most, it begins lagging horribly. This is what we call a memory leak, and it’s a real issue that plagues all game developers (not just pygame).
In short, you need to properly manage how you get rid of useless/dead sprites. You need to fully remove them from the game and free up the memory that they occupied for the next batch of sprites.
This is a bit hard to understand, and even harder to implement. But if you’re serious about improving the performance of your game, you need to give it some time.
Improving Sound Quality
This isn’t directly related to improving (speed) performance in Pygame, but it does improve your sound quality as well as the lag between the sound call and the actual sound. This is about the Pygame mixer, which handles audio in Pygame.
pygame.mixer.pre_init(44100, 16, 2, 4096)
Including the above line into your code, right before the
pygame.init() will significantly improve your audio. It basically initializes the Pygame mixer before Pygame, which improves it.
The first parameter is frequency (Hz), second is bitdepth, third is number of channels (1 for mono, 2 for stereo) and the last is the buffer size. You can adjust these values, as long as you know what you are doing.
Stop Necessary Updating and Rendering
It’s not necessary to update everything in your game, unless it’s being changed. This change alone can speed up your game by 100x, depending on how severe your case of it is.
I came across an example where someone was rendering a huge 3000 x 3000 tile field. Every iteration of the game loop, he would update iterate through all the tiles and update them all. By changing things so that only the updated tiles were rendered, his game went from 2 FPS, to Pygame’s max limit which is 2000.
In short, you need to evaluate your code properly, and come up with a way of only updating those things that are being changed. Similarly, you should check for background objects as well. If something is currently not being drawn to screen, then there is no need to update it either. Only update those objects that are currently in use. Anything that’s offscreen doesn’t need to be drawn either. Only draw it once it enters the screen’s viewport.
This is a pretty tough thing to implement and varies highly (in difficulty and method) from game to game. But it also yields the best rewards, so it’s definitely worth it.
Lower Frame Rate
It’s important to know the right frame rate for your game. In pygame atleast, you should generally aim for about 30. Anything below would be too slow, and anything above might make things difficult on you. Pygame is nowhere as well optimized as other proper game engines.
Games like Chess, Ludo, Cards or anything involving turn based movement isn’t going to benefit from higher frame rates anyway. You may increase the frame rate to 60 for fluid motion games, as long as you follow the optimization tips we have mentioned here.
Don’t think that “what’s the harm in keeping it at higher frame rates even if it doesn’t benefit”. Well, first of all, it’s unnecessary load on your device. Secondly, when frame rates fluctuate too much, it creates micro-stuttering which actually looks pretty bad and makes your experience worse.
This one is a bit weird, and has a pretty small effect, but writing 1 instead of True, saves you one operation. It’s just a drop in the ocean, but no harm in doing it right? (This only has a notable effect on Python versions 2.x, not 3.x and above)
An easy, one line trick that can give you a little boost in FPS, are the double buffering and Full screen flags. Simply add (or modify existing code) the following lines of code into your program.
from pygame.locals import *
flags = FULLSCREEN | DOUBLEBUF
screen = pygame.display.set_mode(resolution, flags, 16)
This will open your game in a special Full screen window where your performance will be slightly improved. The third parameter is “bits-per-pixel” or “bpp”.
Common bit-per-pixel numbers are 8, 16 and 24.
Allowing Only Certain Events
pygame.event.set_allowed([QUIT, KEYDOWN, KEYUP])
It should be fairly obvious why this improves performance. By limiting the number of allowed Pygame events, you reduce the number of checks that pygame has to do every iteration of the game loop.
Avoid Spamming flip()
Once we make a change to a sprite’s position in the Pygame window, many people call the
pygame.display.flip() or the
pygame.display.update() function. The problem here is that you are refreshing the entire window for every change you make. This has a significant hit on performance.
In short, you need to do two things. Do not call
update() until the end of the Game loop, unless it’s actually required for some task. Secondly, try updating only the certain sprite(s) by passing the sprite(s) rect(s) into the
update() function. This updates only those specific sprite(s), saving a lot of performance.
Lazy Loading Modules
This is a helpful little tip that will improve the loading time of your Pygame Window. Instead of importing all the modules right at the start of your program, spread it out a bit.
Often due to the complexity of the game, you use many libraries, which takes a little time to actually load up. And since you do this before you actually initialize pygame and begin drawing backgrounds, the program has to import all the modules before proceeding any further.
In short, spread out your
import statements, importing them only right before they are actually need it. You might want to research this topic separately, and find out the best way to implement lazy loading with modules.
Instead of using your own custom functions, I would recommend you use Python’s many in-built functions. These functions have been highly tested, are safe and faster than regular or custom methods. Python has 40+ built-in-functions you can take advantage of.
Optimize Your Code
Probably the most generic piece of advice you’ll get here, but it’s also one of the most effective when it comes to increasing your performance. Unfortunately, it’s also one of the toughest. You have to re-evaluate the code you’ve written, and come up with a better way of doing it.
Whatever approach you’ve used, there is almost certainly a better way of doing it. Go back to the roots of what you are trying to do, and work from there. The answer is going to be unique each time, in every game, and to every person. And there’s no shortcut either, so don’t even think about it. Search up the term “Python Profiling” to learn more about this technique.
This is the last technique that I have in mind. Honestly, if you’ve reached a point where you feel the need to implement Multi-threading (and nothing else is helping), you’re probably doing something wrong. Pygame is a simple framework, and doesn’t actually have support for advanced features like Multi-threading.
At any rate, I don’t recommend using Pygame related codes in any threads you may create. Try to only move computational code into the threads as Pygame’s behavior can be rather weird on threads, requiring some complex set up with sub processes and processes (that’s a library in Python).
Let us know in the comments section whether our tips for improving performance in pygame helped, and how much of a speed increase you got!
This ariticle was originally posted at CodersLegacy.