Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions doc/builtins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,47 @@ of transparency:
through a ghost.


.. _simple-shapes:

Simple shapes
'''''''''''''

If you don't have images yet, not to worry! You can start by creating actors
from simple colored shapes like a rectangle or a circle. Instead of calling
``Actor("image_name")`` you instead call
``Actor.Shape(width, height, color)``.

Here's an example::

character = Actor.Rectangle(100, 100, "red")
shot = Actor.Ellipse(60, 20, "blue")
sword = Actor.Triangle(50, 15, "green")

The following shapes can be created this way:

.. method:: Actor.Rectangle(width, height, color)

Creates an actor with a filled rectangle of the given color as its
image. To create a perfect square, simply give the same value for both
width and height.

.. method:: Actor.Ellipse(width, height, color)

Creates an actor with a filled circular shape of the given color as its
image. The background of the image is transparent. To create a perfect
circle, simply give the same value for both width and height.

.. method:: Actor.Triangle(width, height, color)

Creates an actor with a filled triangle of the given color as its image.
The triangle points to the right so it always points in the direction of
the ``angle`` of the actor.

If you wanted to define other parameters like anchor or position when creating
the actors, you can still do so just like with a normal Actor construction::

balloon = Actor.Ellipse(50, 50, "red", (150, 150), anchor=("center", "bottom"))

The Keyboard
------------

Expand Down
52 changes: 52 additions & 0 deletions src/pgzero/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,58 @@ def _update_transform(self, function):
"function {!r} does not have a registered order."
"".format(function))

@classmethod
def _make_shape_image(self, kind, width, height, color):
"""Creates a new shape image and loads it into resources. If an image
of the exact parameters already exists, creation is not repeated."""
# Create image name and resource cache key from parameters.
name = kind + str(width) + "x" + str(height) + "_" + str(color)
key = (name, (), ())
# Return without costly image creation if image already exists.
if key in loaders.images._cache:
return name
# Creates the image with transparency (for non-rects) and fills them
# with the appropriate shape.
s = pygame.Surface((width, height), pygame.SRCALPHA)
match kind:
case "__SHAPE_ELLIPSE__":
pygame.draw.ellipse(s, color,
pygame.Rect((0, 0), (width, height)))
case "__SHAPE_TRIANGLE__":
pygame.draw.polygon(s, color,
((0, 0), (width, height / 2), (0, height)))
case _:
s.fill(color)
# Saves the created image in the resource cache for use. This ensures
# smooth interoperability with the normal Actor construction.
loaders.images._cache[key] = s
# Returns the name for use in the Actor construction.
return name

@classmethod
def Rectangle(self, width, height, color, pos=POS_TOPLEFT,
anchor=ANCHOR_CENTER, **kwargs):
"""Creates an actor with a rectangle as an image."""
name = self._make_shape_image("__SHAPE_RECTANGLE__", width, height,
color)
return Actor(name, pos, anchor, **kwargs)

@classmethod
def Ellipse(self, width, height, color, pos=POS_TOPLEFT,
anchor=ANCHOR_CENTER, **kwargs):
"""Creates an actor with an ellipse as an image."""
name = self._make_shape_image("__SHAPE_ELLIPSE__", width, height,
color)
return Actor(name, pos, anchor, **kwargs)

@classmethod
def Triangle(self, width, height, color, pos=POS_TOPLEFT,
anchor=ANCHOR_CENTER, **kwargs):
"""Creates an actor with a triangle as an image."""
name = self._make_shape_image("__SHAPE_TRIANGLE__", width, height,
color)
return Actor(name, pos, anchor, **kwargs)

@property
def anchor(self):
return self._anchor_value
Expand Down
76 changes: 76 additions & 0 deletions test/test_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from pgzero.actor import calculate_anchor, Actor
from pgzero.loaders import set_root
from pgzero.loaders import images


TEST_MODULE = "pgzero.actor"
Expand Down Expand Up @@ -146,3 +147,78 @@ def test_dir_correct(self):
a = Actor("alien")
for attribute in dir(a):
a.__getattr__(attribute)

def test_actor_square(self):
"""The square image is created correctly and the result is a valid
actor."""
square = Actor.Rectangle(10, 10, "red")
name = "__SHAPE_RECTANGLE__10x10_red"
self.assertIn((name, (), ()), images._cache)
surf = images.load(name)
width, height = surf.get_size()
self.assertEqual(width, 10)
self.assertEqual(height, 10)
self.assertEqual(
surf.get_at((width//2, height//2)), (255, 0, 0, 255)
)
self.assertEqual(type(square), Actor)

def test_actor_rectangle(self):
"""The rectangle image is created correctly and the result is a valid
actor."""
square = Actor.Rectangle(10, 5, "green")
name = "__SHAPE_RECTANGLE__10x5_green"
self.assertIn((name, (), ()), images._cache)
surf = images.load(name)
width, height = surf.get_size()
self.assertEqual(width, 10)
self.assertEqual(height, 5)
self.assertEqual(
surf.get_at((width//2, height//2)), (0, 255, 0, 255)
)
self.assertEqual(type(square), Actor)

def test_actor_circle(self):
"""The circular image is created correctly and the result is a valid
actor."""
square = Actor.Ellipse(5, 5, "blue")
name = "__SHAPE_ELLIPSE__5x5_blue"
self.assertIn((name, (), ()), images._cache)
surf = images.load(name)
width, height = surf.get_size()
self.assertEqual(width, 5)
self.assertEqual(height, 5)
self.assertEqual(
surf.get_at((width//2, height//2)), (0, 0, 255, 255)
)
self.assertEqual(type(square), Actor)

def test_actor_ellipse(self):
"""The elliptical image is created correctly and the result is a valid
actor."""
square = Actor.Ellipse(5, 10, "yellow")
name = "__SHAPE_ELLIPSE__5x10_yellow"
self.assertIn((name, (), ()), images._cache)
surf = images.load(name)
width, height = surf.get_size()
self.assertEqual(width, 5)
self.assertEqual(height, 10)
self.assertEqual(
surf.get_at((width//2, height//2)), (255, 255, 0, 255)
)
self.assertEqual(type(square), Actor)

def test_actor_triangle(self):
"""The triangular image is created correctly and the result is a valid
actor."""
square = Actor.Triangle(15, 15, "fuchsia")
name = "__SHAPE_TRIANGLE__15x15_fuchsia"
self.assertIn((name, (), ()), images._cache)
surf = images.load(name)
width, height = surf.get_size()
self.assertEqual(width, 15)
self.assertEqual(height, 15)
self.assertEqual(
surf.get_at((width//2, height//2)), (255, 0, 255, 255)
)
self.assertEqual(type(square), Actor)