pymunk_demo_platformer_09.py Diff

pymunk_demo_platformer_09.py
--- /home/docs/checkouts/readthedocs.org/user_builds/arcade-library/checkouts/stable/doc/tutorials/pymunk_platformer/pymunk_demo_platformer_08.py
+++ /home/docs/checkouts/readthedocs.org/user_builds/arcade-library/checkouts/stable/doc/tutorials/pymunk_platformer/pymunk_demo_platformer_09.py
@@ -1,6 +1,8 @@
 """
 Example of Pymunk Physics Engine Platformer
 """
+import math
+from typing import Optional
 
 import arcade
 
@@ -64,12 +66,20 @@
 # How many pixels to move before we change the texture in the walking animation
 DISTANCE_TO_CHANGE_TEXTURE = 20
 
+# How much force to put on the bullet
+BULLET_MOVE_FORCE = 4500
+
+# Mass of the bullet
+BULLET_MASS = 0.1
+
+# Make bullet less affected by gravity
+BULLET_GRAVITY = 300
+
 
 class PlayerSprite(arcade.Sprite):
-    """Player Sprite"""
-
+    """ Player Sprite """
     def __init__(self):
-        """Init"""
+        """ Init """
         # Let parent initialize
         super().__init__(scale=SPRITE_SCALING_PLAYER)
 
@@ -84,7 +94,7 @@
         # Load textures for idle, jump, and fall states
         idle_texture = arcade.load_texture(f"{main_path}_idle.png")
         jump_texture = arcade.load_texture(f"{main_path}_jump.png")
-        fall_texture = arcade.load_texture(f"{main_path}_fall.png")
+        fall_texture = arcade.load_texture(f"{main_path}_fall.png") 
         # Make pairs of textures facing left and right
         self.idle_texture_pair = idle_texture, idle_texture.flip_left_right()
         self.jump_texture_pair = jump_texture, jump_texture.flip_left_right()
@@ -109,7 +119,7 @@
         self.x_odometer = 0
 
     def pymunk_moved(self, physics_engine, dx, dy, d_angle):
-        """Handle being moved by the pymunk engine"""
+        """ Handle being moved by the pymunk engine """
         # Figure out if we need to face left or right
         if dx < -DEAD_ZONE and self.character_face_direction == RIGHT_FACING:
             self.character_face_direction = LEFT_FACING
@@ -138,6 +148,7 @@
 
         # Have we moved far enough to change the texture?
         if abs(self.x_odometer) > DISTANCE_TO_CHANGE_TEXTURE:
+
             # Reset the odometer
             self.x_odometer = 0
 
@@ -149,22 +160,22 @@
 
 
 class GameWindow(arcade.Window):
-    """Main Window"""
+    """ Main Window """
 
     def __init__(self, width, height, title):
-        """Create the variables"""
+        """ Create the variables """
 
         # Init the parent class
         super().__init__(width, height, title)
 
         # Player sprite
-        self.player_sprite: PlayerSprite | None = None
+        self.player_sprite: PlayerSprite|None = None
 
         # Sprite lists we need
-        self.player_list: arcade.SpriteList | None = None
-        self.wall_list: arcade.SpriteList | None = None
-        self.bullet_list: arcade.SpriteList | None = None
-        self.item_list: arcade.SpriteList | None = None
+        self.player_list: arcade.SpriteList|None = None
+        self.wall_list: arcade.SpriteList|None = None
+        self.bullet_list: arcade.SpriteList|None = None
+        self.item_list: arcade.SpriteList|None = None
 
         # Track the current state of what key is pressed
         self.left_pressed: bool = False
@@ -177,7 +188,7 @@
         self.background_color = arcade.color.AMAZON
 
     def setup(self):
-        """Set up everything with the game"""
+        """ Set up everything with the game """
 
         # Create the sprite lists
         self.player_list = arcade.SpriteList()
@@ -218,7 +229,8 @@
         gravity = (0, -GRAVITY)
 
         # Create the physics engine
-        self.physics_engine = arcade.PymunkPhysicsEngine(damping=damping, gravity=gravity)
+        self.physics_engine = arcade.PymunkPhysicsEngine(damping=damping,
+                                                         gravity=gravity)
 
         # Add the player.
         # For the player, we set the damping to a lower value, which increases
@@ -230,15 +242,13 @@
         # Friction is between two objects in contact. It is important to remember
         # in top-down games that friction moving along the 'floor' is controlled
         # by damping.
-        self.physics_engine.add_sprite(
-            self.player_sprite,
-            friction=PLAYER_FRICTION,
-            mass=PLAYER_MASS,
-            moment_of_inertia=arcade.PymunkPhysicsEngine.MOMENT_INF,
-            collision_type="player",
-            max_horizontal_velocity=PLAYER_MAX_HORIZONTAL_SPEED,
-            max_vertical_velocity=PLAYER_MAX_VERTICAL_SPEED,
-        )
+        self.physics_engine.add_sprite(self.player_sprite,
+                                       friction=PLAYER_FRICTION,
+                                       mass=PLAYER_MASS,
+                                       moment_of_inertia=arcade.PymunkPhysicsEngine.MOMENT_INF,
+                                       collision_type="player",
+                                       max_horizontal_velocity=PLAYER_MAX_HORIZONTAL_SPEED,
+                                       max_vertical_velocity=PLAYER_MAX_VERTICAL_SPEED)
 
         # Create the walls.
         # By setting the body type to PymunkPhysicsEngine.STATIC the walls can't
@@ -247,20 +257,18 @@
         # PymunkPhysicsEngine.KINEMATIC objects will move, but are assumed to be
         # repositioned by code and don't respond to physics forces.
         # Dynamic is default.
-        self.physics_engine.add_sprite_list(
-            self.wall_list,
-            friction=WALL_FRICTION,
-            collision_type="wall",
-            body_type=arcade.PymunkPhysicsEngine.STATIC,
-        )
+        self.physics_engine.add_sprite_list(self.wall_list,
+                                            friction=WALL_FRICTION,
+                                            collision_type="wall",
+                                            body_type=arcade.PymunkPhysicsEngine.STATIC)
 
         # Create the items
-        self.physics_engine.add_sprite_list(
-            self.item_list, friction=DYNAMIC_ITEM_FRICTION, collision_type="item"
-        )
+        self.physics_engine.add_sprite_list(self.item_list,
+                                            friction=DYNAMIC_ITEM_FRICTION,
+                                            collision_type="item")
 
     def on_key_press(self, key, modifiers):
-        """Called whenever a key is pressed."""
+        """Called whenever a key is pressed. """
 
         if key == arcade.key.LEFT:
             self.left_pressed = True
@@ -274,15 +282,69 @@
                 self.physics_engine.apply_impulse(self.player_sprite, impulse)
 
     def on_key_release(self, key, modifiers):
-        """Called when the user releases a key."""
+        """Called when the user releases a key. """
 
         if key == arcade.key.LEFT:
             self.left_pressed = False
         elif key == arcade.key.RIGHT:
             self.right_pressed = False
 
+    def on_mouse_press(self, x, y, button, modifiers):
+        """ Called whenever the mouse button is clicked. """
+
+        bullet = arcade.SpriteSolidColor(width=20, height=5, color=arcade.color.DARK_YELLOW)
+        self.bullet_list.append(bullet)
+
+        # Position the bullet at the player's current location
+        start_x = self.player_sprite.center_x
+        start_y = self.player_sprite.center_y
+        bullet.position = self.player_sprite.position
+
+        # Get from the mouse the destination location for the bullet
+        # IMPORTANT! If you have a scrolling screen, you will also need
+        # to add in self.view_bottom and self.view_left.
+        dest_x = x
+        dest_y = y
+
+        # Do math to calculate how to get the bullet to the destination.
+        # Calculation the angle in radians between the start points
+        # and end points. This is the angle the bullet will travel.
+        x_diff = dest_x - start_x
+        y_diff = dest_y - start_y
+        angle = math.atan2(y_diff, x_diff)
+
+        # What is the 1/2 size of this sprite, so we can figure out how far
+        # away to spawn the bullet
+        size = max(self.player_sprite.width, self.player_sprite.height) / 2
+
+        # Use angle to to spawn bullet away from player in proper direction
+        bullet.center_x += size * math.cos(angle)
+        bullet.center_y += size * math.sin(angle)
+
+        # Set angle of bullet
+        bullet.angle = math.degrees(angle)
+
+        # Gravity to use for the bullet
+        # If we don't use custom gravity, bullet drops too fast, or we have
+        # to make it go too fast.
+        # Force is in relation to bullet's angle.
+        bullet_gravity = (0, -BULLET_GRAVITY)
+
+        # Add the sprite. This needs to be done AFTER setting the fields above.
+        self.physics_engine.add_sprite(bullet,
+                                       mass=BULLET_MASS,
+                                       damping=1.0,
+                                       friction=0.6,
+                                       collision_type="bullet",
+                                       gravity=bullet_gravity,
+                                       elasticity=0.9)
+
+        # Add force to bullet
+        force = (BULLET_MOVE_FORCE, 0)
+        self.physics_engine.apply_force(bullet, force)
+
     def on_update(self, delta_time):
-        """Movement and game logic"""
+        """ Movement and game logic """
 
         is_on_ground = self.physics_engine.is_on_ground(self.player_sprite)
         # Update player forces based on keys pressed
@@ -312,16 +374,15 @@
         self.physics_engine.step()
 
     def on_draw(self):
-        """Draw everything"""
+        """ Draw everything """
         self.clear()
         self.wall_list.draw()
         self.bullet_list.draw()
         self.item_list.draw()
         self.player_list.draw()
 
-
 def main():
-    """Main function"""
+    """ Main function """
     window = GameWindow(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
     window.setup()
     arcade.run()