Skip to content
Merged
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ Arcade [PyPi Release History](https://pypi.org/project/arcade/#history) page.
- Fixed `InputManager.parse()` raising `AttributeError`, so a saved input configuration couldn't be loaded.

### Breaking Changes
- `Sprite`, `BasicSprite`, `SpriteSolidColor`, `SpriteCircle`, `TextureAnimationSprite` and `AnimatedWalkingSprite` raise `TypeError` for keyword arguments they don't accept, instead of silently ignoring them. For example, `Sprite(path, hit_box_algorithm=...)` used to do nothing; set the hit box algorithm when loading the texture instead. `Sprite` now takes `visible` as a named argument.
- `SpriteSolidColor`, `SpriteCircle`, `TextureAnimationSprite` and `AnimatedWalkingSprite` now pass extra keyword arguments on to `Sprite`, so arguments such as `angle` and `visible` work. They used to be dropped.
- This found arguments that had silently done nothing: tilemap image layers passed `filename` and `hit_box_algorithm`, animated tiles passed `path_or_texture`, and platform tutorial steps 19 and 20 passed `scaling` instead of `scale`, so their bullets were drawn at full size instead of 80%.
- `arcade.math.get_angle_degrees()` now measures clockwise from straight up, like `Sprite.angle`: 0 is up, 90 is right, 180 is down and -90 is left. It used to measure clockwise from the right, which disagreed with `get_angle_radians()`, which already measured from up ([#2754](https://github.com/pythonarcade/arcade/issues/2754)). The two now always give the same angle in different units. A sprite whose image points up faces a point with `sprite.angle = get_angle_degrees(...)`. For the old value, subtract 90.
- Fixed the `sprite_rotate_around_tank` example's corrected barrel, which sat 90 degrees off from the mouse, and updated `turn_and_move` for the new angles. `turn_and_move`'s sprite also now stops when moving straight along an axis, which it never did.

Expand Down
2 changes: 1 addition & 1 deletion arcade/examples/platform_tutorial/19_shooting.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ def on_update(self, delta_time):
arcade.play_sound(self.shoot_sound)
bullet = arcade.Sprite(
":resources:images/space_shooter/laserBlue01.png",
scaling=0.8,
scale=0.8,
)
if self.player_sprite.facing_direction == RIGHT_FACING:
bullet.change_x = 12
Expand Down
2 changes: 1 addition & 1 deletion arcade/examples/platform_tutorial/20_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ def on_update(self, delta_time):
arcade.play_sound(self.shoot_sound)
bullet = arcade.Sprite(
":resources:images/space_shooter/laserBlue01.png",
scaling=0.8,
scale=0.8,
)
if self.player_sprite.facing_direction == RIGHT_FACING:
bullet.change_x = 12
Expand Down
6 changes: 6 additions & 0 deletions arcade/sprite/animated.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ class TextureAnimationSprite(Sprite):
Initial y position of the sprite.
scale:
Initial scale of the sprite.
**kwargs:
Passed to :py:class:`~arcade.Sprite`, such as ``angle``, ``scale`` or ``visible``.
"""

def __init__(
Expand All @@ -153,6 +155,7 @@ def __init__(
scale=scale,
center_x=center_x,
center_y=center_y,
**kwargs,
)
self._time = 0.0
self._animation: TextureAnimation | None = None
Expand Down Expand Up @@ -229,6 +232,8 @@ class AnimatedWalkingSprite(Sprite):
Initial x position of the sprite.
center_y:
Initial y position of the sprite.
**kwargs:
Passed to :py:class:`~arcade.Sprite`, such as ``angle``, ``scale`` or ``visible``.
"""

def __init__(
Expand All @@ -243,6 +248,7 @@ def __init__(
scale=scale,
center_x=center_x,
center_y=center_y,
**kwargs,
)
self.state = FACE_RIGHT
self.stand_right_textures: list[Texture] = []
Expand Down
3 changes: 2 additions & 1 deletion arcade/sprite/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class BasicSprite:
Location of the sprite along the X axis in pixels.
center_y:
Location of the sprite along the Y axis in pixels.
visible:
Whether the sprite is drawn.
"""

__slots__ = (
Expand All @@ -63,7 +65,6 @@ def __init__(
center_x: float = 0,
center_y: float = 0,
visible: bool = True,
**kwargs: Any,
) -> None:
self._position = (center_x, center_y)
self._depth = 0.0
Expand Down
7 changes: 6 additions & 1 deletion arcade/sprite/colored.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class SpriteSolidColor(Sprite):
an RGBA tuple, or an RGB tuple.
angle:
Initial angle of the sprite in degrees
**kwargs:
Passed to :py:class:`~arcade.Sprite`, such as ``angle``, ``scale`` or ``visible``.
"""

__slots__ = ()
Expand Down Expand Up @@ -84,6 +86,7 @@ def __init__(
center_x=center_x,
center_y=center_y,
angle=angle,
**kwargs,
)
self.color = Color.from_iterable(color)

Expand Down Expand Up @@ -143,6 +146,8 @@ class SpriteCircle(Sprite):
Initial x position of the sprite
center_y:
Initial y position of the sprite
**kwargs:
Passed to :py:class:`~arcade.Sprite`, such as ``angle``, ``scale`` or ``visible``.
"""

# Local weak cache for textures to avoid creating multiple instances with the same configuration
Expand Down Expand Up @@ -180,5 +185,5 @@ def __init__(
self.__class__._texture_cache[cache_key] = texture

# apply results to the new sprite
super().__init__(texture, center_x=center_x, center_y=center_y)
super().__init__(texture, center_x=center_x, center_y=center_y, **kwargs)
self.color = Color.from_iterable(color)
6 changes: 4 additions & 2 deletions arcade/sprite/sprite.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class Sprite(BasicSprite, PymunkMixin):
Show the image at this many times its original size.
angle:
The initial rotation of the sprite in degrees
visible:
Whether the sprite is drawn.
"""

__slots__ = (
Expand All @@ -68,7 +70,7 @@ def __init__(
center_x: float = 0.0,
center_y: float = 0.0,
angle: float = 0.0,
**kwargs: Any,
visible: bool = True,
) -> None:
if isinstance(path_or_texture, Texture):
_texture = path_or_texture
Expand All @@ -87,7 +89,7 @@ def __init__(
scale=scale,
center_x=center_x,
center_y=center_y,
**kwargs,
visible=visible,
)
PymunkMixin.__init__(self)

Expand Down
7 changes: 3 additions & 4 deletions arcade/tilemap/tilemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,8 +512,8 @@ def _create_sprite_from_tile(
Custom classes for animated tiles must subclass TextureAnimationSprite.
"""
)
# print(custom_class.__name__)
args = {"path_or_texture": image_file, "scale": scaling}
# The animation sets the texture
args = {"scale": scaling}
my_sprite = custom_class(**custom_class_args, **args) # type: ignore
else:
if not custom_class:
Expand Down Expand Up @@ -737,11 +737,10 @@ def _process_image_layer(
"""
)

# The texture was loaded with the hit box algorithm above
args = {
"filename": image_file,
"scale": scaling,
"path_or_texture": my_texture,
"hit_box_algorithm": hit_box_algorithm,
}

my_sprite = custom_class(**custom_class_args, **args) # type: ignore
Expand Down
53 changes: 53 additions & 0 deletions tests/unit/sprite/test_sprite_init_arguments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Sprites raise TypeError for keyword arguments they don't accept."""

import pytest

import arcade

COIN = ":resources:images/items/coinGold.png"


@pytest.mark.parametrize(
"make",
[
lambda: arcade.Sprite(COIN, hit_box_algorithm="None"),
lambda: arcade.BasicSprite(arcade.load_texture(COIN), scaling=2.0),
lambda: arcade.SpriteSolidColor(10, 10, colour=(255, 0, 0)),
lambda: arcade.SpriteCircle(5, arcade.color.RED, bogus=1),
lambda: arcade.TextureAnimationSprite(bogus=1),
lambda: arcade.AnimatedWalkingSprite(bogus=1),
],
ids=[
"Sprite",
"BasicSprite",
"SpriteSolidColor",
"SpriteCircle",
"TextureAnimationSprite",
"AnimatedWalkingSprite",
],
)
def test_unknown_keyword_argument_raises(make):
with pytest.raises(TypeError, match="unexpected keyword argument"):
make()


def test_subclass_init_with_unknown_argument_raises():
class Card(arcade.Sprite):
def __init__(self):
super().__init__(COIN, 1.0, hit_box_algorithm="None")

with pytest.raises(TypeError, match="hit_box_algorithm"):
Card()


def test_visible_argument():
assert arcade.Sprite(COIN, visible=False).visible is False
assert arcade.BasicSprite(arcade.load_texture(COIN), visible=False).visible is False


def test_subclasses_pass_sprite_arguments_on():
# These used to be accepted and silently ignored
assert arcade.SpriteCircle(5, arcade.color.RED, angle=30).angle == 30
assert arcade.SpriteSolidColor(10, 10, visible=False).visible is False
assert arcade.TextureAnimationSprite(angle=45).angle == 45
assert arcade.AnimatedWalkingSprite(angle=90, visible=False).angle == 90
Loading