|
14 | 14 | )
|
15 | 15 | from arcade.gui.experimental.controller import (
|
16 | 16 | UIControllerBridge,
|
| 17 | + UIControllerButtonEvent, |
17 | 18 | UIControllerButtonPressEvent,
|
18 | 19 | UIControllerDpadEvent,
|
19 |
| - UIFocusGroup, |
| 20 | + UIControllerEvent, |
| 21 | + UIControllerStickEvent, |
| 22 | + UIControllerTriggerEvent, |
20 | 23 | )
|
| 24 | +from arcade.gui.experimental.focus import UIFocusGroup |
| 25 | +from arcade.types import Color |
21 | 26 |
|
22 | 27 |
|
23 | 28 | class ControllerIndicator(UIAnchorLayout):
|
| 29 | + """ |
| 30 | + A widget that displays the last controller input. |
| 31 | + """ |
| 32 | + |
24 | 33 | BLANK_TEX = Texture.create_empty("empty", (40, 40), arcade.color.TRANSPARENT_BLACK)
|
| 34 | + TEXTURE_CACHE = {} |
25 | 35 |
|
26 | 36 | def __init__(self):
|
27 | 37 | super().__init__()
|
28 | 38 |
|
29 | 39 | self._indicator = self.add(UIImage(texture=self.BLANK_TEX), anchor_y="bottom", align_y=10)
|
| 40 | + self._indicator.with_background(color=Color(0, 0, 0, 0)) |
| 41 | + self._indicator._strong_background = True |
| 42 | + |
| 43 | + @classmethod |
| 44 | + def get_texture(cls, path: str) -> Texture: |
| 45 | + if path not in cls.TEXTURE_CACHE: |
| 46 | + cls.TEXTURE_CACHE[path] = arcade.load_texture(path) |
| 47 | + return cls.TEXTURE_CACHE[path] |
| 48 | + |
| 49 | + @classmethod |
| 50 | + def input_prompts(cls, event: UIControllerEvent) -> Texture | None: |
| 51 | + if isinstance(event, UIControllerButtonEvent): |
| 52 | + match event.button: |
| 53 | + case "a": |
| 54 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_button_a.png") |
| 55 | + case "b": |
| 56 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_button_b.png") |
| 57 | + case "x": |
| 58 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_button_x.png") |
| 59 | + case "y": |
| 60 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_button_y.png") |
| 61 | + case "rightshoulder": |
| 62 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_rb.png") |
| 63 | + case "leftshoulder": |
| 64 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_lb.png") |
| 65 | + case "start": |
| 66 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_button_start.png") |
| 67 | + case "back": |
| 68 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_button_back.png") |
| 69 | + |
| 70 | + if isinstance(event, UIControllerTriggerEvent): |
| 71 | + match event.name: |
| 72 | + case "lefttrigger": |
| 73 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_lt.png") |
| 74 | + case "righttrigger": |
| 75 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_rt.png") |
| 76 | + |
| 77 | + if isinstance(event, UIControllerDpadEvent): |
| 78 | + match event.vector: |
| 79 | + case (1, 0): |
| 80 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_dpad_right.png") |
| 81 | + case (-1, 0): |
| 82 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_dpad_left.png") |
| 83 | + case (0, 1): |
| 84 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_dpad_up.png") |
| 85 | + case (0, -1): |
| 86 | + return cls.get_texture(":resources:input_prompt/xbox/xbox_dpad_down.png") |
| 87 | + |
| 88 | + if isinstance(event, UIControllerStickEvent) and event.vector.length() > 0.2: |
| 89 | + stick = "l" if event.name == "leftstick" else "r" |
| 90 | + |
| 91 | + # map atan2(y, x) to direction string (up, down, left, right) |
| 92 | + heading = event.vector.heading() |
| 93 | + if 0.785 > heading > -0.785: |
| 94 | + return cls.get_texture(f":resources:input_prompt/xbox/xbox_stick_{stick}_right.png") |
| 95 | + elif 0.785 < heading < 2.356: |
| 96 | + return cls.get_texture(f":resources:input_prompt/xbox/xbox_stick_{stick}_up.png") |
| 97 | + elif heading > 2.356 or heading < -2.356: |
| 98 | + return cls.get_texture(f":resources:input_prompt/xbox/xbox_stick_{stick}_left.png") |
| 99 | + elif -2.356 < heading < -0.785: |
| 100 | + return cls.get_texture(f":resources:input_prompt/xbox/xbox_stick_{stick}_down.png") |
| 101 | + |
| 102 | + return None |
30 | 103 |
|
31 | 104 | def on_event(self, event: UIEvent) -> Optional[bool]:
|
32 |
| - if isinstance(event, UIControllerButtonPressEvent): |
33 |
| - self._indicator.texture = arcade.load_texture( |
34 |
| - f":resources:onscreen_controls/flat_dark/{event.button}.png" |
35 |
| - ) |
36 |
| - arcade.unschedule(self.reset) |
37 |
| - arcade.schedule_once(self.reset, 0.5) |
38 |
| - elif isinstance(event, UIControllerDpadEvent): |
39 |
| - tex_map = { |
40 |
| - (1, 0): "right", |
41 |
| - (-1, 0): "left", |
42 |
| - (0, 1): "up", |
43 |
| - (0, -1): "down", |
44 |
| - } |
45 |
| - |
46 |
| - if event.vector in tex_map: |
47 |
| - self._indicator.texture = arcade.load_texture( |
48 |
| - f":resources:onscreen_controls/flat_dark/{tex_map[event.vector]}.png" |
49 |
| - ) |
| 105 | + if isinstance(event, UIControllerEvent): |
| 106 | + input_texture = self.input_prompts(event) |
| 107 | + |
| 108 | + if input_texture: |
| 109 | + self._indicator.texture = input_texture |
| 110 | + |
50 | 111 | arcade.unschedule(self.reset)
|
51 | 112 | arcade.schedule_once(self.reset, 0.5)
|
52 | 113 |
|
53 | 114 | return super().on_event(event)
|
54 | 115 |
|
55 | 116 | def reset(self, *_):
|
56 |
| - print("Reset") |
57 | 117 | self._indicator.texture = self.BLANK_TEX
|
58 |
| - self.trigger_full_render() |
59 | 118 |
|
60 | 119 |
|
61 | 120 | class ControllerModal(UIMouseFilterMixin, UIFocusGroup):
|
|
0 commit comments