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"""
  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()