-
-
Notifications
You must be signed in to change notification settings - Fork 160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
De-emphasise passing a list of rectangles to update #2532
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -253,9 +253,7 @@ required). | |
| :sl:`Update the full display Surface to the screen` | ||
| :sg:`flip() -> None` | ||
|
||
This will update the contents of the entire display. If your display mode is | ||
using the flags ``pygame.HWSURFACE`` and ``pygame.DOUBLEBUF`` on pygame 1, | ||
this will wait for a vertical retrace and swap the surfaces. | ||
This will update the contents of the entire display. | ||
|
||
When using an ``pygame.OPENGL`` display mode this will perform a gl buffer | ||
swap. | ||
|
@@ -264,23 +262,31 @@ required). | |
|
||
.. function:: update | ||
|
||
| :sl:`Update portions of the screen for software displays` | ||
| :sl:`Update all, or a portion, of the display. For non-OpenGL displays.` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One of my personal bugbears is saying 'screen' all the time when we just mean the portion of the screen that has your pygame-ce application on it. It might be the whole screen, it might just be one window among many on your screen. I prefer |
||
| :sg:`update(rectangle=None) -> None` | ||
| :sg:`update(rectangle_list) -> None` | ||
|
||
This function is like an optimized version of ``pygame.display.flip()`` for | ||
software displays. It allows only a portion of the screen to be updated, | ||
instead of the entire area. If no argument is passed it updates the entire | ||
Surface area like ``pygame.display.flip()``. | ||
For non OpenGL display Surfaces, this function is very similar to | ||
``pygame.display.flip()`` with an optional parameter that allows only | ||
portions of the display surface to be updated, instead of the entire area. | ||
If no argument is passed it updates the entire Surface area like | ||
``pygame.display.flip()``. | ||
|
||
Note that calling ``display.update(None)`` means no part of the window is | ||
updated. Whereas ``display.update()`` means the whole window is updated. | ||
.. note:: calling ``display.update(None)`` means no part of the window is | ||
updated. Whereas ``display.update()`` means the whole window is | ||
updated. | ||
Comment on lines
+275
to
+277
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just updating this to be a proper |
||
|
||
You can pass the function a single rectangle, or a sequence of rectangles. | ||
It is more efficient to pass many rectangles at once than to call update | ||
multiple times with single or a partial list of rectangles. If passing a | ||
sequence of rectangles it is safe to include None values in the list, which | ||
will be skipped. | ||
Generally you do not want to pass a sequence of rectangles as there is a | ||
performance cost per rectangle passed to the function. On modern hardware, | ||
after a very small number of rectangles passed in, the per-rectangle cost | ||
will exceed the saving of updating less pixels. In most applications it is | ||
simply more efficient to update the entire display surface at once, it also | ||
means you do not need to keep track of a list of rectangles for each call | ||
to update. | ||
|
||
If passing a sequence of rectangles it is safe to include None | ||
values in the list, which will be skipped. | ||
|
||
This call cannot be used on ``pygame.OPENGL`` displays and will generate an | ||
exception. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -220,10 +220,11 @@ sprite module. | |
|
||
:class:`RenderUpdates <pygame.sprite.RenderUpdates>` | ||
|
||
This is the Cadillac of rendering ``Groups``. It is inherited from | ||
``RenderClear``, but changes the ``draw()`` method to also return a list of | ||
pygame ``Rects``, which represent all the areas on screen that have been | ||
changed. | ||
This group is inherited from ``RenderClear``, but changes the | ||
``draw()`` method to also return a list of pygame ``Rects``, | ||
which represent all the areas on screen that have been changed. | ||
Generally you don't need to use this group, but it is included for | ||
completeness. | ||
|
||
That is the list of different groups available We'll discuss more about these | ||
rendering groups in the next section. There's nothing stopping you from | ||
|
@@ -241,33 +242,9 @@ The Rendering Groups | |
-------------------- | ||
|
||
From above we can see there are three different rendering groups. We could | ||
probably just get away with the ``RenderUpdates`` one, but it adds overhead not | ||
really needed for something like a scrolling game. So we have a couple tools | ||
here, pick the right one for the right job. | ||
|
||
For a scrolling type game, where the background completely changes every frame, | ||
we obviously don't need to worry about python's update rectangles in the call | ||
to ``display.update()``. You should definitely go with the ``RenderPlain`` | ||
group here to manage your rendering. | ||
|
||
For games where the background is more stationary, you definitely don't want | ||
pygame updating the entire screen (since it doesn't need to). This type of game | ||
usually involves erasing the old position of each object, then drawing it in a | ||
new place for each frame. This way we are only changing what is necessary. | ||
Most of the time you will just want to use the ``RenderUpdates`` class here. | ||
Since you will also want to pass this list of changes to the | ||
``display.update()`` function. | ||
|
||
The ``RenderUpdates`` class also does a good job at minimizing overlapping | ||
areas in the list of updated rectangles. If the previous position and current | ||
position of an object overlap, it will merge them into a single rectangle. | ||
Combined with the fact that it properly handles deleted objects, this is | ||
one powerful ``Group`` class. If you've written a game that manages the changed | ||
rectangles for the objects in a game, you know this the cause for a lot of | ||
messy code in your game. Especially once you start to throw in objects that can | ||
be deleted at any time. All this work is reduced to a ``clear()`` and | ||
``draw()`` method with this monster class. Plus with the overlap checking, it | ||
is likely faster than when you did it manually. | ||
Comment on lines
-244
to
-270
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This stuff reminds me I need to get on with my sprite module refactorings as we seem to have a lot of code also dedicated to this old optimisation feature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes :) |
||
probably just get away with the ``RenderPlain`` one. For a scrolling or stationary | ||
type game, you should probably go with the ``RenderPlain`` group here to manage | ||
your rendering. | ||
|
||
Also note that there's nothing stopping you from mixing and matching these | ||
render groups in your game. You should definitely use multiple rendering groups | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -155,9 +155,9 @@ refresh the entire display once per frame at 60 FPS and beyond. You can have a | |
camera, or dynamic backgrounds and your game should run totally fine at 60 FPS. CPUs are | ||
more powerful nowadays, and you can use ``display.flip()`` without fear. | ||
|
||
That being said there are still some times when this old technique is still useful | ||
for squeezing out a few extra FPS. For example, with a single screen game like | ||
an Asteroids or Space Invaders. Here is the rough process for how it works: | ||
That being said there are still a few rare times when this old technique is still useful | ||
for squeezing out a few extra FPS. For example, if you only need to update half of the display | ||
surface and have a stationary image in the other half. Here is the rough process for how it works: | ||
|
||
Instead of updating the whole screen every frame, only the parts that changed since | ||
the last frame are updated. You do this by keeping track of those rectangles in a list, | ||
|
@@ -171,8 +171,8 @@ for a moving sprite: | |
* Append the sprite's new location to my dirty_rects list. | ||
* Call ``display.update(dirty_rects)`` | ||
|
||
Even though this technique is not required for making performant 2D games with | ||
modern CPUs, it is still useful to be aware of. There are also still plenty of other ways | ||
Even though this technique is not really useful for making performant 2D games with | ||
modern CPUs, it is still nice to be aware of. There are also still plenty of other ways | ||
to accidentally tank your game's performance with poorly optimized rendering logic. | ||
For example, even on modern hardware it's probably too slow to call ``set_at`` once per pixel | ||
on the display surface. Being mindful of performance is still something you'll have to | ||
|
@@ -209,9 +209,10 @@ but in reality it's pretty much just a wrapper around a ``Rect`` and a | |
more intuitive (and fun) to write your game's core logic and classes from | ||
scratch. | ||
|
||
**Premultiplied Alpha** | ||
|
||
There is NO rule six. | ||
--------------------- | ||
Using this alpha blending mode can boost your blitting performance slightly, it even | ||
has its own `tutorial page <tutorials/en/premultiplied-alpha>.` | ||
|
||
|
||
Don't get distracted by side issues. | ||
|
@@ -358,35 +359,30 @@ will avoid this. | |
Colorkey vs. Alpha. | ||
------------------- | ||
|
||
There's a lot of confusion around these two techniques, and much of it comes | ||
There's a lot of confusion around these two terms, and much of it comes | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just because 'Alpha' isn't really a technique on it own, color key transparency is a complete technique. |
||
from the terminology used. | ||
|
||
Per pixel alpha is the best type of alpha to use, it will give you nice | ||
transparent and 'feathered' edges. Basically, each pixel in the source image | ||
has its own alpha value, from 0 to 255. Most image editors will let you export | ||
images with an alpha channel and pygame can load them. | ||
|
||
'Colorkey blitting' involves telling pygame that all pixels of a certain color | ||
in a certain image are transparent instead of whatever color they happen to be. | ||
These transparent pixels are not blitted when the rest of the image is blitted, | ||
and so don't obscure the background. This is how we make sprites that aren't | ||
rectangular in shape. Simply call :meth:`.Surface.set_colorkey()`, and pass in | ||
an RGB tuple -- say (0,0,0). This would make every pixel in the source image | ||
transparent instead of black. | ||
|
||
'Alpha' is different, and it comes in two flavors. 'Image alpha' applies to the | ||
whole image, and is probably what you want. Properly known as 'translucency', | ||
alpha causes each pixel in the source image to be only *partially* opaque. | ||
For example, if you set a surface's alpha to 192 and then blitted it onto a | ||
and so don't obscure the background. Simply call :meth:`.Surface.set_colorkey()`, | ||
and pass in an RGB tuple -- say (0,0,0). This would make every pixel in the source | ||
image transparent instead of black. It used to be significantly cheaper to blit | ||
than per pixel alpha, but this is no longer the case and in many cases it is | ||
actually slower. | ||
|
||
Surface 'Alpha' is also available it applies to the whole surface. Surface alpha | ||
causes each pixel in the source image to be only *partially* opaque. | ||
For example, if you set a Surface's alpha to 192 and then blitted it onto a | ||
background, 3/4 of each pixel's color would come from the source image, and 1/4 | ||
from the background. Alpha is measured from 255 to 0, where 0 is completely | ||
transparent, and 255 is completely opaque. Note that colorkey and alpha | ||
blitting can be combined -- this produces an image that is fully transparent in | ||
some spots, and semi-transparent in others. | ||
|
||
'Per-pixel alpha' is the other flavor of alpha, and it's more complicated. | ||
Basically, each pixel in the source image has its own alpha value, from 0 to | ||
255. Each pixel, therefore, can have a different opacity when blitted onto a | ||
background. This type of alpha can't be mixed with colorkey blitting, | ||
and it overrides per-image alpha. Per-pixel alpha is rarely used in | ||
games, and to use it you have to save your source image in a graphic | ||
editor with a special *alpha channel*. It's complicated -- don't use it | ||
yet. | ||
transparent, and 255 is completely opaque. Note that surface alpha and alpha | ||
blitting can be combined. | ||
|
||
|
||
Software architecture, design patterns, and games. | ||
|
@@ -407,7 +403,12 @@ you start due to somewhat nebulous organizational problems. | |
|
||
This brings us to the concept of software architecture and design patterns. You | ||
may be familiar with pygame's "standard" base template (there are many equivalent | ||
variations of this, so don't stress about the small details too much):: | ||
variations of this, so don't stress about the small details too much): | ||
|
||
.. code-block:: python | ||
:caption: Standard Pygame game loop | ||
:name: game_loop.py | ||
:linenos: | ||
Comment on lines
+406
to
+411
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just think this modern style of .rst code block is neat so I updated it while I was in here. I may also have tweaked the standard game loop pattern a bit. |
||
|
||
import pygame | ||
|
||
|
@@ -417,12 +418,13 @@ variations of this, so don't stress about the small details too much):: | |
|
||
clock = pygame.time.Clock() | ||
|
||
while True: | ||
running = True | ||
|
||
while running: | ||
# Process player inputs. | ||
for event in pygame.event.get(): | ||
if event.type == pygame.QUIT: | ||
pygame.quit() | ||
raise SystemExit | ||
running = False | ||
|
||
# Do logical updates here. | ||
# ... | ||
|
@@ -435,6 +437,8 @@ variations of this, so don't stress about the small details too much):: | |
pygame.display.flip() # Refresh on-screen display | ||
clock.tick(60) # wait until next frame (at 60 FPS) | ||
|
||
pygame.quit() | ||
|
||
It does some initial setup, starts a loop, and then proceeds to repeatedly | ||
collect input, handle the game's logic, and draw the current frame forever until | ||
the program ends. The update, render, wait loop shown here is actually a design | ||
|
@@ -483,7 +487,7 @@ Now, go write that game! | |
Repository, a showcase for community-submitted python game code. He is also | ||
the author of Twitch, an entirely average pygame arcade game.* | ||
|
||
*This guide was substantially updated in 2022.* | ||
*This guide was substantially updated in 2022 and updated again in 2023.* | ||
|
||
.. _Pygame: https://pyga.me/ | ||
.. _SDL: http://libsdl.org | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
#define DOC_DISPLAY_SETMODE "set_mode(size=(0, 0), flags=0, depth=0, display=0, vsync=0) -> Surface\nInitialize a window or screen for display" | ||
#define DOC_DISPLAY_GETSURFACE "get_surface() -> Surface\nGet a reference to the currently set display surface" | ||
#define DOC_DISPLAY_FLIP "flip() -> None\nUpdate the full display Surface to the screen" | ||
#define DOC_DISPLAY_UPDATE "update(rectangle=None) -> None\nupdate(rectangle_list) -> None\nUpdate portions of the screen for software displays" | ||
#define DOC_DISPLAY_UPDATE "update(rectangle=None) -> None\nupdate(rectangle_list) -> None\nUpdate all, or a portion, of the display. For non-OpenGL displays." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. None\nUpdate all, or a portion, of the display. For non-OpenGL displays." The dot between |
||
#define DOC_DISPLAY_GETDRIVER "get_driver() -> name\nGet the name of the pygame display backend" | ||
#define DOC_DISPLAY_INFO "Info() -> VideoInfo\nCreate a video display information object" | ||
#define DOC_DISPLAY_GETWMINFO "get_wm_info() -> dict\nGet information about the current windowing system" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This stuff about pygame and SDL 1 is no longer relevant as we don't build against SDL 1 any more and haven't for a long time.