As a final step before getting busy with refactoring and reorganizing Pygame’s sprite classes, I’ve composed the following short paper describing what I want to get done on each 7 new features planned for this part of the project.
These decisions are based on feedback and suggestions posted on the discussion list and on the #pygame IRC channel, and also on my private research of other open-source game libraries: cocos2d, Pyglet, Spyral & Gloss, among others.
This isn’t a finalized spec of course, just my way of listing things I want to take care of, and along the way getting feedback from the community.
As always, feel free to contact me with any suggestions or comments.
1. Anchor points
The Sprite
object should have an anchor
attribute, that could be set to either:
(x, y)
representing an offset- One of a set of predefined flags i.e.
CENTER
,MIDBOTTOM
,TOPLEFT
, that would remain appropriate even after transformations (scaling, rotating, etc).
The default value would be TOPLEFT
.
The anchor
attribute represents a point in the sprite that is used when moving and transforming the sprite.
So for example, if a sprite’s anchor is (10, 20)
and we move it to (100, 100)
- then in result the anchor pixel would be positioned in (100, 100)
- meaning in fact, that the sprite’s top left coordinates are (90, 80)
.
2. Better positioning
Positioning sprites is currently done by manipulating the sprite’s rect
attribute.
It should be possible to position a Sprite
object using a tuple (x, y)
that would have the same effect.
The tuple would be used in accordance with the sprite’s anchor point, as defined above.
The x
and y
values should be able to be float values, to allow for varying speeds; For example, moving a sprite by 0.1 pixels every frame and then the sprite would move 1 pixel every 10 frames.
The actual rendering would use rounded values.
3. Visual attributes
A sprite should have several attributes that affect its visual representation, and could be changed dynamically during the run of the game.
- Visibility: A boolean to say whether the sprite should be rendered at all. Default is
true
. - Scale: A float defining the aspect between the sprite’s original size and what should appear on screen. Default is
1
. - Rotation angle: A float representing the degree the sprite should be rotated with. The rotation is done around the anchor point. Default is
0
. - Crop rectangle: A
rect
value with coordinates relative to the original sprite image, defining the part of the image to render. Default is((0, 0), (w, h))
, withw
andh
being the image’s width and height values.
4. Smarter layers
A Sprite
object should have a layer
attribute, with an integer value representing the layer’s z-index.
This is actually already done in DirtySprite
and LayeredUpdate
. We should have it for every sprite.
Rendering sprites should behave as in LayeredUpdate
, maintaining a list of sprites ordered by their z-index.
5. Aggregated sprites
A new class AggregatedSprite
extending Sprite
that holds a list of other Sprite
objects.
They are all positioned and rendered regularly on the screen, and the AggregatedSprite
instance could be used to manipulate them together:
- Setting the
AggregatedSprite
’s position to(x, y)
would use these values as an offset for positioning its sprites. - Setting a value of one of the visual attributes would use this value to overwrite the same attribute for the sprites.
6. Sprite picking
The screen should have a method to pick sprites by coordinates - that is, given a position (x, y)
it should return a list of all sprites rendered at this point.
- Optional: Pass a flag to only pick visible sprites (
visible = true
).
7. Automated “dirty” rendering
All of the sprites and sprite groups should have the dirty rendering functionality of DirtySprite
and LayeredDirty
.
Sprites should label themselves dirty automatically as soon as either their position, image, or any other visual attribute were changed.
This would then cause them to be re-rendered only when necessary.