pymunk_demo_platformer_08.py Diff
pymunk_demo_platformer_08.py
--- /home/docs/checkouts/readthedocs.org/user_builds/arcade-library/checkouts/stable/doc/tutorials/pymunk_platformer/pymunk_demo_platformer_07.py
+++ /home/docs/checkouts/readthedocs.org/user_builds/arcade-library/checkouts/stable/doc/tutorials/pymunk_platformer/pymunk_demo_platformer_08.py
@@ -1,7 +1,7 @@
"""
Example of Pymunk Physics Engine Platformer
"""
-from typing import Optional
+
import arcade
SCREEN_TITLE = "PyMunk Platformer"
@@ -54,24 +54,117 @@
# Strength of a jump
PLAYER_JUMP_IMPULSE = 1800
+# Close enough to not-moving to have the animation go to idle.
+DEAD_ZONE = 0.1
+
+# Constants used to track if the player is facing left or right
+RIGHT_FACING = 0
+LEFT_FACING = 1
+
+# How many pixels to move before we change the texture in the walking animation
+DISTANCE_TO_CHANGE_TEXTURE = 20
+
+
+class PlayerSprite(arcade.Sprite):
+ """Player Sprite"""
+
+ def __init__(self):
+ """Init"""
+ # Let parent initialize
+ super().__init__(scale=SPRITE_SCALING_PLAYER)
+
+ # Images from Kenney.nl's Character pack
+ # main_path = ":resources:images/animated_characters/female_adventurer/femaleAdventurer"
+ main_path = ":resources:images/animated_characters/female_person/femalePerson"
+ # main_path = ":resources:images/animated_characters/male_person/malePerson"
+ # main_path = ":resources:images/animated_characters/male_adventurer/maleAdventurer"
+ # main_path = ":resources:images/animated_characters/zombie/zombie"
+ # main_path = ":resources:images/animated_characters/robot/robot"
+
+ # 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")
+ # 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()
+ self.fall_texture_pair = fall_texture, fall_texture.flip_left_right()
+
+ # Load textures for walking and make pairs of textures facing left and right
+ self.walk_textures = []
+ for i in range(8):
+ texture = arcade.load_texture(f"{main_path}_walk{i}.png")
+ self.walk_textures.append((texture, texture.flip_left_right()))
+
+ # Set the initial texture
+ self.texture = self.idle_texture_pair[0]
+
+ # Default to face-right
+ self.character_face_direction = RIGHT_FACING
+
+ # Index of our current texture
+ self.cur_texture = 0
+
+ # How far have we traveled horizontally since changing the texture
+ self.x_odometer = 0
+
+ def pymunk_moved(self, physics_engine, dx, dy, d_angle):
+ """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
+ elif dx > DEAD_ZONE and self.character_face_direction == LEFT_FACING:
+ self.character_face_direction = RIGHT_FACING
+
+ # Are we on the ground?
+ is_on_ground = physics_engine.is_on_ground(self)
+
+ # Add to the odometer how far we've moved
+ self.x_odometer += dx
+
+ # Jumping animation
+ if not is_on_ground:
+ if dy > DEAD_ZONE:
+ self.texture = self.jump_texture_pair[self.character_face_direction]
+ return
+ elif dy < -DEAD_ZONE:
+ self.texture = self.fall_texture_pair[self.character_face_direction]
+ return
+
+ # Idle animation
+ if abs(dx) <= DEAD_ZONE:
+ self.texture = self.idle_texture_pair[self.character_face_direction]
+ return
+
+ # 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
+
+ # Advance the walking animation
+ self.cur_texture += 1
+ if self.cur_texture > 7:
+ self.cur_texture = 0
+ self.texture = self.walk_textures[self.cur_texture][self.character_face_direction]
+
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: arcade.Sprite|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
@@ -84,7 +177,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()
@@ -101,8 +194,8 @@
self.item_list = tile_map.sprite_lists["Dynamic Items"]
# Create player sprite
- self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png",
- SPRITE_SCALING_PLAYER)
+ self.player_sprite = PlayerSprite()
+
# Set player location
grid_x = 1
grid_y = 1
@@ -125,8 +218,7 @@
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
@@ -138,13 +230,15 @@
# 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
@@ -153,18 +247,20 @@
# 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
@@ -178,7 +274,7 @@
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
@@ -186,7 +282,7 @@
self.right_pressed = False
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
@@ -216,15 +312,16 @@
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()