arcade
arcade copied to clipboard
Make Color constants usable without affecting alpha
Enhancement request:
What should be added/changed?
Add an "rgb" method to arcade.types.Color which return a 3-tuple (type "RGB") based on the color, i.e., it leaves out the alpha.
What would it help with?
In arcade 2, Sprite.alpha was separate from Sprite.color, so one could use the constants in arcade.color to set the color without changing the alpha of the sprite. For example, self.color = arcade.color.WHITE would not affect the alpha of the Sprite. Now it does, which means makes the color constants much less usable. With an "rgb" property, one could do self.color = arcade.color.WHITE.rgb which would not affect alpha since the color setter checks the length of the input.
One can currently do the same, of course, by either inputting the color numbers directly as a tuple (but then what are the color constants even good for?) or by grabbing the constant and accessing its r, g, and b properties (but that is very messy).
Arcade 3.0.0.dev23
------------------
vendor: Intel
renderer: Intel(R) UHD Graphics
version: (3, 3)
python: 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)]
platform: win32
pyglet version: 2.0.7
PIL version: 9.4.0
Alternatively, the color constants could simply all be of type RGB, instead of type Color.
With an "rgb" property, one could do
self.color = arcade.color.WHITE.rgbwhich would not affect alpha since the color setter checks the length of the input.
tl;dr: We clearly need to document this better, but self.color = arcade.color.WHITE[:3] already does this.
Example:
import arcade
from arcade.color import RED, GREEN, BLUE
class ColorSlicingExample(arcade.Window):
def __init__(self, width: int = 200, height: int = 200):
super().__init__(width=width, height=height)
self.sprites = arcade.SpriteList()
# Use transparent gray as the initial color
initial_color = (127, ) * 4
# Create a line of sprites which use the RGB values of from color constants.
for index, color_instance in enumerate((RED, BLUE, GREEN)):
sprite = arcade.SpriteCircle(10, initial_color)
sprite.position = 50 + (index * 50), height / 2
# Use only the RGB components from each color constant.
# Color is a subclass of tuple and supports the same operations, including slicing.
sprite.color = color_instance[:3]
self.sprites.append(sprite)
def on_draw(self):
self.clear()
# Each sprite will show a transparent versions of its respective color constant.
self.sprites.draw()
if __name__ == "__main__":
example = ColorSlicingExample()
arcade.run()
I agree a read-only Color.rgb property could be legible. I've brought up adding it to Color in the Discord server before, along with GLSL-like swizzle support. I'm not sure of either for a number of reasons:
- They add complexity
- They might create expectations of component properties on other objects (
Sprite.rgb, etc), which would also increase complexity if added
Decreasing complexity, especially for the Sprite class, is a major goal for 3.0.
Alternatively, the color constants could simply all be of type RGB, instead of type Color.
Both Arcade and pyglet are moving towards RGBA support and defaults. This makes customizing color-related behavior easier. For example, you can define transparent color constants to help show sprite damage states or make game elements visible under a transparent UI.
Although pyglet uses plain tuples, Arcade 3.0's Color type is a tuple subclass with helper methods. This achieves the following:
- Compatibility with pyglet and anything else which expects plain tuples or sequences as colors
- You can use most tuple functionality with
Color, including slicing (color_instance[:3]) - Users can define colors cleanly in their preferred notations:
USER_PREFERS_HEX_STRINGS = Color.from_hex("#123456") # People with old-school web experience USER_PREFERS_FLOATS = Color.from_normalized((1.0, 0.5, 0.5, 1.0)) # Godot users & OpenGL developers USER_PREFERS_HEX_INTS = Color.from_uint32(0x12345678) # People with C experience
Thank you. I think I agree with most (if not all) of that.
I agree about the general idea of RGBA and the color attribute of Sprite etc, but I feel like it is a bit counterintuitive for the color constants to have an alpha value. If I define a color with an alpha value and assign that to a Sprite, I hopefully know what I am doing. However, if I use, e.g., arcade.color.RED, I would not expect that to change the alpha.
Currently, the color setter for BasicSprite checks if the color is of length 3 or 4 - if it is 3, it does not change the alpha value of the sprite. If this behavior is expected to change in the future so as to force length 4, along with removing the alpha property, then I would agree with the color constants being of type Color with a=255. However, if the intention is to keep this tuple length check (implying that setting color can be done both with and without specifying an alpha) and also have both alpha and color as settable properties of BasicSprite (implying that color and alpha can be set separately), then I think the color constants (RED, WHITE, CYAN etc) should be 3-tuples (since I do not intuitively associate any alpha value with colors).
Actually, arcade.types.RGB wouldn't actually work, one would need an "RGB255" type, but that does not exist.
I find the way color works right now to be incredibly confusing. I am clearly supposed to be able to pass a 3-tuple with values 0-255 into the color setter of BasicSprite, but there is no type for it, and the color constants are not of that type, so I am basically forced to do the "[:3]" thing, which I do understand, but it looks a bit weird to put that absolutely everywhere.
I am now noticing that the type hint of the color setter in BasicSprite specifies that the input should be of type Color, which makes no sense given that the implementation checks the length to see if it is 3 or 4?
I am now noticing that the type hint of the color setter in BasicSprite specifies that the input should be of type Color
This doesn't seem to be true on the development branch:
https://github.com/pythonarcade/arcade/blob/b4f9d67ec085417ad25bc07548752de6966e7832/arcade/sprite/base.py#L346-L347
However, the current type annotation is overly restrictive. It's one of the places we should be using the RGBOrA255 type alias. I commented on this further in #1843, which I'd love to have your feedback on.
.color definitely can take 3 or 4 component tuples due to backwards compatibility. The docsstring also mentions this.
We did change the arcade.color* values to all be RGBA.
However.. it could be a good idea to add .rgb and .rgba properties to be more explicit.
TL;DR: I agree, let's do .rgb and .rgba
Adding .rgb and .rgba properties are probably the best balance of readability and compatibility. Although color_instance[:3] works, it's an opportunity for typos. Having the properties also improves ergonomics through tab completion.