Interactive Sprite Widget

Screen shot of interactive sprite widget example
interactive_sprite_widget.py
  1"""
  2Interactive Sprite Widget
  3
  4Demonstrates UIInteractiveSpriteWidget — making sprites clickable
  5inside the Arcade UI system with hover and press feedback.
  6
  7Click a gem to score a point. Gems light up on hover and
  8shrink when pressed.
  9
 10If Python and Arcade are installed, this example can be run from the
 11command line with:
 12python -m arcade.examples.interactive_sprite_widget
 13"""
 14
 15import arcade
 16from arcade.color import TRANSPARENT_BLACK
 17from arcade.gui import UIManager, UIInteractiveSpriteWidget
 18from arcade.gui.property import bind
 19from arcade.gui.surface import Surface
 20from arcade.gui.widgets.layout import UIBoxLayout, UIAnchorLayout
 21
 22WINDOW_WIDTH = 800
 23WINDOW_HEIGHT = 600
 24WINDOW_TITLE = "Interactive Sprite Widget Example"
 25
 26GEM_IMAGES = [
 27    ":resources:images/items/gemBlue.png",
 28    ":resources:images/items/gemGreen.png",
 29    ":resources:images/items/gemRed.png",
 30    ":resources:images/items/gemYellow.png",
 31]
 32GEM_SCALE = 0.75
 33PRESS_SHRINK = 0.85
 34
 35
 36class PressableGemWidget(UIInteractiveSpriteWidget):
 37    """A gem that visually shrinks when pressed, without affecting layout."""
 38
 39    def do_render(self, surface: Surface):
 40        self.prepare_render(surface)
 41        surface.clear(color=TRANSPARENT_BLACK)
 42        if self._sprite is not None:
 43            if self.pressed:
 44                draw_width = self.width * PRESS_SHRINK
 45                draw_height = self.height * PRESS_SHRINK
 46                offset_x = (self.width - draw_width) / 2
 47                offset_y = (self.height - draw_height) / 2
 48                surface.draw_sprite(offset_x, offset_y, draw_width, draw_height, self._sprite)
 49            else:
 50                surface.draw_sprite(0, 0, self.width, self.height, self._sprite)
 51
 52
 53class GameView(arcade.View):
 54
 55    def __init__(self):
 56        super().__init__()
 57        self.ui_manager = UIManager()
 58        self.score = 0
 59        self.score_display = None
 60        self.background_color = arcade.color.DARK_BLUE_GRAY
 61
 62    def setup(self):
 63        self.score = 0
 64        self.score_display = arcade.Text(
 65            "Score: 0", 10, WINDOW_HEIGHT - 30,
 66            arcade.color.WHITE, font_size=18,
 67        )
 68
 69        gem_row = UIBoxLayout(vertical=False, space_between=30)
 70
 71        for image_path in GEM_IMAGES:
 72            sprite = arcade.Sprite(image_path, scale=GEM_SCALE)
 73            widget = PressableGemWidget(sprite=sprite)
 74
 75            def make_hover_callback(wgt, spr):
 76                def on_hover_change(instance):
 77                    if wgt.hovered:
 78                        spr.color = (220, 220, 255)
 79                    else:
 80                        spr.color = (255, 255, 255)
 81                return on_hover_change
 82            bind(widget, "hovered", make_hover_callback(widget, sprite))
 83
 84            def make_click_callback(path):
 85                def on_click(event):
 86                    self.score += 1
 87                    self.score_display.text = f"Score: {self.score}"
 88                return on_click
 89            widget.on_click = make_click_callback(image_path)
 90
 91            gem_row.add(widget)
 92
 93        anchor = UIAnchorLayout(width=WINDOW_WIDTH, height=WINDOW_HEIGHT, size_hint=None)
 94        anchor.add(gem_row, anchor_x="center", anchor_y="center")
 95        self.ui_manager.add(anchor)
 96
 97    def on_show_view(self):
 98        self.ui_manager.enable()
 99
100    def on_hide_view(self):
101        self.ui_manager.disable()
102
103    def on_draw(self):
104        self.clear()
105        self.ui_manager.draw()
106        self.score_display.draw()
107
108
109def main():
110    window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
111    view = GameView()
112    window.show_view(view)
113    view.setup()
114    arcade.run()
115
116
117if __name__ == "__main__":
118    main()