Move to Mouse Click

In this example, the tank turns and moves towards where ever the user clicks the mouse.

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