cymunkを使って雪を降らせる - Kivy Advent Calendar 2013
cymunkは2次元物理エンジンChipMunk (http://chipmunk-physics.net/) のCython実装で、KivyLauncerにもデフォルトで入っています。
https://cymunk.readthedocs.org/en/latest/
オブジェクトの重力、衝突、弾性の計算処理をやってくれるもので、cymunkのサンプルコードではカラフルなボールを弾ませています。
このサンプルコードを
- カラーボールではなく小さい白い結晶にする
- タッチしなくてもランダムで発生させる
- 重力を弱くして、左右に揺れるようにする
- 弾性はほぼゼロにする
- 地面等に着いたら跡は残しつつ計算対象から外す
とすれば、雪が降り積もってるようにみえないかなあ…と思ったので実験してみました。
結果は…
なんか「地面等に着いたら」の処理をきちんとやっていないので、地面を雪玉が転がってたりして。
あまり雪っぽくないかな。
(/sdcard/kivy/letitsnow/)
android.txt | お約束 |
flake.png | 雪の結晶みたいな画像 (なければcircle.pngをリネームして) |
main.py | スクリプト本体 |
(android.txt)
title=letitsnow author=cheeseshop orientation=landscape
(main.py)
from kivy.clock import Clock from kivy.app import App from kivy.graphics import Color, Rectangle from kivy.uix.widget import Widget from kivy.properties import DictProperty, ListProperty from kivy.core.image import Image from cymunk import Space, Segment, Vec2d, Body, Circle from random import random from kivy.lang import Builder from os.path import dirname, join class Snow(Widget): cbounds = ListProperty() cmap = DictProperty({}) flist = ListProperty() def __init__(self, **kwargs): super(Snow, self).__init__(**kwargs) self.init_space() self.bind(size=self.update_bounds, pos=self.update_bounds) self.texture = Image(join(dirname(__file__), 'flake.png')).texture Clock.schedule_interval(self.step, 1 / 30.) def init_space(self): self.space = space = Space() space.iterations = 30 space.gravity = (0, -300) space.sleep_time_threshold = 0.5 space.collision_slop = 0.5 for x in xrange(4): seg = Segment(space.static_body, Vec2d(0, 0), Vec2d(0, 0), 0) seg.elasticity = 0.0 self.cbounds.append(seg) space.add_static(seg) self.update_bounds() def update_bounds(self, *largs): a, b, c, d = self.cbounds x0, y0 = self.pos x1 = self.right y1 = self.top self.space.remove_static(a) self.space.remove_static(b) self.space.remove_static(c) self.space.remove_static(d) a.a = (x0, y0); a.b = (x1, y0) b.a = (x1, y0); b.b = (x1, y1) c.a = (x1, y1); c.b = (x0, y1) d.a = (x0, y1); d.b = (x0, y0) self.space.add_static(a) self.space.add_static(b) self.space.add_static(c) self.space.add_static(d) def step(self, dt): if random() < 0.4: self.add_flake(random() * self.right, self.top - 20, 5) if random() < 0.2: self.space.gravity = (100-random()*200, -50*random()-50) self.space.step(1 / 30.) self.update_objects() def update_objects(self): for body, obj in self.cmap.iteritems(): p = body.position radius, color, rect = obj rect.pos = p.x - radius, p.y - radius rect.size = radius * 2, radius * 2 def add_flake(self, x, y, radius): body = Body(100, 1e9) body.position = x, y circle = Circle(body, radius) circle.elasticity = 0.1 self.space.add(body, circle) with self.canvas.before: color = Color(1, 1, 1) rect = Rectangle( texture=self.texture, pos=(self.x - radius, self.y - radius), size=(radius * 2, radius * 2)) self.cmap[body] = (radius, color, rect) self.flist.append((body, circle) ) if len(self.flist) > 300: body, circle = self.flist.pop(0) self.space.remove(body) self.space.remove(circle) class SnowApp(App): def build(self): return Snow() if __name__ == '__main__': SnowApp().run()