【Blender 2.8 x Python API】ボールで壁を破壊する物理演算
ボールで壁を破壊する物理演算をPython APIだけで実現する。 前準備として、Blenderを起動して、 LayoutタブからScriptingタブに変えて、新規をクリックしておく。
Python API 実装解説
BlenderのPython 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)