皮蒙克物理引擎-钉板#
它使用皮蒙克物理引擎来模拟落入钉板上的球。

pybmunk_pegboard.py#
1"""
2Use Pymunk physics engine.
3
4For more info on Pymunk see:
5https://www.pymunk.org/en/latest/
6
7To install pymunk:
8pip install pymunk
9
10Artwork from https://kenney.nl
11
12If Python and Arcade are installed, this example can be run from the command line with:
13python -m arcade.examples.pymunk_pegboard
14
15Click and drag with the mouse to move the boxes.
16"""
17
18import arcade
19import pymunk
20import random
21import timeit
22import math
23
24SCREEN_WIDTH = 800
25SCREEN_HEIGHT = 800
26SCREEN_TITLE = "Pymunk Pegboard Example"
27
28
29class CircleSprite(arcade.Sprite):
30 def __init__(self, filename, pymunk_shape):
31 super().__init__(filename, center_x=pymunk_shape.body.position.x, center_y=pymunk_shape.body.position.y)
32 self.width = pymunk_shape.radius * 2
33 self.height = pymunk_shape.radius * 2
34 self.pymunk_shape = pymunk_shape
35
36
37class MyGame(arcade.Window):
38 """ Main application class. """
39
40 def __init__(self, width, height, title):
41 super().__init__(width, height, title)
42
43 self.peg_list = arcade.SpriteList()
44 self.ball_list: arcade.SpriteList[CircleSprite] = arcade.SpriteList()
45 self.background_color = arcade.color.DARK_SLATE_GRAY
46
47 self.draw_time = 0
48 self.processing_time = 0
49 self.time = 0
50
51 # -- Pymunk
52 self.space = pymunk.Space()
53 self.space.gravity = (0.0, -900.0)
54
55 self.static_lines = []
56
57 self.ticks_to_next_ball = 10
58
59 body = pymunk.Body(body_type=pymunk.Body.STATIC)
60 shape = pymunk.Segment(body, (0, 10), (SCREEN_WIDTH, 10), 0.0)
61 shape.friction = 10
62 self.space.add(shape, body)
63 self.static_lines.append(shape)
64
65 body = pymunk.Body(body_type=pymunk.Body.STATIC)
66 shape = pymunk.Segment(body, (SCREEN_WIDTH - 50, 10), (SCREEN_WIDTH, 30), 0.0)
67 shape.friction = 10
68 self.space.add(shape, body)
69 self.static_lines.append(shape)
70
71 body = pymunk.Body(body_type=pymunk.Body.STATIC)
72 shape = pymunk.Segment(body, (50, 10), (0, 30), 0.0)
73 shape.friction = 10
74 self.space.add(shape, body)
75 self.static_lines.append(shape)
76
77 radius = 20
78 separation = 150
79 for row in range(6):
80 for column in range(6):
81 x = column * separation + (separation // 2 * (row % 2))
82 y = row * separation + separation // 2
83 body = pymunk.Body(body_type=pymunk.Body.STATIC)
84 body.position = x, y
85 shape = pymunk.Circle(body, radius, pymunk.Vec2d(0, 0))
86 shape.friction = 0.3
87 self.space.add(body, shape)
88
89 sprite = CircleSprite(":resources:images/pinball/bumper.png", shape)
90 self.peg_list.append(sprite)
91
92 def on_draw(self):
93 """
94 Render the screen.
95 """
96
97 # This command has to happen before we start drawing
98 self.clear()
99
100 draw_start_time = timeit.default_timer()
101 self.peg_list.draw()
102 self.ball_list.draw()
103
104 for line in self.static_lines:
105 body = line.body
106
107 pv1 = body.position + line.a.rotated(body.angle)
108 pv2 = body.position + line.b.rotated(body.angle)
109 arcade.draw_line(pv1.x, pv1.y, pv2.x, pv2.y, arcade.color.WHITE, 2)
110
111 # Display timings
112 output = f"Processing time: {self.processing_time:.3f}"
113 arcade.draw_text(output, 20, SCREEN_HEIGHT - 20, arcade.color.WHITE, 12)
114
115 output = f"Drawing time: {self.draw_time:.3f}"
116 arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.WHITE, 12)
117
118 self.draw_time = timeit.default_timer() - draw_start_time
119
120 def on_update(self, delta_time):
121 start_time = timeit.default_timer()
122
123 self.ticks_to_next_ball -= 1
124 if self.ticks_to_next_ball <= 0:
125 self.ticks_to_next_ball = 20
126 mass = 0.5
127 radius = 15
128 inertia = pymunk.moment_for_circle(mass, 0, radius, (0, 0))
129 body = pymunk.Body(mass, inertia)
130 x = random.randint(0, SCREEN_WIDTH)
131 y = SCREEN_HEIGHT
132 body.position = x, y
133 shape = pymunk.Circle(body, radius, pymunk.Vec2d(0, 0))
134 shape.friction = 0.3
135 self.space.add(body, shape)
136
137 sprite = CircleSprite(":resources:images/items/gold_1.png", shape)
138 self.ball_list.append(sprite)
139
140 # Check for balls that fall off the screen
141 ball: CircleSprite
142 for ball in self.ball_list:
143 if ball.pymunk_shape.body.position.y < 0:
144 # Remove balls from physics space
145 self.space.remove(ball.pymunk_shape, ball.pymunk_shape.body)
146 # Remove balls from physics list
147 ball.remove_from_sprite_lists()
148
149 # Update physics
150 # Use a constant time step, don't use delta_time
151 # See "Game loop / moving time forward"
152 # https://www.pymunk.org/en/latest/overview.html#game-loop-moving-time-forward
153 self.space.step(1 / 60.0)
154
155 # Move sprites to where physics objects are
156 for ball in self.ball_list:
157 ball.center_x = ball.pymunk_shape.body.position.x
158 ball.center_y = ball.pymunk_shape.body.position.y
159 # Reverse angle because pymunk rotates ccw
160 ball.angle = math.degrees(-ball.pymunk_shape.body.angle)
161
162 self.time = timeit.default_timer() - start_time
163
164
165def main():
166 MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
167
168 arcade.run()
169
170
171if __name__ == "__main__":
172 main()