Move to Mouse Click#
In this example, the tank turns and moves towards where ever the user clicks the mouse.
1"""
2Turn and Move Example.
3
4Right-click to cause the tank to move to that point.
5
6If Python and Arcade are installed, this example can be run from the command line with:
7python -m arcade.examples.turn_and_move
8"""
9from __future__ import annotations
10
11import math
12import arcade
13
14SCREEN_WIDTH = 800
15SCREEN_HEIGHT = 600
16SCREEN_TITLE = "Turn and Move Example"
17
18# Image might not be lined up right, set this to offset
19IMAGE_ROTATION = 90
20
21
22class Player(arcade.Sprite):
23 """
24 Sprite that turns and moves
25 """
26 def __init__(self):
27 super().__init__(":resources:images/topdown_tanks/tank_green.png")
28
29 # Destination point is where we are going
30 self._destination_point = None
31
32 # Max speed
33 self.speed = 5
34
35 # Max speed we can rotate
36 self.rot_speed = 5
37
38 @property
39 def destination_point(self):
40 return self._destination_point
41
42 @destination_point.setter
43 def destination_point(self, destination_point):
44 self._destination_point = destination_point
45
46 def on_update(self, delta_time: float = 1 / 60):
47 """ Update the player """
48
49 # If we have no destination, don't go anywhere.
50 if not self._destination_point:
51 self.change_x = 0
52 self.change_y = 0
53 return
54
55 # Position the start at our current location
56 start_x = self.center_x
57 start_y = self.center_y
58
59 # Get the destination location
60 dest_x = self._destination_point[0]
61 dest_y = self._destination_point[1]
62
63 # Do math to calculate how to get the sprite to the destination.
64 # Calculation the angle in radians between the start points
65 # and end points. This is the angle the player will travel.
66 x_diff = dest_x - start_x
67 y_diff = dest_y - start_y
68 target_angle_radians = math.atan2(y_diff, x_diff)
69 if target_angle_radians < 0:
70 target_angle_radians += 2 * math.pi
71
72 # What angle are we at now in radians?
73 actual_angle_radians = math.radians(self.angle - IMAGE_ROTATION)
74
75 # How fast can we rotate?
76 rot_speed_radians = math.radians(self.rot_speed)
77
78 # What is the difference between what we want, and where we are?
79 angle_diff_radians = target_angle_radians - actual_angle_radians
80
81 # Figure out if we rotate clockwise or counter-clockwise
82 if abs(angle_diff_radians) <= rot_speed_radians:
83 # Close enough, let's set our angle to the target
84 actual_angle_radians = target_angle_radians
85 clockwise = None
86 elif angle_diff_radians > 0 and abs(angle_diff_radians) < math.pi:
87 clockwise = False
88 elif angle_diff_radians > 0 and abs(angle_diff_radians) >= math.pi:
89 clockwise = True
90 elif angle_diff_radians < 0 and abs(angle_diff_radians) < math.pi:
91 clockwise = True
92 else:
93 clockwise = False
94
95 # Rotate the proper direction if needed
96 if actual_angle_radians != target_angle_radians and clockwise:
97 actual_angle_radians -= rot_speed_radians
98 elif actual_angle_radians != target_angle_radians:
99 actual_angle_radians += rot_speed_radians
100
101 # Keep in a range of 0 to 2pi
102 if actual_angle_radians > 2 * math.pi:
103 actual_angle_radians -= 2 * math.pi
104 elif actual_angle_radians < 0:
105 actual_angle_radians += 2 * math.pi
106
107 # Convert back to degrees
108 self.angle = math.degrees(actual_angle_radians) + IMAGE_ROTATION
109
110 # Are we close to the correct angle? If so, move forward.
111 if abs(angle_diff_radians) < math.pi / 4:
112 self.change_x = math.cos(actual_angle_radians) * self.speed
113 self.change_y = math.sin(actual_angle_radians) * self.speed
114
115 # Fine-tune our change_x/change_y if we are really close to destination
116 # point and just need to set to that location.
117 traveling = False
118 if abs(self.center_x - dest_x) < abs(self.change_x):
119 self.center_x = dest_x
120 else:
121 self.center_x += self.change_x
122 traveling = True
123
124 if abs(self.center_y - dest_y) < abs(self.change_y):
125 self.center_y = dest_y
126 else:
127 self.center_y += self.change_y
128 traveling = True
129
130 # If we have arrived, then cancel our destination point
131 if not traveling:
132 self._destination_point = None
133
134
135class MyGame(arcade.Window):
136 """
137 Main application class.
138 """
139
140 def __init__(self):
141 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)
142
143 self.background_color = arcade.color.SAND
144
145 self.player_sprite = None
146
147 # Sprite Lists
148 self.player_list = None
149
150 def setup(self):
151 """ Set up the game variables. Call to re-start the game. """
152
153 # Sprite Lists
154 self.player_list = arcade.SpriteList()
155 self.player_sprite = Player()
156 self.player_sprite.center_x = 300
157 self.player_sprite.center_y = 300
158 self.player_list.append(self.player_sprite)
159
160 def on_draw(self):
161 """
162 Render the screen.
163 """
164
165 # This command should happen before we start drawing. It will clear
166 # the screen to the background color, and erase what we drew last frame.
167 self.clear()
168
169 # Call draw() on all your sprite lists below
170 self.player_list.draw()
171
172 def on_update(self, delta_time):
173 """
174 All the logic to move, and the game logic goes here.
175 """
176 self.player_list.on_update(delta_time)
177
178 def on_mouse_press(self, x, y, button, key_modifiers):
179 """
180 Called when the user presses a mouse button.
181 """
182 if button == arcade.MOUSE_BUTTON_RIGHT:
183 self.player_sprite.destination_point = x, y
184
185
186def main():
187 """ Main function """
188 game = MyGame()
189 game.center_window()
190 game.setup()
191 arcade.run()
192
193
194if __name__ == "__main__":
195 main()