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