【Blender 2.8 x Python API】ボールで壁を破壊する物理演算

f:id:Shoto:20200610233742g:plain

ボールで壁を破壊する物理演算をPython APIだけで実現する。 前準備として、Blenderを起動して、 LayoutタブからScriptingタブに変えて、新規をクリックしておく。

Python API 実装解説

BlenderPython API bpyをimport。 Blender 2.8からかなり仕様が変わっている。 bpyを操作に調べると分かるが、 ブログや記事に書いてあるコードが動かないことが多い。

# import libraries
import bpy

操作は何度か繰り替えすことを想定している。 なので、繰り返す度に環境をリフレッシュさせるため、全選択&削除を実行。

# Reset objects.
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(True)

light_add()でライトをセット。 以下のパラメーターは、配置するボールと壁に当たるように調整した結果。 と言いつつ、上図はソリッドモードなので関係ないが。 ライトの当たり具合を確認するには、レンダープレビューにする必要がある。

# Add light
bpy.ops.object.light_add(type='AREA', radius=10, location=(0, -20, 20))
bpy.context.object.data.energy = 10000
bpy.context.object.rotation_euler[0] = 0.52

primitive_plane_add()で平面を設置。 object_add(type='PASSIVE')で崩壊する壁を受け止めるようになる。

# Add plane.
bpy.ops.mesh.primitive_plane_add(enter_editmode=False, location=(0, 0, 0))
bpy.ops.transform.resize(value=(10, 40, 1))
bpy.ops.rigidbody.object_add(type='PASSIVE')

primitive_cube_add()でキューブを積み重ねて壁を作る。 object_add(type='ACTIVE')とする事で、平面とは異なり、ボールの衝突によってキューブが物理演算に従って移動する。

# Add wall.
idx = 0
for x in range(-5, 5):
    for y in range(-1, 0):
        for z in range(0, 5):
            bpy.ops.mesh.primitive_cube_add(size=1, enter_editmode=False, location=(x+0.5,y+0.5,z+0.5))
            bpy.ops.rigidbody.object_add(type='ACTIVE')

primitive_uv_sphere_add()で球をセット。 キューブと同様、object_add(type='ACTIVE')とし、ボールを壁へ衝突させる物理演算を実現する。

# Add ball.
bpy.ops.mesh.primitive_uv_sphere_add(radius=1, enter_editmode=False, location=(0, -15, 2))
bpy.ops.rigidbody.object_add(type='ACTIVE')

ボールが動き出すための時間をセット。 30フレームで1秒。 これが0フレーム(0秒)だと、開始時から壁が破壊され始めて、何が起きたかよく分からなくなる。

# Set animation and physical calculation
n_stay = 30

n_stay後のボールの動きをセットする。 まずは加速をアニメーションで作成。
(1)frame_set()でフレームを移動
(2) ボールのIDであるSphereを選択、このIDは球のデフォルトの名前
(3) keyframe_insert()でボールをセットした位置をキーフレームに登録する位置としてセット
(4) アニメーションをONに
(5) keyframe_insert(data_path='kinematic')で上記の内容をキーフレームに登録

bpy.context.scene.frame_set(n_stay+1)  # (1)
obj = bpy.context.scene.objects.get('Sphere') # (2)
obj.keyframe_insert(data_path='location') # (3)
bpy.context.object.rigid_body.kinematic = True  # (4) Animation ON
obj.rigid_body.keyframe_insert(data_path='kinematic')  # (5)

ボールの加速の終わりをセット。 設定方法は上記とほぼ同じ。 frame_set()でスタート位置より+4のフレームに移動し、 Sphereを選択して、 Y軸だけ-15mから12mに移動して、 keyframe_insert(data_path='location')でキーフレームに登録する。 4フレームで27mを進んだことになるので、 この時点で秒速202.5m(= 27m / (4/30)s)出ていることになる。

bpy.context.scene.frame_set(n_stay+5)
obj = bpy.context.scene.objects.get('Sphere')
obj.location = (0, 12, 2)
obj.keyframe_insert(data_path='location')

最後に物理演算の設定。 202.5mになった次のフレームで、アニメーションを解除する。 キーフレームを移動して、ボールを選択するまでは一緒。 その後、kinematicをFalseにした状態で、 keyframe_insert(data_path='kinematic')でキーフレームを登録することで、 アニメーションが解除になり、202.5m/sで放たれたボールが物理演算に従ってシミュレートされる。

bpy.context.scene.frame_set(n_stay+6)
obj = bpy.context.scene.objects.get('Sphere')
bpy.context.object.rigid_body.kinematic = False  # (1)
obj.rigid_body.keyframe_insert(data_path='kinematic')  # Animation OFF  (2)

フレームを1に戻す。 Layoutタブに戻して、タイムラインウィンドウで再生を押すだけでシミュレートできる。

bpy.context.scene.frame_set(1)

全コード

# import libraries
import bpy

# Reset objects.
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(True)

# Add light
bpy.ops.object.light_add(type='AREA', radius=10, location=(0, -20, 20))
bpy.context.object.data.energy = 10000
bpy.context.object.rotation_euler[0] = 0.52

# Add plane.
bpy.ops.mesh.primitive_plane_add(enter_editmode=False, location=(0, 0, 0))
bpy.ops.transform.resize(value=(10, 40, 1))
bpy.ops.rigidbody.object_add(type='PASSIVE')

# Add wall.
idx = 0
for x in range(-5, 5):
    for y in range(-1, 0):
        for z in range(0, 5):
            bpy.ops.mesh.primitive_cube_add(size=1, enter_editmode=False, location=(x+0.5,y+0.5,z+0.5))
            bpy.ops.rigidbody.object_add(type='ACTIVE')

# Add ball.
bpy.ops.mesh.primitive_uv_sphere_add(radius=1, enter_editmode=False, location=(0, -15, 2))
bpy.ops.rigidbody.object_add(type='ACTIVE')

# Set animation and physical calculation
n_stay = 30

bpy.context.scene.frame_set(n_stay+1)
obj = bpy.context.scene.objects.get('Sphere')
obj.keyframe_insert(data_path='location')
bpy.context.object.rigid_body.kinematic = True  # Animation ON
obj.rigid_body.keyframe_insert(data_path='kinematic')

bpy.context.scene.frame_set(n_stay+5)
obj = bpy.context.scene.objects.get('Sphere')
obj.location = (0, 12, 2)
obj.keyframe_insert(data_path='location')

bpy.context.scene.frame_set(n_stay+6)
obj = bpy.context.scene.objects.get('Sphere')
bpy.context.object.rigid_body.kinematic = False  # Animation OFF
obj.rigid_body.keyframe_insert(data_path='kinematic')

bpy.context.scene.frame_set(1)

参考文献