1"""
2Example of Pymunk Physics Engine Platformer
3"""
4from typing import Optional
5import arcade
6
7SCREEN_TITLE = "PyMunk Platformer"
8
9# How big are our image tiles?
10SPRITE_IMAGE_SIZE = 128
11
12# Scale sprites up or down
13SPRITE_SCALING_PLAYER = 0.5
14SPRITE_SCALING_TILES = 0.5
15
16# Scaled sprite size for tiles
17SPRITE_SIZE = int(SPRITE_IMAGE_SIZE * SPRITE_SCALING_PLAYER)
18
19# Size of grid to show on screen, in number of tiles
20SCREEN_GRID_WIDTH = 25
21SCREEN_GRID_HEIGHT = 15
22
23# Size of screen to show, in pixels
24SCREEN_WIDTH = SPRITE_SIZE * SCREEN_GRID_WIDTH
25SCREEN_HEIGHT = SPRITE_SIZE * SCREEN_GRID_HEIGHT
26
27# --- Physics forces. Higher number, faster accelerating.
28
29# Gravity
30GRAVITY = 1500
31
32# Damping - Amount of speed lost per second
33DEFAULT_DAMPING = 1.0
34PLAYER_DAMPING = 0.4
35
36# Friction between objects
37PLAYER_FRICTION = 1.0
38WALL_FRICTION = 0.7
39DYNAMIC_ITEM_FRICTION = 0.6
40
41# Mass (defaults to 1)
42PLAYER_MASS = 2.0
43
44# Keep player from going too fast
45PLAYER_MAX_HORIZONTAL_SPEED = 450
46PLAYER_MAX_VERTICAL_SPEED = 1600
47
48
49class GameWindow(arcade.Window):
50 """ Main Window """
51
52 def __init__(self, width, height, title):
53 """ Create the variables """
54
55 # Init the parent class
56 super().__init__(width, height, title)
57
58 # Player sprite
59 self.player_sprite: Optional[arcade.Sprite] = None
60
61 # Sprite lists we need
62 self.player_list: Optional[arcade.SpriteList] = None
63 self.wall_list: Optional[arcade.SpriteList] = None
64 self.bullet_list: Optional[arcade.SpriteList] = None
65 self.item_list: Optional[arcade.SpriteList] = None
66
67 # Track the current state of what key is pressed
68 self.left_pressed: bool = False
69 self.right_pressed: bool = False
70
71 # Physics engine
72 self.physics_engine = Optional[arcade.PymunkPhysicsEngine]
73
74 # Set background color
75 self.background_color = arcade.color.AMAZON
76
77 def setup(self):
78 """ Set up everything with the game """
79
80 # Create the sprite lists
81 self.player_list = arcade.SpriteList()
82 self.bullet_list = arcade.SpriteList()
83
84 # Map name
85 map_name = ":resources:/tiled_maps/pymunk_test_map.json"
86
87 # Load in TileMap
88 tile_map = arcade.load_tilemap(map_name, SPRITE_SCALING_TILES)
89
90 # Pull the sprite layers out of the tile map
91 self.wall_list = tile_map.sprite_lists["Platforms"]
92 self.item_list = tile_map.sprite_lists["Dynamic Items"]
93
94 # Create player sprite
95 self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png",
96 SPRITE_SCALING_PLAYER)
97 # Set player location
98 grid_x = 1
99 grid_y = 1
100 self.player_sprite.center_x = SPRITE_SIZE * grid_x + SPRITE_SIZE / 2
101 self.player_sprite.center_y = SPRITE_SIZE * grid_y + SPRITE_SIZE / 2
102 # Add to player sprite list
103 self.player_list.append(self.player_sprite)
104
105 # --- Pymunk Physics Engine Setup ---
106
107 # The default damping for every object controls the percent of velocity
108 # the object will keep each second. A value of 1.0 is no speed loss,
109 # 0.9 is 10% per second, 0.1 is 90% per second.
110 # For top-down games, this is basically the friction for moving objects.
111 # For platformers with gravity, this should probably be set to 1.0.
112 # Default value is 1.0 if not specified.
113 damping = DEFAULT_DAMPING
114
115 # Set the gravity. (0, 0) is good for outer space and top-down.
116 gravity = (0, -GRAVITY)
117
118 # Create the physics engine
119 self.physics_engine = arcade.PymunkPhysicsEngine(damping=damping,
120 gravity=gravity)
121
122 # Add the player.
123 # For the player, we set the damping to a lower value, which increases
124 # the damping rate. This prevents the character from traveling too far
125 # after the player lets off the movement keys.
126 # Setting the moment of inertia to PymunkPhysicsEngine.MOMENT_INF prevents it from
127 # rotating.
128 # Friction normally goes between 0 (no friction) and 1.0 (high friction)
129 # Friction is between two objects in contact. It is important to remember
130 # in top-down games that friction moving along the 'floor' is controlled
131 # by damping.
132 self.physics_engine.add_sprite(self.player_sprite,
133 friction=PLAYER_FRICTION,
134 mass=PLAYER_MASS,
135 moment_of_inertia=arcade.PymunkPhysicsEngine.MOMENT_INF,
136 collision_type="player",
137 max_horizontal_velocity=PLAYER_MAX_HORIZONTAL_SPEED,
138 max_vertical_velocity=PLAYER_MAX_VERTICAL_SPEED)
139
140 # Create the walls.
141 # By setting the body type to PymunkPhysicsEngine.STATIC the walls can't
142 # move.
143 # Movable objects that respond to forces are PymunkPhysicsEngine.DYNAMIC
144 # PymunkPhysicsEngine.KINEMATIC objects will move, but are assumed to be
145 # repositioned by code and don't respond to physics forces.
146 # Dynamic is default.
147 self.physics_engine.add_sprite_list(self.wall_list,
148 friction=WALL_FRICTION,
149 collision_type="wall",
150 body_type=arcade.PymunkPhysicsEngine.STATIC)
151
152 # Create the items
153 self.physics_engine.add_sprite_list(self.item_list,
154 friction=DYNAMIC_ITEM_FRICTION,
155 collision_type="item")
156
157 def on_key_press(self, key, modifiers):
158 """Called whenever a key is pressed. """
159 pass
160
161 def on_key_release(self, key, modifiers):
162 """Called when the user releases a key. """
163 pass
164
165 def on_update(self, delta_time):
166 """ Movement and game logic """
167 self.physics_engine.step()
168
169 def on_draw(self):
170 """ Draw everything """
171 self.clear()
172 self.wall_list.draw()
173 self.bullet_list.draw()
174 self.item_list.draw()
175 self.player_list.draw()
176
177def main():
178 """ Main function """
179 window = GameWindow(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
180 window.setup()
181 arcade.run()
182
183
184if __name__ == "__main__":
185 main()