forked from pythonarcade/arcade
-
Notifications
You must be signed in to change notification settings - Fork 2
/
pymunk_box_stacks.py
215 lines (164 loc) · 7.35 KB
/
pymunk_box_stacks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
"""
Use Pymunk physics engine.
For more info on Pymunk see:
http://www.pymunk.org/en/latest/
To install pymunk:
pip install pymunk
Artwork from http://kenney.nl
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.pymunk_box_stacks
Click and drag with the mouse to move the boxes.
"""
import arcade
import pymunk
import timeit
import math
import os
SCREEN_WIDTH = 1800
SCREEN_HEIGHT = 800
SCREEN_TITLE = "Pymunk test"
class PhysicsSprite(arcade.Sprite):
def __init__(self, pymunk_shape, filename):
super().__init__(filename, center_x=pymunk_shape.body.position.x, center_y=pymunk_shape.body.position.y)
self.pymunk_shape = pymunk_shape
class CircleSprite(PhysicsSprite):
def __init__(self, pymunk_shape, filename):
super().__init__(pymunk_shape, filename)
self.width = pymunk_shape.radius * 2
self.height = pymunk_shape.radius * 2
class BoxSprite(PhysicsSprite):
def __init__(self, pymunk_shape, filename, width, height):
super().__init__(pymunk_shape, filename)
self.width = width
self.height = height
class MyGame(arcade.Window):
""" Main application class. """
def __init__(self, width, height, title):
super().__init__(width, height, title)
# Set the working directory (where we expect to find files) to the same
# directory this .py file is in. You can leave this out of your own
# code, but it is needed to easily run the examples using "python -m"
# as mentioned at the top of this program.
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
arcade.set_background_color(arcade.color.DARK_SLATE_GRAY)
# -- Pymunk
self.space = pymunk.Space()
self.space.iterations = 35
self.space.gravity = (0.0, -900.0)
# Lists of sprites or lines
self.sprite_list: arcade.SpriteList[PhysicsSprite] = arcade.SpriteList()
self.static_lines = []
# Used for dragging shapes around with the mouse
self.shape_being_dragged = None
self.last_mouse_position = 0, 0
self.draw_time = 0
self.processing_time = 0
# Create the floor
floor_height = 80
body = pymunk.Body(body_type=pymunk.Body.STATIC)
shape = pymunk.Segment(body, [0, floor_height], [SCREEN_WIDTH, floor_height], 0.0)
shape.friction = 10
self.space.add(shape)
self.static_lines.append(shape)
# Create the stacks of boxes
for row in range(10):
for column in range(10):
size = 32
mass = 1.0
x = 500 + column * 32
y = (floor_height + size / 2) + row * size
moment = pymunk.moment_for_box(mass, (size, size))
body = pymunk.Body(mass, moment)
body.position = pymunk.Vec2d(x, y)
shape = pymunk.Poly.create_box(body, (size, size))
shape.elasticity = 0.2
shape.friction = 0.9
self.space.add(body, shape)
# body.sleep()
sprite = BoxSprite(shape, ":resources:images/tiles/boxCrate_double.png", width=size, height=size)
self.sprite_list.append(sprite)
def on_draw(self):
"""
Render the screen.
"""
# This command has to happen before we start drawing
arcade.start_render()
# Start timing how long this takes
draw_start_time = timeit.default_timer()
# Draw all the sprites
self.sprite_list.draw()
# Draw the lines that aren't sprites
for line in self.static_lines:
body = line.body
pv1 = body.position + line.a.rotated(body.angle)
pv2 = body.position + line.b.rotated(body.angle)
arcade.draw_line(pv1.x, pv1.y, pv2.x, pv2.y, arcade.color.WHITE, 2)
# Display timings
output = f"Processing time: {self.processing_time:.3f}"
arcade.draw_text(output, 20, SCREEN_HEIGHT - 20, arcade.color.WHITE, 12)
output = f"Drawing time: {self.draw_time:.3f}"
arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.WHITE, 12)
self.draw_time = timeit.default_timer() - draw_start_time
def on_mouse_press(self, x, y, button, modifiers):
if button == arcade.MOUSE_BUTTON_LEFT:
self.last_mouse_position = x, y
# See if we clicked on anything
shape_list = self.space.point_query((x, y), 1, pymunk.ShapeFilter())
# If we did, remember what we clicked on
if len(shape_list) > 0:
self.shape_being_dragged = shape_list[0]
elif button == arcade.MOUSE_BUTTON_RIGHT:
# With right mouse button, shoot a heavy coin fast.
mass = 60
radius = 10
inertia = pymunk.moment_for_circle(mass, 0, radius, (0, 0))
body = pymunk.Body(mass, inertia)
body.position = x, y
body.velocity = 2000, 0
shape = pymunk.Circle(body, radius, pymunk.Vec2d(0, 0))
shape.friction = 0.3
self.space.add(body, shape)
sprite = CircleSprite(shape, ":resources:images/items/coinGold.png")
self.sprite_list.append(sprite)
def on_mouse_release(self, x, y, button, modifiers):
if button == arcade.MOUSE_BUTTON_LEFT:
# Release the item we are holding (if any)
self.shape_being_dragged = None
def on_mouse_motion(self, x, y, dx, dy):
if self.shape_being_dragged is not None:
# If we are holding an object, move it with the mouse
self.last_mouse_position = x, y
self.shape_being_dragged.shape.body.position = self.last_mouse_position
self.shape_being_dragged.shape.body.velocity = dx * 20, dy * 20
def on_update(self, delta_time):
start_time = timeit.default_timer()
# Check for balls that fall off the screen
for sprite in self.sprite_list:
if sprite.pymunk_shape.body.position.y < 0:
# Remove balls from physics space
self.space.remove(sprite.pymunk_shape, sprite.pymunk_shape.body)
# Remove balls from physics list
sprite.remove_from_sprite_lists()
# Update physics
# Use a constant time step, don't use delta_time
# See "Game loop / moving time forward"
# http://www.pymunk.org/en/latest/overview.html#game-loop-moving-time-forward
self.space.step(1 / 60.0)
# If we are dragging an object, make sure it stays with the mouse. Otherwise
# gravity will drag it down.
if self.shape_being_dragged is not None:
self.shape_being_dragged.shape.body.position = self.last_mouse_position
self.shape_being_dragged.shape.body.velocity = 0, 0
# Move sprites to where physics objects are
for sprite in self.sprite_list:
sprite.center_x = sprite.pymunk_shape.body.position.x
sprite.center_y = sprite.pymunk_shape.body.position.y
sprite.angle = math.degrees(sprite.pymunk_shape.body.angle)
# Save the time it took to do this.
self.processing_time = timeit.default_timer() - start_time
def main():
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.run()
if __name__ == "__main__":
main()