|
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