diff --git a/arcade/sprite/base.py b/arcade/sprite/base.py index 68e0d57b30..a1f878b4a9 100644 --- a/arcade/sprite/base.py +++ b/arcade/sprite/base.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Iterable, List, TypeVar, Any import arcade -from arcade.types import Point, Color, RGBA255, PointList +from arcade.types import Point, Color, RGBA255, RGBOrA255, PointList from arcade.color import BLACK from arcade.hitbox import HitBox from arcade.texture import Texture @@ -38,6 +38,7 @@ class BasicSprite: "_color", "_texture", "_hit_box", + "_visible", "sprite_lists", "_angle", "__weakref__", @@ -49,6 +50,7 @@ def __init__( scale: float = 1.0, center_x: float = 0, center_y: float = 0, + visible: bool = True, **kwargs: Any, ) -> None: self._position = (center_x, center_y) @@ -57,6 +59,7 @@ def __init__( self._width = texture.width * scale self._height = texture.height * scale self._scale = scale, scale + self._visible = bool(visible) self._color: Color = Color(255, 255, 255, 255) self.sprite_lists: List["SpriteList"] = [] @@ -293,29 +296,44 @@ def top(self, amount: float): @property def visible(self) -> bool: - """ - Get or set the visibility of this sprite. - This is a shortcut for changing the alpha value of a sprite - to 0 or 255:: + """Get or set the visibility of this sprite. + + When set to ``False``, each :py:class:`~arcade.SpriteList` and + its attached shaders will treat the sprite as if has an + :py:attr:`.alpha` of 0. However, the sprite's actual values for + :py:attr:`.alpha` and :py:attr:`.color` will not change. + + .. code-block:: python + + # The initial color of the sprite + >>> sprite.color + Color(255, 255, 255, 255) # Make the sprite invisible - sprite.visible = False - # Change back to visible - sprite.visible = True - # Toggle visible - sprite.visible = not sprite.visible + >>> sprite.visible = False + # The sprite's color value has not changed + >>> sprite.color + Color(255, 255, 255, 255) + # The sprite's alpha value hasn't either + >>> sprite.alpha + 255 + + # Restore visibility + >>> sprite.visible = True + # Shorthand to toggle visible + >>> sprite.visible = not sprite.visible """ - return self._color[3] > 0 + return self._visible @visible.setter def visible(self, value: bool): - self._color = Color( - self._color[0], - self._color[1], - self._color[2], - 255 if value else 0, - ) + value = bool(value) + if self._visible == value: + return + + self._visible = value + for sprite_list in self.sprite_lists: sprite_list._update_color(self) @@ -345,27 +363,22 @@ def color(self) -> Color: return self._color @color.setter - def color(self, color: RGBA255): - if len(color) == 4: - if ( - self._color[0] == color[0] - and self._color[1] == color[1] - and self._color[2] == color[2] - and self._color[3] == color[3] - ): - return - self._color = Color.from_iterable(color) - - elif len(color) == 3: - if ( - self._color[0] == color[0] - and self._color[1] == color[1] - and self._color[2] == color[2] - ): - return - self._color = Color(color[0], color[1], color[2], self._color[3]) + def color(self, color: RGBOrA255): + if color == self._color: + return + + r, g, b, *_a = color + + if _a: + if len(_a) > 1: + raise ValueError(f"iterable must unpack to 3 or 4 values not {len(color)}") + a = _a[0] else: - raise ValueError("Color must be three or four ints from 0-255") + a = self._color.a + + # We don't handle alpha and .visible interactions here + # because it's implemented in SpriteList._update_color + self._color = Color(r, g, b, a) for sprite_list in self.sprite_lists: sprite_list._update_color(self) diff --git a/arcade/sprite_list/sprite_list.py b/arcade/sprite_list/sprite_list.py index 5cc832ada7..a608466b36 100644 --- a/arcade/sprite_list/sprite_list.py +++ b/arcade/sprite_list/sprite_list.py @@ -1262,7 +1262,7 @@ def _update_color(self, sprite: SpriteType) -> None: self._sprite_color_data[slot * 4] = int(sprite._color[0]) self._sprite_color_data[slot * 4 + 1] = int(sprite._color[1]) self._sprite_color_data[slot * 4 + 2] = int(sprite._color[2]) - self._sprite_color_data[slot * 4 + 3] = int(sprite._color[3]) + self._sprite_color_data[slot * 4 + 3] = int(sprite._color[3] * sprite._visible) self._sprite_color_changed = True def _update_size(self, sprite: SpriteType) -> None: diff --git a/tests/unit/sprite/test_sprite.py b/tests/unit/sprite/test_sprite.py index 0ebcb20017..1e88f6221a 100644 --- a/tests/unit/sprite/test_sprite.py +++ b/tests/unit/sprite/test_sprite.py @@ -321,15 +321,19 @@ def test_visible(): assert sprite.alpha == 255 assert sprite.visible is True + # initialise alpha value + sprite.alpha = 100 + assert sprite.alpha == 100 + # Make invisible sprite.visible = False assert sprite.visible is False - assert sprite.alpha == 0 + assert sprite.alpha == 100 # Make visible again sprite.visible = True assert sprite.visible is True - assert sprite.alpha == 255 + assert sprite.alpha == 100 def test_sprite_scale_xy(window):