Turn and Move

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