著者:飯尾 淳
本連載では「Pythonを昔から使っているものの、それほど使いこなしてはいない」という筆者が、いろいろな日常業務をPythonで処理することで、立派な「蛇使い」に育つことを目指します。その過程を温かく見守ってください。皆さんと共に勉強していきましょう。第29回では、物体の動きや力の作用など、現実世界の物理法則を数値的に計算してシミュレーションする「物理演算」にチャレンジします。
シェルスクリプトマガジン Vol.99は以下のリンク先でご購入できます。


図6 playground.pyファイルの最後の部分にあるコード
def main():
demo = PhysicsDemo()
demo.run()
if __name__ == "__main__":
doprof = 0
if not doprof:
main()
else:
import cProfile
import pstats
prof = cProfile.run("main()", "profile.prof")
stats = pstats.Stats("profile.prof")
stats.strip_dirs()
stats.sort_stats("cumulative", "time", "calls")
stats.print_stats(30)
図7 PhysicsDemoクラスのrun()メソッドのコード
def run(self):
while self.running:
self.loop()
図8 PhysicsDemoクラスのloop()メソッドのコード
def loop(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
(略)
self.space.gravity = g.rotated_degrees(45)
mpos = pygame.mouse.get_pos()
if pygame.key.get_mods() & \
pygame.KMOD_SHIFT and pygame.mouse.get_pressed()[2]:
p = self.flipyv(Vec2d(*mpos))
self.poly_points.append(p)
hit = self.space.point_query_nearest(
self.flipyv(Vec2d(*mpos)), 0, pm.ShapeFilter()
)
if hit != None:
self.shape_to_remove = hit.shape
else:
self.shape_to_remove = None
### Update physics
if self.run_physics:
x = 1
dt = 1.0 / 60.0 / x
for x in range(x):
self.space.step(dt)
for ball in self.balls:
# ball.body.reset_forces()
pass
for poly in self.polys:
# poly.body.reset_forces()
pass
### Draw stuff
self.draw()
### Check for objects outside of the screen,
# we can remove those Balls
xs = []
for ball in self.balls:
if (
ball.body.position.x < -1000
or ball.body.position.x > 1000
or ball.body.position.y < -1000
or ball.body.position.y > 1000
):
xs.append(ball)
for ball in xs:
self.space.remove(ball, ball.body)
self.balls.remove(ball)
# Polys
(略)
### Tick clock and update fps in title
self.clock.tick(50)
pygame.display.set_caption("fps: " + str(self.clock.get_fps()))
図9 create_ball()メソッドのコード
def create_ball(self, point, mass=1.0, radius=15.0):
moment = pm.moment_for_circle(mass, 0.0, radius)
ball_body = pm.Body(mass, moment)
ball_body.position = Vec2d(*point)
ball_shape = pm.Circle(ball_body, radius)
ball_shape.friction = 1.5
ball_shape.collision_type = COLLTYPE_DEFAULT
self.space.add(ball_body, ball_shape)
return ball_shape
図10 PhysicsDemoクラスのコンストラクタのコード
def __init__(self):
self.running = True
### Init pygame and create screen
pygame.init()
self.w, self.h = 600, 600
self.screen = pygame.display.set_mode((self.w, self.h))
self.clock = pygame.time.Clock()
### Init pymunk and create space
self.space = pm.Space()
self.space.gravity = (0.0, -900.0)
### Walls
self.walls = []
self.create_wall_segments([(100, 50), (500, 50)])
## Balls
# balls = [createBall(space, (100,300))]
self.balls = []
### Polys
self.polys = []
h = 10
for y in range(1, h):
# for x in range(1, y):
x = 0
s = 10
p = Vec2d(300, 40) + Vec2d(0, y * s * 2)
self.polys.append(self.create_box(p, size=s, mass=1))
self.run_physics = True
### Wall under construction
self.wall_points = []
### Poly under construction
self.poly_points = []
self.shape_to_remove = None
self.mouse_contact = None
図11 playground.pyファイルの変更差分
133a134,135
> pygame.draw.circle(self.screen,
> pygame.Color("skyblue"), p, int(r), 0)
140c142
< pygame.draw.lines(self.screen, pygame.Color("lightgray"), False, [pv1, pv2])
---
> pygame.draw.lines(self.screen, pygame.Color("gray"), False, [pv1, pv2], 3)
148c150,151
< color = pygame.Color("green")
---
> color = pygame.Color("forestgreen")
> color2 = pygame.Color("lightgreen")
151c154,156
< pygame.draw.lines(self.screen, color, False, ps)
---
> color2 = pygame.Color("orange")
> pygame.draw.polygon(self.screen, color2, ps, 0)
> pygame.draw.polygon(self.screen, color, ps, 2)
※「[」を「[」に、「< 」を「<」に置き換えてください。